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

98 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)
```json
{
"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:
```bash
# 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)
```json
{
"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.