The DIG Network APT repository + its AWS infrastructure. Ubuntu/Debian users
install the DIG ecosystem with apt — dig-node (the node service, run via systemd)
and digstore (the content-addressable store CLI) — from a flat, GPG-signed apt
repository served at https://apt.dig.net.
# 1. Trust the DIG signing key
curl -fsSL https://apt.dig.net/dig.gpg | sudo gpg --dearmor -o /usr/share/keyrings/dig.gpg
# 2. Add the apt source (signed-by pins it to the DIG key)
echo "deb [signed-by=/usr/share/keyrings/dig.gpg] https://apt.dig.net stable main" \
| sudo tee /etc/apt/sources.list.d/dig.list
# 3. Install
sudo apt update
sudo apt install dig-node digstore
# 4. The node runs as a systemd service
systemctl status dig-node| Package | Installs | Service |
|---|---|---|
dig-node |
/usr/bin/dig-node + dig-node.service |
yes — systemctl enable --now dig-node (loopback 127.0.0.1:9778, runs as the dig-node system account, cache at /var/lib/dig-node) |
digstore |
/usr/bin/digstore + /usr/bin/digs |
no — just the CLI on PATH |
digs is a first-class alias binary for digstore — digs <args> behaves identically
to digstore <args>. It ships in the same upstream release tarball as digstore and
is installed alongside it (see PKG_digstore_EXTRA_BINS in config.sh).
Configure the node with systemctl edit dig-node (env: DIG_NODE_HOST,
DIG_NODE_PORT, DIG_RPC_UPSTREAM). DIG_NODE_HOST / DIG_NODE_PORT are the
dig-node binary's stable env-var names — the unit's Environment= lines match them.
See the docs: https://docs.dig.net/docs/run-a-node.
| Path | What |
|---|---|
/llms.txt |
Agent entry point (site map + install contract) |
/sitemap.xml |
XML sitemap (one public page) |
/robots.txt |
Crawl policy — full indexing allowed, points at sitemap.xml |
/feed.xml |
Atom feed listing the currently published packages (generated at build time by generate_feed() in packaging/repo/generate-repo.sh) |
/dig.gpg |
The signing public key |
/dists/stable/Release |
Repo metadata |
upstream GitHub releases this repo (CI) AWS
───────────────────────── ────────────── ───
DIG-Network/digstore ─┐ packaging/build-deb.sh ─┐
DIG-Network/dig-node ─┘ (download asset → lay │ make repo → S3 apt-dig-net
out deb → dpkg-deb) ├─ pool/main/*.deb └─ CloudFront
packaging/repo/ │ dists/stable/... └─ apt.dig.net
generate-repo.sh │ (Packages/Release/
(Packages/Release + │ InRelease + dig.gpg)
GPG sign) ┘
packaging/config.sh— the single source of truth: which repo publishes each package, the per-arch release-asset name template, the Debian control metadata, and whether the package installs a service. Add a package = edit this file.packaging/build-deb.sh— resolves the latest release of each package's repo, downloads the per-arch asset, lays out the.deb(control + binary on PATH + systemd unit/maintainer scripts for the service package), and builds it withdpkg-deb. Missing assets are skipped, never fatal (see "Upstream asset contract").packaging/repo/generate-repo.sh— turns the pool of.debsinto a flat, GPG-signed apt repo (Packages/Packages.gz/Release/Release.gpg/InRelease) forstable main, exports the signing public key todig.gpg, and writesfeed.xml(an Atom feed of the currently published packages, viagenerate_feed()).site/— the static landing page (index.html),llms.txt,sitemap.xml, androbots.txt; copied verbatim into the repo root bymake repo.infra/— Terraform for the S3 bucket + CloudFront + Route53 + ACM..github/workflows/deploy.yml— build → sign → S3 sync → CloudFront invalidate..github/workflows/ci.yml— shellcheck + actionlint + terraform fmt/validate + the test suite (real deb build + signed-repo round-trip).
The packages are built from the upstream GitHub release assets, resolved at build
time. The asset names packaging expects are declared per package in config.sh:
| Package | Repo | Expected asset (per arch) |
|---|---|---|
digstore |
DIG-Network/digstore |
digstore-<ver>-{x86_64,aarch64}-unknown-linux-gnu.tar.gz (contains digstore + digs) |
dig-node |
DIG-Network/dig-node |
dig-node-<ver>-linux-{x64,arm64} (bare binary) |
Asset availability:
DIG-Network/digstore'spublish-binary.ymlpublishes a raw per-archdigstore-<ver>-{x86_64,aarch64}-unknown-linux-gnu.tar.gzrelease asset (alongside theDigStore-Setup-*.AppImage/.dmg/.exeinstallers) — which is whatconfig.shtargets. UntilDIG-Network/digstore#16lands, that tarball contains onlydigstore;digs(a first-class alias binary,digs <args>==digstore <args>) is resolved as an optional extra (PKG_digstore_EXTRA_BINS) and skipped non-fatally on any release that predates it, so the pipeline stays green either way — once the tarball carriesdigs, packaging picks it up with no code change.DIG-Network/dig-node'srelease.ymlpublishes rawdig-node-<ver>-linux-{x64,arm64}binaries on a tag — which is whatconfig.shtargets. (Nolinux-arm64asset is published yet, so arm64 is skipped non-fatally; see the note below.)
The build resolves each template; if the asset is absent it skips that package
(non-fatal), and the apt repo is published with whatever debs DID build. The pipeline
stays green. When upstream attaches matching assets, packaging picks them up with no
code change. To point at a different source without editing config.sh, set per-run
env overrides in CI: DIGSTORE_TAG, DIGSTORE_ASSET_TEMPLATE, DIG_NODE_TAG,
DIG_NODE_ASSET_TEMPLATE.
arm64is best-effort: an arch with no matching asset is skipped the same way.
Requires a Linux host (or WSL) with dpkg-dev (dpkg-deb), apt-utils
(apt-ftparchive), and gnupg.
make test # run the test suite (self-skips legs whose tooling is absent)
make lint # shellcheck + actionlint + terraform fmt/validate (each optional)
make debs # download upstream assets + build every available .deb into dist/pool/main
make repo GPG_FPR=<fingerprint> # assemble + sign the repo into dist/ (omit GPG_FPR for unsigned)tests/test_version_resolution.sh— version/tag normalisation + asset-name resolution (pure logic; runs anywhere).tests/test_deb_layout.sh—DEBIAN/controlrendering + a realdpkg-debbuild of a service package, asserting the binary lands on PATH, the unit ships + enables, and the maintainer scripts are present.tests/test_repo_metadata.sh— a realapt-ftparchivepool scan +Releasegeneration + GPG sign and verify round-trip (provesInRelease/Release.gpgverify against the publisheddig.gpg).
The deploy is push-to-main (mirrors dig.net / status.dig.net): build the debs,
sign the repo, aws s3 sync dist s3://<bucket> --delete, aws cloudfront create-invalidation. Auth is GitHub OIDC (no stored AWS keys).
It has two no-op gates so it is green before the infra/secret exist:
- Signing key — without the
APT_GPG_PRIVATE_KEYsecret the repo is built unsigned and the S3 sync is skipped (an unsigned repo is useless to apt). - Infra — without the repo vars
APT_S3_BUCKET,APT_CLOUDFRONT_DISTRIBUTION_ID,CI_DEPLOY_ROLE_ARNthe sync is skipped (build still verified).
Nothing here is provisioned yet. Until it is, the deploy builds + verifies and no-ops cleanly.
1. GPG signing key (secret APT_GPG_PRIVATE_KEY)
# Generate an ed25519 signing key (no passphrase, for CI), export the PRIVATE key,
# and store it as the repo/org secret APT_GPG_PRIVATE_KEY (ascii-armored).
cat > keygen <<'EOF'
%no-protection
Key-Type: eddsa
Key-Curve: ed25519
Key-Usage: sign
Name-Real: DIG Network
Name-Email: packages@dig.net
Expire-Date: 0
%commit
EOF
gpg --batch --gen-key keygen
FPR=$(gpg --list-secret-keys --with-colons | awk -F: '/^fpr:/{print $10; exit}')
gpg --armor --export-secret-keys "$FPR" > apt-signing-private.asc
gh secret set APT_GPG_PRIVATE_KEY --repo DIG-Network/apt.dig.net < apt-signing-private.asc
# Keep apt-signing-private.asc OFFLINE/secret; CI exports the matching PUBLIC key
# to /dig.gpg automatically. Never commit either.2. AWS infra — apply the Terraform (or create equivalently with the AWS CLI):
cd infra
terraform init
terraform apply # defaults target bucket apt-dig-net, the *.dig.net cert + zone
# Note the outputs: s3_bucket, cloudfront_distribution_idThis creates: S3 bucket apt-dig-net (private, OAC), a CloudFront distribution
(reusing the *.dig.net ACM cert aafcd24b-…, with caching disabled for dists/* +
dig.gpg so apt update is never stale), and the apt.dig.net A/AAAA aliases in the
dig.net Route53 zone.
3. OIDC deploy role + repo vars — mirror the other sites' least-privilege role
(s3:*Object/s3:ListBucket on apt-dig-net, cloudfront:CreateInvalidation on the
distribution; trust repo:DIG-Network/apt.dig.net on main). Then set the repo vars:
gh variable set APT_S3_BUCKET --repo DIG-Network/apt.dig.net --body apt-dig-net
gh variable set APT_CLOUDFRONT_DISTRIBUTION_ID --repo DIG-Network/apt.dig.net --body <dist-id>
gh variable set CI_DEPLOY_ROLE_ARN --repo DIG-Network/apt.dig.net --body <role-arn>Once the secret + vars exist, the next push to main publishes the signed repo and
https://apt.dig.net goes live.
This is a public repo. No secrets live in the code: the GPG private key is the
CI secret APT_GPG_PRIVATE_KEY (imported into a scratch keyring at deploy time and
never written to the repo output — only the public key is exported to /dig.gpg),
and AWS access is via OIDC (no stored keys). Never print or commit a private key.