feat(credentials): mirror Forgejo crypto secrets into Vault (CONTRACT_002)

Close the known gap: foundation/forgejo/service-credentials held only the
admin user/pw; the crypto secrets Forgejo auto-generates into app.ini were
never captured. Make that path single-owned at GATE B and write admin +
crypto together.

- credentials.ts: drop the forgejo block from the GATE-A writer (its crypto
  secrets don't exist until Forgejo first-starts) and add
  writeForgejoCredentialsToVault — runs after forgejo.ready, reads SECRET_KEY,
  INTERNAL_TOKEN, LFS_JWT_SECRET ([server]) and oauth2 JWT_SECRET straight off
  the live app.ini via docker-exec (ADR-007), and puts the full path. One
  writer per Vault path avoids a put/patch race on re-runs.
- index.ts: wire it at GATE B (dependsOn vault.init + forgejo.ready).

Keys: forgejoAdminUser, forgejoAdminPassword, forgejoSecretKey,
forgejoInternalToken, forgejoJwtSecret, forgejoOauth2JwtSecret.

Validated live: forgejo path now has all six; postgres/rustfs paths intact
through the GATE-A writer replacement; idempotent at 43 unchanged.

FINDING: forgejoSecretKey mirrors EMPTY — skipping the web installer
(INSTALL_LOCK) left Forgejo's [security] SECRET_KEY unset. Fixed next commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Niemann 2026-06-30 23:28:17 +02:00
parent f2ef9bc922
commit fbd1ad4d1d
2 changed files with 76 additions and 10 deletions

View file

@ -14,6 +14,7 @@ import {
generateCredentials,
writeCredentialsToVault,
writeBackupCredentialsToVault,
writeForgejoCredentialsToVault,
} from "./components/credentials";
import { deployPostgres } from "./components/postgres";
import { deployRustfs } from "./components/rustfs";
@ -75,12 +76,21 @@ const forgejoBootstrap = bootstrapForgejo(ctx, {
sshPublicKey,
});
const runner = cfg.features.runner ? deployRunner(ctx, forgejo) : undefined;
// Mirror Forgejo's admin + app.ini crypto secrets into Vault (CONTRACT_002 §2.3);
// GATE B — needs app.ini, which exists only once Forgejo has started.
const forgejoCreds = writeForgejoCredentialsToVault(
ctx,
vault,
credentials.forgejo,
forgejo.ready,
);
// =============================================================================
// Stack outputs (extended as phases land).
// vaultCreds (T06) is a gate for Forgejo (T08) — it has no output to export yet.
void vaultCreds;
void backupCreds; // CONTRACT_002 backup/backup-credentials mirror; no secret output
void forgejoCreds; // CONTRACT_002 forgejo/service-credentials mirror; no secret output
export const phase = "T10-runner"; // forge + CI runner live
export const caddyImageId = proxy.imageId;