feat(bootstrap): postgres data-plane + remote helper (T03)
foundation-postgres (postgres:17, digest-pinned in VERSIONS) on foundation-net, internal only (5432 unpublished); named volume foundation-postgres-data with retainOnDelete. The forgejo login role + database are created post-boot by an idempotent, readiness-gated remote.Command (ADR-007), since 5432 isn't reachable from the operator. Adds the generator half of credentials.ts (@pulumi/random → CONTRACT_002 postgres keys) and lib/remote.ts (vmConnection over the VM SSH path). Live on cx33 Helsinki: container healthy, role 'forgejo' + db 'forgejo' present, no published ports. Acceptance T03 met. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2e11fd2448
commit
6edba60612
8 changed files with 252 additions and 13 deletions
50
bootstrap/components/credentials.ts
Normal file
50
bootstrap/components/credentials.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// components/credentials.ts
|
||||
//
|
||||
// CONTRACT_002 single owner of credential GENERATION + Vault distribution.
|
||||
//
|
||||
// Generation (`generateCredentials`) is pure @pulumi/random — no Vault, no
|
||||
// network — so it can be created in Phase 3 and consumed by the data-plane
|
||||
// components (postgres.ts needs its password at container boot). Distribution
|
||||
// (`writeCredentialsToVault`, T06) is the half that depends on Vault being
|
||||
// unsealed (Gate A); it writes every value to the KV paths in CONTRACT_002 §2.3.
|
||||
// Splitting the two halves resolves the ordering tension between "Postgres up in
|
||||
// Phase 3" and "secrets in Vault in Phase 5" without giving up single ownership.
|
||||
//
|
||||
// camelCase keys, no exceptions (CONTRACT_002 §2.2): the Vault write JSON-encodes
|
||||
// these objects, so the key names ARE the camelCase Vault keys downstream reads.
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import { RandomPassword } from "@pulumi/random";
|
||||
import { DeployCtx } from "../lib/context";
|
||||
|
||||
/** `foundation/postgres/service-credentials` (CONTRACT_002 §2.3). */
|
||||
export interface PostgresCredentials {
|
||||
superUser: string; // "postgres" — image default superuser (deterministic)
|
||||
superPassword: pulumi.Output<string>;
|
||||
forgejoDbUser: string; // "forgejo" (deterministic)
|
||||
forgejoDbPassword: pulumi.Output<string>;
|
||||
}
|
||||
|
||||
/** Everything generateCredentials() produces; grows as Wave-2 tasks land. */
|
||||
export interface FoundationCredentials {
|
||||
postgres: PostgresCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* High-entropy alphanumeric secret. special:false keeps values safe to drop into
|
||||
* connection strings / app.ini / psql literals without escaping (len 28 ≈ 166 bits).
|
||||
*/
|
||||
function secret(name: string, length = 28): pulumi.Output<string> {
|
||||
return new RandomPassword(name, { length, special: false }).result;
|
||||
}
|
||||
|
||||
/** Generate all egg credentials (pure; no dependencies). CONTRACT_002 writer. */
|
||||
export function generateCredentials(ctx: DeployCtx): FoundationCredentials {
|
||||
return {
|
||||
postgres: {
|
||||
superUser: "postgres",
|
||||
superPassword: secret("postgres-super-password"),
|
||||
forgejoDbUser: "forgejo",
|
||||
forgejoDbPassword: secret("forgejo-db-password"),
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue