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.
5.2 KiB
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 (30–90 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.envmust bechmod 600AND listed in.gitignoreBEFORE the first write. See_blocks/domain-has-secrets.md. Repo-level "Secrets and variables → Actions" is the deployment copy — rotate source.envwhen 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.postureis exactly one choice.SECRETS.requiredis fully enumerated fromDEPLOY+RELEASE; noTODOplaceholders.- 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_statusnon-empty.