Secrets¶
apt-charliek has one durable secret: the ed25519 GPG signing-only private key. It lives in three places:
| Where | Format | Purpose |
|---|---|---|
.secrets/apt-signing-key.asc |
armored | Local on-disk copy, synced across your laptops via envsecrets |
.secrets/apt-signing-key.fpr |
plain text | Fingerprint convenience file |
APT_SIGNING_KEY GitHub Actions secret |
armored | Consumed by publish.yml |
APT_SIGNING_KEY_FPR GitHub Actions secret |
plain text | Tells publish.sh which key in the imported keyring to use |
There is no copy in Google Secret Manager or any other vault. The two places (your laptops + GitHub Actions) are the security boundary. If both are compromised, an attacker has signing capability.
The pubkey at gs://apt.stridelabs.ai/pubkey.gpg is regenerated on every publish; it's derived from the secret, not separately stored.
One-time generation¶
KEY_UID='Charlie Knudsen apt repo <apt@stridelabs.ai>'
gpg --batch --pinentry-mode loopback --passphrase '' \
--quick-generate-key "$KEY_UID" ed25519 sign 0
# 'sign' = signing-only (no encryption capability)
# '0' = no expiration; --passphrase '' = no passphrase (CI consumes unattended)
# Match the key by user-id, not by listing all keys — if you have other keys
# in your keyring, a blind `awk … exit` would grab the wrong one.
FPR=$(gpg --list-secret-keys --with-colons "$KEY_UID" \
| awk -F: '/^fpr:/ { print $10; exit }')
[ -n "$FPR" ] || { echo "could not resolve fingerprint for $KEY_UID" >&2; exit 1; }
mkdir -p .secrets
gpg --armor --export-secret-keys "$FPR" > .secrets/apt-signing-key.asc
echo -n "$FPR" > .secrets/apt-signing-key.fpr
envsecrets push -m "Initial apt signing key"
# Upload to GitHub Actions
gh secret set APT_SIGNING_KEY --repo charliek/apt-charliek --body-file .secrets/apt-signing-key.asc
gh secret set APT_SIGNING_KEY_FPR --repo charliek/apt-charliek --body-file .secrets/apt-signing-key.fpr
Why ed25519, signing-only, no expiration:
- ed25519 keys are short, fast, and supported by every apt version we care about.
- Signing-only avoids accidentally using the same key for anything else.
- No expiration removes the "key expired and
apt updateis broken for every user" foot-gun. Indie scope; documented tradeoff.
On a new machine¶
git clone git@github.com:charliek/apt-charliek.git
cd apt-charliek
envsecrets pull
# .secrets/apt-signing-key.{asc,fpr} restored
To use the key locally (e.g. to sign a republish out-of-band):
Rotation¶
If you suspect the key is compromised, or just want to rotate on a schedule:
- Generate a new ed25519 key (same command as above) into a fresh
$GNUPGHOME. - Overwrite
.secrets/apt-signing-key.{asc,fpr}with the new key's exports. envsecrets push -m "Rotate apt signing key".- Update
APT_SIGNING_KEYandAPT_SIGNING_KEY_FPRGitHub secrets. - Trigger
publish.ymlviaworkflow_dispatchto publish the newpubkey.gpg. - Notify users to re-pull the pubkey:
curl -fsSL https://apt.stridelabs.ai/pubkey.gpg | \
sudo tee /etc/apt/keyrings/apt-charliek.gpg > /dev/null
sudo apt update
The old pubkey can stay published alongside the new one if you want a transition window, but for an indie repo the cleaner path is "swap, send the snippet, done."
Secrets the CI runner uses¶
| Secret | Set by |
|---|---|
GCP_WIF_PROVIDER |
infra/setup.sh prints the value; gh secret set it |
GCP_SA_EMAIL |
infra/setup.sh prints the value; gh secret set it |
APT_SIGNING_KEY |
gh secret set ... --body-file .secrets/apt-signing-key.asc |
APT_SIGNING_KEY_FPR |
gh secret set ... --body-file .secrets/apt-signing-key.fpr |
GITHUB_TOKEN |
Auto-provided by GitHub Actions for gh release download |
Source-repo secret¶
Source repos that fire repository_dispatch need an APT_DISPATCH_TOKEN secret. That's a separate fine-grained PAT scoped to charliek/apt-charliek only with Contents: write. Created and managed outside this repo. See Adding a Package.