@wildo-ai/platform-infra-providers 1.1.7 → 1.1.9
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/dist/esm/aws-s3/aws-s3.infra.module.d.ts.map +1 -1
- package/dist/esm/aws-s3/aws-s3.infra.module.js.map +1 -1
- package/dist/esm/clamav-self/clamav-self.infra.module.d.ts.map +1 -1
- package/dist/esm/immudb-self/immudb-self.infra.module.d.ts.map +1 -1
- package/dist/esm/immudb-self/immudb-self.infra.module.js.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/minio-self/minio-self.infra.module.d.ts.map +1 -1
- package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.d.ts.map +1 -1
- package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.js.map +1 -1
- package/dist/esm/mongodb-self/mongodb-self.infra.module.d.ts.map +1 -1
- package/dist/esm/mongodb-self/mongodb-self.infra.module.js.map +1 -1
- package/dist/esm/postgres-self/postgres-self.infra.module.d.ts.map +1 -1
- package/dist/esm/postgres-self/postgres-self.infra.module.js.map +1 -1
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.d.ts +46 -0
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.d.ts.map +1 -1
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.js +71 -0
- package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.js.map +1 -1
- package/dist/esm/redis-self/redis-self.infra.module.d.ts.map +1 -1
- package/dist/esm/redis-self/redis-self.infra.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/rabbitmq-self/__tests__/rabbitmq-self.infra.module.test.ts +49 -1
- package/src/rabbitmq-self/rabbitmq-self.infra.module.ts +75 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-s3.infra.module.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"aws-s3.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/aws-s3/aws-s3.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,OAAO,EAIL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AAEvC,eAAO,MAAM,wBAAwB,EAAE,mBA2DtC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-s3.infra.module.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"aws-s3.infra.module.js","sourceRoot":"","sources":["../../../../src/aws-s3/aws-s3.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,aAAa,GAGd,MAAM,+BAA+B,CAAC;AAEvC,MAAM,CAAC,MAAM,wBAAwB,GAAwB;IAC3D,QAAQ,EAAE,aAAa,CAAC,MAAM;IAC9B,qBAAqB,EAAE,CAAC,eAAe,CAAC,cAAc,CAAC;IAEvD;;;;OAIG;IACH,gBAAgB,EACd,iGAAiG;UAC/F,4CAA4C;IAEhD;;;;OAIG;IACH,2BAA2B,EAAE,EAAE;IAE/B,IAAI;QACF,OAAO;YACL,4CAA4C;YAC5C,iCAAiC;YACjC,aAAa,EAAE;gBACb,OAAO,EAAE;oBACP,gBAAgB,EAAE,IAAI;oBACtB,6DAA6D;oBAC7D,yDAAyD;oBACzD,8CAA8C;oBAC9C,eAAe,EAAE,MAAM;iBACxB;gBACD,aAAa,EAAE;oBACb,oBAAoB;oBACpB,oBAAoB;oBACpB,uDAAuD;oBACvD,yDAAyD;oBACzD,yDAAyD;oBACzD,2DAA2D;oBAC3D,qDAAqD;oBACrD,iCAAiC;oBACjC,gBAAgB;iBACjB;gBACD,SAAS,EAAE;oBACT,0BAA0B,CAAC,YAAY;oBACvC,0BAA0B,CAAC,SAAS;iBACrC;aACF;YACD,gEAAgE;YAChE,oEAAoE;YACpE,mEAAmE;YACnE,oEAAoE;SACrE,CAAC;IACJ,CAAC;IAED,2EAA2E;IAC3E,oEAAoE;IACpE,8EAA8E;IAC9E,0DAA0D;CAC3D,CAAC","sourcesContent":["/**\n * `AwsS3InfraProviderModule` — seventh concrete `InfraProviderModule`\n * (Slice 5.1 of `infra-provider-planner.md`).\n *\n * Implements the `AWS_S3` `InfraProvider`. Shares the one-bucket,\n * prefix-namespaced object layout of `MinioSelfInfraProviderModule` —\n * the only differences are (a) no compose / K8s contribution (S3 is\n * managed), and (b) the bucket endpoint is the regional AWS hostname\n * instead of the in-cluster `minio` service.\n *\n * ## ⚠️ NOT USABLE TODAY — selecting this provider leaves apps with no\n * storage credential (2026-08-10)\n *\n * The application-tier `STORAGE_ACCESS_KEY` / `STORAGE_SECRET_KEY` env\n * vars were removed **for every provider**: they were the object store's\n * ROOT pair, handed to every app, minion and worker. Applications now\n * obtain short-lived, prefix-confined credentials from apps-manager.\n *\n * That replacement is implemented for **MinIO only**. apps-manager's\n * `ObjectStorageStsBrokerService` speaks MinIO's STS `AssumeRole` with an\n * inline session policy; AWS STS requires a role ARN the broker does not\n * supply, so an `AWS_S3` deployment would fail at storage initialisation\n * with no credential and no path to one.\n *\n * No environment selects this provider today (every example uses\n * `BackingServiceSource.SELF` → MinIO), which is why this is recorded as\n * a gap rather than an outage. `s3: { source: MANAGED }` and\n * `provider: 'aws'` still RESOLVE here, but since #1693 such a\n * declaration is refused at authoring time: `unservableReason` below\n * takes this provider out of the servable set until the AWS STS path\n * exists. Tracked in\n * `.claude/plans/object-storage-root-credential-distribution.md`.\n *\n * The module still reuses the `STORAGE_*` env contract for the\n * NON-secret values (endpoint, region, bucket, provider); it is only the\n * credential half that is gone.\n *\n * **Why this module exists despite \"no compose / no mint\"**: it\n * gives the lockfile a stable digest entry (so a switch from\n * `minio-self` to `aws-s3` shows up as a `wildo lock check` drift)\n * AND it formalizes which env vars the operator must set for AWS\n * (`STORAGE_REGION` is mandatory; `STORAGE_ENDPOINT` is optional —\n * defaults to the AWS regional endpoint when unset).\n *\n * **No mint methods here** — and the reason is no longer the one this\n * block used to give. It said per-app credentials were out of scope\n * \"because the engine adapter uses a single shared SDK client\", which\n * stopped being true when the adapter moved to a credential provider that\n * rebuilds its client whenever the credential changes. Per-application\n * isolation exists; what is missing for AWS is only the broker's ability\n * to speak AWS STS.\n *\n * Nor should the missing piece be per-app IAM users or roles, which is\n * what this block previously anticipated. The MinIO path deliberately\n * rejected long-lived per-principal accounts in favour of `AssumeRole`\n * with an inline session policy — nothing long-lived exists to leak, and\n * credential-rotation safety dissolves instead of needing a design. The\n * AWS equivalent is the same shape: `AssumeRole` against a role ARN the\n * operator supplies, with a session policy confining the principal to\n * `applications/<applicationId>/`. That role ARN is the one input the\n * broker does not have today, and is the whole of the gap.\n */\n\nimport {\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n} from '@wildo-ai/platform-config-lib';\n\nexport const AwsS3InfraProviderModule: InfraProviderModule = {\n provider: InfraProvider.AWS_S3,\n supportedCapabilities: [InfraCapability.OBJECT_STORAGE],\n\n /**\n * What the ⚠️ block above says, made mechanical: a declaration resolving here (`s3: { source:\n * MANAGED }`, or `provider: 'aws'`) is refused by `wildo config validate` / `config sync` /\n * `lock check` until this field is deleted — in the change that teaches the broker AWS STS (#14).\n */\n unservableReason:\n 'The object-storage credential broker speaks only MinIO AssumeRole, so an application on AWS S3 '\n + 'would receive no storage credential (#14).',\n\n /**\n * Shared bucket, shared access key — no per-app credential is minted, so there is nothing\n * per-principal to confine. Confinement for object storage is done by STS session policy at\n * request time, not by a secret in the application blob.\n */\n perPrincipalSecretBlobPaths: [],\n\n plan(): InfraProviderPlanContribution {\n return {\n // No compose service — S3 is fully managed.\n // No K8s manifest — same reason.\n platformAdmin: {\n envVars: {\n STORAGE_PROVIDER: 's3',\n // Endpoint and port have AWS defaults; operator may override\n // for region-specific or VPC-endpoint topologies via the\n // env's authored `backingServices.s3` config.\n STORAGE_USE_SSL: 'true',\n },\n secretEnvVars: [\n 'STORAGE_ACCESS_KEY',\n 'STORAGE_SECRET_KEY',\n // Region is sensitive-ish (deployment topology) and is\n // delivered via the secret env channel for symmetry with\n // the access keys, even though it's not strictly secret.\n // The legacy treatment was env-var, not secret-env-var; if\n // operators object, this can flip to `envVars` later\n // without breaking the contract.\n 'STORAGE_REGION',\n ],\n consumers: [\n InfraPlatformAdminConsumer.APPS_MANAGER,\n InfraPlatformAdminConsumer.COMPANION,\n ],\n },\n // Same shared bucket as MinIO_Self. Apps receive the NON-secret\n // `STORAGE_*` values via the bootstrap env contract; the credential\n // half is no longer emitted for any provider — see the ⚠️ block in\n // this module's JSDoc for why that makes AWS_S3 currently unusable.\n };\n },\n\n // No `mintAppCredentials` / `revokeAppCredentials` — not because the model\n // is shared-bucket (it is, but that is not the reason), but because\n // per-application isolation is delivered by BROKERED STS SESSIONS rather than\n // by minted long-lived accounts. See this module's JSDoc.\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clamav-self.infra.module.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"clamav-self.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/clamav-self/clamav-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAIL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AAIvC,eAAO,MAAM,6BAA6B,EAAE,mBAiC3C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"immudb-self.infra.module.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"immudb-self.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/immudb-self/immudb-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,EASL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AAKvC;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAC/D,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAC/D,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAiB/D;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAI5E;AAED,2GAA2G;AAC3G,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhE;AA0BD,eAAO,MAAM,6BAA6B,EAAE,mBA0E3C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"immudb-self.infra.module.js","sourceRoot":"","sources":["../../../src/immudb-self/immudb-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EACL,+BAA+B,EAI/B,wBAAwB,EACxB,eAAe,EACf,0BAA0B,EAC1B,aAAa,GAGd,MAAM,+BAA+B,CAAC;AAEvC,iEAAiE;AACjE,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,uBAAuB,GAAG,cAAc,CAAC;AAE/C;;;GAGG;AACH,MAAM,wBAAwB,GAAG,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC;AACtD,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAgB;IACxD,OAAO,QAAQ,CAAC,MAAM,IAAI,CAAC;WACtB,QAAQ,CAAC,MAAM,IAAI,EAAE;WACrB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;WACtB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;WACtB,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,oBAA4B;IAClE,MAAM,OAAO,GAAG,oBAAoB,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1F,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3F,OAAO,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC;AAC/B,CAAC;AAED,2GAA2G;AAC3G,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,OAAO,GAAG,YAAY,QAAQ,CAAC;AACjC,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAoC,EAAE,IAAY;IAC9E,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,+BAA+B,CAAC,mBAAmB,EAAE,CAAC;QACxG,MAAM,IAAI,KAAK,CACb,iCAAiC,IAAI,gBAAgB,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,WAAW;cAC9G,gHAAgH;cAChH,iGAAiG,CACpG,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAoC;IAC/D,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,gGAAgG,CAAC,CAAC;IACpH,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,uBAAuB,CAAC,CAAC;IAC5E,IAAI,QAAQ,IAAI,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,wBAAwB,CAAC,EAAE,CAAC;QACtG,MAAM,IAAI,KAAK,CACb,8DAA8D,QAAQ,2CAA2C;cAC/G,wBAAwB,wBAAwB,6DAA6D,CAChH,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,IAAI,uBAAuB,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,6BAA6B,GAAwB;IAChE,QAAQ,EAAE,aAAa,CAAC,WAAW;IACnC,qBAAqB,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC;IAE1D;;;OAGG;IACH,2BAA2B,EAAE,EAAE;IAE/B,IAAI;QACF,OAAO;YACL,qBAAqB,EAAE,CAAC,QAAQ,CAAC;YACjC,YAAY,EAAE,CAAC,yDAAyD,CAAC;YACzE,aAAa,EAAE;gBACb,OAAO,EAAE;oBACP,iBAAiB,EAAE,QAAQ;oBAC3B,iBAAiB,EAAE,MAAM,CAAC,mBAAmB,CAAC;iBAC/C;gBACD,yGAAyG;gBACzG,iEAAiE;gBACjE,aAAa,EAAE,CAAC,uBAAuB,CAAC;gBACxC,SAAS,EAAE,CAAC,0BAA0B,CAAC,YAAY,CAAC;aACrD;YACD,UAAU,EAAE;gBACV,qBAAqB,EAAE,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,oBAAoB,CAAC;aAC1F;SACF,CAAC;IACJ,CAAC;IAED,gIAAgI;IAChI,KAAK,CAAC,kBAAkB,CAAC,OAAoC;QAC3D,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,oBAAoB,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,8EAA8E,oBAAoB,KAAK;kBACrG,sEAAsE,CACzE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CACb,uGAAuG;kBACrG,+FAA+F,CAClG,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAiC,CAAC;QAC9D,MAAM,WAAW,CAAC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;QACnD,MAAM,WAAW,CAAC,kBAAkB,CAAC;YACnC,QAAQ;YACR,QAAQ;YACR,YAAY;YACZ,UAAU,EAAE,wBAAwB,CAAC,UAAU;SAChD,CAAC,CAAC;QACH,OAAO;YACL,gBAAgB,EAAE;gBAChB,CAAC,oBAAoB,CAAC,EAAE,QAAQ;gBAChC,CAAC,oBAAoB,CAAC,EAAE,QAAQ;gBAChC,CAAC,oBAAoB,CAAC,EAAE,YAAY;aACrC;SACF,CAAC;IACJ,CAAC;IAED,2IAA2I;IAC3I,KAAK,CAAC,oBAAoB,CAAC,OAAoC;QAC7D,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;QACtD,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAiC,CAAC;QAC9D,MAAM,WAAW,CAAC,sBAAsB,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;IAC1G,CAAC;IAED,sFAAsF;CACvF,CAAC","sourcesContent":["/**\n * `ImmudbSelfInfraProviderModule` — the self-hosted tamper-evident audit store (#1696).\n *\n * ## What it replaces\n *\n * Until #1696 the platform ran ONE immudb and every application's audit sink logged into it as the server's\n * built-in `immudb` SYSADMIN, into the same `defaultdb`: any application's backend held a superuser credential over\n * every application's tamper-evident audit data, and the server's admin password was copied from the OWNING\n * application's provider secret, so every hosted application had to be handed the owner's sysadmin password to write\n * at all. `IMMUDB_SELF` was servable only as an operator-credentialed exemption because nothing could mint anything\n * narrower.\n *\n * ## What it mints\n *\n * Per application: ONE immudb database and ONE user holding `READ_WRITE` on that database only — the permission an\n * audit sink needs to append and verify, and nothing that administers users, databases or the server. The values\n * reach the application in its platform secrets at `database.immudb.{username,password,database}`, the same paths the\n * engine's immudb provider reads first (`platformMintedSecretPath`), so the operator-authored `IMMUDB_USER` /\n * `IMMUDB_PASSWORD` / `IMMUDB_DATABASE` no longer decide anything on a self-hosted platform.\n *\n * The admin credential apps-manager mints with is a framework-managed PLATFORM secret (`immudb.adminPassword`),\n * delivered to apps-manager only (`IMMUDB_ADMIN_*`), and never to an application.\n *\n * ## Naming, and why it is this narrow\n *\n * immudb accepts only letters and digits in a database name (\"punctuation marks and symbols are not allowed in\n * database name\", immudb issue #1770) and `^[a-zA-Z0-9_]+$` up to 63 characters for a username. The application\n * namespace (`<slug>-<environment>`) carries hyphens, so the database name is its lowercased letters and digits plus\n * a short digest of the namespace itself: stripping punctuation alone would give `wonder-todos` and `wondertodos` the\n * SAME database, which is exactly the sharing this module exists to remove.\n * spec-cite: immudb v1.11.1 `pkg/auth/user.go` — `usernameRegex = ^[a-zA-Z0-9_]+$`, `maxUsernameLen = 63`\n * (https://raw.githubusercontent.com/codenotary/immudb/v1.11.1/pkg/auth/user.go, fetched 2026-09-29); the database\n * name rule is the server's error text quoted in https://github.com/codenotary/immudb/issues/1770.\n *\n * ## Runtime principals\n *\n * The APPLICATION BACKEND only. A minion's audit events go through the same application's audit trail, so it\n * receives the application's credential through the ordinary delivery (`perPrincipalSecretBlobPaths` is empty, so\n * nothing is stripped for it). A named principal passed here is refused rather than given a second database.\n *\n * ## Revocation\n *\n * `revokeAppCredentials` DEACTIVATES the user and revokes its permission — immudb has no user deletion, and the\n * database and its events are kept, because an append-only audit history must outlive the credential that wrote it\n * (#1307, #1562). `mintAppCredentials` re-activates and re-grants, which is what a restore runs.\n */\n\nimport { createHash } from 'node:crypto';\n\nimport {\n ApplicationRuntimePrincipalKind,\n AppCredentialMintingContext,\n AppCredentialMintingResult,\n IImmudbAdminClient,\n ImmudbDatabasePermission,\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n} from '@wildo-ai/platform-config-lib';\n\n/** immudb's gRPC port (matches `INFRA_DEFAULT_PORTS.IMMUDB`). */\nconst DEFAULT_IMMUDB_PORT = 3322;\n\n/**\n * The `Wildo_Application_Secrets` paths this module writes — the SAME strings the engine's immudb provider names as\n * `platformMintedSecretPath` (`IMMUDB_MINTED_SECRET_PATHS` in external-connectors-private). Each side is pinned to the\n * engine's `AppConfigurationSecrets.database.immudb` schema, which is where the two ends meet: these constants by\n * apps-manager's `immudb-minted-secret-paths` test, the engine's by saas-backend-lib's\n * `app-config-secrets.boundaries` test. The lifecycle imports these, never literals.\n */\nexport const IMMUDB_USERNAME_PATH = 'database.immudb.username';\nexport const IMMUDB_PASSWORD_PATH = 'database.immudb.password';\nexport const IMMUDB_DATABASE_PATH = 'database.immudb.database';\n\n/**\n * `applicationConfigInputs` key for the database name RECORDED at the first mint (apps-manager passes the stored\n * `database.immudb.database` back, so a revoke, a restore and a re-registration address the database the first mint\n * created even if the derivation ever changes). Not an authored value: `backingServices.immudb.database` is not read\n * for a self-hosted store.\n */\nconst DATABASE_NAME_INPUT_KEY = 'databaseName';\n\n/**\n * The longest database name whose `<name>_audit` user still fits immudb's 63-character username limit, and the\n * database name's own character rule (letters and digits). A recorded name outside either is refused rather than sent.\n */\nconst MAX_DATABASE_NAME_LENGTH = 63 - '_audit'.length;\nconst DATABASE_NAME_PATTERN = /^[a-zA-Z0-9]+$/;\n\n/**\n * immudb's password rule, checked here so a non-compliant value fails naming the application rather than as a bare\n * gRPC error from the server.\n * spec-cite: immudb v1.11.1 `pkg/auth/passwords.go` — \"password must have between 8 and 32 letters, digits and\n * special characters of which at least 1 uppercase letter, 1 digit and 1 special character\"\n * (https://raw.githubusercontent.com/codenotary/immudb/v1.11.1/pkg/auth/passwords.go, fetched 2026-09-29).\n */\nexport function isImmudbCompliantPassword(password: string): boolean {\n return password.length >= 8\n && password.length <= 32\n && /[A-Z]/.test(password)\n && /[0-9]/.test(password)\n && /[^A-Za-z0-9]/.test(password);\n}\n\n/**\n * The per-application database name: the namespace's lowercased letters and digits (capped) plus the first 8 hex of\n * its SHA-256, so two namespaces differing only in punctuation still get two databases. Deterministic, so a revoke,\n * a restore and a re-registration all address the database the first mint created.\n */\nexport function buildImmudbDatabaseName(applicationNamespace: string): string {\n const letters = applicationNamespace.toLowerCase().replace(/[^a-z0-9]/g, '').slice(0, 40);\n const digest = createHash('sha256').update(applicationNamespace).digest('hex').slice(0, 8);\n return `${letters}${digest}`;\n}\n\n/** The per-application audit user: the database name plus a role suffix, inside immudb's username rule. */\nexport function buildImmudbUsername(databaseName: string): string {\n return `${databaseName}_audit`;\n}\n\nfunction refuseNamedPrincipal(context: AppCredentialMintingContext, verb: string): void {\n if (context.principal && context.principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n throw new Error(\n `ImmudbSelfInfraProviderModule.${verb}: principal '${context.principal.kind}:${context.principal.name}' is not `\n + 'the application backend. An application has ONE audit database and ONE audit user; its runtimes write through '\n + 'the application\\'s credential, so a per-principal immudb credential is never minted or revoked.',\n );\n }\n}\n\nfunction resolveDatabaseName(context: AppCredentialMintingContext): string {\n if (!context.applicationNamespace) {\n throw new Error('ImmudbSelfInfraProviderModule: applicationNamespace is empty. The orchestrator MUST supply it.');\n }\n const recorded = context.applicationConfigInputs?.[DATABASE_NAME_INPUT_KEY];\n if (recorded && (!DATABASE_NAME_PATTERN.test(recorded) || recorded.length > MAX_DATABASE_NAME_LENGTH)) {\n throw new Error(\n `ImmudbSelfInfraProviderModule: the recorded database name '${recorded}' is outside immudb's rules (letters and `\n + `digits only, at most ${MAX_DATABASE_NAME_LENGTH} characters so its audit user fits the 63-character limit).`,\n );\n }\n return recorded || buildImmudbDatabaseName(context.applicationNamespace);\n}\n\nexport const ImmudbSelfInfraProviderModule: InfraProviderModule = {\n provider: InfraProvider.IMMUDB_SELF,\n supportedCapabilities: [InfraCapability.AUDIT_LOG_STORAGE],\n\n /**\n * Empty: minions write through the application's audit credential (see the module JSDoc), so the delivery must\n * not strip it from them.\n */\n perPrincipalSecretBlobPaths: [],\n\n plan(): InfraProviderPlanContribution {\n return {\n dockerComposeServices: ['immudb'],\n k8sResources: ['templates/infra/k8s/internal/immudb/deployment.yaml.hbs'],\n platformAdmin: {\n envVars: {\n IMMUDB_ADMIN_HOST: 'immudb',\n IMMUDB_ADMIN_PORT: String(DEFAULT_IMMUDB_PORT),\n },\n // The server's built-in sysadmin password: a framework-managed platform secret (`immudb.adminPassword`),\n // written by the CLI's env builder into apps-manager's env only.\n secretEnvVars: ['IMMUDB_ADMIN_PASSWORD'],\n consumers: [InfraPlatformAdminConsumer.APPS_MANAGER],\n },\n appRuntime: {\n perAppSecretBlobPaths: [IMMUDB_USERNAME_PATH, IMMUDB_PASSWORD_PATH, IMMUDB_DATABASE_PATH],\n },\n };\n },\n\n // assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — READ_WRITE on the application's own database only, never ADMIN or SYSADMIN.\n async mintAppCredentials(context: AppCredentialMintingContext): Promise<AppCredentialMintingResult> {\n refuseNamedPrincipal(context, 'mintAppCredentials');\n const password = context.secretInputs[IMMUDB_PASSWORD_PATH];\n if (!password) {\n throw new Error(\n `ImmudbSelfInfraProviderModule.mintAppCredentials: secretInputs is missing '${IMMUDB_PASSWORD_PATH}'. `\n + 'The orchestrator MUST supply the password — modules are upsert-only.',\n );\n }\n if (!isImmudbCompliantPassword(password)) {\n throw new Error(\n 'ImmudbSelfInfraProviderModule.mintAppCredentials: the supplied password does not meet immudb\\'s rule '\n + '(8 to 32 characters with at least one uppercase letter, one digit and one special character).',\n );\n }\n const databaseName = resolveDatabaseName(context);\n const username = buildImmudbUsername(databaseName);\n const adminClient = context.adminClient as IImmudbAdminClient;\n await adminClient.ensureDatabase({ databaseName });\n await adminClient.upsertDatabaseUser({\n username,\n password,\n databaseName,\n permission: ImmudbDatabasePermission.READ_WRITE,\n });\n return {\n secretBlobValues: {\n [IMMUDB_USERNAME_PATH]: username,\n [IMMUDB_PASSWORD_PATH]: password,\n [IMMUDB_DATABASE_PATH]: databaseName,\n },\n };\n },\n\n // assurance-control: WILDO.IDENTITY.CREDENTIAL_LIFECYCLE — a withdrawn application's audit user is deactivated and its permission revoked.\n async revokeAppCredentials(context: AppCredentialMintingContext): Promise<void> {\n refuseNamedPrincipal(context, 'revokeAppCredentials');\n const databaseName = resolveDatabaseName(context);\n const adminClient = context.adminClient as IImmudbAdminClient;\n await adminClient.deactivateDatabaseUser({ username: buildImmudbUsername(databaseName), databaseName });\n },\n\n // No `mintPlatformServiceCredentials`: no platform service writes to the audit store.\n};\n"]}
|
|
1
|
+
{"version":3,"file":"immudb-self.infra.module.js","sourceRoot":"","sources":["../../../../src/immudb-self/immudb-self.infra.module.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `ImmudbSelfInfraProviderModule` — the self-hosted tamper-evident audit store (#1696).\n *\n * ## What it replaces\n *\n * Until #1696 the platform ran ONE immudb and every application's audit sink logged into it as the server's\n * built-in `immudb` SYSADMIN, into the same `defaultdb`: any application's backend held a superuser credential over\n * every application's tamper-evident audit data, and the server's admin password was copied from the OWNING\n * application's provider secret, so every hosted application had to be handed the owner's sysadmin password to write\n * at all. `IMMUDB_SELF` was servable only as an operator-credentialed exemption because nothing could mint anything\n * narrower.\n *\n * ## What it mints\n *\n * Per application: ONE immudb database and ONE user holding `READ_WRITE` on that database only — the permission an\n * audit sink needs to append and verify, and nothing that administers users, databases or the server. The values\n * reach the application in its platform secrets at `database.immudb.{username,password,database}`, the same paths the\n * engine's immudb provider reads first (`platformMintedSecretPath`), so the operator-authored `IMMUDB_USER` /\n * `IMMUDB_PASSWORD` / `IMMUDB_DATABASE` no longer decide anything on a self-hosted platform.\n *\n * The admin credential apps-manager mints with is a framework-managed PLATFORM secret (`immudb.adminPassword`),\n * delivered to apps-manager only (`IMMUDB_ADMIN_*`), and never to an application.\n *\n * ## Naming, and why it is this narrow\n *\n * immudb accepts only letters and digits in a database name (\"punctuation marks and symbols are not allowed in\n * database name\", immudb issue #1770) and `^[a-zA-Z0-9_]+$` up to 63 characters for a username. The application\n * namespace (`<slug>-<environment>`) carries hyphens, so the database name is its lowercased letters and digits plus\n * a short digest of the namespace itself: stripping punctuation alone would give `wonder-todos` and `wondertodos` the\n * SAME database, which is exactly the sharing this module exists to remove.\n * spec-cite: immudb v1.11.1 `pkg/auth/user.go` — `usernameRegex = ^[a-zA-Z0-9_]+$`, `maxUsernameLen = 63`\n * (https://raw.githubusercontent.com/codenotary/immudb/v1.11.1/pkg/auth/user.go, fetched 2026-09-29); the database\n * name rule is the server's error text quoted in https://github.com/codenotary/immudb/issues/1770.\n *\n * ## Runtime principals\n *\n * The APPLICATION BACKEND only. A minion's audit events go through the same application's audit trail, so it\n * receives the application's credential through the ordinary delivery (`perPrincipalSecretBlobPaths` is empty, so\n * nothing is stripped for it). A named principal passed here is refused rather than given a second database.\n *\n * ## Revocation\n *\n * `revokeAppCredentials` DEACTIVATES the user and revokes its permission — immudb has no user deletion, and the\n * database and its events are kept, because an append-only audit history must outlive the credential that wrote it\n * (#1307, #1562). `mintAppCredentials` re-activates and re-grants, which is what a restore runs.\n */\n\nimport { createHash } from 'node:crypto';\n\nimport {\n ApplicationRuntimePrincipalKind,\n AppCredentialMintingContext,\n AppCredentialMintingResult,\n IImmudbAdminClient,\n ImmudbDatabasePermission,\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n} from '@wildo-ai/platform-config-lib';\n\n/** immudb's gRPC port (matches `INFRA_DEFAULT_PORTS.IMMUDB`). */\nconst DEFAULT_IMMUDB_PORT = 3322;\n\n/**\n * The `Wildo_Application_Secrets` paths this module writes — the SAME strings the engine's immudb provider names as\n * `platformMintedSecretPath` (`IMMUDB_MINTED_SECRET_PATHS` in external-connectors-private). Each side is pinned to the\n * engine's `AppConfigurationSecrets.database.immudb` schema, which is where the two ends meet: these constants by\n * apps-manager's `immudb-minted-secret-paths` test, the engine's by saas-backend-lib's\n * `app-config-secrets.boundaries` test. The lifecycle imports these, never literals.\n */\nexport const IMMUDB_USERNAME_PATH = 'database.immudb.username';\nexport const IMMUDB_PASSWORD_PATH = 'database.immudb.password';\nexport const IMMUDB_DATABASE_PATH = 'database.immudb.database';\n\n/**\n * `applicationConfigInputs` key for the database name RECORDED at the first mint (apps-manager passes the stored\n * `database.immudb.database` back, so a revoke, a restore and a re-registration address the database the first mint\n * created even if the derivation ever changes). Not an authored value: `backingServices.immudb.database` is not read\n * for a self-hosted store.\n */\nconst DATABASE_NAME_INPUT_KEY = 'databaseName';\n\n/**\n * The longest database name whose `<name>_audit` user still fits immudb's 63-character username limit, and the\n * database name's own character rule (letters and digits). A recorded name outside either is refused rather than sent.\n */\nconst MAX_DATABASE_NAME_LENGTH = 63 - '_audit'.length;\nconst DATABASE_NAME_PATTERN = /^[a-zA-Z0-9]+$/;\n\n/**\n * immudb's password rule, checked here so a non-compliant value fails naming the application rather than as a bare\n * gRPC error from the server.\n * spec-cite: immudb v1.11.1 `pkg/auth/passwords.go` — \"password must have between 8 and 32 letters, digits and\n * special characters of which at least 1 uppercase letter, 1 digit and 1 special character\"\n * (https://raw.githubusercontent.com/codenotary/immudb/v1.11.1/pkg/auth/passwords.go, fetched 2026-09-29).\n */\nexport function isImmudbCompliantPassword(password: string): boolean {\n return password.length >= 8\n && password.length <= 32\n && /[A-Z]/.test(password)\n && /[0-9]/.test(password)\n && /[^A-Za-z0-9]/.test(password);\n}\n\n/**\n * The per-application database name: the namespace's lowercased letters and digits (capped) plus the first 8 hex of\n * its SHA-256, so two namespaces differing only in punctuation still get two databases. Deterministic, so a revoke,\n * a restore and a re-registration all address the database the first mint created.\n */\nexport function buildImmudbDatabaseName(applicationNamespace: string): string {\n const letters = applicationNamespace.toLowerCase().replace(/[^a-z0-9]/g, '').slice(0, 40);\n const digest = createHash('sha256').update(applicationNamespace).digest('hex').slice(0, 8);\n return `${letters}${digest}`;\n}\n\n/** The per-application audit user: the database name plus a role suffix, inside immudb's username rule. */\nexport function buildImmudbUsername(databaseName: string): string {\n return `${databaseName}_audit`;\n}\n\nfunction refuseNamedPrincipal(context: AppCredentialMintingContext, verb: string): void {\n if (context.principal && context.principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n throw new Error(\n `ImmudbSelfInfraProviderModule.${verb}: principal '${context.principal.kind}:${context.principal.name}' is not `\n + 'the application backend. An application has ONE audit database and ONE audit user; its runtimes write through '\n + 'the application\\'s credential, so a per-principal immudb credential is never minted or revoked.',\n );\n }\n}\n\nfunction resolveDatabaseName(context: AppCredentialMintingContext): string {\n if (!context.applicationNamespace) {\n throw new Error('ImmudbSelfInfraProviderModule: applicationNamespace is empty. The orchestrator MUST supply it.');\n }\n const recorded = context.applicationConfigInputs?.[DATABASE_NAME_INPUT_KEY];\n if (recorded && (!DATABASE_NAME_PATTERN.test(recorded) || recorded.length > MAX_DATABASE_NAME_LENGTH)) {\n throw new Error(\n `ImmudbSelfInfraProviderModule: the recorded database name '${recorded}' is outside immudb's rules (letters and `\n + `digits only, at most ${MAX_DATABASE_NAME_LENGTH} characters so its audit user fits the 63-character limit).`,\n );\n }\n return recorded || buildImmudbDatabaseName(context.applicationNamespace);\n}\n\nexport const ImmudbSelfInfraProviderModule: InfraProviderModule = {\n provider: InfraProvider.IMMUDB_SELF,\n supportedCapabilities: [InfraCapability.AUDIT_LOG_STORAGE],\n\n /**\n * Empty: minions write through the application's audit credential (see the module JSDoc), so the delivery must\n * not strip it from them.\n */\n perPrincipalSecretBlobPaths: [],\n\n plan(): InfraProviderPlanContribution {\n return {\n dockerComposeServices: ['immudb'],\n k8sResources: ['templates/infra/k8s/internal/immudb/deployment.yaml.hbs'],\n platformAdmin: {\n envVars: {\n IMMUDB_ADMIN_HOST: 'immudb',\n IMMUDB_ADMIN_PORT: String(DEFAULT_IMMUDB_PORT),\n },\n // The server's built-in sysadmin password: a framework-managed platform secret (`immudb.adminPassword`),\n // written by the CLI's env builder into apps-manager's env only.\n secretEnvVars: ['IMMUDB_ADMIN_PASSWORD'],\n consumers: [InfraPlatformAdminConsumer.APPS_MANAGER],\n },\n appRuntime: {\n perAppSecretBlobPaths: [IMMUDB_USERNAME_PATH, IMMUDB_PASSWORD_PATH, IMMUDB_DATABASE_PATH],\n },\n };\n },\n\n // assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — READ_WRITE on the application's own database only, never ADMIN or SYSADMIN.\n async mintAppCredentials(context: AppCredentialMintingContext): Promise<AppCredentialMintingResult> {\n refuseNamedPrincipal(context, 'mintAppCredentials');\n const password = context.secretInputs[IMMUDB_PASSWORD_PATH];\n if (!password) {\n throw new Error(\n `ImmudbSelfInfraProviderModule.mintAppCredentials: secretInputs is missing '${IMMUDB_PASSWORD_PATH}'. `\n + 'The orchestrator MUST supply the password — modules are upsert-only.',\n );\n }\n if (!isImmudbCompliantPassword(password)) {\n throw new Error(\n 'ImmudbSelfInfraProviderModule.mintAppCredentials: the supplied password does not meet immudb\\'s rule '\n + '(8 to 32 characters with at least one uppercase letter, one digit and one special character).',\n );\n }\n const databaseName = resolveDatabaseName(context);\n const username = buildImmudbUsername(databaseName);\n const adminClient = context.adminClient as IImmudbAdminClient;\n await adminClient.ensureDatabase({ databaseName });\n await adminClient.upsertDatabaseUser({\n username,\n password,\n databaseName,\n permission: ImmudbDatabasePermission.READ_WRITE,\n });\n return {\n secretBlobValues: {\n [IMMUDB_USERNAME_PATH]: username,\n [IMMUDB_PASSWORD_PATH]: password,\n [IMMUDB_DATABASE_PATH]: databaseName,\n },\n };\n },\n\n // assurance-control: WILDO.IDENTITY.CREDENTIAL_LIFECYCLE — a withdrawn application's audit user is deactivated and its permission revoked.\n async revokeAppCredentials(context: AppCredentialMintingContext): Promise<void> {\n refuseNamedPrincipal(context, 'revokeAppCredentials');\n const databaseName = resolveDatabaseName(context);\n const adminClient = context.adminClient as IImmudbAdminClient;\n await adminClient.deactivateDatabaseUser({ username: buildImmudbUsername(databaseName), databaseName });\n },\n\n // No `mintPlatformServiceCredentials`: no platform service writes to the audit store.\n};\n"]}
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAaxF,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,cAAc,wCAAwC,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,mBAAmB,CAYrE,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qCAAqC,EAAE,WAAW,CAAC,aAAa,CAA4B,CAAC;AAE1G;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,IAAI,WAAW,CAAC,aAAa,CAAC,CAM1E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kCAAkC,IAAI,WAAW,CAAC,MAAM,CAAC,CAQxE;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,WAAW,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAY7F"}
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","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 { InfraProvider, type 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 { ImmudbSelfInfraProviderModule } from './immudb-self/immudb-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 * from './immudb-self/immudb-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). `AWS_S3` declares `unservableReason`\n * (no AWS STS path in the credential broker, #14), so it plans but is\n * not servable.\n * - `CLAMAV_SELF` — stateless infra (no auth, no per-app state;\n * `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 * A declaration resolving to a provider outside\n * {@link collectServableInfraProviders} is REFUSED by `wildo config\n * validate`, `config sync` and `lock check` (#1693) — it no longer falls\n * through to an authored-entry digest.\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 // #1696: one immudb database and one confined user per application, replacing the operator-credentialed\n // exemption under which every application logged in as the server's sysadmin.\n ImmudbSelfInfraProviderModule,\n];\n\n/**\n * Providers the platform serves WITHOUT a module in {@link INFRA_PROVIDER_MODULES}, because their\n * application credential is the operator's to supply rather than the catalogue's to mint.\n *\n * ## Why this is the one exception, and why it is a list of exemptions rather than of refusals\n *\n * The servable set is DERIVED from the catalogue ({@link collectServableInfraProviders}); a hand-kept\n * list of refused providers would silently admit every provider added later. So the only thing\n * written down by hand is the exception, and it must be argued for here, one entry at a time.\n *\n * EMPTY since #1696, and kept as the named place such an exemption would be argued. `IMMUDB_SELF` was the one entry:\n * the self-hosted audit store had no module, and every application on the server logged in as its sysadmin into\n * one shared database. Its module now mints one database and one confined user per application, which makes it\n * servable on its own — and REFUSED on a platform that holds no immudb admin credential, like every minting kind.\n */\nexport const OPERATOR_CREDENTIALED_INFRA_PROVIDERS: ReadonlySet<InfraProvider> = new Set<InfraProvider>();\n\n/**\n * The providers a `backingServices` declaration may resolve to — every module in the catalogue that\n * does not declare `unservableReason`, plus {@link OPERATOR_CREDENTIALED_INFRA_PROVIDERS}.\n *\n * The one source both lockfile projections (`wildo config sync`, `wildo lock check`) and\n * `wildo config validate` pass to the shared check in `@wildo-ai/platform-config-lib`\n * (`findUnservableBackingServiceDeclarations`), so all three refuse the same declarations (#1693).\n * Derived, so a module entering the catalogue makes its provider declarable the day it lands.\n */\nexport function collectServableInfraProviders(): ReadonlySet<InfraProvider> {\n const servable = new Set<InfraProvider>(OPERATOR_CREDENTIALED_INFRA_PROVIDERS);\n for (const module of INFRA_PROVIDER_MODULES) {\n if (module.unservableReason === undefined) servable.add(module.provider);\n }\n return servable;\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"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"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","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 { InfraProvider, type 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 { ImmudbSelfInfraProviderModule } from './immudb-self/immudb-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 * from './immudb-self/immudb-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). `AWS_S3` declares `unservableReason`\n * (no AWS STS path in the credential broker, #14), so it plans but is\n * not servable.\n * - `CLAMAV_SELF` — stateless infra (no auth, no per-app state;\n * `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 * A declaration resolving to a provider outside\n * {@link collectServableInfraProviders} is REFUSED by `wildo config\n * validate`, `config sync` and `lock check` (#1693) — it no longer falls\n * through to an authored-entry digest.\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 // #1696: one immudb database and one confined user per application, replacing the operator-credentialed\n // exemption under which every application logged in as the server's sysadmin.\n ImmudbSelfInfraProviderModule,\n];\n\n/**\n * Providers the platform serves WITHOUT a module in {@link INFRA_PROVIDER_MODULES}, because their\n * application credential is the operator's to supply rather than the catalogue's to mint.\n *\n * ## Why this is the one exception, and why it is a list of exemptions rather than of refusals\n *\n * The servable set is DERIVED from the catalogue ({@link collectServableInfraProviders}); a hand-kept\n * list of refused providers would silently admit every provider added later. So the only thing\n * written down by hand is the exception, and it must be argued for here, one entry at a time.\n *\n * EMPTY since #1696, and kept as the named place such an exemption would be argued. `IMMUDB_SELF` was the one entry:\n * the self-hosted audit store had no module, and every application on the server logged in as its sysadmin into\n * one shared database. Its module now mints one database and one confined user per application, which makes it\n * servable on its own — and REFUSED on a platform that holds no immudb admin credential, like every minting kind.\n */\nexport const OPERATOR_CREDENTIALED_INFRA_PROVIDERS: ReadonlySet<InfraProvider> = new Set<InfraProvider>();\n\n/**\n * The providers a `backingServices` declaration may resolve to — every module in the catalogue that\n * does not declare `unservableReason`, plus {@link OPERATOR_CREDENTIALED_INFRA_PROVIDERS}.\n *\n * The one source both lockfile projections (`wildo config sync`, `wildo lock check`) and\n * `wildo config validate` pass to the shared check in `@wildo-ai/platform-config-lib`\n * (`findUnservableBackingServiceDeclarations`), so all three refuse the same declarations (#1693).\n * Derived, so a module entering the catalogue makes its provider declarable the day it lands.\n */\nexport function collectServableInfraProviders(): ReadonlySet<InfraProvider> {\n const servable = new Set<InfraProvider>(OPERATOR_CREDENTIALED_INFRA_PROVIDERS);\n for (const module of INFRA_PROVIDER_MODULES) {\n if (module.unservableReason === undefined) servable.add(module.provider);\n }\n return servable;\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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"minio-self.infra.module.d.ts","sourceRoot":"","sources":["
|
|
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,mBA6C1C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-atlas.infra.module.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"mongodb-atlas.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/mongodb-atlas/mongodb-atlas.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH,OAAO,EAQL,mBAAmB,EAKpB,MAAM,+BAA+B,CAAC;AA0IvC,eAAO,MAAM,+BAA+B,EAAE,mBA2K7C,CAAC"}
|
|
@@ -1 +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 the Atlas naming and grant policy; the USERNAME\n * policy itself is shared with the self-hosted module through\n * `buildMongodbPrincipalUsername` (`<applicationNamespace>_user` for the\n * application backend, `<namespace>_<kind>_<name>_user` for a principal).\n *\n * **What this module reads from `AppCredentialMintingContext`**:\n * - `applicationNamespace` + `principal` — the username source (above).\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.\n * - `applicationConfigInputs['clusterName']` — required, the Atlas\n * cluster name. apps-manager resolves it once (`resolveAtlasClusterName`)\n * for the application user and every principal user, and a later\n * reconcile reads back the cluster recorded at registration.\n * - `resourceGrants` — required for a named principal: its declared\n * access, to which the engine floor is added here.\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 cluster's connection\n * string (its SRV address, or the `mongoURIWithOptions` seed list\n * when it has none, #1969) 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`**: for a runtime principal, apps-manager's\n * declaration reconcile calls it when a minion is removed. For the application\n * backend, apps-manager calls it when the application is withdrawn from the\n * platform (`deregisterApplication`, #1307): the database user is deleted and\n * the database is kept, so `restoreApplication` can re-mint it.\n *\n * **Runtime principals (minions), #1694**: each gets its OWN Atlas database\n * user holding ONE Atlas custom database role — the Atlas twin of the\n * self-hosted module's collection-scoped user:\n * - the username comes from `buildMongodbPrincipalUsername`, the SAME\n * derivation the self-hosted module uses, so it can never be the backend's\n * `<namespace>_user`, and the mint and the revoke cannot disagree about it;\n * - the role carries the principal's declared `resourceAccess` PLUS the engine\n * execution-history floor every enabled Node minion needs, derived through\n * `deriveMinionDatabaseGrants` → `buildMinionMongodbPrivileges`, the same\n * pair the self-hosted module uses, then translated into Atlas's action names;\n * - the user is scoped to the application's cluster alone;\n * - revoking deletes that user and that role, and nothing else.\n *\n * Before #1694 the module ignored `principal` and derived `<namespace>_user`\n * for every input, so a minion mint would have overwritten the backend's user\n * and a minion revoke would have deleted it; apps-manager refused the\n * combination at registration instead.\n */\n\nimport {\n ApplicationRuntimePrincipalKind,\n AppCredentialMintingContext,\n AppCredentialMintingResult,\n IMongoAtlasClient,\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n deriveMinionDatabaseGrants,\n resolveGrantStorageNames,\n type ApplicationRuntimePrincipal,\n} from '@wildo-ai/platform-config-lib';\nimport {\n buildMinionMongodbPrivileges,\n buildMongodbPrincipalUsername,\n type MongodbCollectionPrivilege,\n} from '../mongodb-self/mongodb-self.infra.module';\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 cluster's connection string — its SRV\n * address, or the `mongoURIWithOptions` seed list when it has none (#1969) — 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 * A named runtime principal (a minion) — anything but the application backend, which keeps the\n * application-level `<namespace>_user` and its database-wide role.\n */\nfunction isNamedPrincipal(principal: ApplicationRuntimePrincipal | undefined): principal is ApplicationRuntimePrincipal {\n return principal !== undefined && principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND;\n}\n\n/**\n * The Atlas custom role a principal's user holds — derived from the principal's username exactly as the\n * self-hosted module derives its role (`<username>_role`), so the mint and the revoke name the same role.\n *\n * Custom roles are PROJECT-wide in Atlas, not per database or cluster, so the name must be unique across\n * every application the project hosts. It is: the username embeds the application namespace.\n */\nfunction buildAtlasPrincipalRoleName(username: string): string {\n return `${username}_role`;\n}\n\n/**\n * Atlas's rule for a custom role name, checked before any request so a name Atlas would refuse fails here,\n * naming the principal, rather than as a bare HTTP 400 from the Admin API.\n *\n * spec-cite: https://www.mongodb.com/docs/atlas/security-add-mongodb-roles/ — \"The specified role name can\n * only contain letters, numbers, underscores or hyphens.\" (fetched 2026-09-27). The OpenAPI schema for\n * `UserCustomDBRole.roleName` states no pattern, so the prose page is the authority.\n */\nconst ATLAS_CUSTOM_ROLE_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;\n\n/**\n * MongoDB's privilege action (the vocabulary `buildMinionMongodbPrivileges` speaks, shared with the\n * self-hosted module) → the Atlas Admin API's name for the same action.\n *\n * semantic-literal-boundary: both sides are EXTERNAL vocabularies — MongoDB's `createRole` action names and\n * the Atlas Admin API's `DatabasePrivilegeAction.action` enum — and this table is the one place they meet.\n * spec-cite: Atlas Administration API v1.0 OpenAPI (github.com/mongodb/openapi, `openapi/v1-deprecated/v1.json`),\n * schema `DatabasePrivilegeAction`, enum `action`: `FIND`, `INSERT`, `REMOVE`, `UPDATE` (fetched 2026-09-27).\n *\n * Closed on purpose: an action with no entry is REFUSED rather than dropped (a silently narrower role reads\n * as a working grant until the minion fails) or passed through (a spelling Atlas does not know is a 400 at\n * best). Grants only ever reach Atlas through this table, so nothing can widen them on the way.\n */\nconst ATLAS_PRIVILEGE_ACTION_BY_MONGODB_ACTION: Readonly<Record<string, string>> = {\n find: 'FIND',\n insert: 'INSERT',\n update: 'UPDATE',\n remove: 'REMOVE',\n};\n\nfunction toAtlasPrivileges(privileges: readonly MongodbCollectionPrivilege[]): MongodbCollectionPrivilege[] {\n return privileges.map((privilege) => ({\n resource: privilege.resource,\n actions: privilege.actions.map((action) => {\n const atlasAction = ATLAS_PRIVILEGE_ACTION_BY_MONGODB_ACTION[action];\n if (!atlasAction) {\n throw new Error(\n `MongodbAtlasInfraProviderModule: MongoDB privilege action '${action}' has no Atlas Admin API equivalent `\n + `in ATLAS_PRIVILEGE_ACTION_BY_MONGODB_ACTION. Refusing to mint a role that would silently lack it.`,\n );\n }\n return atlasAction;\n }),\n }));\n}\n\n/** The principal's username and role, checked against Atlas's role-name rule. One derivation for mint and revoke. */\nfunction resolvePrincipalAtlasIdentity(\n applicationNamespace: string,\n principal: ApplicationRuntimePrincipal,\n): { username: string; roleName: string } {\n const username = buildMongodbPrincipalUsername(applicationNamespace, principal);\n // The whole point of this path. `buildMongodbPrincipalUsername` returns the backend's name only for the\n // backend, but this is the one assertion whose failure would hand a minion the backend's user, so it is\n // made here rather than inferred from the helper.\n if (username === buildMongodbPrincipalUsername(applicationNamespace, undefined)) {\n throw new Error(\n `MongodbAtlasInfraProviderModule: principal '${principal.kind}:${principal.name ?? ''}' resolved to the `\n + `application backend's Atlas user '${username}'. Refusing: minting would overwrite it and revoking would delete it.`,\n );\n }\n const roleName = buildAtlasPrincipalRoleName(username);\n if (!ATLAS_CUSTOM_ROLE_NAME_PATTERN.test(roleName)) {\n throw new Error(\n `MongodbAtlasInfraProviderModule: the custom role name '${roleName}' for principal `\n + `'${principal.kind}:${principal.name ?? ''}' is not one Atlas accepts (letters, numbers, underscores or hyphens only).`,\n );\n }\n return { username, roleName };\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 let username: string;\n let connectionString: string;\n if (isNamedPrincipal(context.principal)) {\n // A named principal gets a COLLECTION-SCOPED user on ONE custom role (#1694) — never the\n // application's `<namespace>_user`, which holds `readWrite` on the whole database.\n //\n // `resourceGrants` is REQUIRED and its absence is an error rather than a default, exactly as in the\n // self-hosted module: falling back to the application-wide upsert would hand a minion `readWrite` on\n // the whole database AND overwrite the backend's password, through a path that looks narrow.\n const grants = context.resourceGrants;\n if (!grants) {\n throw new Error(\n `MongodbAtlasInfraProviderModule.mintAppCredentials: `\n + `principal '${context.principal.kind}:${context.principal.name}' has no resourceGrants. `\n + `Refusing to fall back to the database-wide application user. The orchestrator must pass `\n + `an explicit empty resource grant when only the mandatory runtime floor is required.`,\n );\n }\n const identity = resolvePrincipalAtlasIdentity(context.applicationNamespace, context.principal);\n username = identity.username;\n // Authored declaration + the engine floor, through the SAME derivation the self-hosted and PostgreSQL\n // adapters use, so the three cannot disagree about what every runtime principal needs.\n const databaseGrants = deriveMinionDatabaseGrants(grants);\n ({ connectionString } = await adminClient.upsertCollectionScopedDatabaseUser({\n clusterName,\n databaseName,\n username,\n password,\n roleName: identity.roleName,\n privileges: toAtlasPrivileges(buildMinionMongodbPrivileges({\n databaseName,\n readCollections: resolveGrantStorageNames(databaseGrants.read),\n writeCollections: resolveGrantStorageNames(databaseGrants.write),\n appendCollections: resolveGrantStorageNames(databaseGrants.append),\n maintainCollections: resolveGrantStorageNames(databaseGrants.maintain),\n })),\n }));\n } else {\n username = buildMongodbPrincipalUsername(context.applicationNamespace, context.principal);\n // POST /databaseUsers (with PATCH-fallback) — idempotent upsert.\n // Returns the cluster's connection string (SRV address, or the seed list when it has none) with credentials embedded.\n ({ connectionString } = await adminClient.upsertApplicationDatabase({\n clusterName,\n databaseName,\n username,\n password,\n }));\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 an Atlas database user. Idempotent.\n *\n * **Caller status**: for a runtime principal, apps-manager's declaration reconcile calls this when a\n * minion is removed. For the application backend (and every minion), apps-manager's withdrawal calls it\n * (`deregisterApplication`, #1307): the user is deleted, the database is kept, and `restoreApplication`\n * re-mints the user with the recorded password.\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 if (isNamedPrincipal(context.principal)) {\n // The principal's user AND its role, derived through the SAME function the mint uses. Before #1694\n // this derived `<namespace>_user` for every input, so revoking a minion would have deleted the\n // application backend's user — `invariant-call-site-enumeration.md` #18, on the Atlas side.\n await adminClient.dropCollectionScopedDatabaseUser(\n resolvePrincipalAtlasIdentity(context.applicationNamespace, context.principal),\n );\n return;\n }\n await adminClient.deleteDatabaseUser(buildMongodbPrincipalUsername(context.applicationNamespace, context.principal));\n },\n\n // No `mintPlatformServiceCredentials` (D-4).\n};\n"]}
|
|
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 the Atlas naming and grant policy; the USERNAME\n * policy itself is shared with the self-hosted module through\n * `buildMongodbPrincipalUsername` (`<applicationNamespace>_user` for the\n * application backend, `<namespace>_<kind>_<name>_user` for a principal).\n *\n * **What this module reads from `AppCredentialMintingContext`**:\n * - `applicationNamespace` + `principal` — the username source (above).\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.\n * - `applicationConfigInputs['clusterName']` — required, the Atlas\n * cluster name. apps-manager resolves it once (`resolveAtlasClusterName`)\n * for the application user and every principal user, and a later\n * reconcile reads back the cluster recorded at registration.\n * - `resourceGrants` — required for a named principal: its declared\n * access, to which the engine floor is added here.\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 cluster's connection\n * string (its SRV address, or the `mongoURIWithOptions` seed list\n * when it has none, #1969) 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`**: for a runtime principal, apps-manager's\n * declaration reconcile calls it when a minion is removed. For the application\n * backend, apps-manager calls it when the application is withdrawn from the\n * platform (`deregisterApplication`, #1307): the database user is deleted and\n * the database is kept, so `restoreApplication` can re-mint it.\n *\n * **Runtime principals (minions), #1694**: each gets its OWN Atlas database\n * user holding ONE Atlas custom database role — the Atlas twin of the\n * self-hosted module's collection-scoped user:\n * - the username comes from `buildMongodbPrincipalUsername`, the SAME\n * derivation the self-hosted module uses, so it can never be the backend's\n * `<namespace>_user`, and the mint and the revoke cannot disagree about it;\n * - the role carries the principal's declared `resourceAccess` PLUS the engine\n * execution-history floor every enabled Node minion needs, derived through\n * `deriveMinionDatabaseGrants` → `buildMinionMongodbPrivileges`, the same\n * pair the self-hosted module uses, then translated into Atlas's action names;\n * - the user is scoped to the application's cluster alone;\n * - revoking deletes that user and that role, and nothing else.\n *\n * Before #1694 the module ignored `principal` and derived `<namespace>_user`\n * for every input, so a minion mint would have overwritten the backend's user\n * and a minion revoke would have deleted it; apps-manager refused the\n * combination at registration instead.\n */\n\nimport {\n ApplicationRuntimePrincipalKind,\n AppCredentialMintingContext,\n AppCredentialMintingResult,\n IMongoAtlasClient,\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n deriveMinionDatabaseGrants,\n resolveGrantStorageNames,\n type ApplicationRuntimePrincipal,\n} from '@wildo-ai/platform-config-lib';\nimport {\n buildMinionMongodbPrivileges,\n buildMongodbPrincipalUsername,\n type MongodbCollectionPrivilege,\n} from '../mongodb-self/mongodb-self.infra.module';\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 cluster's connection string — its SRV\n * address, or the `mongoURIWithOptions` seed list when it has none (#1969) — 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 * A named runtime principal (a minion) — anything but the application backend, which keeps the\n * application-level `<namespace>_user` and its database-wide role.\n */\nfunction isNamedPrincipal(principal: ApplicationRuntimePrincipal | undefined): principal is ApplicationRuntimePrincipal {\n return principal !== undefined && principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND;\n}\n\n/**\n * The Atlas custom role a principal's user holds — derived from the principal's username exactly as the\n * self-hosted module derives its role (`<username>_role`), so the mint and the revoke name the same role.\n *\n * Custom roles are PROJECT-wide in Atlas, not per database or cluster, so the name must be unique across\n * every application the project hosts. It is: the username embeds the application namespace.\n */\nfunction buildAtlasPrincipalRoleName(username: string): string {\n return `${username}_role`;\n}\n\n/**\n * Atlas's rule for a custom role name, checked before any request so a name Atlas would refuse fails here,\n * naming the principal, rather than as a bare HTTP 400 from the Admin API.\n *\n * spec-cite: https://www.mongodb.com/docs/atlas/security-add-mongodb-roles/ — \"The specified role name can\n * only contain letters, numbers, underscores or hyphens.\" (fetched 2026-09-27). The OpenAPI schema for\n * `UserCustomDBRole.roleName` states no pattern, so the prose page is the authority.\n */\nconst ATLAS_CUSTOM_ROLE_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;\n\n/**\n * MongoDB's privilege action (the vocabulary `buildMinionMongodbPrivileges` speaks, shared with the\n * self-hosted module) → the Atlas Admin API's name for the same action.\n *\n * semantic-literal-boundary: both sides are EXTERNAL vocabularies — MongoDB's `createRole` action names and\n * the Atlas Admin API's `DatabasePrivilegeAction.action` enum — and this table is the one place they meet.\n * spec-cite: Atlas Administration API v1.0 OpenAPI (github.com/mongodb/openapi, `openapi/v1-deprecated/v1.json`),\n * schema `DatabasePrivilegeAction`, enum `action`: `FIND`, `INSERT`, `REMOVE`, `UPDATE` (fetched 2026-09-27).\n *\n * Closed on purpose: an action with no entry is REFUSED rather than dropped (a silently narrower role reads\n * as a working grant until the minion fails) or passed through (a spelling Atlas does not know is a 400 at\n * best). Grants only ever reach Atlas through this table, so nothing can widen them on the way.\n */\nconst ATLAS_PRIVILEGE_ACTION_BY_MONGODB_ACTION: Readonly<Record<string, string>> = {\n find: 'FIND',\n insert: 'INSERT',\n update: 'UPDATE',\n remove: 'REMOVE',\n};\n\nfunction toAtlasPrivileges(privileges: readonly MongodbCollectionPrivilege[]): MongodbCollectionPrivilege[] {\n return privileges.map((privilege) => ({\n resource: privilege.resource,\n actions: privilege.actions.map((action) => {\n const atlasAction = ATLAS_PRIVILEGE_ACTION_BY_MONGODB_ACTION[action];\n if (!atlasAction) {\n throw new Error(\n `MongodbAtlasInfraProviderModule: MongoDB privilege action '${action}' has no Atlas Admin API equivalent `\n + `in ATLAS_PRIVILEGE_ACTION_BY_MONGODB_ACTION. Refusing to mint a role that would silently lack it.`,\n );\n }\n return atlasAction;\n }),\n }));\n}\n\n/** The principal's username and role, checked against Atlas's role-name rule. One derivation for mint and revoke. */\nfunction resolvePrincipalAtlasIdentity(\n applicationNamespace: string,\n principal: ApplicationRuntimePrincipal,\n): { username: string; roleName: string } {\n const username = buildMongodbPrincipalUsername(applicationNamespace, principal);\n // The whole point of this path. `buildMongodbPrincipalUsername` returns the backend's name only for the\n // backend, but this is the one assertion whose failure would hand a minion the backend's user, so it is\n // made here rather than inferred from the helper.\n if (username === buildMongodbPrincipalUsername(applicationNamespace, undefined)) {\n throw new Error(\n `MongodbAtlasInfraProviderModule: principal '${principal.kind}:${principal.name ?? ''}' resolved to the `\n + `application backend's Atlas user '${username}'. Refusing: minting would overwrite it and revoking would delete it.`,\n );\n }\n const roleName = buildAtlasPrincipalRoleName(username);\n if (!ATLAS_CUSTOM_ROLE_NAME_PATTERN.test(roleName)) {\n throw new Error(\n `MongodbAtlasInfraProviderModule: the custom role name '${roleName}' for principal `\n + `'${principal.kind}:${principal.name ?? ''}' is not one Atlas accepts (letters, numbers, underscores or hyphens only).`,\n );\n }\n return { username, roleName };\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 let username: string;\n let connectionString: string;\n if (isNamedPrincipal(context.principal)) {\n // A named principal gets a COLLECTION-SCOPED user on ONE custom role (#1694) — never the\n // application's `<namespace>_user`, which holds `readWrite` on the whole database.\n //\n // `resourceGrants` is REQUIRED and its absence is an error rather than a default, exactly as in the\n // self-hosted module: falling back to the application-wide upsert would hand a minion `readWrite` on\n // the whole database AND overwrite the backend's password, through a path that looks narrow.\n const grants = context.resourceGrants;\n if (!grants) {\n throw new Error(\n `MongodbAtlasInfraProviderModule.mintAppCredentials: `\n + `principal '${context.principal.kind}:${context.principal.name}' has no resourceGrants. `\n + `Refusing to fall back to the database-wide application user. The orchestrator must pass `\n + `an explicit empty resource grant when only the mandatory runtime floor is required.`,\n );\n }\n const identity = resolvePrincipalAtlasIdentity(context.applicationNamespace, context.principal);\n username = identity.username;\n // Authored declaration + the engine floor, through the SAME derivation the self-hosted and PostgreSQL\n // adapters use, so the three cannot disagree about what every runtime principal needs.\n const databaseGrants = deriveMinionDatabaseGrants(grants);\n ({ connectionString } = await adminClient.upsertCollectionScopedDatabaseUser({\n clusterName,\n databaseName,\n username,\n password,\n roleName: identity.roleName,\n privileges: toAtlasPrivileges(buildMinionMongodbPrivileges({\n databaseName,\n readCollections: resolveGrantStorageNames(databaseGrants.read),\n writeCollections: resolveGrantStorageNames(databaseGrants.write),\n appendCollections: resolveGrantStorageNames(databaseGrants.append),\n maintainCollections: resolveGrantStorageNames(databaseGrants.maintain),\n })),\n }));\n } else {\n username = buildMongodbPrincipalUsername(context.applicationNamespace, context.principal);\n // POST /databaseUsers (with PATCH-fallback) — idempotent upsert.\n // Returns the cluster's connection string (SRV address, or the seed list when it has none) with credentials embedded.\n ({ connectionString } = await adminClient.upsertApplicationDatabase({\n clusterName,\n databaseName,\n username,\n password,\n }));\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 an Atlas database user. Idempotent.\n *\n * **Caller status**: for a runtime principal, apps-manager's declaration reconcile calls this when a\n * minion is removed. For the application backend (and every minion), apps-manager's withdrawal calls it\n * (`deregisterApplication`, #1307): the user is deleted, the database is kept, and `restoreApplication`\n * re-mints the user with the recorded password.\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 if (isNamedPrincipal(context.principal)) {\n // The principal's user AND its role, derived through the SAME function the mint uses. Before #1694\n // this derived `<namespace>_user` for every input, so revoking a minion would have deleted the\n // application backend's user — `invariant-call-site-enumeration.md` #18, on the Atlas side.\n await adminClient.dropCollectionScopedDatabaseUser(\n resolvePrincipalAtlasIdentity(context.applicationNamespace, context.principal),\n );\n return;\n }\n await adminClient.deleteDatabaseUser(buildMongodbPrincipalUsername(context.applicationNamespace, context.principal));\n },\n\n // No `mintPlatformServiceCredentials` (D-4).\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-self.infra.module.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"mongodb-self.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/mongodb-self/mongodb-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAOL,mBAAmB,EAKnB,KAAK,2BAA2B,EACjC,MAAM,+BAA+B,CAAC;AA2FvC,qDAAqD;AACrD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,kGAAkG;IAClG,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,GAAG,0BAA0B,EAAE,CAe/B;AA4BD;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAC3C,oBAAoB,EAAE,MAAM,EAC5B,SAAS,EAAE,2BAA2B,GAAG,SAAS,GACjD,MAAM,CAMR;AAED,eAAO,MAAM,8BAA8B,EAAE,mBAwM5C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-self.infra.module.js","sourceRoot":"","sources":["../../../src/mongodb-self/mongodb-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAIL,eAAe,EACf,0BAA0B,EAC1B,aAAa,EAGb,+BAA+B,EAC/B,0BAA0B,EAC1B,wBAAwB,GAEzB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAC1D,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAC1D;;;;;;;GAOG;AACH,MAAM,+BAA+B,GAA0B,CAAC,qBAAqB,EAAE,qBAAqB,CAAC,CAAC;AAG9G,MAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAE/D,MAAM,uBAAuB,GAAG,cAAc,CAAC;AAC/C,MAAM,2BAA2B,GAAG,iBAAiB,CAAC;AAEtD,MAAM,8BAA8B,GAAG,kBAAkB,CAAC;AAE1D;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,oBAA4B;IACxD,OAAO,GAAG,oBAAoB,OAAO,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,oBAAoB,GAAsB,CAAC,MAAM,CAAC,CAAC;AACzD,MAAM,qBAAqB,GAAsB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAChF;;;;;GAKG;AACH,MAAM,sBAAsB,GAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,wBAAwB,GAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAQjF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAO5C;IACC,MAAM,UAAU,GAAiC,EAAE,CAAC;IACpD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC/C,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAChD,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,EAAE,CAAC;QACvD,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,EAAE,CAAC;QACzD,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,wBAAwB,CAAC,EAAE,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,qBAAqB,CAC5B,eAAuB,EACvB,YAAoB,EACpB,QAAgB,EAChB,QAAgB;IAEhB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;IACxC,MAAM,CAAC,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,CAAC,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,CAAC,QAAQ,GAAG,GAAG,GAAG,YAAY,CAAC;IACrC,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,6BAA6B,CAC3C,oBAA4B,EAC5B,SAAkD;IAElD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,+BAA+B,CAAC,mBAAmB,EAAE,CAAC;QACzF,OAAO,oBAAoB,CAAC,oBAAoB,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,aAAa,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACpG,OAAO,GAAG,oBAAoB,IAAI,SAAS,CAAC,IAAI,IAAI,aAAa,OAAO,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,8BAA8B,GAAwB;IACjE,QAAQ,EAAE,aAAa,CAAC,YAAY;IACpC,qBAAqB,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC;IAC1D,2BAA2B,EAAE,+BAA+B;IAE5D,IAAI;QACF,OAAO;YACL,qBAAqB,EAAE,CAAC,SAAS,CAAC;YAClC,YAAY,EAAE,CAAC,0DAA0D,CAAC;YAC1E,aAAa,EAAE;gBACb,OAAO,EAAE,EAAE;gBACX,wDAAwD;gBACxD,qDAAqD;gBACrD,aAAa,EAAE,CAAC,mBAAmB,CAAC;gBACpC,SAAS,EAAE;oBACT,0BAA0B,CAAC,YAAY;oBACvC,0BAA0B,CAAC,SAAS;iBACrC;aACF;YACD,UAAU,EAAE;gBACV,uDAAuD;gBACvD,8DAA8D;gBAC9D,yDAAyD;gBACzD,8DAA8D;gBAC9D,6DAA6D;gBAC7D,qBAAqB,EAAE,CAAC,GAAG,+BAA+B,CAAC;aAC5D;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,OAAoC;QAEpC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAoC,CAAC;QACjE,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,0BAA0B,CAAC,CAAC;QAClE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,4BAA4B,0BAA0B,KAAK;gBAC3D,4EAA4E,CAC7E,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,iCAAiC;gBACjC,qDAAqD,CACtD,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,uBAAuB,CAAC,CAAC;QAChF,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,uCAAuC,uBAAuB,KAAK;gBACnE,yDAAyD,CAC1D,CAAC;QACJ,CAAC;QACD,MAAM,eAAe,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,2BAA2B,CAAC,CAAC;QACvF,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,uCAAuC,2BAA2B,KAAK;gBACvE,2DAA2D,CAC5D,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,6BAA6B,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhG,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,+BAA+B,CAAC,mBAAmB,EAAE,CAAC;YACxG,uEAAuE;YACvE,uEAAuE;YACvE,4DAA4D;YAC5D,EAAE;YACF,uEAAuE;YACvE,0EAA0E;YAC1E,uEAAuE;YACvE,0EAA0E;YAC1E,2CAA2C;YAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;YACtC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,qDAAqD;sBACnD,cAAc,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,2BAA2B;sBACzF,0FAA0F;sBAC1F,qFAAqF,CACxF,CAAC;YACJ,CAAC;YACD,MAAM,cAAc,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;YAE1D,MAAM,WAAW,CAAC,kCAAkC,CAAC;gBACnD,eAAe;gBACf,YAAY;gBACZ,QAAQ;gBACR,QAAQ;gBACR,QAAQ,EAAE,GAAG,QAAQ,OAAO;gBAC5B,qEAAqE;gBACrE,qEAAqE;gBACrE,uEAAuE;gBACvE,sEAAsE;gBACtE,4DAA4D;gBAC5D,UAAU,EAAE,4BAA4B,CAAC;oBACvC,YAAY;oBACZ,eAAe,EAAE,wBAAwB,CAAC,cAAc,CAAC,IAAI,CAAC;oBAC9D,gBAAgB,EAAE,wBAAwB,CAAC,cAAc,CAAC,KAAK,CAAC;oBAChE,iBAAiB,EAAE,wBAAwB,CAAC,cAAc,CAAC,MAAM,CAAC;oBAClE,mBAAmB,EAAE,wBAAwB,CAAC,cAAc,CAAC,QAAQ,CAAC;iBACvE,CAAC;aACH,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,yDAAyD;YACzD,kCAAkC;YAClC,MAAM,WAAW,CAAC,6BAA6B,CAAC;gBAC9C,eAAe;gBACf,YAAY;gBACZ,QAAQ;gBACR,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;QAED,mGAAmG;QACnG,4DAA4D;QAC5D,6CAA6C;QAC7C,MAAM,gBAAgB,GAAG,qBAAqB,CAC5C,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,QAAQ,CACT,CAAC;QAEF,OAAO;YACL,gBAAgB,EAAE;gBAChB,CAAC,qBAAqB,CAAC,EAAE,QAAQ;gBACjC,CAAC,qBAAqB,CAAC,EAAE,QAAQ;aAClC;YACD,gBAAgB,EAAE;gBAChB,CAAC,8BAA8B,CAAC,EAAE,gBAAgB;aACnD;SACF,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,oBAAoB,CAAC,OAAoC;QAC7D,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,uDAAuD;gBACvD,iCAAiC;gBACjC,qDAAqD,CACtD,CAAC;QACJ,CAAC;QACD,MAAM,eAAe,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,2BAA2B,CAAC,CAAC;QACvF,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,uDAAuD;gBACvD,uCAAuC,2BAA2B,KAAK;gBACvE,2DAA2D,CAC5D,CAAC;QACJ,CAAC;QACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAoC,CAAC;QACjE,6EAA6E;QAC7E,6EAA6E;QAC7E,yEAAyE;QACzE,8DAA8D;QAC9D,kDAAkD;QAClD,MAAM,QAAQ,GAAG,6BAA6B,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhG,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,+BAA+B,CAAC,mBAAmB,EAAE,CAAC;YACxG,2EAA2E;YAC3E,yEAAyE;YACzE,uCAAuC;YACvC,MAAM,YAAY,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,uBAAuB,CAAC,CAAC;YAChF,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CACb,uDAAuD;sBACrD,uCAAuC,uBAAuB,kBAAkB;sBAChF,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,oCAAoC;sBACxF,wFAAwF,CAC3F,CAAC;YACJ,CAAC;YACD,MAAM,WAAW,CAAC,gCAAgC,CAAC;gBACjD,eAAe;gBACf,YAAY;gBACZ,QAAQ;gBACR,QAAQ,EAAE,GAAG,QAAQ,OAAO;aAC7B,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,WAAW,CAAC,6BAA6B,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,6CAA6C;CAC9C,CAAC","sourcesContent":["/**\n * `MongodbSelfInfraProviderModule` — fifth concrete `InfraProviderModule`\n * (Slice 4.B.3 of `infra-provider-planner.md`, generalizing the\n * pattern to self-managed MongoDB).\n *\n * Implements the `MONGODB_SELF` `InfraProvider` over the\n * `IMongoSelfAdminClient` transport (admin-side mongoose connection,\n * exposed by `MongoSelfManagerService` after the Slice 4.B.3 5C\n * policy/transport split). Owns the MongoDB username policy\n * (`buildMongodbPrincipalUsername`, which the Atlas module also uses, so a\n * user has one name whichever provider holds the store) and\n * the per-app connection URL derivation (same scheme as the admin URL) by swapping admin credentials\n * with per-app credentials in the admin connect URL the orchestrator\n * reads from `MONGO_CONNECT_URL`.\n *\n * **What this module reads from `AppCredentialMintingContext`**:\n * - `applicationNamespace` + `principal` — the username source\n * (`buildMongodbPrincipalUsername`: `${ns}_user` for the application\n * backend, `${ns}_<kind>_<name>_user` for a runtime principal).\n * - `resourceGrants` — required for a named principal: its declared\n * access, to which `deriveMinionDatabaseGrants` adds the engine floor.\n * - `secretInputs['database.mongodb.password']` — orchestrator-\n * supplied password (module is upsert-only, never generator).\n * - `applicationConfigInputs['databaseName']` — required.\n * - `applicationConfigInputs['adminConnectUrl']` — required, the\n * admin-scoped MongoDB connect URL the orchestrator reads from\n * `MONGO_CONNECT_URL` env at app-init time.\n * - `adminClient` — narrowed to `IMongoSelfAdminClient`.\n *\n * **Two audiences this module serves:**\n *\n * 1. **Audience-1 (platform-admin)**: cluster-wide Mongo admin\n * connect URL (`MONGO_CONNECT_URL`). Consumers: `apps-manager`\n * (mints per-app users via `mintAppCredentials`), `companion`\n * (mirrors apps-manager admin role for local dev).\n *\n * 2. **Audience-3 (app-scoped, via `mintAppCredentials`)**:\n * per-customer-app Mongo database user. Result threaded into\n * `Wildo_Application_Secrets.database.mongodb.{username,password}`\n * via `_merge_WildoApplicationSecrets`; the per-app connect URL\n * returned via `runtimeArtifacts.connectionString` for\n * transient app-init operations.\n *\n * **NOT served (D-4)**: audience-2 not applicable.\n */\n\nimport {\n AppCredentialMintingContext,\n AppCredentialMintingResult,\n IMongoSelfAdminClient,\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n ApplicationRuntimePrincipalKind,\n deriveMinionDatabaseGrants,\n resolveGrantStorageNames,\n type ApplicationRuntimePrincipal,\n} from '@wildo-ai/platform-config-lib';\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\nconst MONGODB_PASSWORD_INPUT_KEY = 'database.mongodb.password';\n\nconst DATABASE_NAME_INPUT_KEY = 'databaseName';\nconst ADMIN_CONNECT_URL_INPUT_KEY = 'adminConnectUrl';\n\nconst CONNECTION_STRING_ARTIFACT_KEY = 'connectionString';\n\n/**\n * The application backend's username (`${ns}_user`). Reached only through\n * `buildMongodbPrincipalUsername`, which both MongoDB modules call, so the\n * persistence shape is identical regardless of provider.\n */\nfunction buildMongodbUsername(applicationNamespace: string): string {\n return `${applicationNamespace}_user`;\n}\n\n/**\n * MongoDB privilege ACTIONS, per grant axis.\n *\n * Named here rather than inlined because these ARE the security semantics: what\n * \"read\" and \"write\" mean once they reach the database. Mongo's action\n * vocabulary is an external one, so it lives at this adapter — the module that\n * speaks Mongo — and never leaks into the platform's grant model.\n *\n * `find` alone for read: no `listCollections`, no `collStats`, nothing that\n * enumerates. A minion granted read on one resource should not be able to\n * discover what else exists.\n *\n * `insert`/`update`/`remove` for write, and deliberately NOT `find`. The\n * platform-level derivation refuses to widen write into read, and re-adding it\n * here would defeat that decision at the last hop — see\n * `deriveMinionResourceGrants`.\n */\nconst MONGODB_READ_ACTIONS: readonly string[] = ['find'];\nconst MONGODB_WRITE_ACTIONS: readonly string[] = ['insert', 'update', 'remove'];\n/**\n * Append-only: `insert` and `find`, WITHOUT `update` or `remove`. Carries the engine's audit-log\n * floor, where a principal must be able to record what it did and must never be able to unrecord it.\n *\n * `find` is not a widening — see {@link MONGODB_MAINTAIN_ACTIONS}, whose note covers both floors.\n */\nconst MONGODB_APPEND_ACTIONS: readonly string[] = ['insert', 'find'];\n/**\n * Durable runtime state may be inserted, read back and updated, never removed.\n *\n * ## Why the two FLOORS carry `find` and the author-declared write axis above does not\n *\n * Without it this axis could not perform the update the engine actually issues. Measured 2026-09-16\n * against the running replica set, as a throwaway role holding exactly these actions:\n *\n * | actions | operation | outcome |\n * |---|---|---|\n * | `insert`, `update` | `updateOne({_id}, …)` | works |\n * | `insert`, `update` | **`findOneAndUpdate`** | **Unauthorized** |\n * | `insert`, `find`, `update` | `findOneAndUpdate` | works; `deleteOne` still Unauthorized |\n *\n * The third row is the one that matters: `RepositoryResourceBackendService` updates through\n * `findOneAndUpdate`, so a role without `find` could not update at all — only a filter-less write no\n * caller issues. Note MongoDB and PostgreSQL differ here, which is why each was measured rather than\n * inferred from the other: Mongo's `update` action covers its own filter, while PostgreSQL requires\n * `SELECT` for the columns a `WHERE` reads.\n *\n * `find` on the APPEND axis was NOT broken on MongoDB — `insertOne` succeeds without it. It is added\n * so an axis means the same thing on both adapters: `deriveMinionDatabaseGrants` exists precisely so\n * the two cannot drift, and an append grant that carried read-back on one adapter and not the other\n * would make the same declaration yield two different authorities.\n *\n * **What these floors withhold is untouched.** Append still holds no `update` and no `remove`;\n * maintain still holds no `remove`. Both were re-probed after adding `find`, and `deleteOne` stayed\n * Unauthorized. The author-declared write axis keeps write-without-read deliberately, because there\n * the author has a remedy — declare the read — which `deriveMinionResourceGrants` argues for at\n * length. These two axes are engine-imposed and refuse any declaration, so no author action could\n * ever have repaired them.\n */\nconst MONGODB_MAINTAIN_ACTIONS: readonly string[] = ['insert', 'find', 'update'];\n\n/** One `createRole`/`updateRole` privilege entry. */\nexport interface MongodbCollectionPrivilege {\n readonly resource: { readonly db: string; readonly collection: string };\n readonly actions: readonly string[];\n}\n\n/**\n * Turn a minion's grants into collection-scoped MongoDB privileges (D14, R1).\n *\n * ## What this deliberately never produces\n *\n * No `dbAdmin` and no `readWrite` — the two roles the application backend\n * currently holds on the whole database. `dbAdmin` is banned outright by D14;\n * `readWrite` is database-WIDE, so granting it to a minion would make every\n * collection reachable regardless of what `resourceAccess` declared, which is\n * the containment this exists to create.\n *\n * No privilege on the database RESOURCE either (`{ db, collection: '' }`), which\n * is Mongo's way of saying \"every collection in this database\". Every entry\n * names one collection explicitly.\n *\n * ## Read and write are separate entries on purpose\n *\n * A collection granted both axes yields TWO privilege entries rather than one\n * merged action list. Mongo unions them identically, but the emitted role then\n * reads back as the declaration that produced it — which is what makes an\n * operator inspecting `db.getRole()` able to check the grant against the config\n * without re-deriving it.\n */\nexport function buildMinionMongodbPrivileges(input: {\n databaseName: string;\n readCollections: readonly string[];\n writeCollections: readonly string[];\n /** Insert-only — see {@link MONGODB_APPEND_ACTIONS}. Optional so existing pins read unchanged. */\n appendCollections?: readonly string[];\n maintainCollections?: readonly string[];\n}): MongodbCollectionPrivilege[] {\n const privileges: MongodbCollectionPrivilege[] = [];\n for (const collection of input.readCollections) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_READ_ACTIONS] });\n }\n for (const collection of input.writeCollections) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_WRITE_ACTIONS] });\n }\n for (const collection of input.appendCollections ?? []) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_APPEND_ACTIONS] });\n }\n for (const collection of input.maintainCollections ?? []) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_MAINTAIN_ACTIONS] });\n }\n return privileges;\n}\n\n/**\n * Derive the per-app connect URL by swapping admin credentials and\n * the database path in the authored `adminConnectUrl`. Mirrors the\n * legacy logic at\n * `wildo-application-lifecycle.service.ts:469-479`:\n *\n * const parsed = new URL(adminConnectUrl);\n * parsed.username = perAppUsername;\n * parsed.password = perAppPassword;\n * parsed.pathname = '/' + databaseName;\n *\n * URL-encoding handled automatically by the WHATWG URL class.\n */\nfunction buildPerAppConnectUrl(\n adminConnectUrl: string,\n databaseName: string,\n username: string,\n password: string,\n): string {\n const parsed = new URL(adminConnectUrl);\n parsed.username = encodeURIComponent(username);\n parsed.password = encodeURIComponent(password);\n parsed.pathname = '/' + databaseName;\n return parsed.toString();\n}\n\n/**\n * Per-principal Mongo username — the SAME derivation the revoke path must use.\n *\n * The application backend keeps `<namespace>_user` VERBATIM so an existing\n * deployment's user is never orphaned. Third time this shape appears (Redis,\n * RabbitMQ, now Mongo): a revoke deriving it differently deletes the BACKEND's\n * database user while believing it is removing a minion's.\n *\n * Exported because BOTH MongoDB modules name principals through it — the Atlas\n * module too (#1694) — so a principal's user has one name whichever provider\n * holds the store, and neither module can drift into the backend's name.\n */\nexport function buildMongodbPrincipalUsername(\n applicationNamespace: string,\n principal: ApplicationRuntimePrincipal | undefined,\n): string {\n if (!principal || principal.kind === ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n return buildMongodbUsername(applicationNamespace);\n }\n const sanitizedName = (principal.name ?? '').replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_+|_+$/g, '');\n return `${applicationNamespace}_${principal.kind}_${sanitizedName}_user`;\n}\n\nexport const MongodbSelfInfraProviderModule: InfraProviderModule = {\n provider: InfraProvider.MONGODB_SELF,\n supportedCapabilities: [InfraCapability.DOCUMENT_DATABASE],\n perPrincipalSecretBlobPaths: PER_PRINCIPAL_SECRET_BLOB_PATHS,\n\n plan(): InfraProviderPlanContribution {\n return {\n dockerComposeServices: ['mongodb'],\n k8sResources: ['templates/infra/k8s/internal/mongodb/deployment.yaml.hbs'],\n platformAdmin: {\n envVars: {},\n // The single MONGO_CONNECT_URL secret carries the admin\n // credentials embedded; legacy field name preserved.\n secretEnvVars: ['MONGO_CONNECT_URL'],\n consumers: [\n InfraPlatformAdminConsumer.APPS_MANAGER,\n InfraPlatformAdminConsumer.COMPANION,\n ],\n },\n appRuntime: {\n // Same persistence shape as Atlas — per-app username +\n // password go into secrets. The stored host/protocol are read\n // by apps-manager from the connection string this module\n // RETURNS when the application declares its provider (#1876),\n // and from BASE_MONGO_SERVER_URL only when it declares none.\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 IMongoSelfAdminClient;\n const password = context.secretInputs[MONGODB_PASSWORD_INPUT_KEY];\n if (!password) {\n throw new Error(\n `MongodbSelfInfraProviderModule.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 `MongodbSelfInfraProviderModule.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 `MongodbSelfInfraProviderModule.mintAppCredentials: ` +\n `applicationConfigInputs is missing '${DATABASE_NAME_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the per-app database name.`,\n );\n }\n const adminConnectUrl = context.applicationConfigInputs?.[ADMIN_CONNECT_URL_INPUT_KEY];\n if (!adminConnectUrl) {\n throw new Error(\n `MongodbSelfInfraProviderModule.mintAppCredentials: ` +\n `applicationConfigInputs is missing '${ADMIN_CONNECT_URL_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the admin Mongo connect URL.`,\n );\n }\n\n const username = buildMongodbPrincipalUsername(context.applicationNamespace, context.principal);\n\n if (context.principal && context.principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n // A named principal gets a COLLECTION-SCOPED user (Step 3 / R1 / D14):\n // one custom role carrying authored business grants plus the mandatory\n // runtime floor, and no inherited database-wide privileges.\n //\n // `resourceGrants` is REQUIRED here and its absence is an error rather\n // than a default. Falling back to the app-wide upsert would hand a minion\n // `readWrite` + `dbAdmin` on the whole database — the exact over-grant\n // this path exists to replace, arriving silently through a code path that\n // looks like it is doing the narrow thing.\n const grants = context.resourceGrants;\n if (!grants) {\n throw new Error(\n `MongodbSelfInfraProviderModule.mintAppCredentials: `\n + `principal '${context.principal.kind}:${context.principal.name}' has no resourceGrants. `\n + `Refusing to fall back to the database-wide application user. The orchestrator must pass `\n + `an explicit empty resource grant when only the mandatory runtime floor is required.`,\n );\n }\n const databaseGrants = deriveMinionDatabaseGrants(grants);\n\n await adminClient.upsertCollectionScopedDatabaseUser({\n adminConnectUrl,\n databaseName,\n username,\n password,\n roleName: `${username}_role`,\n // Resource types → collection names HERE, at the adapter that speaks\n // Mongo. The grant contract itself stays in the platform vocabulary.\n // Authored declaration + the engine floor, through the SAME derivation\n // the PostgreSQL adapter uses — so the two cannot disagree about what\n // the framework itself requires of every runtime principal.\n privileges: buildMinionMongodbPrivileges({\n databaseName,\n readCollections: resolveGrantStorageNames(databaseGrants.read),\n writeCollections: resolveGrantStorageNames(databaseGrants.write),\n appendCollections: resolveGrantStorageNames(databaseGrants.append),\n maintainCollections: resolveGrantStorageNames(databaseGrants.maintain),\n }),\n });\n } else {\n // createUser-or-updateUser over an admin-scoped mongoose\n // connection — idempotent upsert.\n await adminClient.upsertApplicationDatabaseUser({\n adminConnectUrl,\n databaseName,\n username,\n password,\n });\n }\n\n // Derive the per-app connection URL (the admin URL's own scheme) by swapping credentials + db path\n // in the authored admin URL. The orchestrator uses this for\n // transient app-init seeding; not persisted.\n const connectionString = buildPerAppConnectUrl(\n adminConnectUrl,\n databaseName,\n username,\n password,\n );\n\n return {\n secretBlobValues: {\n [MONGODB_USERNAME_PATH]: username,\n [MONGODB_PASSWORD_PATH]: password,\n },\n runtimeArtifacts: {\n [CONNECTION_STRING_ARTIFACT_KEY]: connectionString,\n },\n };\n },\n\n /**\n * Revoke a principal's Mongo database user (and a minion's custom role); the database\n * and its documents are kept.\n *\n * **Caller status**: apps-manager runtime-principal declaration reconciliation\n * calls this for a removed minion when its application has a MongoDB store, and\n * application withdrawal (`deregisterApplication`, #1307) for the backend and every\n * minion; `restoreApplication` re-mints them.\n * The named principal and database scope identify the grant being revoked.\n */\n async revokeAppCredentials(context: AppCredentialMintingContext): Promise<void> {\n if (!context.applicationNamespace) {\n throw new Error(\n `MongodbSelfInfraProviderModule.revokeAppCredentials: ` +\n `applicationNamespace is empty. ` +\n `The orchestrator MUST supply a non-empty namespace.`,\n );\n }\n const adminConnectUrl = context.applicationConfigInputs?.[ADMIN_CONNECT_URL_INPUT_KEY];\n if (!adminConnectUrl) {\n throw new Error(\n `MongodbSelfInfraProviderModule.revokeAppCredentials: ` +\n `applicationConfigInputs is missing '${ADMIN_CONNECT_URL_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the admin Mongo connect URL.`,\n );\n }\n const adminClient = context.adminClient as IMongoSelfAdminClient;\n // Derived through the SAME function the mint uses. Deriving it independently\n // here — which is what this line did before named principals existed — meant\n // revoking a minion left its credential live AND dropped the APPLICATION\n // BACKEND's database user, because the backend's name is what\n // `buildMongodbUsername` returns for every input.\n const username = buildMongodbPrincipalUsername(context.applicationNamespace, context.principal);\n\n if (context.principal && context.principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n // A scoped principal owns a user AND a custom role. Routing it through the\n // user-only door would leave a privileged role behind that the next mint\n // adopts without anybody declaring it.\n const databaseName = context.applicationConfigInputs?.[DATABASE_NAME_INPUT_KEY];\n if (!databaseName) {\n throw new Error(\n `MongodbSelfInfraProviderModule.revokeAppCredentials: `\n + `applicationConfigInputs is missing '${DATABASE_NAME_INPUT_KEY}' for principal `\n + `'${context.principal.kind}:${context.principal.name}'. The role to drop lives IN that `\n + `database — revoking without it would drop the user and silently orphan its privileges.`,\n );\n }\n await adminClient.dropCollectionScopedDatabaseUser({\n adminConnectUrl,\n databaseName,\n username,\n roleName: `${username}_role`,\n });\n return;\n }\n\n await adminClient.deleteApplicationDatabaseUser({ adminConnectUrl, username });\n },\n\n // No `mintPlatformServiceCredentials` (D-4).\n};\n"]}
|
|
1
|
+
{"version":3,"file":"mongodb-self.infra.module.js","sourceRoot":"","sources":["../../../../src/mongodb-self/mongodb-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAIL,eAAe,EACf,0BAA0B,EAC1B,aAAa,EAGb,+BAA+B,EAC/B,0BAA0B,EAC1B,wBAAwB,GAEzB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAC1D,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAC1D;;;;;;;GAOG;AACH,MAAM,+BAA+B,GAA0B,CAAC,qBAAqB,EAAE,qBAAqB,CAAC,CAAC;AAG9G,MAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAE/D,MAAM,uBAAuB,GAAG,cAAc,CAAC;AAC/C,MAAM,2BAA2B,GAAG,iBAAiB,CAAC;AAEtD,MAAM,8BAA8B,GAAG,kBAAkB,CAAC;AAE1D;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,oBAA4B;IACxD,OAAO,GAAG,oBAAoB,OAAO,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,oBAAoB,GAAsB,CAAC,MAAM,CAAC,CAAC;AACzD,MAAM,qBAAqB,GAAsB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAChF;;;;;GAKG;AACH,MAAM,sBAAsB,GAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,wBAAwB,GAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAQjF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAO5C;IACC,MAAM,UAAU,GAAiC,EAAE,CAAC;IACpD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC/C,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAChD,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,EAAE,CAAC;QACvD,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,EAAE,CAAC;QACzD,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,wBAAwB,CAAC,EAAE,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,qBAAqB,CAC5B,eAAuB,EACvB,YAAoB,EACpB,QAAgB,EAChB,QAAgB;IAEhB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;IACxC,MAAM,CAAC,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,CAAC,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,CAAC,QAAQ,GAAG,GAAG,GAAG,YAAY,CAAC;IACrC,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,6BAA6B,CAC3C,oBAA4B,EAC5B,SAAkD;IAElD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,+BAA+B,CAAC,mBAAmB,EAAE,CAAC;QACzF,OAAO,oBAAoB,CAAC,oBAAoB,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,aAAa,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACpG,OAAO,GAAG,oBAAoB,IAAI,SAAS,CAAC,IAAI,IAAI,aAAa,OAAO,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,8BAA8B,GAAwB;IACjE,QAAQ,EAAE,aAAa,CAAC,YAAY;IACpC,qBAAqB,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC;IAC1D,2BAA2B,EAAE,+BAA+B;IAE5D,IAAI;QACF,OAAO;YACL,qBAAqB,EAAE,CAAC,SAAS,CAAC;YAClC,YAAY,EAAE,CAAC,0DAA0D,CAAC;YAC1E,aAAa,EAAE;gBACb,OAAO,EAAE,EAAE;gBACX,wDAAwD;gBACxD,qDAAqD;gBACrD,aAAa,EAAE,CAAC,mBAAmB,CAAC;gBACpC,SAAS,EAAE;oBACT,0BAA0B,CAAC,YAAY;oBACvC,0BAA0B,CAAC,SAAS;iBACrC;aACF;YACD,UAAU,EAAE;gBACV,uDAAuD;gBACvD,8DAA8D;gBAC9D,yDAAyD;gBACzD,8DAA8D;gBAC9D,6DAA6D;gBAC7D,qBAAqB,EAAE,CAAC,GAAG,+BAA+B,CAAC;aAC5D;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,OAAoC;QAEpC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAoC,CAAC;QACjE,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,0BAA0B,CAAC,CAAC;QAClE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,4BAA4B,0BAA0B,KAAK;gBAC3D,4EAA4E,CAC7E,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,iCAAiC;gBACjC,qDAAqD,CACtD,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,uBAAuB,CAAC,CAAC;QAChF,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,uCAAuC,uBAAuB,KAAK;gBACnE,yDAAyD,CAC1D,CAAC;QACJ,CAAC;QACD,MAAM,eAAe,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,2BAA2B,CAAC,CAAC;QACvF,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,qDAAqD;gBACrD,uCAAuC,2BAA2B,KAAK;gBACvE,2DAA2D,CAC5D,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,6BAA6B,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhG,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,+BAA+B,CAAC,mBAAmB,EAAE,CAAC;YACxG,uEAAuE;YACvE,uEAAuE;YACvE,4DAA4D;YAC5D,EAAE;YACF,uEAAuE;YACvE,0EAA0E;YAC1E,uEAAuE;YACvE,0EAA0E;YAC1E,2CAA2C;YAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;YACtC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,qDAAqD;sBACnD,cAAc,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,2BAA2B;sBACzF,0FAA0F;sBAC1F,qFAAqF,CACxF,CAAC;YACJ,CAAC;YACD,MAAM,cAAc,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;YAE1D,MAAM,WAAW,CAAC,kCAAkC,CAAC;gBACnD,eAAe;gBACf,YAAY;gBACZ,QAAQ;gBACR,QAAQ;gBACR,QAAQ,EAAE,GAAG,QAAQ,OAAO;gBAC5B,qEAAqE;gBACrE,qEAAqE;gBACrE,uEAAuE;gBACvE,sEAAsE;gBACtE,4DAA4D;gBAC5D,UAAU,EAAE,4BAA4B,CAAC;oBACvC,YAAY;oBACZ,eAAe,EAAE,wBAAwB,CAAC,cAAc,CAAC,IAAI,CAAC;oBAC9D,gBAAgB,EAAE,wBAAwB,CAAC,cAAc,CAAC,KAAK,CAAC;oBAChE,iBAAiB,EAAE,wBAAwB,CAAC,cAAc,CAAC,MAAM,CAAC;oBAClE,mBAAmB,EAAE,wBAAwB,CAAC,cAAc,CAAC,QAAQ,CAAC;iBACvE,CAAC;aACH,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,yDAAyD;YACzD,kCAAkC;YAClC,MAAM,WAAW,CAAC,6BAA6B,CAAC;gBAC9C,eAAe;gBACf,YAAY;gBACZ,QAAQ;gBACR,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;QAED,mGAAmG;QACnG,4DAA4D;QAC5D,6CAA6C;QAC7C,MAAM,gBAAgB,GAAG,qBAAqB,CAC5C,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,QAAQ,CACT,CAAC;QAEF,OAAO;YACL,gBAAgB,EAAE;gBAChB,CAAC,qBAAqB,CAAC,EAAE,QAAQ;gBACjC,CAAC,qBAAqB,CAAC,EAAE,QAAQ;aAClC;YACD,gBAAgB,EAAE;gBAChB,CAAC,8BAA8B,CAAC,EAAE,gBAAgB;aACnD;SACF,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,oBAAoB,CAAC,OAAoC;QAC7D,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,uDAAuD;gBACvD,iCAAiC;gBACjC,qDAAqD,CACtD,CAAC;QACJ,CAAC;QACD,MAAM,eAAe,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,2BAA2B,CAAC,CAAC;QACvF,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,uDAAuD;gBACvD,uCAAuC,2BAA2B,KAAK;gBACvE,2DAA2D,CAC5D,CAAC;QACJ,CAAC;QACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAoC,CAAC;QACjE,6EAA6E;QAC7E,6EAA6E;QAC7E,yEAAyE;QACzE,8DAA8D;QAC9D,kDAAkD;QAClD,MAAM,QAAQ,GAAG,6BAA6B,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhG,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,+BAA+B,CAAC,mBAAmB,EAAE,CAAC;YACxG,2EAA2E;YAC3E,yEAAyE;YACzE,uCAAuC;YACvC,MAAM,YAAY,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC,uBAAuB,CAAC,CAAC;YAChF,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CACb,uDAAuD;sBACrD,uCAAuC,uBAAuB,kBAAkB;sBAChF,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,oCAAoC;sBACxF,wFAAwF,CAC3F,CAAC;YACJ,CAAC;YACD,MAAM,WAAW,CAAC,gCAAgC,CAAC;gBACjD,eAAe;gBACf,YAAY;gBACZ,QAAQ;gBACR,QAAQ,EAAE,GAAG,QAAQ,OAAO;aAC7B,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,WAAW,CAAC,6BAA6B,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,6CAA6C;CAC9C,CAAC","sourcesContent":["/**\n * `MongodbSelfInfraProviderModule` — fifth concrete `InfraProviderModule`\n * (Slice 4.B.3 of `infra-provider-planner.md`, generalizing the\n * pattern to self-managed MongoDB).\n *\n * Implements the `MONGODB_SELF` `InfraProvider` over the\n * `IMongoSelfAdminClient` transport (admin-side mongoose connection,\n * exposed by `MongoSelfManagerService` after the Slice 4.B.3 5C\n * policy/transport split). Owns the MongoDB username policy\n * (`buildMongodbPrincipalUsername`, which the Atlas module also uses, so a\n * user has one name whichever provider holds the store) and\n * the per-app connection URL derivation (same scheme as the admin URL) by swapping admin credentials\n * with per-app credentials in the admin connect URL the orchestrator\n * reads from `MONGO_CONNECT_URL`.\n *\n * **What this module reads from `AppCredentialMintingContext`**:\n * - `applicationNamespace` + `principal` — the username source\n * (`buildMongodbPrincipalUsername`: `${ns}_user` for the application\n * backend, `${ns}_<kind>_<name>_user` for a runtime principal).\n * - `resourceGrants` — required for a named principal: its declared\n * access, to which `deriveMinionDatabaseGrants` adds the engine floor.\n * - `secretInputs['database.mongodb.password']` — orchestrator-\n * supplied password (module is upsert-only, never generator).\n * - `applicationConfigInputs['databaseName']` — required.\n * - `applicationConfigInputs['adminConnectUrl']` — required, the\n * admin-scoped MongoDB connect URL the orchestrator reads from\n * `MONGO_CONNECT_URL` env at app-init time.\n * - `adminClient` — narrowed to `IMongoSelfAdminClient`.\n *\n * **Two audiences this module serves:**\n *\n * 1. **Audience-1 (platform-admin)**: cluster-wide Mongo admin\n * connect URL (`MONGO_CONNECT_URL`). Consumers: `apps-manager`\n * (mints per-app users via `mintAppCredentials`), `companion`\n * (mirrors apps-manager admin role for local dev).\n *\n * 2. **Audience-3 (app-scoped, via `mintAppCredentials`)**:\n * per-customer-app Mongo database user. Result threaded into\n * `Wildo_Application_Secrets.database.mongodb.{username,password}`\n * via `_merge_WildoApplicationSecrets`; the per-app connect URL\n * returned via `runtimeArtifacts.connectionString` for\n * transient app-init operations.\n *\n * **NOT served (D-4)**: audience-2 not applicable.\n */\n\nimport {\n AppCredentialMintingContext,\n AppCredentialMintingResult,\n IMongoSelfAdminClient,\n InfraCapability,\n InfraPlatformAdminConsumer,\n InfraProvider,\n InfraProviderModule,\n InfraProviderPlanContribution,\n ApplicationRuntimePrincipalKind,\n deriveMinionDatabaseGrants,\n resolveGrantStorageNames,\n type ApplicationRuntimePrincipal,\n} from '@wildo-ai/platform-config-lib';\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\nconst MONGODB_PASSWORD_INPUT_KEY = 'database.mongodb.password';\n\nconst DATABASE_NAME_INPUT_KEY = 'databaseName';\nconst ADMIN_CONNECT_URL_INPUT_KEY = 'adminConnectUrl';\n\nconst CONNECTION_STRING_ARTIFACT_KEY = 'connectionString';\n\n/**\n * The application backend's username (`${ns}_user`). Reached only through\n * `buildMongodbPrincipalUsername`, which both MongoDB modules call, so the\n * persistence shape is identical regardless of provider.\n */\nfunction buildMongodbUsername(applicationNamespace: string): string {\n return `${applicationNamespace}_user`;\n}\n\n/**\n * MongoDB privilege ACTIONS, per grant axis.\n *\n * Named here rather than inlined because these ARE the security semantics: what\n * \"read\" and \"write\" mean once they reach the database. Mongo's action\n * vocabulary is an external one, so it lives at this adapter — the module that\n * speaks Mongo — and never leaks into the platform's grant model.\n *\n * `find` alone for read: no `listCollections`, no `collStats`, nothing that\n * enumerates. A minion granted read on one resource should not be able to\n * discover what else exists.\n *\n * `insert`/`update`/`remove` for write, and deliberately NOT `find`. The\n * platform-level derivation refuses to widen write into read, and re-adding it\n * here would defeat that decision at the last hop — see\n * `deriveMinionResourceGrants`.\n */\nconst MONGODB_READ_ACTIONS: readonly string[] = ['find'];\nconst MONGODB_WRITE_ACTIONS: readonly string[] = ['insert', 'update', 'remove'];\n/**\n * Append-only: `insert` and `find`, WITHOUT `update` or `remove`. Carries the engine's audit-log\n * floor, where a principal must be able to record what it did and must never be able to unrecord it.\n *\n * `find` is not a widening — see {@link MONGODB_MAINTAIN_ACTIONS}, whose note covers both floors.\n */\nconst MONGODB_APPEND_ACTIONS: readonly string[] = ['insert', 'find'];\n/**\n * Durable runtime state may be inserted, read back and updated, never removed.\n *\n * ## Why the two FLOORS carry `find` and the author-declared write axis above does not\n *\n * Without it this axis could not perform the update the engine actually issues. Measured 2026-09-16\n * against the running replica set, as a throwaway role holding exactly these actions:\n *\n * | actions | operation | outcome |\n * |---|---|---|\n * | `insert`, `update` | `updateOne({_id}, …)` | works |\n * | `insert`, `update` | **`findOneAndUpdate`** | **Unauthorized** |\n * | `insert`, `find`, `update` | `findOneAndUpdate` | works; `deleteOne` still Unauthorized |\n *\n * The third row is the one that matters: `RepositoryResourceBackendService` updates through\n * `findOneAndUpdate`, so a role without `find` could not update at all — only a filter-less write no\n * caller issues. Note MongoDB and PostgreSQL differ here, which is why each was measured rather than\n * inferred from the other: Mongo's `update` action covers its own filter, while PostgreSQL requires\n * `SELECT` for the columns a `WHERE` reads.\n *\n * `find` on the APPEND axis was NOT broken on MongoDB — `insertOne` succeeds without it. It is added\n * so an axis means the same thing on both adapters: `deriveMinionDatabaseGrants` exists precisely so\n * the two cannot drift, and an append grant that carried read-back on one adapter and not the other\n * would make the same declaration yield two different authorities.\n *\n * **What these floors withhold is untouched.** Append still holds no `update` and no `remove`;\n * maintain still holds no `remove`. Both were re-probed after adding `find`, and `deleteOne` stayed\n * Unauthorized. The author-declared write axis keeps write-without-read deliberately, because there\n * the author has a remedy — declare the read — which `deriveMinionResourceGrants` argues for at\n * length. These two axes are engine-imposed and refuse any declaration, so no author action could\n * ever have repaired them.\n */\nconst MONGODB_MAINTAIN_ACTIONS: readonly string[] = ['insert', 'find', 'update'];\n\n/** One `createRole`/`updateRole` privilege entry. */\nexport interface MongodbCollectionPrivilege {\n readonly resource: { readonly db: string; readonly collection: string };\n readonly actions: readonly string[];\n}\n\n/**\n * Turn a minion's grants into collection-scoped MongoDB privileges (D14, R1).\n *\n * ## What this deliberately never produces\n *\n * No `dbAdmin` and no `readWrite` — the two roles the application backend\n * currently holds on the whole database. `dbAdmin` is banned outright by D14;\n * `readWrite` is database-WIDE, so granting it to a minion would make every\n * collection reachable regardless of what `resourceAccess` declared, which is\n * the containment this exists to create.\n *\n * No privilege on the database RESOURCE either (`{ db, collection: '' }`), which\n * is Mongo's way of saying \"every collection in this database\". Every entry\n * names one collection explicitly.\n *\n * ## Read and write are separate entries on purpose\n *\n * A collection granted both axes yields TWO privilege entries rather than one\n * merged action list. Mongo unions them identically, but the emitted role then\n * reads back as the declaration that produced it — which is what makes an\n * operator inspecting `db.getRole()` able to check the grant against the config\n * without re-deriving it.\n */\nexport function buildMinionMongodbPrivileges(input: {\n databaseName: string;\n readCollections: readonly string[];\n writeCollections: readonly string[];\n /** Insert-only — see {@link MONGODB_APPEND_ACTIONS}. Optional so existing pins read unchanged. */\n appendCollections?: readonly string[];\n maintainCollections?: readonly string[];\n}): MongodbCollectionPrivilege[] {\n const privileges: MongodbCollectionPrivilege[] = [];\n for (const collection of input.readCollections) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_READ_ACTIONS] });\n }\n for (const collection of input.writeCollections) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_WRITE_ACTIONS] });\n }\n for (const collection of input.appendCollections ?? []) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_APPEND_ACTIONS] });\n }\n for (const collection of input.maintainCollections ?? []) {\n privileges.push({ resource: { db: input.databaseName, collection }, actions: [...MONGODB_MAINTAIN_ACTIONS] });\n }\n return privileges;\n}\n\n/**\n * Derive the per-app connect URL by swapping admin credentials and\n * the database path in the authored `adminConnectUrl`. Mirrors the\n * legacy logic at\n * `wildo-application-lifecycle.service.ts:469-479`:\n *\n * const parsed = new URL(adminConnectUrl);\n * parsed.username = perAppUsername;\n * parsed.password = perAppPassword;\n * parsed.pathname = '/' + databaseName;\n *\n * URL-encoding handled automatically by the WHATWG URL class.\n */\nfunction buildPerAppConnectUrl(\n adminConnectUrl: string,\n databaseName: string,\n username: string,\n password: string,\n): string {\n const parsed = new URL(adminConnectUrl);\n parsed.username = encodeURIComponent(username);\n parsed.password = encodeURIComponent(password);\n parsed.pathname = '/' + databaseName;\n return parsed.toString();\n}\n\n/**\n * Per-principal Mongo username — the SAME derivation the revoke path must use.\n *\n * The application backend keeps `<namespace>_user` VERBATIM so an existing\n * deployment's user is never orphaned. Third time this shape appears (Redis,\n * RabbitMQ, now Mongo): a revoke deriving it differently deletes the BACKEND's\n * database user while believing it is removing a minion's.\n *\n * Exported because BOTH MongoDB modules name principals through it — the Atlas\n * module too (#1694) — so a principal's user has one name whichever provider\n * holds the store, and neither module can drift into the backend's name.\n */\nexport function buildMongodbPrincipalUsername(\n applicationNamespace: string,\n principal: ApplicationRuntimePrincipal | undefined,\n): string {\n if (!principal || principal.kind === ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n return buildMongodbUsername(applicationNamespace);\n }\n const sanitizedName = (principal.name ?? '').replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_+|_+$/g, '');\n return `${applicationNamespace}_${principal.kind}_${sanitizedName}_user`;\n}\n\nexport const MongodbSelfInfraProviderModule: InfraProviderModule = {\n provider: InfraProvider.MONGODB_SELF,\n supportedCapabilities: [InfraCapability.DOCUMENT_DATABASE],\n perPrincipalSecretBlobPaths: PER_PRINCIPAL_SECRET_BLOB_PATHS,\n\n plan(): InfraProviderPlanContribution {\n return {\n dockerComposeServices: ['mongodb'],\n k8sResources: ['templates/infra/k8s/internal/mongodb/deployment.yaml.hbs'],\n platformAdmin: {\n envVars: {},\n // The single MONGO_CONNECT_URL secret carries the admin\n // credentials embedded; legacy field name preserved.\n secretEnvVars: ['MONGO_CONNECT_URL'],\n consumers: [\n InfraPlatformAdminConsumer.APPS_MANAGER,\n InfraPlatformAdminConsumer.COMPANION,\n ],\n },\n appRuntime: {\n // Same persistence shape as Atlas — per-app username +\n // password go into secrets. The stored host/protocol are read\n // by apps-manager from the connection string this module\n // RETURNS when the application declares its provider (#1876),\n // and from BASE_MONGO_SERVER_URL only when it declares none.\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 IMongoSelfAdminClient;\n const password = context.secretInputs[MONGODB_PASSWORD_INPUT_KEY];\n if (!password) {\n throw new Error(\n `MongodbSelfInfraProviderModule.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 `MongodbSelfInfraProviderModule.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 `MongodbSelfInfraProviderModule.mintAppCredentials: ` +\n `applicationConfigInputs is missing '${DATABASE_NAME_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the per-app database name.`,\n );\n }\n const adminConnectUrl = context.applicationConfigInputs?.[ADMIN_CONNECT_URL_INPUT_KEY];\n if (!adminConnectUrl) {\n throw new Error(\n `MongodbSelfInfraProviderModule.mintAppCredentials: ` +\n `applicationConfigInputs is missing '${ADMIN_CONNECT_URL_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the admin Mongo connect URL.`,\n );\n }\n\n const username = buildMongodbPrincipalUsername(context.applicationNamespace, context.principal);\n\n if (context.principal && context.principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n // A named principal gets a COLLECTION-SCOPED user (Step 3 / R1 / D14):\n // one custom role carrying authored business grants plus the mandatory\n // runtime floor, and no inherited database-wide privileges.\n //\n // `resourceGrants` is REQUIRED here and its absence is an error rather\n // than a default. Falling back to the app-wide upsert would hand a minion\n // `readWrite` + `dbAdmin` on the whole database — the exact over-grant\n // this path exists to replace, arriving silently through a code path that\n // looks like it is doing the narrow thing.\n const grants = context.resourceGrants;\n if (!grants) {\n throw new Error(\n `MongodbSelfInfraProviderModule.mintAppCredentials: `\n + `principal '${context.principal.kind}:${context.principal.name}' has no resourceGrants. `\n + `Refusing to fall back to the database-wide application user. The orchestrator must pass `\n + `an explicit empty resource grant when only the mandatory runtime floor is required.`,\n );\n }\n const databaseGrants = deriveMinionDatabaseGrants(grants);\n\n await adminClient.upsertCollectionScopedDatabaseUser({\n adminConnectUrl,\n databaseName,\n username,\n password,\n roleName: `${username}_role`,\n // Resource types → collection names HERE, at the adapter that speaks\n // Mongo. The grant contract itself stays in the platform vocabulary.\n // Authored declaration + the engine floor, through the SAME derivation\n // the PostgreSQL adapter uses — so the two cannot disagree about what\n // the framework itself requires of every runtime principal.\n privileges: buildMinionMongodbPrivileges({\n databaseName,\n readCollections: resolveGrantStorageNames(databaseGrants.read),\n writeCollections: resolveGrantStorageNames(databaseGrants.write),\n appendCollections: resolveGrantStorageNames(databaseGrants.append),\n maintainCollections: resolveGrantStorageNames(databaseGrants.maintain),\n }),\n });\n } else {\n // createUser-or-updateUser over an admin-scoped mongoose\n // connection — idempotent upsert.\n await adminClient.upsertApplicationDatabaseUser({\n adminConnectUrl,\n databaseName,\n username,\n password,\n });\n }\n\n // Derive the per-app connection URL (the admin URL's own scheme) by swapping credentials + db path\n // in the authored admin URL. The orchestrator uses this for\n // transient app-init seeding; not persisted.\n const connectionString = buildPerAppConnectUrl(\n adminConnectUrl,\n databaseName,\n username,\n password,\n );\n\n return {\n secretBlobValues: {\n [MONGODB_USERNAME_PATH]: username,\n [MONGODB_PASSWORD_PATH]: password,\n },\n runtimeArtifacts: {\n [CONNECTION_STRING_ARTIFACT_KEY]: connectionString,\n },\n };\n },\n\n /**\n * Revoke a principal's Mongo database user (and a minion's custom role); the database\n * and its documents are kept.\n *\n * **Caller status**: apps-manager runtime-principal declaration reconciliation\n * calls this for a removed minion when its application has a MongoDB store, and\n * application withdrawal (`deregisterApplication`, #1307) for the backend and every\n * minion; `restoreApplication` re-mints them.\n * The named principal and database scope identify the grant being revoked.\n */\n async revokeAppCredentials(context: AppCredentialMintingContext): Promise<void> {\n if (!context.applicationNamespace) {\n throw new Error(\n `MongodbSelfInfraProviderModule.revokeAppCredentials: ` +\n `applicationNamespace is empty. ` +\n `The orchestrator MUST supply a non-empty namespace.`,\n );\n }\n const adminConnectUrl = context.applicationConfigInputs?.[ADMIN_CONNECT_URL_INPUT_KEY];\n if (!adminConnectUrl) {\n throw new Error(\n `MongodbSelfInfraProviderModule.revokeAppCredentials: ` +\n `applicationConfigInputs is missing '${ADMIN_CONNECT_URL_INPUT_KEY}'. ` +\n `The orchestrator MUST supply the admin Mongo connect URL.`,\n );\n }\n const adminClient = context.adminClient as IMongoSelfAdminClient;\n // Derived through the SAME function the mint uses. Deriving it independently\n // here — which is what this line did before named principals existed — meant\n // revoking a minion left its credential live AND dropped the APPLICATION\n // BACKEND's database user, because the backend's name is what\n // `buildMongodbUsername` returns for every input.\n const username = buildMongodbPrincipalUsername(context.applicationNamespace, context.principal);\n\n if (context.principal && context.principal.kind !== ApplicationRuntimePrincipalKind.APPLICATION_BACKEND) {\n // A scoped principal owns a user AND a custom role. Routing it through the\n // user-only door would leave a privileged role behind that the next mint\n // adopts without anybody declaring it.\n const databaseName = context.applicationConfigInputs?.[DATABASE_NAME_INPUT_KEY];\n if (!databaseName) {\n throw new Error(\n `MongodbSelfInfraProviderModule.revokeAppCredentials: `\n + `applicationConfigInputs is missing '${DATABASE_NAME_INPUT_KEY}' for principal `\n + `'${context.principal.kind}:${context.principal.name}'. The role to drop lives IN that `\n + `database — revoking without it would drop the user and silently orphan its privileges.`,\n );\n }\n await adminClient.dropCollectionScopedDatabaseUser({\n adminConnectUrl,\n databaseName,\n username,\n roleName: `${username}_role`,\n });\n return;\n }\n\n await adminClient.deleteApplicationDatabaseUser({ adminConnectUrl, username });\n },\n\n // No `mintPlatformServiceCredentials` (D-4).\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-self.infra.module.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"postgres-self.infra.module.d.ts","sourceRoot":"","sources":["../../../../src/postgres-self/postgres-self.infra.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAEH,OAAO,EAOL,mBAAmB,EAMpB,MAAM,+BAA+B,CAAC;AA6HvC,eAAO,MAAM,+BAA+B,EAAE,mBAgM7C,CAAC"}
|