How It Works¶
apt-charliek is a static apt repository: a tree of files in a Google Cloud Storage bucket, fronted by Cloudflare for HTTPS and caching. There is no server-side component.
Pipeline¶
Source repos (charliek/prox, charliek/shed, charliek/envsecrets, ...)
│
│ git push --tags v1.2.3
▼
Source repo's release.yaml
• GoReleaser builds binaries
• nfpm produces *.deb, attached to GitHub Release
• Final step fires repository_dispatch event_type=publish
│
▼
charliek/apt-charliek/.github/workflows/publish.yml
• WIF auth to GCP
• Import APT_SIGNING_KEY into ephemeral keyring
• rsync gs://apt.stridelabs.ai/pool → ./pool
• For each tracked package: gh release download → ./pool/main/<l>/<pkg>/
• apt-ftparchive packages → Packages, gzip
• apt-ftparchive release → Release
• gpg --clearsign → InRelease
• gpg --detach-sign --armor → Release.gpg
• Upload pool/ first (additive), then dists/ (atomic flip, InRelease last)
• Set Cache-Control: no-cache on dists/* (belt-and-suspenders for Cloudflare)
• Smoke test via docker run ubuntu:noble
│
▼
gs://apt.stridelabs.ai/ (public read, Object Versioning enabled)
│
▼
Cloudflare proxy at apt.stridelabs.ai
• SSL: Full
• Cache rules: dists/* bypass; pool/* edge-cache 1 month
│
▼
End user runs `apt update && apt install <pkg>`
End-to-end latency from git push --tags in a source repo to apt update showing the new version: ~3–5 minutes.
Security model¶
The trust boundary is GPG signing, not TLS.
- Every
ReleaseandInReleasefile indists/noble/is signed with an ed25519 key whose pubkey lives athttps://apt.stridelabs.ai/pubkey.gpg. - Users wire that pubkey into
signed-by=in their sources list. apt-secure refuses to use any metadata it can't verify against that key. - The GCS bucket is public-read by design — no secrets in transit, no auth needed.
- Cloudflare uses Full (not Strict) SSL because GCS's cert is
*.storage.googleapis.com, which doesn't matchapt.stridelabs.ai. Strict would fail the chain check; Full validates the chain but not the hostname. This is acceptable because GPG signing is the actual integrity guarantee.
Idempotency¶
The publish workflow is idempotent by construction. Each run:
- Pulls the current
pool/from GCS. - Re-scans every tracked package's latest GitHub release.
- Regenerates
Packages,Release,InReleasefrom scratch. - Uploads
pool/first, thendists/, withInReleaselast.
A missed repository_dispatch is not catastrophic — the next dispatch (or workflow_dispatch) republishes everything anyway. The client_payload is logged for observability but ignored for correctness.
What this is not¶
- Not a build system: source repos build their own debs and attach them to GitHub releases. apt-charliek only repackages.
- Not multi-distro: one suite (
noble), one libadwaita ABI baseline. Add a parallel suite if a future Ubuntu version needs different debs. - Not authenticated: public-read bucket, public-read repo. For private/auth'd packages, folio's Cloud Run + GCS pattern is the better template.
- Not mirrored: single origin. Adding mirroring would mean enabling
Acquire-By-Hash, generatingby-hash/directories, and pushing to additional buckets.