feat(bootstrap): write data-plane creds to Vault KV v2 (T06)

writeCredentialsToVault distributes the generated postgres + rustfs
service-credentials into the Vault `foundation` kv-v2 mount at the CONTRACT_002
paths, over docker-exec/SSH (ADR-007) since 8200 isn't reachable from the
operator. Secret values go in as a JSON object on the container's stdin (never
argv); the root token from the vault-init output authenticates. dependsOn
vault.init = GATE A. Idempotent: kv-v2 enable is guarded, `vault kv put`
overwrites. Forgejo crypto secrets, the runner token, registry tokens, and backup
creds are written by their own tasks (T08/T10/T12).

Live on cx33 Helsinki: foundation/{postgres,rustfs}/service-credentials present
with every CONTRACT_002 camelCase key non-empty; mount is kv v2. Acceptance T06
met for the data-plane slice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Niemann 2026-06-30 21:37:26 +02:00
parent 0e81635d88
commit fa242e4e76
2 changed files with 92 additions and 7 deletions

View file

@ -9,7 +9,10 @@ import { loadConfig } from "./config";
import { buildBaseContext, DeployCtx } from "./lib/context";
import { deployNetwork } from "./components/network";
import { deployDns } from "./components/dns";
import { generateCredentials } from "./components/credentials";
import {
generateCredentials,
writeCredentialsToVault,
} from "./components/credentials";
import { deployPostgres } from "./components/postgres";
import { deployRustfs } from "./components/rustfs";
import { deployVault } from "./components/vault";
@ -35,11 +38,10 @@ const credentials = generateCredentials(ctx);
const postgres = deployPostgres(ctx, credentials.postgres);
const rustfs = deployRustfs(ctx, credentials.rustfs);
const vault = deployVault(ctx);
//
// --- GATE A: Vault init + unseal (T05) → run.sh captures unseal keys to encrypted
// config; credentials.ts (T06) dependsOn vault.init.
// writeCredentialsToVault(ctx, credentials, { vault });
//
// --- GATE A: Vault init + unseal (T05). T06 writes the generated data-plane creds
// into Vault (CONTRACT_002), dependsOn vault.init so it runs only once unsealed.
const vaultCreds = writeCredentialsToVault(ctx, credentials, vault);
// =============================================================================
// PHASE 6 — FORGE (depends on: credentials, GATE A)
// T07 caddy · T08 forgejo · T10 runner
@ -51,7 +53,10 @@ const vault = deployVault(ctx);
// =============================================================================
// Stack outputs (extended as phases land).
export const phase = "T05-vault"; // data-plane complete: postgres, rustfs, vault
// vaultCreds (T06) is a gate for Forgejo (T08) — it has no output to export yet.
void vaultCreds;
export const phase = "T06-credentials"; // data-plane + creds-in-Vault
export const networkName = network.name;
export const vmTarget = `${cfg.vm.user}@${cfg.vm.host}`;
export const postgresEndpoint = postgres.endpoint;