KeiSeiKit-1.0/skills/ci-scaffold/phase-4-secrets.md
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

5.2 KiB
Raw Blame History

Phase 4 — Secrets posture (OIDC vs PAT; RULE 0.8 scaffold)

Decides how CI obtains credentials. Default bias is OIDC (short-lived, no stored secret); fall back to PAT only when the provider has no OIDC (e.g. Forgejo → AWS, npm trusted-publishing not configured, custom SSH deploy). Every chosen secret is referenced by NAME ONLY per RULE 0.8 — this skill NEVER writes a value.

4a — Posture click (AskUserQuestion, single-select)

{
  "questions": [
    {
      "question": "Credential posture for CI?",
      "header": "Secrets",
      "multiSelect": false,
      "options": [
        {"label": "OIDC-first (recommended)",
         "description": "Cloud roles trust token.actions.githubusercontent.com; no long-lived keys stored. Requires DEPLOY ∈ {aws-oidc, gcp-oidc} and PLATFORM = GitHub Actions."},
        {"label": "PAT fallback (when OIDC unavailable)",
         "description": "Long-lived scoped tokens stored in repo secrets. Rotation schedule mandatory (3090 days). Used for Cloudflare, npm, DockerHub, custom SSH."},
        {"label": "Hybrid — OIDC where possible, PAT elsewhere",
         "description": "Most real setups. Skill emits both sections of the scaffold."},
        {"label": "No secrets (public CI tests only)",
         "description": "ci.yml + security.yml do not need credentials. deploy.yml / release.yml skipped."}
      ]
    }
  ]
}

Store as SECRETS.posture.

If PLATFORM = Forgejo Actions and the user picked OIDC-first, warn: "Forgejo does not serve a JWKS endpoint. Use the bastion pattern from _blocks/ci-forgejo-actions.md OR switch to PAT-fallback." Offer both constructive paths (NO DOWNGRADE) and re-ask.

4b — Enumerate required secrets (no AskUserQuestion; derived from DEPLOY + RELEASE)

Walk the matrix below. For each hit, add to SECRETS.required.

DEPLOY / RELEASE OIDC posture PAT fallback posture
aws-oidc AWS_ROLE_ARN (repo var, not secret); AWS_REGION AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (last-resort, rotate 30d)
gcp-oidc GCP_WORKLOAD_IDENTITY_PROVIDER + GCP_SERVICE_ACCOUNT GCP_SA_KEY JSON (avoid; Google deprecates static keys 2026)
cloudflare (Workers OIDC preview; most prod still token) CLOUDFLARE_API_TOKEN (scopes per self-sufficiency.md); CLOUDFLARE_ACCOUNT_ID
modal n/a (Modal has its own token model) MODAL_TOKEN_ID + MODAL_TOKEN_SECRET; cost tier check pre-launch
registry (GHCR) built-in GITHUB_TOKEN write-packages
registry (ECR) Uses AWS OIDC role AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
registry (Forgejo) FORGEJO_TOKEN (built-in at runner)
custom SSH SSH_PRIVATE_KEY (ed25519, generated fresh per repo), SSH_HOST, SSH_USER
RELEASE=cargo-release crates.io trusted publishing (2025+) CARGO_REGISTRY_TOKEN
RELEASE=changesets npm trusted publishing (2026 preview) NPM_TOKEN

4c — Emit secrets/ci.env scaffold (inline; no file write)

Print as a fenced code block. Example when posture is OIDC-first + cargo-release:

# secrets/ci.env — paths and NAMES only. chmod 600 + .gitignore before writing values.
# RULE 0.8: reference by env-var name. NEVER paste a literal here.

# OIDC (no secrets stored; vars on the provider side)
AWS_ROLE_ARN=            # arn:aws:iam::<account>:role/gha-deployer — set as repo VAR, not secret
AWS_REGION=              # eu-north-1

# Release publishing
CARGO_REGISTRY_TOKEN=    # trusted-publishing preferred; fallback PAT only if TP unavailable

Append the reminder once:

secrets/ci.env must be chmod 600 AND listed in .gitignore BEFORE the first write. See _blocks/domain-has-secrets.md. Repo-level "Secrets and variables → Actions" is the deployment copy — rotate source .env when repo secret rotates, not the other way around.

4d — Confirm repo-side secret registration (AskUserQuestion, multi-select)

{
  "questions": [
    {
      "question": "For each name I listed, confirm it is REGISTERED on the platform (Settings → Actions → Secrets or Repo Variables):",
      "header": "Registered",
      "multiSelect": true,
      "options": [
        {"label": "All names present and current (rotated within the last 90 days)", "description": "Proceed to Phase 5"},
        {"label": "Some names missing — I will register now and re-run", "description": "Skill exits; re-enter after registration"},
        {"label": "I use a secrets manager (Vault / 1Password CLI / Doppler) that syncs to the platform", "description": "Acceptable; confirm sync is green"},
        {"label": "None registered yet — show me the platform link",   "description": "Emit link per PLATFORM and exit"}
      ]
    }
  ]
}

Store the answer as SECRETS.registration_status. Any answer other than the first pauses Phase 5.

Verify-criterion

  • SECRETS.posture is exactly one choice.
  • SECRETS.required is fully enumerated from DEPLOY + RELEASE; no TODO placeholders.
  • The printed scaffold has NO literal values — every = is followed by whitespace or a # comment.
  • Forgejo + OIDC combination has either the bastion pattern documented or the user opted into PAT-fallback.
  • SECRETS.registration_status non-empty.