docs(contracts): add CONTRACT_001-004 — T00
Interface contracts unblocking the parallel fan-out (T01-T07): - 001 config schema (single stack, passphrase + VERSIONS + Pulumi config) - 002 Vault path layout (foundation/<service>/<type>-credentials, camelCase) - 003 container network/DNS/ports/volumes (foundation-net, named volumes) - 004 backup artifact format + restore order (Vault->PG->RustFS->Forgejo) ADR_F001 (layered platform) already satisfied by ADR-004. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f18676e6b3
commit
188e30e23e
5 changed files with 299 additions and 0 deletions
60
documentation/contracts/CONTRACT_002_vault_path_layout.md
Normal file
60
documentation/contracts/CONTRACT_002_vault_path_layout.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Contract — CONTRACT_002 — Vault Path Layout
|
||||
|
||||
**Between**: `bootstrap/components/credentials.ts` (writer) ↔ every service component (reader)
|
||||
**Status**: Agreed (pending implementation validation)
|
||||
**Realizes**: PLAN-002 §4 · **Consistent with**: ADR-002, `002_platform_architecture.md` §3
|
||||
|
||||
## Interface
|
||||
|
||||
### 2.1 Mount
|
||||
- **KV v2 mount**: `foundation` (one mount for the whole egg).
|
||||
- **Path scheme**: `foundation/<service>/<type>-credentials` (mirrors the proven olsicloud4 scheme
|
||||
`olsicloud4/<project>/<stage>/<type>-credentials`, dropping the stage — Layer 0 is single-stage).
|
||||
|
||||
### 2.2 Key naming — **camelCase, no exceptions**
|
||||
Keys are produced by `JSON.stringify()` of TypeScript objects, so they are **camelCase**
|
||||
(e.g. `postgresSuperPassword`). Any future ESO `remoteRef.property` (Layer 1) must match exactly.
|
||||
This is the documented footgun in `002_platform_architecture.md` §3 — honour it from day one.
|
||||
|
||||
### 2.3 Paths and keys
|
||||
|
||||
| Path | Keys (camelCase) | Generated by |
|
||||
|------|------------------|--------------|
|
||||
| `foundation/postgres/service-credentials` | `postgresSuperUser`, `postgresSuperPassword`, `forgejoDbUser`, `forgejoDbPassword` | `@pulumi/random` |
|
||||
| `foundation/rustfs/service-credentials` | `rustfsAdminUser`, `rustfsAdminPassword`, `rustfsServiceKeyId`, `rustfsServiceKeySecret` | `@pulumi/random` |
|
||||
| `foundation/forgejo/service-credentials` | `forgejoSecretKey`, `forgejoInternalToken`, `forgejoJwtSecret`, `forgejoOauth2JwtSecret`, `forgejoAdminUser`, `forgejoAdminPassword` | `@pulumi/random` |
|
||||
| `foundation/forgejo/registry-credentials` | `ociPushToken`, `npmPushToken` | Forgejo API post-bootstrap → Vault |
|
||||
| `foundation/runner/service-credentials` | `runnerRegistrationToken` | Forgejo `generate-runner-token` → Vault |
|
||||
| `foundation/backup/backup-credentials` | `offsiteAccessKey`, `offsiteSecretKey`, `offsiteEndpoint`, `backupAgeRecipient`, `backupAgeIdentity` | seeded once + `@pulumi/random` (age key) |
|
||||
| `foundation/cloudflare/api-credentials` | `cloudflareApiToken` | seeded once (mirror of config secret) |
|
||||
| `foundation/project/project-credentials` | *(empty, `disableRead: true`)* | manual one-time seed slot (ADR-002 pattern) |
|
||||
|
||||
### 2.4 What is NOT in Vault (the bootstrap exception)
|
||||
Vault's **own** `rootToken` and `unsealKeys` cannot live in Vault (chicken-egg). They live in the
|
||||
passphrase-encrypted Pulumi config (`vaultCredentials:*`, CONTRACT_001 §1.3). This is the single
|
||||
deliberate exception and the hinge of the whole trust chain (PLAN-002 §4.1).
|
||||
|
||||
### 2.5 Access model
|
||||
- **Day-zero (Layer 0)**: components read from Vault using the root token (from config) during
|
||||
`pulumi up`, or values are rendered into container env/`app.ini` directly by Pulumi. No AppRole yet.
|
||||
- **Steady-state / Layer 1**: introduce a per-consumer **AppRole + scoped policy** per service
|
||||
(`foundation/<service>/*` read-only), mirroring the `SecretStore vault-<project>-<stage>` pattern.
|
||||
Policy stubs live in `packages/pulumi-vault/policy.ts` (vendored from olsicloud4 `modules/vault`).
|
||||
|
||||
## Ownership
|
||||
- **Writer**: `credentials.ts` owns generation + write. It is the **only** writer of
|
||||
`*-credentials` paths (single source of truth; rotation = `pulumi up --replace`, ADR-002).
|
||||
- **Readers**: each service component reads only its own service path.
|
||||
|
||||
## Assumptions
|
||||
- KV **v2** (versioned) — enables rotation history + rollback.
|
||||
- Vault audit log enabled at init (records every read).
|
||||
|
||||
## Validation
|
||||
- After T06: assert every key above exists at the correct path with non-empty value (idempotent
|
||||
re-run produces no diff). A `vault kv get` smoke check per path.
|
||||
|
||||
## Change Process
|
||||
New credential = add a row here + flip the matching `features.<x>` flag (CONTRACT_001). Never add a
|
||||
secret to git or config that could instead be generated into Vault. Renames are breaking — version
|
||||
this contract and update writer + reader together.
|
||||
Loading…
Add table
Add a link
Reference in a new issue