feat(secret-audit): credential lifecycle audit tooling #16

Merged
john merged 1 commit from feat/credential-lifecycle-audit into main 2026-08-02 08:48:00 +00:00
Owner

What

Credential lifecycle audit tooling — weekly automated detection of stale, shadowed, dead, and undeclared secrets across the haskytech Forgejo org.

Why

Fleet-wide credential staleness audit (2026-08-02) found:

  • 9 repo-level secrets shadowing org secrets (rotation silently strands them)
  • 29 dead Coolify secrets from decommissioned platform
  • 3 generations of deploy SSH keys with no cleanup
  • No rotation schedule or staleness detection

Shadows and dead secrets have been cleaned up. This PR prevents recurrence.

What's included

  • config/secrets.yaml — credential registry declaring 10 secrets with rotation classes (token=90d, key=365d), allowlisted repo-level overrides, and dead credentials
  • scripts/secret_audit.py — audit script checking staleness (warn at 75%, alert at 100%), shadows, dead secrets, undeclared secrets, and missing secrets
  • .forgejo/workflows/secret-audit.yml — weekly Monday 1am + manual trigger, GChat notification on findings
  • docs/secret-rotation.md — rotation runbook emphasizing delete-then-create (Forgejo PUT doesn't reset created_at)

Key design decisions

  • Rotation is DELETE-then-create, never PUT — Forgejo's created_at doesn't update on overwrite
  • CI never runs --fix; deletions are human-initiated from workstation (re-creation needs Vaultwarden plaintext)
  • Exit code 2 = alerts present (not a crash) — notification fires before job fails
  • Repo enumeration is live (GET /orgs/repos), not from config/repos.yaml, so unlisted repos can't hide shadows

Remaining work (not in this PR)

  • ~/.git-credentials cleanup (13 entries, 3+ stale)
  • Dokku authorized key cleanup (legacy public keys still installed)
  • First rotation of CI_FORGEJO_TOKEN using the new runbook
## What Credential lifecycle audit tooling — weekly automated detection of stale, shadowed, dead, and undeclared secrets across the haskytech Forgejo org. ## Why Fleet-wide credential staleness audit (2026-08-02) found: - 9 repo-level secrets shadowing org secrets (rotation silently strands them) - 29 dead Coolify secrets from decommissioned platform - 3 generations of deploy SSH keys with no cleanup - No rotation schedule or staleness detection Shadows and dead secrets have been cleaned up. This PR prevents recurrence. ## What's included - **config/secrets.yaml** — credential registry declaring 10 secrets with rotation classes (token=90d, key=365d), allowlisted repo-level overrides, and dead credentials - **scripts/secret_audit.py** — audit script checking staleness (warn at 75%, alert at 100%), shadows, dead secrets, undeclared secrets, and missing secrets - **.forgejo/workflows/secret-audit.yml** — weekly Monday 1am + manual trigger, GChat notification on findings - **docs/secret-rotation.md** — rotation runbook emphasizing delete-then-create (Forgejo PUT doesn't reset created_at) ## Key design decisions - Rotation is DELETE-then-create, never PUT — Forgejo's created_at doesn't update on overwrite - CI never runs --fix; deletions are human-initiated from workstation (re-creation needs Vaultwarden plaintext) - Exit code 2 = alerts present (not a crash) — notification fires before job fails - Repo enumeration is live (GET /orgs/repos), not from config/repos.yaml, so unlisted repos can't hide shadows ## Remaining work (not in this PR) - ~/.git-credentials cleanup (13 entries, 3+ stale) - Dokku authorized key cleanup (legacy public keys still installed) - First rotation of CI_FORGEJO_TOKEN using the new runbook
Weekly audit of Forgejo org and repo secrets: staleness against
per-class rotation intervals, repo-level shadows of org secrets,
retired credentials that still exist, and undeclared entries.

- config/secrets.yaml: credential registry (class, interval,
  sanctioned repo-level overrides, dead credentials)
- scripts/secret_audit.py: audit + guarded --fix cleanup
- .forgejo/workflows/secret-audit.yml: Monday 1am, GChat report
- docs/secret-rotation.md: per-class rotation runbook

Rotation is delete-then-create: Forgejo PUT upserts without touching
created_at, so an in-place update never resets the staleness clock.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JKyHVbVjWM2TZhZhfzhTmu
john merged commit ed5f831526 into main 2026-08-02 08:48:00 +00:00
john deleted branch feat/credential-lifecycle-audit 2026-08-02 08:48:00 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
haskytech/haskytech-ops!16
No description provided.