Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Installing a release

This page is for operators who run Telcoin Network on Linux x86_64. Each release is a signed git tag plus these files on the tag’s GitHub release:

FileContent
telcoin-network-<TAG>-x86_64-unknown-linux-gnu.tar.gzThe telcoin-network binary, with LICENSE-APACHE, LICENSE-MIT and NOTICE
IMAGE_DIGESTThe linux/amd64 image at ghcr.io/telcoin-association/telcoin-network, named by its content digest
SHA256SUMSThe SHA-256 of the tarball, of IMAGE_DIGEST, and of the network’s three chain-config files
SHA256SUMS.ascA maintainer’s detached OpenPGP signature over SHA256SUMS

There are no release builds for other platforms. On those, build from source as the README describes.

Release channels

The tag name sets the network, the build features and the image tags.

TagNetworkBuild featuresImage tags
vX.Y.Zmainnetnone:vX.Y.Z, :latest
vX.Y.Z-rcNmainnet release candidatenone:vX.Y.Z-rcN only
vX.Y.Z-adiriAdiri testnetadiri:vX.Y.Z-adiri, :adiri
vX.Y.Z-adiri-rcNAdiri release candidateadiri:vX.Y.Z-adiri-rcN only

Running with --chain adiri needs a build with the adiri feature, so Adiri nodes use the -adiri releases. Release candidates are for testing and do not belong in production. :latest and :adiri move to the newest release in their channel when it is published, so deployments should pin the image digest instead (see Path B). GitHub marks every Adiri release and every release candidate as a pre-release, so the repository’s “Latest” release never points at one of them. Release notes lists the changes in each release.

What the signature proves

SHA256SUMS has five lines: IMAGE_DIGEST, which names the image by its content digest, the tarball, and the network’s committee.yaml, genesis.yaml and parameters.yaml by their path in the repository, under chain-configs/testnet/ for an -adiri tag and chain-configs/mainnet/ for any other. One signature over SHA256SUMS therefore covers the binary, the image and the chain configuration. The blocks below extract the chain-config files from the tag, so Path A checks all five lines and Path B checks every line but the tarball’s. SHA256SUMS.asc is made with a maintainer’s OpenPGP signing key, held on a YubiKey, which it never leaves. The same key signs the git tag. The keys allowed to sign are in .github/maintainer-gpg-keys/ on main, and their fingerprints are in the maintainer release keys table in SECURITY.md.

A good signature proves that a maintainer on that list approved these exact files. It does not prove that the binary was built from the tagged source. Release builds are not reproducible, and releases have no SBOM and no third-party build provenance. The Commit SHA that --version prints is written in by the build, so it names the commit the build was given; it is not evidence that the binary came from that commit. Before a release is published, the maintainers’ tooling checks that the binary in the tarball is byte-identical to the one in the image and that --version names the tagged commit. Image tags such as :adiri are not signed; only the digest in IMAGE_DIGEST is.

Before you start

You need curl, git and GnuPG (gpg and gpgv), plus Docker for Path B. On Debian or Ubuntu:

sudo apt-get install -y curl git gnupg

Start each path in a new, empty directory and run its commands in one shell. The first command of each block stops on a directory that is not empty, so a file left from an earlier download is never checked in place of this release’s. Set TAG on the first line to the release you are installing.

Both paths take the maintainer keys from main, never from the release’s tag. A tag therefore cannot bring its own key, and a key that is revoked or removed on main stops verifying at once. The tag is fetched into the same checkout by its full name, refs/tags/<TAG>, because git clone --branch <TAG> prefers a branch of the same name, if one exists, and says nothing.

Path A: tarball

In a new, empty directory, these commands clone main for the maintainer keys, fetch the tag, extract the chain-config files from it, download the four release files, stopping at the first download that fails, check the signature over SHA256SUMS, and check every hash in it:

TAG=v0.17.0-adiri
REPO=Telcoin-Association/telcoin-network
BASE="https://github.com/$REPO/releases/download/$TAG"
if [ -n "$(ls -A)" ]; then echo "Run these commands in a new, empty directory" >&2; false; fi &&
git clone --quiet --depth 1 --branch main "https://github.com/$REPO.git" tn-main &&
git -C tn-main fetch --quiet --depth 1 origin "refs/tags/$TAG:refs/tags/$TAG" &&
git -C tn-main archive "refs/tags/$TAG" chain-configs | tar -xf - &&
for k in tn-main/.github/maintainer-gpg-keys/*.asc; do gpg --dearmor < "$k"; done > tn-release-keys.gpg &&
gpg --show-keys --with-fingerprint tn-main/.github/maintainer-gpg-keys/*.asc &&
curl -fsSL --fail-early --remote-name-all "$BASE/SHA256SUMS" "$BASE/SHA256SUMS.asc" "$BASE/IMAGE_DIGEST" "$BASE/telcoin-network-$TAG-x86_64-unknown-linux-gnu.tar.gz" &&
gpgv --status-fd 1 --keyring ./tn-release-keys.gpg SHA256SUMS.asc SHA256SUMS |
awk '$2 ~ /^(BADSIG|ERRSIG|EXPSIG|EXPKEYSIG|REVKEYSIG)$/ { bad = 1 } $2 ~ /^(GOODSIG|BADSIG|ERRSIG|EXPSIG|EXPKEYSIG|REVKEYSIG)$/ { s = $2; print $2 } $2 == "VALIDSIG" && s == "GOODSIG" { print "signed by primary key " $NF; n++ } END { exit (n && !bad) ? 0 : 1 }' &&
sha256sum --check SHA256SUMS &&
echo "Signature and hashes verified"

Each command runs only if the one before it passed. Expect:

  • GOODSIG, then signed by primary key followed by a fingerprint;
  • sha256sum to print OK after IMAGE_DIGEST, after the three chain-configs/ files and after telcoin-network-<TAG>-x86_64-unknown-linux-gnu.tar.gz;
  • Signature and hashes verified as the last line.

gpgv prints Good signature from and exits 0 even when the signing key is revoked or expired, so the block reads gpgv’s status lines instead: it counts only a GOODSIG, and it fails when any signature in SHA256SUMS.asc is BADSIG, ERRSIG, EXPSIG, EXPKEYSIG or REVKEYSIG, even if another one is good. The release pipeline applies the same rule before a maintainer signs and before a release is published. If the last line Signature and hashes verified is missing, the files are not verified; stop, do not extract or run anything from this release, and report it. The lines above it show which check failed, and When verification fails explains each one.

Chain-config files

SHA256SUMS lists the network’s committee.yaml, genesis.yaml and parameters.yaml by their path in the repository: chain-configs/testnet/ for an -adiri tag and chain-configs/mainnet/ for any other. These are the files built into the release’s binary for --chain. The block extracts them from the tag into chain-configs/, so sha256sum checks them against their signed hashes, and you can use these copies. To check copies you already have, run this from the directory that holds their chain-configs/ folder, such as the root of a checkout of the tag, with the path of the verified SHA256SUMS:

grep '  chain-configs/' /path/to/SHA256SUMS | sha256sum --check

It must print OK for all three files.

Check the key fingerprints

The keys come from main, so the tag cannot change them. Check that they are the maintainers’ keys: every primary key fingerprint that gpg --show-keys printed, including the one after signed by primary key, must appear in both of these places:

  • the maintainer release keys table in tn-main/SECURITY.md, from the same main checkout (also on GitHub), with a Status that is not revoked;
  • the maintainer’s GitHub account at https://github.com/<handle>.gpg, where <handle> is the key file’s name without .asc.

This loop prints the keys GitHub has for each handle:

for k in tn-main/.github/maintainer-gpg-keys/*.asc; do
  h=$(basename "$k" .asc)
  echo "== $h"
  curl -fsSL "https://github.com/$h.gpg" | gpg --show-keys --with-fingerprint
done

If a fingerprint is missing from either place, is different, or has the Status revoked, stop and report it.

Check the binary

Run this only after the last line was Signature and hashes verified and the fingerprints matched:

tar -xzf "telcoin-network-$TAG-x86_64-unknown-linux-gnu.tar.gz" &&
cd "telcoin-network-$TAG-x86_64-unknown-linux-gnu" &&
./telcoin-network --version &&
git -C ../tn-main rev-parse "refs/tags/$TAG^{commit}"

The tarball holds one directory, telcoin-network-<TAG>-x86_64-unknown-linux-gnu/, with telcoin-network, LICENSE-APACHE, LICENSE-MIT and NOTICE in it. The --version output must include these lines:

  • telcoin-network-cli Version: X.Y.Z as the first line, where X.Y.Z is the tag without its leading v and without -adiri or -rcN (telcoin-network-cli Version: 0.17.0 for v0.17.0-adiri);
  • Commit SHA: followed by the commit that git rev-parse printed, which is the commit the tag points to;
  • Build Features: with nothing after it for a mainnet tag (vX.Y.Z or vX.Y.Z-rcN); for an -adiri tag, adiri, possibly with faucet, which the adiri feature implies, and nothing else.

Releases cut before this process print Version: 0.1.0 after the program name.

Install the binary:

sudo install -m 0755 telcoin-network /usr/local/bin/telcoin-network

Record the tarball’s SHA-256 from SHA256SUMS and the --version output in your operator inventory, as the release and network update process asks.

Path B: Docker image

The image needs only three of the release files, SHA256SUMS, SHA256SUMS.asc and IMAGE_DIGEST, and the chain-config files from the tag. In a new, empty directory, run the Path A block without the tarball. Its last check drops only the tarball’s line from SHA256SUMS with grep, so sha256sum checks IMAGE_DIGEST and the three chain-config files and fails when one of them is missing:

TAG=v0.17.0-adiri
REPO=Telcoin-Association/telcoin-network
BASE="https://github.com/$REPO/releases/download/$TAG"
if [ -n "$(ls -A)" ]; then echo "Run these commands in a new, empty directory" >&2; false; fi &&
git clone --quiet --depth 1 --branch main "https://github.com/$REPO.git" tn-main &&
git -C tn-main fetch --quiet --depth 1 origin "refs/tags/$TAG:refs/tags/$TAG" &&
git -C tn-main archive "refs/tags/$TAG" chain-configs | tar -xf - &&
for k in tn-main/.github/maintainer-gpg-keys/*.asc; do gpg --dearmor < "$k"; done > tn-release-keys.gpg &&
gpg --show-keys --with-fingerprint tn-main/.github/maintainer-gpg-keys/*.asc &&
curl -fsSL --fail-early --remote-name-all "$BASE/SHA256SUMS" "$BASE/SHA256SUMS.asc" "$BASE/IMAGE_DIGEST" &&
gpgv --status-fd 1 --keyring ./tn-release-keys.gpg SHA256SUMS.asc SHA256SUMS |
awk '$2 ~ /^(BADSIG|ERRSIG|EXPSIG|EXPKEYSIG|REVKEYSIG)$/ { bad = 1 } $2 ~ /^(GOODSIG|BADSIG|ERRSIG|EXPSIG|EXPKEYSIG|REVKEYSIG)$/ { s = $2; print $2 } $2 == "VALIDSIG" && s == "GOODSIG" { print "signed by primary key " $NF; n++ } END { exit (n && !bad) ? 0 : 1 }' &&
grep -v -F "  telcoin-network-$TAG-x86_64-unknown-linux-gnu.tar.gz" SHA256SUMS | sha256sum --check &&
echo "Signature and hashes verified"

Expect GOODSIG, then signed by primary key followed by a fingerprint, then OK after IMAGE_DIGEST and after the three chain-configs/ files, and Signature and hashes verified as the last line. If the last line Signature and hashes verified is missing, the files are not verified; stop, do not pull or run the image, and report it. Then check the key fingerprints as in Path A, and stop if one is missing, different or revoked.

Check the image

Run this only after the signature, the hash and the fingerprints have passed. It refuses an IMAGE_DIGEST that does not name the release repository by digest:

IMAGE_REF=$(cat IMAGE_DIGEST)
case "$IMAGE_REF" in
  ghcr.io/telcoin-association/telcoin-network@sha256:*) docker pull "$IMAGE_REF" && docker run --rm --network none --entrypoint /usr/local/bin/telcoin "$IMAGE_REF" --version ;;
  *) echo "IMAGE_DIGEST does not name a telcoin-network image by digest: $IMAGE_REF" >&2 ;;
esac

In the image the binary is /usr/local/bin/telcoin, and its --version output must show the same three lines as in Path A, with the commit that git -C tn-main rev-parse "refs/tags/$TAG^{commit}" prints.

Docker checks an image pulled by digest against that digest, so the image you pulled is the one the signature covers. Put the full content of IMAGE_DIGEST (ghcr.io/telcoin-association/telcoin-network@sha256:...) in compose files and systemd units, not a tag. The image is linux/amd64 only.

If you already pulled by tag, compare the two lines this prints; they must be equal:

docker image inspect --format '{{index .RepoDigests 0}}' "ghcr.io/telcoin-association/telcoin-network:$TAG"
cat IMAGE_DIGEST

Optional: check the tag signature

The tag carries its own signature from the same key. To check it in a throwaway keyring, from the directory you started in:

GNUPGHOME=$(mktemp -d)
export GNUPGHOME
gpg --quiet --import tn-main/.github/maintainer-gpg-keys/*.asc
git -C tn-main verify-tag "$TAG"
unset GNUPGHOME

git verify-tag prints Good signature from and a warning that the key is not certified with a trusted signature. The warning is expected in a fresh keyring; the fingerprint check is what ties the key to a maintainer.

When verification fails

Never extract, install or run anything from a release that failed one of these checks.

SymptomMeaningAction
The last line is not Signature and hashes verifiedA step of the block failed, and the lines above say whichFind that line in this table; do not extract or run anything from the release.
Run these commands in a new, empty directoryThe directory holds files already, possibly from another downloadStart again in a new, empty directory.
git fetch cannot find refs/tags/<TAG>, or curl fails with 404TAG is wrong, or the release is not published yetCheck TAG against the releases page.
BADSIG (gpgv prints BAD signature)SHA256SUMS changed after it was signedStop and report it.
ERRSIG (gpgv prints Can't check signature: No public key)The signing key is not in the allowlist on main: TAG is wrong, the files belong to another release, or the key was removed after this release was signedCheck TAG; if it is right, stop and report it.
REVKEYSIG (gpgv still prints Good signature)The signing key was revoked, for example after a YubiKey was lostStop and report it. A release signed before the revocation can no longer be verified against main, which is intended.
EXPKEYSIG (gpgv still prints Good signature)The signing key has expiredStop and report it. An older release signed before the expiry cannot be verified against main until a maintainer extends the key there.
EXPSIGThe signature itself has expiredStop and report it.
One of the rows above next to a GOODSIGAnother signature in SHA256SUMS.asc is bad, unverifiable, expired or revoked, and the block refuses the file as a wholeStop and report it.
sha256sum prints FAILEDA file differs from its signed hashCheck TAG and run the block again in a new, empty directory; if it fails again, stop and report it.
sha256sum prints FAILED open or read for a chain-configs/ filegit archive did not extract it from the tagCheck that the tag was fetched; if it was, stop and report it.
A fingerprint is missing from, or differs from, SECURITY.md or GitHub, or its SECURITY.md Status is revokedThe keys on main are not the current maintainer keysStop and report it.
IMAGE_DIGEST does not name a telcoin-network image by digestThe signed IMAGE_DIGEST points outside the release repositoryStop and report it.
Commit SHA differs from the tag’s commit, Build Features is not empty on a mainnet tag, or on an -adiri tag lacks adiri or lists anything but adiri and faucetThe binary does not match the releaseStop and report it.
exec format error, or Docker warns that the image platform does not match the hostThe release is built for Linux x86_64 onlyUse an x86_64 host, or build from source.
docker pull fails with denied or unauthorizedThe image is not publicly readableTell the maintainers.

Report a failed check through the security policy.