@automagik/genie is cosign keyless only. There is no long-lived private key, no hardware-backed offline key, no public-key fingerprint to pin. "Rotation" here means rotating the certificate-identity pin (workflow-path@ref, OIDC issuer, and provenance source-uri) that operators cross-check against SECURITY.md, the repository's .well-known/security.txt and the in-repo witnesses..github/workflows/sign-attest.yml, the workflow
the certificate names, is renamed or split. The Fulcio certificate SAN
embeds the workflow path, so a move changes the certificate identity
verifiers must accept.genie update also verifies a
second identity, which every binary embeds
(src/lib/delivery-evidence-verify.ts): the release's delivery evidence
must be signed by .github/workflows/release-publish.yml@refs/heads/main.
Renaming, splitting or moving that workflow, or signing it from another
ref, breaks genie update on every host already running a release,
because those binaries carry the old identity.
scripts/reconcile-release-assets.sh carries it too.automagik-dev/genie becomes something else.
Both the signer identity regexp and the SLSA provenance source-uri must
move atomically.certificate-oidc-issuer must change with it.| Constraint | Detail |
| Minimum approvers | Two Namastex security officers (independent GitHub accounts) |
| Pinning channels | SECURITY.md, the repository's .well-known/security.txt, the in-repo witnesses |
| Grace period | Minimum 72 hours in which the OLD and NEW identities both verify |
| Retirement test | scripts/verify-release.sh <tag> MUST verify a post-rotation release |
| Audit trail | Rotation PR signed by both officers; the Phase 1 tracking issue records the Filed by (GPG fingerprint). |
automagik-dev/genie titled
SIGNING_CERT_IDENTITY_<YYYYMMDD> using the
.github/ISSUE_TEMPLATE/signing-key-fingerprint.md template.certificate-identity-regexpcertificate-oidc-issuersource-uriSECURITY.md, the repository's
.well-known/security.txt and the in-repo witnesses
(scripts/check-fingerprint-pinning.sh checks them). If they already drift, stop and open an incident: rotation
cannot overlay a broken baseline..github/workflows/sign-attest.yml (if workflow path changes).scripts/check-fingerprint-pinning.sh checks
(.github/ISSUE_TEMPLATE/signing-key-fingerprint.md,
.github/cosign.pub, scripts/verify-release.sh, install.sh) and
the copies the release and update paths use, such as
src/genie-commands/update.ts, scripts/reconcile-release-assets.sh
and .github/workflows/release-publish.yml.
git grep -n 'sign-attest.*@refs/heads/main' lists every copy, tests
included.SECURITY.md so its pinned values match the new identity
byte-for-byte..well-known/security.txt in the repository, which is served
from its raw.githubusercontent.com URL.git commit -S with a GPG key that appears
on their GitHub profile. No CI job counts the co-authors, so reviewers
check for both verified signatures and refuse a single-officer PR.sign-attest.yml under the new
certificate identity. scripts/verify-release.sh <tag> MUST verify that
release; if it fails, the rotation is aborted and rolled back.SECURITY.md under a ## Previous pinning
heading so operators running the previous release can still verify it
with scripts/verify-release.sh.## Previous pinning section and is never deleted.scripts/verify-release.sh against the first post-rotation release, and
any operator still running a release signed before the rotation is
instructed to pull the new release.SECURITY.md drops the ## Previous pinning section.automagik-dev/genie-signing-drill repo (or a local fork pointing at a
fixture workflow), without touching the production signing identity.123456789101112131415161718192021222324252627282930313233343536373839# 1. Stand up a disposable fixture directory under /tmp. No production repos. # Run this from a clone of automagik-dev/genie: the drill rehearses the # rotation on a copy of its verification script. export DRILL=$(mktemp -d -t genie-signing-drill-XXXXXX) cp scripts/verify-release.sh "${DRILL}/verify-release.sh" cd "${DRILL}" # 2. Produce a fake signed release bundle using cosign against a throwaway # Fulcio identity. `cosign sign-blob --yes` will mint an ephemeral cert # from the drill operator's OIDC login (GitHub OAuth, short-lived). echo "drill tarball" > drill.tar.gz cosign sign-blob \ --yes \ --bundle drill.tar.gz.bundle \ drill.tar.gz # 3. Rehearse the rotation edit on the copy: set WORKFLOW_IDENTITY_REGEXP and # OIDC_ISSUER in ./verify-release.sh to the identity and the issuer of the # certificate that step 2 minted. # 4. Fabricate a minimal SLSA provenance file. Real provenance is generated # by slsa-github-generator; the dry-run uses a hand-rolled fixture to # exercise the local verify path, NOT to validate provenance contract. cat > drill.tar.gz.intoto.jsonl <<'EOF' {"drill": true} EOF # 5. Exercise the copy. Expected: the cosign step passes and the script stops # at the SLSA provenance step, because the drill provenance is # intentionally invalid. That proves the rehearsed identity is accepted. ./verify-release.sh --local drill.tar.gz || echo "exit=$?" # 6. Flip one byte in the tarball and re-run. Expected: the cosign step # fails. Tamper detection works end to end. printf '\x01' | dd of=drill.tar.gz bs=1 count=1 conv=notrunc ./verify-release.sh --local drill.tar.gz || echo "exit=$?" # 7. Clean up. rm -rf "${DRILL}"
signing-rotation-drill
and do not ship the rotation PR until the deviation is understood.SECURITY.md, the
repository's .well-known/security.txt and the in-repo witnesses change in
the same PR. scripts/check-fingerprint-pinning.sh fails a PR that leaves
one of them behind..github/cosign.pub: the documented NO-PINNED-KEY sentinel (keyless only)..github/workflows/sign-attest.yml: the signing workflow the certificate
identity names; .github/workflows/release.yml calls it..github/ISSUE_TEMPLATE/signing-key-fingerprint.md: the pinned-issue template.scripts/check-fingerprint-pinning.sh: checks that every witness carries
the same pin; .github/workflows/signing-identity-pin.yml runs it on pull
requests that touch the pin.scripts/verify-release.sh: the local verification script operators run.
There is no supported flag that bypasses a failed verification.