8.2 KiB
Verify your Morphit download
Morphit's source is public and pushed to three independent git hosts — the project's own Forgejo, plus GitHub and Codeberg (Forgejo mirrors every commit and signed tag to them automatically) — so no single host can quietly disappear or tamper with it. This page shows you how to prove that the code you got is the genuine, unmodified release.
Pick whichever matches how you got the code:
| You have… | Verify with | Proves |
|---|---|---|
| a git clone (from any mirror) | git verify-tag vX.Y.Z |
the release tag was GPG-signed by Morphit's key |
| the source tarball (from the release page) | verify-download.mjs (+ git verify-tag) |
the bytes match what @morphit anchored on-chain, tied to the GPG-signed release tag |
Both trace back to the same GPG key, whose fingerprint @morphit
publishes on the Blurt chain (a morphit_release_v1 operation with a
distribution block). The expected fingerprint and hash come from the
blockchain, not from the host you downloaded from — so a malicious
mirror can't serve you a bad file and a matching "expected" value on
its own web page.
Option A — verify a git clone (the signed tag)
If you cloned the repo from any of the three mirrors, verify the
release tag's signature. First import Morphit's public key (once) and
confirm its fingerprint against the one on the project's /security
page:
gpg --keyserver keyserver.ubuntu.com --recv-keys <FINGERPRINT>
Then, in your clone:
git fetch --tags
git verify-tag vX.Y.Z
git checkout vX.Y.Z
You want Good signature from Morphit's key. Because the commit and
the signed tag are byte-identical across Forgejo, GitHub, and Codeberg,
a tag that verifies is the genuine release no matter which mirror you
cloned from. That's the fully decentralized path — it needs no release
assets and no single host.
Option B — verify the source tarball
The release page carries the source tarball, its checksum, and the anchor CI recorded on-chain:
morphit-vX.Y.Z.tar.gz— the sourcemorphit-vX.Y.Z.tar.gz.sha256— its SHA-256distribution-anchor.env— the anchor CI wrote (the SHA-256 and the signing-key fingerprint that also went on-chain)morphit-vX.Y.Z.tar.gz.asc— a detached GPG signature, present only if the project signs tarballs in CI. The release tag is always GPG-signed regardless (Option A), so tarball signing is optional.
Step 1 — cross-check the on-chain anchor (always available)
@morphit publishes each release's hash, signing-key fingerprint, and
mirror list onto the Blurt chain (a morphit_release_v1 operation with
a distribution block). The bundled verifier reads that anchor
directly from a Blurt node and compares it to your file:
node scripts/verify-download.mjs morphit-vX.Y.Z.tar.gz
or pin the version you expect:
node scripts/verify-download.mjs morphit-vX.Y.Z.tar.gz --version X.Y.Z
It prints your file's SHA-256, fetches the on-chain anchor, and tells you plainly whether they match. On a match it also shows the GPG fingerprint the release was signed with (feeds Step 2), the mirror repos, and — if the release was pinned to IPFS — a content-addressed IPFS CID (the CID is the hash, so no gateway can serve altered bytes under it).
Because the expected hash and fingerprint come from the blockchain, not from the host you downloaded from, a malicious mirror can't serve you a bad file and a matching "expected" value on its own web page.
Note the two artifacts differ: this checks the canonical release
tarball from the release page. A source archive auto-generated by
GitHub or Codeberg from the tag has different bytes and won't match this
hash — for those, use Option A (git verify-tag) instead.
If it reports a mismatch, do not trust the download.
The verifier is deliberately tiny and dependency-free (only Node
built-ins). Read it — it's scripts/verify-download.mjs, about two
hundred lines — so you don't have to take even it on trust.
Step 2 — confirm the signing key
Step 1 ties your tarball's bytes to the fingerprint @morphit anchored
on-chain. To confirm that fingerprint really is Morphit's key, verify
the signed tag: clone any mirror and run Option A
(git verify-tag vX.Y.Z). A Good signature from that same fingerprint
closes the loop — the bytes match the chain, and the chain's key signed
the tag.
If the release page also carries a .asc, you can additionally check a
signature directly on the bytes:
# import the key once, and confirm its fingerprint against /security
gpg --keyserver keyserver.ubuntu.com --recv-keys <FINGERPRINT>
gpg --fingerprint <FINGERPRINT>
# then verify the tarball
gpg --verify morphit-vX.Y.Z.tar.gz.asc morphit-vX.Y.Z.tar.gz
sha256sum -c morphit-vX.Y.Z.tar.gz.sha256
A "Good signature" warning about the key not being certified is fine — that just means you haven't personally signed the key; the fingerprint match is what matters. If any check fails, stop — the file is not what Morphit published.
Pick a Blurt node
By default it tries a few public nodes. To choose your own:
MORPHIT_RPC=https://rpc.beblurt.com node scripts/verify-download.mjs morphit-vX.Y.Z.tar.gz
If a host is unreachable
The code lives on three git hosts, so if one is down, clone from
another and use Option A (git verify-tag) — the signed tag is
identical everywhere:
git clone https://codeberg.org/agorise/morphit.git # or the GitHub / Forgejo URL
cd morphit && git verify-tag vX.Y.Z
If the release was also pinned to IPFS, the verifier prints its CID. That
CID names a small release directory — the signed tarball plus its
.sha256/.asc, the release notes, and a metadata.json — so you can
browse it, read the notes, or pull the exact bytes and re-run the Option B
checks:
# list what's in the release directory
ipfs ls <CID>
# fetch the signed tarball out of it (its bytes are content-addressed, so
# this is the same file the on-chain SHA-256 covers)
ipfs get <CID>/morphit-vX.Y.Z.tar.gz -o morphit-vX.Y.Z.tar.gz
No ipfs installed? A raw CID resolves on any public gateway (the CID is
the hash, so the gateway cannot serve altered bytes):
curl -fsSLo morphit-vX.Y.Z.tar.gz https://ipfs.io/ipfs/<CID>/morphit-vX.Y.Z.tar.gz
curl -fsSL https://ipfs.io/ipfs/<CID>/metadata.json # version + sha256 + notes
Every Morphit instance re-hosts the current release over its own IPFS (Kubo) node, so this content stays reachable without depending on any commercial pinning service.
That CID is immutable — it only ever names that release. To always fetch
the latest release over IPFS, Morphit also publishes a stable IPNS
name (a k51… string, shown on the on-chain anchor as ipns_name and on
the download page). It resolves through a w3name-aware gateway —
dweb.link or w3s.link — to the newest release directory, which always
contains a stable-named morphit-latest.tar.gz:
# always the latest signed tarball, by name instead of by CID
curl -fsSLo morphit-latest.tar.gz https://dweb.link/ipns/<name>/morphit-latest.tar.gz
# ...then run the same Option B checks on it (the on-chain SHA-256 tells you
# which version you actually got). Browse https://dweb.link/ipns/<name>/ for
# the versioned filename, release notes, and metadata.json.
Use dweb.link or w3s.link for the IPNS name — not ipfs.io/ipns/…. The
name is published via w3name, which those gateways resolve; the DHT-only
ipfs.io/ipns path does not (a raw ipfs.io/ipfs/<CID> fetch works fine — this
caveat is only for the k51… name).
IPNS is a convenience for discovery only — it is a mutable pointer, so a
copy fetched this way is still only trustworthy once it passes the
source_sha256 + GPG checks above. The immutable ipfs_cid and the signed
tag remain the verification anchors.
Because the signed tag, the GPG signature, and the on-chain hash are all host-independent, code that passes verification is the genuine release no matter where you pulled it from.
Exit codes (for scripting verify-download.mjs)
0 verified · 1 MISMATCH (do not trust) · 2 usage error ·
3 couldn't reach any Blurt node · 4 no anchor found on chain.