foundation-rustfs (rustfs/rustfs digest-pinned) on foundation-net, internal only (9000/9001 unpublished); named volume foundation-rustfs-data with retainOnDelete. The four buckets (forgejo-packages/-artifacts/-lfs, foundation-backups) and a scoped service account with generated keys (CONTRACT_002 rustfs slice) are provisioned post-boot by an idempotent, readiness-gated remote.Command using a throwaway mc container (ADR-007). RustFS speaks enough MinIO admin API for `svcacct add`; `mc ready` is unreliable so readiness gates on `mc ls`; the mc image's busybox lacks grep so existence checks use a shell `case`. Pins the IMAGE_MC tool image in VERSIONS. Live on cx33 Helsinki: 4 buckets present, service key registered, put/get roundtrip OK, no published ports. Acceptance T04 met. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
65 lines
2.8 KiB
TypeScript
65 lines
2.8 KiB
TypeScript
// 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>;
|
|
}
|
|
|
|
/** `foundation/rustfs/service-credentials` (CONTRACT_002 §2.3). */
|
|
export interface RustfsCredentials {
|
|
adminUser: string; // "rustfsadmin" — root access key name (deterministic)
|
|
adminPassword: pulumi.Output<string>; // root secret key (RUSTFS_SECRET_KEY)
|
|
serviceKeyId: pulumi.Output<string>; // scoped S3 key Forgejo/backup use
|
|
serviceKeySecret: pulumi.Output<string>;
|
|
}
|
|
|
|
/** Everything generateCredentials() produces; grows as Wave-2 tasks land. */
|
|
export interface FoundationCredentials {
|
|
postgres: PostgresCredentials;
|
|
rustfs: RustfsCredentials;
|
|
}
|
|
|
|
/**
|
|
* 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"),
|
|
},
|
|
rustfs: {
|
|
adminUser: "rustfsadmin",
|
|
adminPassword: secret("rustfs-admin-password"),
|
|
serviceKeyId: secret("rustfs-service-key-id", 20), // S3 access-key id
|
|
serviceKeySecret: secret("rustfs-service-key-secret", 40), // S3 secret
|
|
},
|
|
};
|
|
}
|