@wildo-ai/platform-infra-providers 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +34 -0
- package/dist/esm/aws-s3/aws-s3.infra.module.d.ts +63 -0
- package/dist/esm/aws-s3/aws-s3.infra.module.d.ts.map +1 -0
- package/dist/esm/aws-s3/aws-s3.infra.module.js +110 -0
- package/dist/esm/aws-s3/aws-s3.infra.module.js.map +1 -0
- package/dist/esm/clamav-self/clamav-self.infra.module.d.ts +30 -0
- package/dist/esm/clamav-self/clamav-self.infra.module.d.ts.map +1 -0
- package/dist/esm/clamav-self/clamav-self.infra.module.js +61 -0
- package/dist/esm/clamav-self/clamav-self.infra.module.js.map +1 -0
- package/dist/esm/gotenberg-self/gotenberg-self.infra.module.d.ts +26 -0
- package/dist/esm/gotenberg-self/gotenberg-self.infra.module.d.ts.map +1 -0
- package/dist/esm/gotenberg-self/gotenberg-self.infra.module.js +54 -0
- package/dist/esm/gotenberg-self/gotenberg-self.infra.module.js.map +1 -0
- package/dist/esm/index.d.ts +88 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +125 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/minio-self/minio-self.infra.module.d.ts +64 -0
- package/dist/esm/minio-self/minio-self.infra.module.d.ts.map +1 -0
- package/dist/esm/minio-self/minio-self.infra.module.js +105 -0
- package/dist/esm/minio-self/minio-self.infra.module.js.map +1 -0
- package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.d.ts +52 -0
- package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.d.ts.map +1 -0
- package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.js +201 -0
- package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.js.map +1 -0
- package/dist/esm/mongodb-self/mongodb-self.infra.module.d.ts +81 -0
- package/dist/esm/mongodb-self/mongodb-self.infra.module.d.ts.map +1 -0
- package/dist/esm/mongodb-self/mongodb-self.infra.module.js +311 -0
- package/dist/esm/mongodb-self/mongodb-self.infra.module.js.map +1 -0
- package/dist/esm/postgres-self/postgres-self.infra.module.d.ts +70 -0
- package/dist/esm/postgres-self/postgres-self.infra.module.d.ts.map +1 -0
- package/dist/esm/postgres-self/postgres-self.infra.module.js +328 -0
- package/dist/esm/postgres-self/postgres-self.infra.module.js.map +1 -0
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.d.ts +88 -0
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.d.ts.map +1 -0
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.js +473 -0
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.js.map +1 -0
- package/dist/esm/redis-self/redis-self.infra.module.d.ts +89 -0
- package/dist/esm/redis-self/redis-self.infra.module.d.ts.map +1 -0
- package/dist/esm/redis-self/redis-self.infra.module.js +331 -0
- package/dist/esm/redis-self/redis-self.infra.module.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +52 -0
- package/src/aws-s3/__tests__/aws-s3.infra.module.test.ts +67 -0
- package/src/aws-s3/aws-s3.infra.module.ts +120 -0
- package/src/clamav-self/__tests__/clamav-self.infra.module.test.ts +65 -0
- package/src/clamav-self/clamav-self.infra.module.ts +72 -0
- package/src/gotenberg-self/__tests__/gotenberg-self.infra.module.test.ts +65 -0
- package/src/gotenberg-self/gotenberg-self.infra.module.ts +65 -0
- package/src/index.ts +133 -0
- package/src/minio-self/__tests__/minio-self.infra.module.test.ts +70 -0
- package/src/minio-self/minio-self.infra.module.ts +116 -0
- package/src/mongodb-atlas/__tests__/mongodb-atlas.infra.module.test.ts +198 -0
- package/src/mongodb-atlas/mongodb-atlas.infra.module.ts +236 -0
- package/src/mongodb-self/__tests__/mongodb-self.infra.module.test.ts +419 -0
- package/src/mongodb-self/mongodb-self.infra.module.ts +384 -0
- package/src/postgres-self/__tests__/postgres-self.infra.module.test.ts +344 -0
- package/src/postgres-self/postgres-self.infra.module.ts +377 -0
- package/src/rabbitmq-self/__tests__/rabbitmq-self.infra.module.test.ts +499 -0
- package/src/rabbitmq-self/rabbitmq-self.infra.module.ts +531 -0
- package/src/redis-self/__tests__/redis-self.infra.module.test.ts +341 -0
- package/src/redis-self/redis-self.infra.module.ts +372 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@wildo-ai/platform-infra-providers` — concrete `InfraProviderModule`
|
|
3
|
+
* catalog (Slice 2.5c-1 of `infra-provider-planner.md`).
|
|
4
|
+
*
|
|
5
|
+
* Tier-portable: the modules import only `@wildo-ai/platform-config-lib`
|
|
6
|
+
* (contracts) and have no DI / mongoose / axios runtime dependencies.
|
|
7
|
+
* This lets both `platform-apps-manager` (orchestrator) and
|
|
8
|
+
* `wildo-cli` (lockfile materializer) consume the same module
|
|
9
|
+
* objects — apps-manager passes a real admin-client implementation
|
|
10
|
+
* via `AppCredentialMintingContext.adminClient`; wildo-cli only
|
|
11
|
+
* needs `module.plan()` for the lockfile digest.
|
|
12
|
+
*
|
|
13
|
+
* Each module is exported by name (no intermediary barrels per
|
|
14
|
+
* platform/CLAUDE.md "no intermediary index.ts barrels"). The
|
|
15
|
+
* `INFRA_PROVIDER_MODULES` map below is the canonical catalog —
|
|
16
|
+
* consumers iterate it (or look up by `InfraProvider` value) instead
|
|
17
|
+
* of importing modules one by one.
|
|
18
|
+
*/
|
|
19
|
+
import { RabbitmqSelfInfraProviderModule } from './rabbitmq-self/rabbitmq-self.infra.module.js';
|
|
20
|
+
import { RedisSelfInfraProviderModule } from './redis-self/redis-self.infra.module.js';
|
|
21
|
+
import { PostgresSelfInfraProviderModule } from './postgres-self/postgres-self.infra.module.js';
|
|
22
|
+
import { MongodbAtlasInfraProviderModule } from './mongodb-atlas/mongodb-atlas.infra.module.js';
|
|
23
|
+
import { MongodbSelfInfraProviderModule } from './mongodb-self/mongodb-self.infra.module.js';
|
|
24
|
+
// Slice 5 of `infra-provider-planner.md` — object-storage + stateless infra.
|
|
25
|
+
import { MinioSelfInfraProviderModule } from './minio-self/minio-self.infra.module.js';
|
|
26
|
+
import { AwsS3InfraProviderModule } from './aws-s3/aws-s3.infra.module.js';
|
|
27
|
+
import { ClamavSelfInfraProviderModule } from './clamav-self/clamav-self.infra.module.js';
|
|
28
|
+
import { GotenbergSelfInfraProviderModule } from './gotenberg-self/gotenberg-self.infra.module.js';
|
|
29
|
+
export { RabbitmqSelfInfraProviderModule } from './rabbitmq-self/rabbitmq-self.infra.module.js';
|
|
30
|
+
export { RedisSelfInfraProviderModule } from './redis-self/redis-self.infra.module.js';
|
|
31
|
+
export { PostgresSelfInfraProviderModule } from './postgres-self/postgres-self.infra.module.js';
|
|
32
|
+
export { MongodbAtlasInfraProviderModule } from './mongodb-atlas/mongodb-atlas.infra.module.js';
|
|
33
|
+
export { MongodbSelfInfraProviderModule } from './mongodb-self/mongodb-self.infra.module.js';
|
|
34
|
+
export { MinioSelfInfraProviderModule } from './minio-self/minio-self.infra.module.js';
|
|
35
|
+
export { AwsS3InfraProviderModule } from './aws-s3/aws-s3.infra.module.js';
|
|
36
|
+
export { ClamavSelfInfraProviderModule } from './clamav-self/clamav-self.infra.module.js';
|
|
37
|
+
export { GotenbergSelfInfraProviderModule } from './gotenberg-self/gotenberg-self.infra.module.js';
|
|
38
|
+
/**
|
|
39
|
+
* Canonical `InfraProvider` → `InfraProviderModule` catalog. Caller
|
|
40
|
+
* (CLI, apps-manager) builds a `Map<InfraProvider, InfraProviderModule>`
|
|
41
|
+
* from this constant when threading modules into
|
|
42
|
+
* `materializeInfraSection`.
|
|
43
|
+
*
|
|
44
|
+
* **Why a const array instead of a `Map` literal**: arrays serialize
|
|
45
|
+
* stably under tsc and roll up to a deterministic order. The
|
|
46
|
+
* downstream `Map` is built once at program start; the catalog
|
|
47
|
+
* itself is a value-level fixture.
|
|
48
|
+
*
|
|
49
|
+
* **Coverage** (Slice 5 baseline, 2026-05-10):
|
|
50
|
+
* - `RABBITMQ_SELF`, `REDIS_SELF`, `POSTGRES_SELF`, `MONGODB_SELF`,
|
|
51
|
+
* `MONGODB_ATLAS` — five stateful modules with mint/revoke
|
|
52
|
+
* methods.
|
|
53
|
+
* - `MINIO_SELF`, `AWS_S3` — object storage (shared-bucket model;
|
|
54
|
+
* no per-app credentials).
|
|
55
|
+
* - `CLAMAV_SELF`, `GOTENBERG_SELF` — stateless infra (no auth, no
|
|
56
|
+
* per-app state; `plan()` only).
|
|
57
|
+
*
|
|
58
|
+
* **NOT covered** (catalog entries without modules):
|
|
59
|
+
* - `POSTGRES_MANAGED`, `POSTGRES_AWS_RDS`, `POSTGRES_GCP_CLOUD_SQL`,
|
|
60
|
+
* `POSTGRES_AZURE_DATABASE`, `POSTGRES_SUPABASE`, `POSTGRES_NEON`,
|
|
61
|
+
* `POSTGRES_RENDER` — managed-Postgres brand catalog entries
|
|
62
|
+
* (Slice 4.B.0). No module yet; trigger = real customer.
|
|
63
|
+
*
|
|
64
|
+
* `materializeInfraSection` falls back to the authored-entry digest
|
|
65
|
+
* when a selected `InfraProvider` is not in the map (preserves the
|
|
66
|
+
* Slice 2.5b behavior for uncovered providers).
|
|
67
|
+
*/
|
|
68
|
+
export const INFRA_PROVIDER_MODULES = [
|
|
69
|
+
RabbitmqSelfInfraProviderModule,
|
|
70
|
+
RedisSelfInfraProviderModule,
|
|
71
|
+
PostgresSelfInfraProviderModule,
|
|
72
|
+
MongodbAtlasInfraProviderModule,
|
|
73
|
+
MongodbSelfInfraProviderModule,
|
|
74
|
+
MinioSelfInfraProviderModule,
|
|
75
|
+
AwsS3InfraProviderModule,
|
|
76
|
+
ClamavSelfInfraProviderModule,
|
|
77
|
+
GotenbergSelfInfraProviderModule,
|
|
78
|
+
];
|
|
79
|
+
/**
|
|
80
|
+
* Every `Wildo_Application_Secrets` JSON path that ANY provider module can mint per runtime
|
|
81
|
+
* principal — the union of the catalog's own static declarations.
|
|
82
|
+
*
|
|
83
|
+
* ## Why this is derived rather than written down
|
|
84
|
+
*
|
|
85
|
+
* The consumer is the secret-DELIVERY path in apps-manager: it strips exactly this set from the
|
|
86
|
+
* application blob before overlaying a principal's own material, so a principal receives a
|
|
87
|
+
* credential only where one was minted FOR IT. A hand-maintained copy at that consumer would be a
|
|
88
|
+
* second source — add a provider that mints a new credential path and delivery keeps shipping the
|
|
89
|
+
* APPLICATION's value for it, silently. That is the defect class this repo has paid for twice: the
|
|
90
|
+
* party that CONSTRAINS is not the party that DECLARES.
|
|
91
|
+
*
|
|
92
|
+
* Derived from `INFRA_PROVIDER_MODULES`, so a new module is covered the day it enters the catalog.
|
|
93
|
+
*
|
|
94
|
+
* Deliberately the union across ALL modules, not the ones a given application selected: a principal
|
|
95
|
+
* must not receive an application-wide credential for a provider it does not use either, and the
|
|
96
|
+
* selected set is not knowable at the delivery call site without threading app config through it.
|
|
97
|
+
* Stripping a path no module minted for this app is harmless — it is absent from the blob anyway.
|
|
98
|
+
*/
|
|
99
|
+
export function collectPerPrincipalSecretBlobPaths() {
|
|
100
|
+
const paths = new Set();
|
|
101
|
+
for (const module of INFRA_PROVIDER_MODULES) {
|
|
102
|
+
for (const path of module.perPrincipalSecretBlobPaths) {
|
|
103
|
+
paths.add(path);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return paths;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Build a `Map<InfraProvider, InfraProviderModule>` for the
|
|
110
|
+
* canonical catalog. Callers store the result once at startup and
|
|
111
|
+
* pass it to `materializeInfraSection` (digest derivation) or to
|
|
112
|
+
* orchestrator paths that look up modules by `InfraProvider`.
|
|
113
|
+
*/
|
|
114
|
+
export function buildInfraProviderModuleMap() {
|
|
115
|
+
const map = new Map();
|
|
116
|
+
for (const m of INFRA_PROVIDER_MODULES) {
|
|
117
|
+
if (map.has(m.provider)) {
|
|
118
|
+
throw new Error(`INFRA_PROVIDER_MODULES contains duplicate provider '${m.provider}' — ` +
|
|
119
|
+
`each InfraProvider may have at most one module.`);
|
|
120
|
+
}
|
|
121
|
+
map.set(m.provider, m);
|
|
122
|
+
}
|
|
123
|
+
return map;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAAE,+BAA+B,EAAE,MAAM,4CAA4C,CAAC;AAC7F,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,+BAA+B,EAAE,MAAM,4CAA4C,CAAC;AAC7F,OAAO,EAAE,+BAA+B,EAAE,MAAM,4CAA4C,CAAC;AAC7F,OAAO,EAAE,8BAA8B,EAAE,MAAM,0CAA0C,CAAC;AAC1F,6EAA6E;AAC7E,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AACvF,OAAO,EAAE,gCAAgC,EAAE,MAAM,8CAA8C,CAAC;AAEhG,OAAO,EAAE,+BAA+B,EAAE,MAAM,4CAA4C,CAAC;AAC7F,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,+BAA+B,EAAE,MAAM,4CAA4C,CAAC;AAC7F,OAAO,EAAE,+BAA+B,EAAE,MAAM,4CAA4C,CAAC;AAC7F,OAAO,EAAE,8BAA8B,EAAE,MAAM,0CAA0C,CAAC;AAC1F,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AACvF,OAAO,EAAE,gCAAgC,EAAE,MAAM,8CAA8C,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAuC;IACxE,+BAA+B;IAC/B,4BAA4B;IAC5B,+BAA+B;IAC/B,+BAA+B;IAC/B,8BAA8B;IAC9B,4BAA4B;IAC5B,wBAAwB;IACxB,6BAA6B;IAC7B,gCAAgC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,kCAAkC;IAChD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,MAAM,IAAI,sBAAsB,EAAE,CAAC;QAC5C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,2BAA2B,EAAE,CAAC;YACtD,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B;IACzC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsC,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,sBAAsB,EAAE,CAAC;QACvC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,uDAAuD,CAAC,CAAC,QAAQ,MAAM;gBACvE,iDAAiD,CAClD,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * `@wildo-ai/platform-infra-providers` — concrete `InfraProviderModule`\n * catalog (Slice 2.5c-1 of `infra-provider-planner.md`).\n *\n * Tier-portable: the modules import only `@wildo-ai/platform-config-lib`\n * (contracts) and have no DI / mongoose / axios runtime dependencies.\n * This lets both `platform-apps-manager` (orchestrator) and\n * `wildo-cli` (lockfile materializer) consume the same module\n * objects — apps-manager passes a real admin-client implementation\n * via `AppCredentialMintingContext.adminClient`; wildo-cli only\n * needs `module.plan()` for the lockfile digest.\n *\n * Each module is exported by name (no intermediary barrels per\n * platform/CLAUDE.md \"no intermediary index.ts barrels\"). The\n * `INFRA_PROVIDER_MODULES` map below is the canonical catalog —\n * consumers iterate it (or look up by `InfraProvider` value) instead\n * of importing modules one by one.\n */\n\nimport type { InfraProvider, InfraProviderModule } from '@wildo-ai/platform-config-lib';\n\nimport { RabbitmqSelfInfraProviderModule } from './rabbitmq-self/rabbitmq-self.infra.module';\nimport { RedisSelfInfraProviderModule } from './redis-self/redis-self.infra.module';\nimport { PostgresSelfInfraProviderModule } from './postgres-self/postgres-self.infra.module';\nimport { MongodbAtlasInfraProviderModule } from './mongodb-atlas/mongodb-atlas.infra.module';\nimport { MongodbSelfInfraProviderModule } from './mongodb-self/mongodb-self.infra.module';\n// Slice 5 of `infra-provider-planner.md` — object-storage + stateless infra.\nimport { MinioSelfInfraProviderModule } from './minio-self/minio-self.infra.module';\nimport { AwsS3InfraProviderModule } from './aws-s3/aws-s3.infra.module';\nimport { ClamavSelfInfraProviderModule } from './clamav-self/clamav-self.infra.module';\nimport { GotenbergSelfInfraProviderModule } from './gotenberg-self/gotenberg-self.infra.module';\n\nexport { RabbitmqSelfInfraProviderModule } from './rabbitmq-self/rabbitmq-self.infra.module';\nexport { RedisSelfInfraProviderModule } from './redis-self/redis-self.infra.module';\nexport { PostgresSelfInfraProviderModule } from './postgres-self/postgres-self.infra.module';\nexport { MongodbAtlasInfraProviderModule } from './mongodb-atlas/mongodb-atlas.infra.module';\nexport { MongodbSelfInfraProviderModule } from './mongodb-self/mongodb-self.infra.module';\nexport { MinioSelfInfraProviderModule } from './minio-self/minio-self.infra.module';\nexport { AwsS3InfraProviderModule } from './aws-s3/aws-s3.infra.module';\nexport { ClamavSelfInfraProviderModule } from './clamav-self/clamav-self.infra.module';\nexport { GotenbergSelfInfraProviderModule } from './gotenberg-self/gotenberg-self.infra.module';\n\n/**\n * Canonical `InfraProvider` → `InfraProviderModule` catalog. Caller\n * (CLI, apps-manager) builds a `Map<InfraProvider, InfraProviderModule>`\n * from this constant when threading modules into\n * `materializeInfraSection`.\n *\n * **Why a const array instead of a `Map` literal**: arrays serialize\n * stably under tsc and roll up to a deterministic order. The\n * downstream `Map` is built once at program start; the catalog\n * itself is a value-level fixture.\n *\n * **Coverage** (Slice 5 baseline, 2026-05-10):\n * - `RABBITMQ_SELF`, `REDIS_SELF`, `POSTGRES_SELF`, `MONGODB_SELF`,\n * `MONGODB_ATLAS` — five stateful modules with mint/revoke\n * methods.\n * - `MINIO_SELF`, `AWS_S3` — object storage (shared-bucket model;\n * no per-app credentials).\n * - `CLAMAV_SELF`, `GOTENBERG_SELF` — stateless infra (no auth, no\n * per-app state; `plan()` only).\n *\n * **NOT covered** (catalog entries without modules):\n * - `POSTGRES_MANAGED`, `POSTGRES_AWS_RDS`, `POSTGRES_GCP_CLOUD_SQL`,\n * `POSTGRES_AZURE_DATABASE`, `POSTGRES_SUPABASE`, `POSTGRES_NEON`,\n * `POSTGRES_RENDER` — managed-Postgres brand catalog entries\n * (Slice 4.B.0). No module yet; trigger = real customer.\n *\n * `materializeInfraSection` falls back to the authored-entry digest\n * when a selected `InfraProvider` is not in the map (preserves the\n * Slice 2.5b behavior for uncovered providers).\n */\nexport const INFRA_PROVIDER_MODULES: ReadonlyArray<InfraProviderModule> = [\n RabbitmqSelfInfraProviderModule,\n RedisSelfInfraProviderModule,\n PostgresSelfInfraProviderModule,\n MongodbAtlasInfraProviderModule,\n MongodbSelfInfraProviderModule,\n MinioSelfInfraProviderModule,\n AwsS3InfraProviderModule,\n ClamavSelfInfraProviderModule,\n GotenbergSelfInfraProviderModule,\n];\n\n/**\n * Every `Wildo_Application_Secrets` JSON path that ANY provider module can mint per runtime\n * principal — the union of the catalog's own static declarations.\n *\n * ## Why this is derived rather than written down\n *\n * The consumer is the secret-DELIVERY path in apps-manager: it strips exactly this set from the\n * application blob before overlaying a principal's own material, so a principal receives a\n * credential only where one was minted FOR IT. A hand-maintained copy at that consumer would be a\n * second source — add a provider that mints a new credential path and delivery keeps shipping the\n * APPLICATION's value for it, silently. That is the defect class this repo has paid for twice: the\n * party that CONSTRAINS is not the party that DECLARES.\n *\n * Derived from `INFRA_PROVIDER_MODULES`, so a new module is covered the day it enters the catalog.\n *\n * Deliberately the union across ALL modules, not the ones a given application selected: a principal\n * must not receive an application-wide credential for a provider it does not use either, and the\n * selected set is not knowable at the delivery call site without threading app config through it.\n * Stripping a path no module minted for this app is harmless — it is absent from the blob anyway.\n */\nexport function collectPerPrincipalSecretBlobPaths(): ReadonlySet<string> {\n const paths = new Set<string>();\n for (const module of INFRA_PROVIDER_MODULES) {\n for (const path of module.perPrincipalSecretBlobPaths) {\n paths.add(path);\n }\n }\n return paths;\n}\n\n/**\n * Build a `Map<InfraProvider, InfraProviderModule>` for the\n * canonical catalog. Callers store the result once at startup and\n * pass it to `materializeInfraSection` (digest derivation) or to\n * orchestrator paths that look up modules by `InfraProvider`.\n */\nexport function buildInfraProviderModuleMap(): ReadonlyMap<InfraProvider, InfraProviderModule> {\n const map = new Map<InfraProvider, InfraProviderModule>();\n for (const m of INFRA_PROVIDER_MODULES) {\n if (map.has(m.provider)) {\n throw new Error(\n `INFRA_PROVIDER_MODULES contains duplicate provider '${m.provider}' — ` +\n `each InfraProvider may have at most one module.`,\n );\n }\n map.set(m.provider, m);\n }\n return map;\n}\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `MinioSelfInfraProviderModule` — sixth concrete `InfraProviderModule`
|
|
3
|
+
* (Slice 5.1 of `infra-provider-planner.md`, generalizing the
|
|
4
|
+
* Slice 2 / 4.B pattern to object storage).
|
|
5
|
+
*
|
|
6
|
+
* Implements the `MINIO_SELF` `InfraProvider`. One shared MinIO bucket
|
|
7
|
+
* serves every application; the object-key tree is namespaced
|
|
8
|
+
* `${prefix}/applications/${applicationId}/files/${fileId}`.
|
|
9
|
+
*
|
|
10
|
+
* `mintAppCredentials` / `revokeAppCredentials` are absent from THIS
|
|
11
|
+
* module, and that remains accurate — but it no longer means what it
|
|
12
|
+
* used to. Per-application credential isolation exists; it is simply
|
|
13
|
+
* not implemented here. See below.
|
|
14
|
+
*
|
|
15
|
+
* ## ⚠️ Corrected 2026-08-10 — this block described a posture the
|
|
16
|
+
* system no longer has
|
|
17
|
+
*
|
|
18
|
+
* It previously stated that every app shares "one access-key/secret-key
|
|
19
|
+
* pair", that per-app isolation "comes from `STORAGE_BUCKET_PREFIX`",
|
|
20
|
+
* and that app-scoped credentials were "NONE — apps consume the shared
|
|
21
|
+
* `STORAGE_*` env". All three were true when written and are now false:
|
|
22
|
+
*
|
|
23
|
+
* - Applications no longer receive any long-lived storage credential.
|
|
24
|
+
* `STORAGE_ACCESS_KEY` / `STORAGE_SECRET_KEY` are no longer emitted
|
|
25
|
+
* into application-tier env at all — they were the object store's
|
|
26
|
+
* ROOT pair, and handing them to every app, minion and worker is the
|
|
27
|
+
* exposure this was corrected to remove.
|
|
28
|
+
* - Isolation is enforced by a **credential**, not by a prefix
|
|
29
|
+
* convention. `apps-manager`'s `ObjectStorageStsBrokerService` issues
|
|
30
|
+
* short-lived STS sessions whose inline policy confines each
|
|
31
|
+
* application to `applications/<applicationId>/`, keyed on the
|
|
32
|
+
* platform-minted id — never on `STORAGE_BUCKET_PREFIX`, which an
|
|
33
|
+
* application authors for itself and therefore cannot be a security
|
|
34
|
+
* boundary. The prefix is now cosmetic namespacing.
|
|
35
|
+
*
|
|
36
|
+
* The three prerequisites this block listed for per-app credentials
|
|
37
|
+
* were addressed rather than deferred: the adapter threads per-principal
|
|
38
|
+
* credentials through a credential provider (1); the session policy
|
|
39
|
+
* replaces per-app bucket-policy authoring (2); and rotation safety (3)
|
|
40
|
+
* dissolved — STS credentials expire by construction, so there is no
|
|
41
|
+
* long-lived key whose rotation must be coordinated across pods.
|
|
42
|
+
*
|
|
43
|
+
* **Why the mint methods still do not live here.** MinIO's JS client
|
|
44
|
+
* ships no user-management API, so minting would need the `mc` binary or
|
|
45
|
+
* hand-written admin-API crypto. STS needs neither, and belongs with the
|
|
46
|
+
* service that holds the admin credential — apps-manager — rather than
|
|
47
|
+
* with this planning module, which contributes deployment topology.
|
|
48
|
+
*
|
|
49
|
+
* **Audience-1 (platform-admin)**: cluster-wide MinIO admin creds
|
|
50
|
+
* delivered via env. Consumers: `apps-manager` (which brokers STS from
|
|
51
|
+
* them) and `companion`. Whether these consumers should hold *root*
|
|
52
|
+
* specifically, rather than a narrower admin credential, is OQ-2 in the
|
|
53
|
+
* plan below.
|
|
54
|
+
*
|
|
55
|
+
* **Audience-3 (app-scoped)**: no per-app MinIO STATE is minted here —
|
|
56
|
+
* but applications DO receive a per-application storage credential at
|
|
57
|
+
* runtime, from apps-manager. "No app-scoped audience in this module" is
|
|
58
|
+
* not "no app-scoped credential in the system".
|
|
59
|
+
*
|
|
60
|
+
* See `.claude/plans/object-storage-root-credential-distribution.md`.
|
|
61
|
+
*/
|
|
62
|
+
import { InfraProviderModule } from '@wildo-ai/platform-config-lib';
|
|
63
|
+
export declare const MinioSelfInfraProviderModule: InfraProviderModule;
|
|
64
|
+
//# sourceMappingURL=minio-self.infra.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minio-self.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/minio-self/minio-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,OAAO,EAIL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AAIvC,eAAO,MAAM,4BAA4B,EAAE,mBA2C1C,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `MinioSelfInfraProviderModule` — sixth concrete `InfraProviderModule`
|
|
3
|
+
* (Slice 5.1 of `infra-provider-planner.md`, generalizing the
|
|
4
|
+
* Slice 2 / 4.B pattern to object storage).
|
|
5
|
+
*
|
|
6
|
+
* Implements the `MINIO_SELF` `InfraProvider`. One shared MinIO bucket
|
|
7
|
+
* serves every application; the object-key tree is namespaced
|
|
8
|
+
* `${prefix}/applications/${applicationId}/files/${fileId}`.
|
|
9
|
+
*
|
|
10
|
+
* `mintAppCredentials` / `revokeAppCredentials` are absent from THIS
|
|
11
|
+
* module, and that remains accurate — but it no longer means what it
|
|
12
|
+
* used to. Per-application credential isolation exists; it is simply
|
|
13
|
+
* not implemented here. See below.
|
|
14
|
+
*
|
|
15
|
+
* ## ⚠️ Corrected 2026-08-10 — this block described a posture the
|
|
16
|
+
* system no longer has
|
|
17
|
+
*
|
|
18
|
+
* It previously stated that every app shares "one access-key/secret-key
|
|
19
|
+
* pair", that per-app isolation "comes from `STORAGE_BUCKET_PREFIX`",
|
|
20
|
+
* and that app-scoped credentials were "NONE — apps consume the shared
|
|
21
|
+
* `STORAGE_*` env". All three were true when written and are now false:
|
|
22
|
+
*
|
|
23
|
+
* - Applications no longer receive any long-lived storage credential.
|
|
24
|
+
* `STORAGE_ACCESS_KEY` / `STORAGE_SECRET_KEY` are no longer emitted
|
|
25
|
+
* into application-tier env at all — they were the object store's
|
|
26
|
+
* ROOT pair, and handing them to every app, minion and worker is the
|
|
27
|
+
* exposure this was corrected to remove.
|
|
28
|
+
* - Isolation is enforced by a **credential**, not by a prefix
|
|
29
|
+
* convention. `apps-manager`'s `ObjectStorageStsBrokerService` issues
|
|
30
|
+
* short-lived STS sessions whose inline policy confines each
|
|
31
|
+
* application to `applications/<applicationId>/`, keyed on the
|
|
32
|
+
* platform-minted id — never on `STORAGE_BUCKET_PREFIX`, which an
|
|
33
|
+
* application authors for itself and therefore cannot be a security
|
|
34
|
+
* boundary. The prefix is now cosmetic namespacing.
|
|
35
|
+
*
|
|
36
|
+
* The three prerequisites this block listed for per-app credentials
|
|
37
|
+
* were addressed rather than deferred: the adapter threads per-principal
|
|
38
|
+
* credentials through a credential provider (1); the session policy
|
|
39
|
+
* replaces per-app bucket-policy authoring (2); and rotation safety (3)
|
|
40
|
+
* dissolved — STS credentials expire by construction, so there is no
|
|
41
|
+
* long-lived key whose rotation must be coordinated across pods.
|
|
42
|
+
*
|
|
43
|
+
* **Why the mint methods still do not live here.** MinIO's JS client
|
|
44
|
+
* ships no user-management API, so minting would need the `mc` binary or
|
|
45
|
+
* hand-written admin-API crypto. STS needs neither, and belongs with the
|
|
46
|
+
* service that holds the admin credential — apps-manager — rather than
|
|
47
|
+
* with this planning module, which contributes deployment topology.
|
|
48
|
+
*
|
|
49
|
+
* **Audience-1 (platform-admin)**: cluster-wide MinIO admin creds
|
|
50
|
+
* delivered via env. Consumers: `apps-manager` (which brokers STS from
|
|
51
|
+
* them) and `companion`. Whether these consumers should hold *root*
|
|
52
|
+
* specifically, rather than a narrower admin credential, is OQ-2 in the
|
|
53
|
+
* plan below.
|
|
54
|
+
*
|
|
55
|
+
* **Audience-3 (app-scoped)**: no per-app MinIO STATE is minted here —
|
|
56
|
+
* but applications DO receive a per-application storage credential at
|
|
57
|
+
* runtime, from apps-manager. "No app-scoped audience in this module" is
|
|
58
|
+
* not "no app-scoped credential in the system".
|
|
59
|
+
*
|
|
60
|
+
* See `.claude/plans/object-storage-root-credential-distribution.md`.
|
|
61
|
+
*/
|
|
62
|
+
import { InfraCapability, InfraPlatformAdminConsumer, InfraProvider, } from '@wildo-ai/platform-config-lib';
|
|
63
|
+
const DEFAULT_MINIO_PORT = 9000;
|
|
64
|
+
export const MinioSelfInfraProviderModule = {
|
|
65
|
+
provider: InfraProvider.MINIO_SELF,
|
|
66
|
+
supportedCapabilities: [InfraCapability.OBJECT_STORAGE],
|
|
67
|
+
/**
|
|
68
|
+
* Shared bucket, shared root key — same as AWS S3: object-storage confinement is an STS session
|
|
69
|
+
* policy minted per request, never a per-principal secret in the application blob.
|
|
70
|
+
*/
|
|
71
|
+
perPrincipalSecretBlobPaths: [],
|
|
72
|
+
plan() {
|
|
73
|
+
return {
|
|
74
|
+
dockerComposeServices: ['minio'],
|
|
75
|
+
k8sResources: ['templates/infra/k8s/internal/minio/deployment.yaml.hbs'],
|
|
76
|
+
platformAdmin: {
|
|
77
|
+
envVars: {
|
|
78
|
+
STORAGE_PROVIDER: 'minio',
|
|
79
|
+
STORAGE_ENDPOINT: 'minio',
|
|
80
|
+
STORAGE_PORT: String(DEFAULT_MINIO_PORT),
|
|
81
|
+
STORAGE_USE_SSL: 'false',
|
|
82
|
+
},
|
|
83
|
+
// Cluster-wide bucket access creds. Every app uses the same
|
|
84
|
+
// pair. Per-app isolation is via `STORAGE_BUCKET_PREFIX`
|
|
85
|
+
// (see module JSDoc).
|
|
86
|
+
secretEnvVars: ['STORAGE_ACCESS_KEY', 'STORAGE_SECRET_KEY'],
|
|
87
|
+
consumers: [
|
|
88
|
+
InfraPlatformAdminConsumer.APPS_MANAGER,
|
|
89
|
+
InfraPlatformAdminConsumer.COMPANION,
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
// Apps receive `STORAGE_*` via the bootstrap env contract
|
|
93
|
+
// (engine-side `application-backend-env.contracts.ts`). No
|
|
94
|
+
// per-app blob-path additions because the values are static
|
|
95
|
+
// across all apps in the same env. `appRuntime` intentionally
|
|
96
|
+
// empty so the contribution stays an honest "shared bucket,
|
|
97
|
+
// no per-app state".
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
// No `mintAppCredentials` / `revokeAppCredentials` —
|
|
101
|
+
// shared-bucket model (see module JSDoc).
|
|
102
|
+
// No `mintPlatformServiceCredentials` — platform services use the
|
|
103
|
+
// same shared bucket as customer apps.
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=minio-self.infra.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minio-self.infra.module.js","sourceRoot":"","sources":["../../../../src/minio-self/minio-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,aAAa,GAGd,MAAM,+BAA+B,CAAC;AAEvC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,MAAM,CAAC,MAAM,4BAA4B,GAAwB;IAC/D,QAAQ,EAAE,aAAa,CAAC,UAAU;IAClC,qBAAqB,EAAE,CAAC,eAAe,CAAC,cAAc,CAAC;IAEvD;;;OAGG;IACH,2BAA2B,EAAE,EAAE;IAE/B,IAAI;QACF,OAAO;YACL,qBAAqB,EAAE,CAAC,OAAO,CAAC;YAChC,YAAY,EAAE,CAAC,wDAAwD,CAAC;YACxE,aAAa,EAAE;gBACb,OAAO,EAAE;oBACP,gBAAgB,EAAE,OAAO;oBACzB,gBAAgB,EAAE,OAAO;oBACzB,YAAY,EAAE,MAAM,CAAC,kBAAkB,CAAC;oBACxC,eAAe,EAAE,OAAO;iBACzB;gBACD,4DAA4D;gBAC5D,yDAAyD;gBACzD,sBAAsB;gBACtB,aAAa,EAAE,CAAC,oBAAoB,EAAE,oBAAoB,CAAC;gBAC3D,SAAS,EAAE;oBACT,0BAA0B,CAAC,YAAY;oBACvC,0BAA0B,CAAC,SAAS;iBACrC;aACF;YACD,0DAA0D;YAC1D,2DAA2D;YAC3D,4DAA4D;YAC5D,8DAA8D;YAC9D,4DAA4D;YAC5D,qBAAqB;SACtB,CAAC;IACJ,CAAC;IAED,qDAAqD;IACrD,0CAA0C;IAC1C,kEAAkE;IAClE,uCAAuC;CACxC,CAAC","sourcesContent":["/**\n * `MinioSelfInfraProviderModule` — sixth concrete `InfraProviderModule`\n * (Slice 5.1 of `infra-provider-planner.md`, generalizing the\n * Slice 2 / 4.B pattern to object storage).\n *\n * Implements the `MINIO_SELF` `InfraProvider`. One shared MinIO bucket\n * serves every application; the object-key tree is namespaced\n * `${prefix}/applications/${applicationId}/files/${fileId}`.\n *\n * `mintAppCredentials` / `revokeAppCredentials` are absent from THIS\n * module, and that remains accurate — but it no longer means what it\n * used to. Per-application credential isolation exists; it is simply\n * not implemented here. See below.\n *\n * ## ⚠️ Corrected 2026-08-10 — this block described a posture the\n * system no longer has\n *\n * It previously stated that every app shares \"one access-key/secret-key\n * pair\", that per-app isolation \"comes from `STORAGE_BUCKET_PREFIX`\",\n * and that app-scoped credentials were \"NONE — apps consume the shared\n * `STORAGE_*` env\". All three were true when written and are now false:\n *\n * - Applications no longer receive any long-lived storage credential.\n * `STORAGE_ACCESS_KEY` / `STORAGE_SECRET_KEY` are no longer emitted\n * into application-tier env at all — they were the object store's\n * ROOT pair, and handing them to every app, minion and worker is the\n * exposure this was corrected to remove.\n * - Isolation is enforced by a **credential**, not by a prefix\n * convention. `apps-manager`'s `ObjectStorageStsBrokerService` issues\n * short-lived STS sessions whose inline policy confines each\n * application to `applications/<applicationId>/`, keyed on the\n * platform-minted id — never on `STORAGE_BUCKET_PREFIX`, which an\n * application authors for itself and therefore cannot be a security\n * boundary. The prefix is now cosmetic namespacing.\n *\n * The three prerequisites this block listed for per-app credentials\n * were addressed rather than deferred: the adapter threads per-principal\n * credentials through a credential provider (1); the session policy\n * replaces per-app bucket-policy authoring (2); and rotation safety (3)\n * dissolved — STS credentials expire by construction, so there is no\n * long-lived key whose rotation must be coordinated across pods.\n *\n * **Why the mint methods still do not live here.** MinIO's JS client\n * ships no user-management API, so minting would need the `mc` binary or\n * hand-written admin-API crypto. STS needs neither, and belongs with the\n * service that holds the admin credential — apps-manager — rather than\n * with this planning module, which contributes deployment topology.\n *\n * **Audience-1 (platform-admin)**: cluster-wide MinIO admin creds\n * delivered via env. Consumers: `apps-manager` (which brokers STS from\n * them) and `companion`. Whether these consumers should hold *root*\n * specifically, rather than a narrower admin credential, is OQ-2 in the\n * plan below.\n *\n * **Audience-3 (app-scoped)**: no per-app MinIO STATE is minted here —\n * but applications DO receive a per-application storage credential at\n * runtime, from apps-manager. \"No app-scoped audience in this module\" is\n * not \"no app-scoped credential in the system\".\n *\n * See `.claude/plans/object-storage-root-credential-distribution.md`.\n */\n\nimport {\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n} from '@wildo-ai/platform-config-lib';\n\nconst DEFAULT_MINIO_PORT = 9000;\n\nexport const MinioSelfInfraProviderModule: InfraProviderModule = {\n provider: InfraProvider.MINIO_SELF,\n supportedCapabilities: [InfraCapability.OBJECT_STORAGE],\n\n /**\n * Shared bucket, shared root key — same as AWS S3: object-storage confinement is an STS session\n * policy minted per request, never a per-principal secret in the application blob.\n */\n perPrincipalSecretBlobPaths: [],\n\n plan(): InfraProviderPlanContribution {\n return {\n dockerComposeServices: ['minio'],\n k8sResources: ['templates/infra/k8s/internal/minio/deployment.yaml.hbs'],\n platformAdmin: {\n envVars: {\n STORAGE_PROVIDER: 'minio',\n STORAGE_ENDPOINT: 'minio',\n STORAGE_PORT: String(DEFAULT_MINIO_PORT),\n STORAGE_USE_SSL: 'false',\n },\n // Cluster-wide bucket access creds. Every app uses the same\n // pair. Per-app isolation is via `STORAGE_BUCKET_PREFIX`\n // (see module JSDoc).\n secretEnvVars: ['STORAGE_ACCESS_KEY', 'STORAGE_SECRET_KEY'],\n consumers: [\n InfraPlatformAdminConsumer.APPS_MANAGER,\n InfraPlatformAdminConsumer.COMPANION,\n ],\n },\n // Apps receive `STORAGE_*` via the bootstrap env contract\n // (engine-side `application-backend-env.contracts.ts`). No\n // per-app blob-path additions because the values are static\n // across all apps in the same env. `appRuntime` intentionally\n // empty so the contribution stays an honest \"shared bucket,\n // no per-app state\".\n };\n },\n\n // No `mintAppCredentials` / `revokeAppCredentials` —\n // shared-bucket model (see module JSDoc).\n // No `mintPlatformServiceCredentials` — platform services use the\n // same shared bucket as customer apps.\n};\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `MongodbAtlasInfraProviderModule` — fourth concrete `InfraProviderModule`
|
|
3
|
+
* (Slice 4.B.3 of `infra-provider-planner.md`, generalizing the Slice
|
|
4
|
+
* 2 / 4.B.1 / 4.B.2 pattern to MongoDB Atlas).
|
|
5
|
+
*
|
|
6
|
+
* Implements the `MONGODB_ATLAS` `InfraProvider` over the
|
|
7
|
+
* `IMongoAtlasClient` transport (Atlas REST API + digest auth, exposed
|
|
8
|
+
* by `MongoAtlasManagerService` after the Slice 4.B.3 5C policy/
|
|
9
|
+
* transport split). Owns ALL per-app naming policy — the per-app
|
|
10
|
+
* username convention (`<applicationNamespace>_user`, mirroring the
|
|
11
|
+
* legacy `getOrGenerateCredentials.generateNewCredentials`).
|
|
12
|
+
*
|
|
13
|
+
* **What this module reads from `AppCredentialMintingContext`**:
|
|
14
|
+
* - `applicationNamespace` — used as the source for the per-app
|
|
15
|
+
* username (legacy convention `${ns}_user`).
|
|
16
|
+
* - `secretInputs['database.mongodb.password']` — orchestrator-
|
|
17
|
+
* supplied password (module is upsert-only, never generator).
|
|
18
|
+
* - `applicationConfigInputs['databaseName']` — required, the
|
|
19
|
+
* per-app database name (orchestrator derives via
|
|
20
|
+
* `deriveDefaultMongoDatabaseName`).
|
|
21
|
+
* - `applicationConfigInputs['clusterName']` — required, the
|
|
22
|
+
* Atlas cluster name (orchestrator passes `appInfo.clusterName ||
|
|
23
|
+
* 'Cluster0'`).
|
|
24
|
+
* - `adminClient` — narrowed to `IMongoAtlasClient`.
|
|
25
|
+
*
|
|
26
|
+
* **Two audiences this module serves:**
|
|
27
|
+
*
|
|
28
|
+
* 1. **Audience-1 (platform-admin)**: Atlas API public/private key
|
|
29
|
+
* pair + project id + base URL. Consumers: `apps-manager` only
|
|
30
|
+
* (companion uses self-managed locally; Atlas is remote-only).
|
|
31
|
+
*
|
|
32
|
+
* 2. **Audience-3 (app-scoped, via `mintAppCredentials`)**:
|
|
33
|
+
* per-customer-app Atlas database user. Result threaded into
|
|
34
|
+
* `Wildo_Application_Secrets.database.mongodb.{username,password}`
|
|
35
|
+
* via `_merge_WildoApplicationSecrets`; the SRV connection
|
|
36
|
+
* string returned via `runtimeArtifacts.connectionString` for
|
|
37
|
+
* transient app-init operations (NOT persisted — the persistence
|
|
38
|
+
* shape is split between config (host/protocol/options/database)
|
|
39
|
+
* and secrets (username/password)).
|
|
40
|
+
*
|
|
41
|
+
* **NOT served (D-4 of Slice 4.B locked decisions, 2026-05-10)**:
|
|
42
|
+
* - **Audience-2 (platform-service-scoped)**: no platform service
|
|
43
|
+
* uses Atlas for inter-service messaging today. Method omitted.
|
|
44
|
+
*
|
|
45
|
+
* **`revokeAppCredentials`**: declared and tested, but the integration
|
|
46
|
+
* call site does NOT exist in `wildo-application-lifecycle.service.ts`
|
|
47
|
+
* (no app-deletion ceremony today — F6 of plan review 2026-05-06).
|
|
48
|
+
* Method ships as a forward-compat seam.
|
|
49
|
+
*/
|
|
50
|
+
import { InfraProviderModule } from '@wildo-ai/platform-config-lib';
|
|
51
|
+
export declare const MongodbAtlasInfraProviderModule: InfraProviderModule;
|
|
52
|
+
//# sourceMappingURL=mongodb-atlas.infra.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongodb-atlas.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/mongodb-atlas/mongodb-atlas.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAOL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AAqDvC,eAAO,MAAM,+BAA+B,EAAE,mBA2H7C,CAAC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `MongodbAtlasInfraProviderModule` — fourth concrete `InfraProviderModule`
|
|
3
|
+
* (Slice 4.B.3 of `infra-provider-planner.md`, generalizing the Slice
|
|
4
|
+
* 2 / 4.B.1 / 4.B.2 pattern to MongoDB Atlas).
|
|
5
|
+
*
|
|
6
|
+
* Implements the `MONGODB_ATLAS` `InfraProvider` over the
|
|
7
|
+
* `IMongoAtlasClient` transport (Atlas REST API + digest auth, exposed
|
|
8
|
+
* by `MongoAtlasManagerService` after the Slice 4.B.3 5C policy/
|
|
9
|
+
* transport split). Owns ALL per-app naming policy — the per-app
|
|
10
|
+
* username convention (`<applicationNamespace>_user`, mirroring the
|
|
11
|
+
* legacy `getOrGenerateCredentials.generateNewCredentials`).
|
|
12
|
+
*
|
|
13
|
+
* **What this module reads from `AppCredentialMintingContext`**:
|
|
14
|
+
* - `applicationNamespace` — used as the source for the per-app
|
|
15
|
+
* username (legacy convention `${ns}_user`).
|
|
16
|
+
* - `secretInputs['database.mongodb.password']` — orchestrator-
|
|
17
|
+
* supplied password (module is upsert-only, never generator).
|
|
18
|
+
* - `applicationConfigInputs['databaseName']` — required, the
|
|
19
|
+
* per-app database name (orchestrator derives via
|
|
20
|
+
* `deriveDefaultMongoDatabaseName`).
|
|
21
|
+
* - `applicationConfigInputs['clusterName']` — required, the
|
|
22
|
+
* Atlas cluster name (orchestrator passes `appInfo.clusterName ||
|
|
23
|
+
* 'Cluster0'`).
|
|
24
|
+
* - `adminClient` — narrowed to `IMongoAtlasClient`.
|
|
25
|
+
*
|
|
26
|
+
* **Two audiences this module serves:**
|
|
27
|
+
*
|
|
28
|
+
* 1. **Audience-1 (platform-admin)**: Atlas API public/private key
|
|
29
|
+
* pair + project id + base URL. Consumers: `apps-manager` only
|
|
30
|
+
* (companion uses self-managed locally; Atlas is remote-only).
|
|
31
|
+
*
|
|
32
|
+
* 2. **Audience-3 (app-scoped, via `mintAppCredentials`)**:
|
|
33
|
+
* per-customer-app Atlas database user. Result threaded into
|
|
34
|
+
* `Wildo_Application_Secrets.database.mongodb.{username,password}`
|
|
35
|
+
* via `_merge_WildoApplicationSecrets`; the SRV connection
|
|
36
|
+
* string returned via `runtimeArtifacts.connectionString` for
|
|
37
|
+
* transient app-init operations (NOT persisted — the persistence
|
|
38
|
+
* shape is split between config (host/protocol/options/database)
|
|
39
|
+
* and secrets (username/password)).
|
|
40
|
+
*
|
|
41
|
+
* **NOT served (D-4 of Slice 4.B locked decisions, 2026-05-10)**:
|
|
42
|
+
* - **Audience-2 (platform-service-scoped)**: no platform service
|
|
43
|
+
* uses Atlas for inter-service messaging today. Method omitted.
|
|
44
|
+
*
|
|
45
|
+
* **`revokeAppCredentials`**: declared and tested, but the integration
|
|
46
|
+
* call site does NOT exist in `wildo-application-lifecycle.service.ts`
|
|
47
|
+
* (no app-deletion ceremony today — F6 of plan review 2026-05-06).
|
|
48
|
+
* Method ships as a forward-compat seam.
|
|
49
|
+
*/
|
|
50
|
+
import { InfraCapability, InfraPlatformAdminConsumer, InfraProvider, } from '@wildo-ai/platform-config-lib';
|
|
51
|
+
/**
|
|
52
|
+
* The `Wildo_Application_Secrets` JSON paths this module writes
|
|
53
|
+
* through `mintAppCredentials.secretBlobValues`. Declared once and
|
|
54
|
+
* reused as `appRuntime.perAppSecretBlobPaths` in `plan()` AND as
|
|
55
|
+
* the keys of the result, so a refactor that renames a path fails
|
|
56
|
+
* loudly at one place.
|
|
57
|
+
*/
|
|
58
|
+
const MONGODB_USERNAME_PATH = 'database.mongodb.username';
|
|
59
|
+
const MONGODB_PASSWORD_PATH = 'database.mongodb.password';
|
|
60
|
+
/**
|
|
61
|
+
* The paths this module mints PER RUNTIME PRINCIPAL — the single source for both consumers.
|
|
62
|
+
*
|
|
63
|
+
* Read by `InfraProviderModule.perPrincipalSecretBlobPaths` (statically, so apps-manager can strip
|
|
64
|
+
* these from the application blob before delivering a principal's own material) AND spread into
|
|
65
|
+
* `appRuntime.perAppSecretBlobPaths` inside `plan()`. Declaring the list twice is how the delivery
|
|
66
|
+
* path would silently stop covering a path this module still mints.
|
|
67
|
+
*/
|
|
68
|
+
const PER_PRINCIPAL_SECRET_BLOB_PATHS = [MONGODB_USERNAME_PATH, MONGODB_PASSWORD_PATH];
|
|
69
|
+
/**
|
|
70
|
+
* Key the module looks for in `secretInputs` for the orchestrator-
|
|
71
|
+
* supplied password.
|
|
72
|
+
*/
|
|
73
|
+
const MONGODB_PASSWORD_INPUT_KEY = 'database.mongodb.password';
|
|
74
|
+
/**
|
|
75
|
+
* Required keys in `applicationConfigInputs`. The orchestrator
|
|
76
|
+
* derives both before calling `mintAppCredentials`.
|
|
77
|
+
*/
|
|
78
|
+
const DATABASE_NAME_INPUT_KEY = 'databaseName';
|
|
79
|
+
const CLUSTER_NAME_INPUT_KEY = 'clusterName';
|
|
80
|
+
/**
|
|
81
|
+
* Key in `runtimeArtifacts` carrying the SRV connection string with
|
|
82
|
+
* credentials embedded. Exposed for orchestrator transient use
|
|
83
|
+
* (app-init seeding); deliberately NOT persisted.
|
|
84
|
+
*/
|
|
85
|
+
const CONNECTION_STRING_ARTIFACT_KEY = 'connectionString';
|
|
86
|
+
/**
|
|
87
|
+
* Compute the per-app Atlas database username. Mirrors the legacy
|
|
88
|
+
* convention at
|
|
89
|
+
* `wildo-application-lifecycle.service.ts:2607` /
|
|
90
|
+
* `generateNewCredentials` — `mongoUsername = ${ns}_user`.
|
|
91
|
+
* Centralized here so future changes touch one site.
|
|
92
|
+
*/
|
|
93
|
+
function buildMongodbUsername(applicationNamespace) {
|
|
94
|
+
return `${applicationNamespace}_user`;
|
|
95
|
+
}
|
|
96
|
+
export const MongodbAtlasInfraProviderModule = {
|
|
97
|
+
provider: InfraProvider.MONGODB_ATLAS,
|
|
98
|
+
supportedCapabilities: [InfraCapability.DOCUMENT_DATABASE],
|
|
99
|
+
perPrincipalSecretBlobPaths: PER_PRINCIPAL_SECRET_BLOB_PATHS,
|
|
100
|
+
plan() {
|
|
101
|
+
return {
|
|
102
|
+
// Atlas is remote-only — no compose service / no K8s manifest.
|
|
103
|
+
// The plan's `externalEndpoints` (in the wider envelope) carry
|
|
104
|
+
// the Atlas host topology; this module's `plan()` covers only
|
|
105
|
+
// admin-API access + per-app secret paths.
|
|
106
|
+
platformAdmin: {
|
|
107
|
+
envVars: {
|
|
108
|
+
// ATLAS_API_BASE_URL is non-secret because it identifies
|
|
109
|
+
// the Atlas API surface (default https://cloud.mongodb.com/api/atlas/v1.0).
|
|
110
|
+
// ATLAS_PROJECT_ID + ATLAS_CLUSTER_NAME are non-secret
|
|
111
|
+
// identifiers — public information about the customer's
|
|
112
|
+
// Atlas project.
|
|
113
|
+
ATLAS_API_BASE_URL: 'https://cloud.mongodb.com/api/atlas/v1.0',
|
|
114
|
+
// PROJECT_ID + CLUSTER_NAME default to authored values; the
|
|
115
|
+
// CLI's env-vars-builder writes them per-env from the
|
|
116
|
+
// operator's `wildo.infra.<env>.config.ts > backingServices.mongodb`.
|
|
117
|
+
ATLAS_PROJECT_ID: '',
|
|
118
|
+
ATLAS_CLUSTER_NAME: 'Cluster0',
|
|
119
|
+
},
|
|
120
|
+
secretEnvVars: ['ATLAS_PUBLIC_KEY', 'ATLAS_PRIVATE_KEY'],
|
|
121
|
+
// Atlas is admin-API only for apps-manager. Companion runs
|
|
122
|
+
// locally against self-managed Mongo, NOT Atlas (no
|
|
123
|
+
// companion consumer for the Atlas variant).
|
|
124
|
+
consumers: [InfraPlatformAdminConsumer.APPS_MANAGER],
|
|
125
|
+
},
|
|
126
|
+
appRuntime: {
|
|
127
|
+
// Static topology for the app pod. The actual host /
|
|
128
|
+
// protocol / options for Atlas connections are derived
|
|
129
|
+
// server-side at runtime from cluster info; the persistence
|
|
130
|
+
// shape mirrors the legacy `_merge_WildoApplicationConfig`
|
|
131
|
+
// mongodb section.
|
|
132
|
+
perAppSecretBlobPaths: [...PER_PRINCIPAL_SECRET_BLOB_PATHS],
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
async mintAppCredentials(context) {
|
|
137
|
+
const adminClient = context.adminClient;
|
|
138
|
+
const password = context.secretInputs[MONGODB_PASSWORD_INPUT_KEY];
|
|
139
|
+
if (!password) {
|
|
140
|
+
throw new Error(`MongodbAtlasInfraProviderModule.mintAppCredentials: ` +
|
|
141
|
+
`secretInputs is missing '${MONGODB_PASSWORD_INPUT_KEY}'. ` +
|
|
142
|
+
`The orchestrator MUST supply the Mongo password — modules are upsert-only.`);
|
|
143
|
+
}
|
|
144
|
+
if (!context.applicationNamespace) {
|
|
145
|
+
throw new Error(`MongodbAtlasInfraProviderModule.mintAppCredentials: ` +
|
|
146
|
+
`applicationNamespace is empty. ` +
|
|
147
|
+
`The orchestrator MUST supply a non-empty namespace.`);
|
|
148
|
+
}
|
|
149
|
+
const databaseName = context.applicationConfigInputs?.[DATABASE_NAME_INPUT_KEY];
|
|
150
|
+
if (!databaseName) {
|
|
151
|
+
throw new Error(`MongodbAtlasInfraProviderModule.mintAppCredentials: ` +
|
|
152
|
+
`applicationConfigInputs is missing '${DATABASE_NAME_INPUT_KEY}'. ` +
|
|
153
|
+
`The orchestrator MUST supply the per-app database name.`);
|
|
154
|
+
}
|
|
155
|
+
const clusterName = context.applicationConfigInputs?.[CLUSTER_NAME_INPUT_KEY];
|
|
156
|
+
if (!clusterName) {
|
|
157
|
+
throw new Error(`MongodbAtlasInfraProviderModule.mintAppCredentials: ` +
|
|
158
|
+
`applicationConfigInputs is missing '${CLUSTER_NAME_INPUT_KEY}'. ` +
|
|
159
|
+
`The orchestrator MUST supply the Atlas cluster name.`);
|
|
160
|
+
}
|
|
161
|
+
const username = buildMongodbUsername(context.applicationNamespace);
|
|
162
|
+
// POST /databaseUsers (with PATCH-fallback) — idempotent upsert.
|
|
163
|
+
// Returns the SRV connection string with credentials embedded.
|
|
164
|
+
const { connectionString } = await adminClient.upsertApplicationDatabase({
|
|
165
|
+
clusterName,
|
|
166
|
+
databaseName,
|
|
167
|
+
username,
|
|
168
|
+
password,
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
secretBlobValues: {
|
|
172
|
+
[MONGODB_USERNAME_PATH]: username,
|
|
173
|
+
// Echo of input password — proves upsert-only.
|
|
174
|
+
[MONGODB_PASSWORD_PATH]: password,
|
|
175
|
+
},
|
|
176
|
+
runtimeArtifacts: {
|
|
177
|
+
// Transient only — orchestrator uses for app-init seeding,
|
|
178
|
+
// does NOT persist.
|
|
179
|
+
[CONNECTION_STRING_ARTIFACT_KEY]: connectionString,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
/**
|
|
184
|
+
* Tear down the per-app Atlas database user. Idempotent.
|
|
185
|
+
*
|
|
186
|
+
* **Caller status**: no caller wires this in Slices 1–4.B.3.
|
|
187
|
+
* Forward-compat seam.
|
|
188
|
+
*/
|
|
189
|
+
async revokeAppCredentials(context) {
|
|
190
|
+
if (!context.applicationNamespace) {
|
|
191
|
+
throw new Error(`MongodbAtlasInfraProviderModule.revokeAppCredentials: ` +
|
|
192
|
+
`applicationNamespace is empty. ` +
|
|
193
|
+
`The orchestrator MUST supply a non-empty namespace.`);
|
|
194
|
+
}
|
|
195
|
+
const adminClient = context.adminClient;
|
|
196
|
+
const username = buildMongodbUsername(context.applicationNamespace);
|
|
197
|
+
await adminClient.deleteDatabaseUser(username);
|
|
198
|
+
},
|
|
199
|
+
// No `mintPlatformServiceCredentials` (D-4).
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=mongodb-atlas.infra.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongodb-atlas.infra.module.js","sourceRoot":"","sources":["../../../../src/mongodb-atlas/mongodb-atlas.infra.module.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `MongodbAtlasInfraProviderModule` — fourth concrete `InfraProviderModule`\n * (Slice 4.B.3 of `infra-provider-planner.md`, generalizing the Slice\n * 2 / 4.B.1 / 4.B.2 pattern to MongoDB Atlas).\n *\n * Implements the `MONGODB_ATLAS` `InfraProvider` over the\n * `IMongoAtlasClient` transport (Atlas REST API + digest auth, exposed\n * by `MongoAtlasManagerService` after the Slice 4.B.3 5C policy/\n * transport split). Owns ALL per-app naming policy — the per-app\n * username convention (`<applicationNamespace>_user`, mirroring the\n * legacy `getOrGenerateCredentials.generateNewCredentials`).\n *\n * **What this module reads from `AppCredentialMintingContext`**:\n * - `applicationNamespace` — used as the source for the per-app\n * username (legacy convention `${ns}_user`).\n * - `secretInputs['database.mongodb.password']` — orchestrator-\n * supplied password (module is upsert-only, never generator).\n * - `applicationConfigInputs['databaseName']` — required, the\n * per-app database name (orchestrator derives via\n * `deriveDefaultMongoDatabaseName`).\n * - `applicationConfigInputs['clusterName']` — required, the\n * Atlas cluster name (orchestrator passes `appInfo.clusterName ||\n * 'Cluster0'`).\n * - `adminClient` — narrowed to `IMongoAtlasClient`.\n *\n * **Two audiences this module serves:**\n *\n * 1. **Audience-1 (platform-admin)**: Atlas API public/private key\n * pair + project id + base URL. Consumers: `apps-manager` only\n * (companion uses self-managed locally; Atlas is remote-only).\n *\n * 2. **Audience-3 (app-scoped, via `mintAppCredentials`)**:\n * per-customer-app Atlas database user. Result threaded into\n * `Wildo_Application_Secrets.database.mongodb.{username,password}`\n * via `_merge_WildoApplicationSecrets`; the SRV connection\n * string returned via `runtimeArtifacts.connectionString` for\n * transient app-init operations (NOT persisted — the persistence\n * shape is split between config (host/protocol/options/database)\n * and secrets (username/password)).\n *\n * **NOT served (D-4 of Slice 4.B locked decisions, 2026-05-10)**:\n * - **Audience-2 (platform-service-scoped)**: no platform service\n * uses Atlas for inter-service messaging today. Method omitted.\n *\n * **`revokeAppCredentials`**: declared and tested, but the integration\n * call site does NOT exist in `wildo-application-lifecycle.service.ts`\n * (no app-deletion ceremony today — F6 of plan review 2026-05-06).\n * Method ships as a forward-compat seam.\n */\n\nimport {\n AppCredentialMintingContext,\n AppCredentialMintingResult,\n IMongoAtlasClient,\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n} from '@wildo-ai/platform-config-lib';\n\n/**\n * The `Wildo_Application_Secrets` JSON paths this module writes\n * through `mintAppCredentials.secretBlobValues`. Declared once and\n * reused as `appRuntime.perAppSecretBlobPaths` in `plan()` AND as\n * the keys of the result, so a refactor that renames a path fails\n * loudly at one place.\n */\nconst MONGODB_USERNAME_PATH = 'database.mongodb.username';\nconst MONGODB_PASSWORD_PATH = 'database.mongodb.password';\n/**\n * The paths this module mints PER RUNTIME PRINCIPAL — the single source for both consumers.\n *\n * Read by `InfraProviderModule.perPrincipalSecretBlobPaths` (statically, so apps-manager can strip\n * these from the application blob before delivering a principal's own material) AND spread into\n * `appRuntime.perAppSecretBlobPaths` inside `plan()`. Declaring the list twice is how the delivery\n * path would silently stop covering a path this module still mints.\n */\nconst PER_PRINCIPAL_SECRET_BLOB_PATHS: ReadonlyArray<string> = [MONGODB_USERNAME_PATH, MONGODB_PASSWORD_PATH];\n\n\n/**\n * Key the module looks for in `secretInputs` for the orchestrator-\n * supplied password.\n */\nconst MONGODB_PASSWORD_INPUT_KEY = 'database.mongodb.password';\n\n/**\n * Required keys in `applicationConfigInputs`. The orchestrator\n * derives both before calling `mintAppCredentials`.\n */\nconst DATABASE_NAME_INPUT_KEY = 'databaseName';\nconst CLUSTER_NAME_INPUT_KEY = 'clusterName';\n\n/**\n * Key in `runtimeArtifacts` carrying the SRV connection string with\n * credentials embedded. Exposed for orchestrator transient use\n * (app-init seeding); deliberately NOT persisted.\n */\nconst CONNECTION_STRING_ARTIFACT_KEY = 'connectionString';\n\n/**\n * Compute the per-app Atlas database username. Mirrors the legacy\n * convention at\n * `wildo-application-lifecycle.service.ts:2607` /\n * `generateNewCredentials` — `mongoUsername = ${ns}_user`.\n * Centralized here so future changes touch one site.\n */\nfunction buildMongodbUsername(applicationNamespace: string): string {\n return `${applicationNamespace}_user`;\n}\n\nexport const MongodbAtlasInfraProviderModule: InfraProviderModule = {\n provider: InfraProvider.MONGODB_ATLAS,\n supportedCapabilities: [InfraCapability.DOCUMENT_DATABASE],\n perPrincipalSecretBlobPaths: PER_PRINCIPAL_SECRET_BLOB_PATHS,\n\n plan(): InfraProviderPlanContribution {\n return {\n // Atlas is remote-only — no compose service / no K8s manifest.\n // The plan's `externalEndpoints` (in the wider envelope) carry\n // the Atlas host topology; this module's `plan()` covers only\n // admin-API access + per-app secret paths.\n platformAdmin: {\n envVars: {\n // ATLAS_API_BASE_URL is non-secret because it identifies\n // the Atlas API surface (default https://cloud.mongodb.com/api/atlas/v1.0).\n // ATLAS_PROJECT_ID + ATLAS_CLUSTER_NAME are non-secret\n // identifiers — public information about the customer's\n // Atlas project.\n ATLAS_API_BASE_URL: 'https://cloud.mongodb.com/api/atlas/v1.0',\n // PROJECT_ID + CLUSTER_NAME default to authored values; the\n // CLI's env-vars-builder writes them per-env from the\n // operator's `wildo.infra.<env>.config.ts > backingServices.mongodb`.\n ATLAS_PROJECT_ID: '',\n ATLAS_CLUSTER_NAME: 'Cluster0',\n },\n secretEnvVars: ['ATLAS_PUBLIC_KEY', 'ATLAS_PRIVATE_KEY'],\n // Atlas is admin-API only for apps-manager. Companion runs\n // locally against self-managed Mongo, NOT Atlas (no\n // companion consumer for the Atlas variant).\n consumers: [InfraPlatformAdminConsumer.APPS_MANAGER],\n },\n appRuntime: {\n // Static topology for the app pod. The actual host /\n // protocol / options for Atlas connections are derived\n // server-side at runtime from cluster info; the persistence\n // shape mirrors the legacy `_merge_WildoApplicationConfig`\n // mongodb section.\n perAppSecretBlobPaths: [...PER_PRINCIPAL_SECRET_BLOB_PATHS],\n },\n };\n },\n\n async mintAppCredentials(\n context: AppCredentialMintingContext,\n ): Promise<AppCredentialMintingResult> {\n const adminClient = context.adminClient as IMongoAtlasClient;\n const password = context.secretInputs[MONGODB_PASSWORD_INPUT_KEY];\n if (!password) {\n throw new Error(\n `MongodbAtlasInfraProviderModule.mintAppCredentials: ` +\n `secretInputs is missing '${MONGODB_PASSWORD_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the Mongo password — modules are upsert-only.`,\n );\n }\n if (!context.applicationNamespace) {\n throw new Error(\n `MongodbAtlasInfraProviderModule.mintAppCredentials: ` +\n `applicationNamespace is empty. ` +\n `The orchestrator MUST supply a non-empty namespace.`,\n );\n }\n const databaseName = context.applicationConfigInputs?.[DATABASE_NAME_INPUT_KEY];\n if (!databaseName) {\n throw new Error(\n `MongodbAtlasInfraProviderModule.mintAppCredentials: ` +\n `applicationConfigInputs is missing '${DATABASE_NAME_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the per-app database name.`,\n );\n }\n const clusterName = context.applicationConfigInputs?.[CLUSTER_NAME_INPUT_KEY];\n if (!clusterName) {\n throw new Error(\n `MongodbAtlasInfraProviderModule.mintAppCredentials: ` +\n `applicationConfigInputs is missing '${CLUSTER_NAME_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the Atlas cluster name.`,\n );\n }\n\n const username = buildMongodbUsername(context.applicationNamespace);\n\n // POST /databaseUsers (with PATCH-fallback) — idempotent upsert.\n // Returns the SRV connection string with credentials embedded.\n const { connectionString } = await adminClient.upsertApplicationDatabase({\n clusterName,\n databaseName,\n username,\n password,\n });\n\n return {\n secretBlobValues: {\n [MONGODB_USERNAME_PATH]: username,\n // Echo of input password — proves upsert-only.\n [MONGODB_PASSWORD_PATH]: password,\n },\n runtimeArtifacts: {\n // Transient only — orchestrator uses for app-init seeding,\n // does NOT persist.\n [CONNECTION_STRING_ARTIFACT_KEY]: connectionString,\n },\n };\n },\n\n /**\n * Tear down the per-app Atlas database user. Idempotent.\n *\n * **Caller status**: no caller wires this in Slices 1–4.B.3.\n * Forward-compat seam.\n */\n async revokeAppCredentials(context: AppCredentialMintingContext): Promise<void> {\n if (!context.applicationNamespace) {\n throw new Error(\n `MongodbAtlasInfraProviderModule.revokeAppCredentials: ` +\n `applicationNamespace is empty. ` +\n `The orchestrator MUST supply a non-empty namespace.`,\n );\n }\n const adminClient = context.adminClient as IMongoAtlasClient;\n const username = buildMongodbUsername(context.applicationNamespace);\n await adminClient.deleteDatabaseUser(username);\n },\n\n // No `mintPlatformServiceCredentials` (D-4).\n};\n"]}
|