@wildo-ai/platform-infra-providers 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/LICENSE +34 -0
  2. package/dist/esm/aws-s3/aws-s3.infra.module.d.ts +63 -0
  3. package/dist/esm/aws-s3/aws-s3.infra.module.d.ts.map +1 -0
  4. package/dist/esm/aws-s3/aws-s3.infra.module.js +110 -0
  5. package/dist/esm/aws-s3/aws-s3.infra.module.js.map +1 -0
  6. package/dist/esm/clamav-self/clamav-self.infra.module.d.ts +30 -0
  7. package/dist/esm/clamav-self/clamav-self.infra.module.d.ts.map +1 -0
  8. package/dist/esm/clamav-self/clamav-self.infra.module.js +61 -0
  9. package/dist/esm/clamav-self/clamav-self.infra.module.js.map +1 -0
  10. package/dist/esm/gotenberg-self/gotenberg-self.infra.module.d.ts +26 -0
  11. package/dist/esm/gotenberg-self/gotenberg-self.infra.module.d.ts.map +1 -0
  12. package/dist/esm/gotenberg-self/gotenberg-self.infra.module.js +54 -0
  13. package/dist/esm/gotenberg-self/gotenberg-self.infra.module.js.map +1 -0
  14. package/dist/esm/index.d.ts +88 -0
  15. package/dist/esm/index.d.ts.map +1 -0
  16. package/dist/esm/index.js +125 -0
  17. package/dist/esm/index.js.map +1 -0
  18. package/dist/esm/minio-self/minio-self.infra.module.d.ts +64 -0
  19. package/dist/esm/minio-self/minio-self.infra.module.d.ts.map +1 -0
  20. package/dist/esm/minio-self/minio-self.infra.module.js +105 -0
  21. package/dist/esm/minio-self/minio-self.infra.module.js.map +1 -0
  22. package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.d.ts +52 -0
  23. package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.d.ts.map +1 -0
  24. package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.js +201 -0
  25. package/dist/esm/mongodb-atlas/mongodb-atlas.infra.module.js.map +1 -0
  26. package/dist/esm/mongodb-self/mongodb-self.infra.module.d.ts +81 -0
  27. package/dist/esm/mongodb-self/mongodb-self.infra.module.d.ts.map +1 -0
  28. package/dist/esm/mongodb-self/mongodb-self.infra.module.js +311 -0
  29. package/dist/esm/mongodb-self/mongodb-self.infra.module.js.map +1 -0
  30. package/dist/esm/postgres-self/postgres-self.infra.module.d.ts +70 -0
  31. package/dist/esm/postgres-self/postgres-self.infra.module.d.ts.map +1 -0
  32. package/dist/esm/postgres-self/postgres-self.infra.module.js +328 -0
  33. package/dist/esm/postgres-self/postgres-self.infra.module.js.map +1 -0
  34. package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.d.ts +88 -0
  35. package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.d.ts.map +1 -0
  36. package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.js +473 -0
  37. package/dist/esm/rabbitmq-self/rabbitmq-self.infra.module.js.map +1 -0
  38. package/dist/esm/redis-self/redis-self.infra.module.d.ts +89 -0
  39. package/dist/esm/redis-self/redis-self.infra.module.d.ts.map +1 -0
  40. package/dist/esm/redis-self/redis-self.infra.module.js +331 -0
  41. package/dist/esm/redis-self/redis-self.infra.module.js.map +1 -0
  42. package/dist/tsconfig.build.tsbuildinfo +1 -0
  43. package/package.json +52 -0
  44. package/src/aws-s3/__tests__/aws-s3.infra.module.test.ts +67 -0
  45. package/src/aws-s3/aws-s3.infra.module.ts +120 -0
  46. package/src/clamav-self/__tests__/clamav-self.infra.module.test.ts +65 -0
  47. package/src/clamav-self/clamav-self.infra.module.ts +72 -0
  48. package/src/gotenberg-self/__tests__/gotenberg-self.infra.module.test.ts +65 -0
  49. package/src/gotenberg-self/gotenberg-self.infra.module.ts +65 -0
  50. package/src/index.ts +133 -0
  51. package/src/minio-self/__tests__/minio-self.infra.module.test.ts +70 -0
  52. package/src/minio-self/minio-self.infra.module.ts +116 -0
  53. package/src/mongodb-atlas/__tests__/mongodb-atlas.infra.module.test.ts +198 -0
  54. package/src/mongodb-atlas/mongodb-atlas.infra.module.ts +236 -0
  55. package/src/mongodb-self/__tests__/mongodb-self.infra.module.test.ts +419 -0
  56. package/src/mongodb-self/mongodb-self.infra.module.ts +384 -0
  57. package/src/postgres-self/__tests__/postgres-self.infra.module.test.ts +344 -0
  58. package/src/postgres-self/postgres-self.infra.module.ts +377 -0
  59. package/src/rabbitmq-self/__tests__/rabbitmq-self.infra.module.test.ts +499 -0
  60. package/src/rabbitmq-self/rabbitmq-self.infra.module.ts +531 -0
  61. package/src/redis-self/__tests__/redis-self.infra.module.test.ts +341 -0
  62. package/src/redis-self/redis-self.infra.module.ts +372 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * `AwsS3InfraProviderModule` — seventh concrete `InfraProviderModule`
3
+ * (Slice 5.1 of `infra-provider-planner.md`).
4
+ *
5
+ * Implements the `AWS_S3` `InfraProvider`. Shares the one-bucket,
6
+ * prefix-namespaced object layout of `MinioSelfInfraProviderModule` —
7
+ * the only differences are (a) no compose / K8s contribution (S3 is
8
+ * managed), and (b) the bucket endpoint is the regional AWS hostname
9
+ * instead of the in-cluster `minio` service.
10
+ *
11
+ * ## ⚠️ NOT USABLE TODAY — selecting this provider leaves apps with no
12
+ * storage credential (2026-08-10)
13
+ *
14
+ * The application-tier `STORAGE_ACCESS_KEY` / `STORAGE_SECRET_KEY` env
15
+ * vars were removed **for every provider**: they were the object store's
16
+ * ROOT pair, handed to every app, minion and worker. Applications now
17
+ * obtain short-lived, prefix-confined credentials from apps-manager.
18
+ *
19
+ * That replacement is implemented for **MinIO only**. apps-manager's
20
+ * `ObjectStorageStsBrokerService` speaks MinIO's STS `AssumeRole` with an
21
+ * inline session policy; AWS STS requires a role ARN the broker does not
22
+ * supply, so an `AWS_S3` deployment would fail at storage initialisation
23
+ * with no credential and no path to one.
24
+ *
25
+ * No environment selects this provider today (every example uses
26
+ * `BackingServiceSource.SELF` → MinIO), which is why this is recorded as
27
+ * a gap rather than an outage. But it IS reachable — `s3: { source:
28
+ * MANAGED }` or `provider: 'aws'` resolves here — so **do not select
29
+ * `AWS_S3` until the AWS STS path exists.** Tracked in
30
+ * `.claude/plans/object-storage-root-credential-distribution.md`.
31
+ *
32
+ * The module still reuses the `STORAGE_*` env contract for the
33
+ * NON-secret values (endpoint, region, bucket, provider); it is only the
34
+ * credential half that is gone.
35
+ *
36
+ * **Why this module exists despite "no compose / no mint"**: it
37
+ * gives the lockfile a stable digest entry (so a switch from
38
+ * `minio-self` to `aws-s3` shows up as a `wildo lock check` drift)
39
+ * AND it formalizes which env vars the operator must set for AWS
40
+ * (`STORAGE_REGION` is mandatory; `STORAGE_ENDPOINT` is optional —
41
+ * defaults to the AWS regional endpoint when unset).
42
+ *
43
+ * **No mint methods here** — and the reason is no longer the one this
44
+ * block used to give. It said per-app credentials were out of scope
45
+ * "because the engine adapter uses a single shared SDK client", which
46
+ * stopped being true when the adapter moved to a credential provider that
47
+ * rebuilds its client whenever the credential changes. Per-application
48
+ * isolation exists; what is missing for AWS is only the broker's ability
49
+ * to speak AWS STS.
50
+ *
51
+ * Nor should the missing piece be per-app IAM users or roles, which is
52
+ * what this block previously anticipated. The MinIO path deliberately
53
+ * rejected long-lived per-principal accounts in favour of `AssumeRole`
54
+ * with an inline session policy — nothing long-lived exists to leak, and
55
+ * credential-rotation safety dissolves instead of needing a design. The
56
+ * AWS equivalent is the same shape: `AssumeRole` against a role ARN the
57
+ * operator supplies, with a session policy confining the principal to
58
+ * `applications/<applicationId>/`. That role ARN is the one input the
59
+ * broker does not have today, and is the whole of the gap.
60
+ */
61
+
62
+ import {
63
+ InfraCapability,
64
+ InfraPlatformAdminConsumer,
65
+ InfraProvider,
66
+ InfraProviderModule,
67
+ InfraProviderPlanContribution,
68
+ } from '@wildo-ai/platform-config-lib';
69
+
70
+ export const AwsS3InfraProviderModule: InfraProviderModule = {
71
+ provider: InfraProvider.AWS_S3,
72
+ supportedCapabilities: [InfraCapability.OBJECT_STORAGE],
73
+
74
+ /**
75
+ * Shared bucket, shared access key — no per-app credential is minted, so there is nothing
76
+ * per-principal to confine. Confinement for object storage is done by STS session policy at
77
+ * request time, not by a secret in the application blob.
78
+ */
79
+ perPrincipalSecretBlobPaths: [],
80
+
81
+ plan(): InfraProviderPlanContribution {
82
+ return {
83
+ // No compose service — S3 is fully managed.
84
+ // No K8s manifest — same reason.
85
+ platformAdmin: {
86
+ envVars: {
87
+ STORAGE_PROVIDER: 's3',
88
+ // Endpoint and port have AWS defaults; operator may override
89
+ // for region-specific or VPC-endpoint topologies via the
90
+ // env's authored `backingServices.s3` config.
91
+ STORAGE_USE_SSL: 'true',
92
+ },
93
+ secretEnvVars: [
94
+ 'STORAGE_ACCESS_KEY',
95
+ 'STORAGE_SECRET_KEY',
96
+ // Region is sensitive-ish (deployment topology) and is
97
+ // delivered via the secret env channel for symmetry with
98
+ // the access keys, even though it's not strictly secret.
99
+ // The legacy treatment was env-var, not secret-env-var; if
100
+ // operators object, this can flip to `envVars` later
101
+ // without breaking the contract.
102
+ 'STORAGE_REGION',
103
+ ],
104
+ consumers: [
105
+ InfraPlatformAdminConsumer.APPS_MANAGER,
106
+ InfraPlatformAdminConsumer.COMPANION,
107
+ ],
108
+ },
109
+ // Same shared bucket as MinIO_Self. Apps receive the NON-secret
110
+ // `STORAGE_*` values via the bootstrap env contract; the credential
111
+ // half is no longer emitted for any provider — see the ⚠️ block in
112
+ // this module's JSDoc for why that makes AWS_S3 currently unusable.
113
+ };
114
+ },
115
+
116
+ // No `mintAppCredentials` / `revokeAppCredentials` — not because the model
117
+ // is shared-bucket (it is, but that is not the reason), but because
118
+ // per-application isolation is delivered by BROKERED STS SESSIONS rather than
119
+ // by minted long-lived accounts. See this module's JSDoc.
120
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Tests for `ClamavSelfInfraProviderModule` (Slice 5.2 of
3
+ * `infra-provider-planner.md`).
4
+ */
5
+
6
+ import { describe, expect, it } from 'vitest';
7
+ import {
8
+ InfraCapability,
9
+ InfraPlatformAdminConsumer,
10
+ InfraProvider,
11
+ } from '@wildo-ai/platform-config-lib';
12
+ import { ClamavSelfInfraProviderModule } from '../clamav-self.infra.module';
13
+
14
+ describe('ClamavSelfInfraProviderModule.plan()', () => {
15
+ const contribution = ClamavSelfInfraProviderModule.plan({
16
+ environment: 'local',
17
+ runtime: 'docker-compose',
18
+ backingServiceKey: 'clamav',
19
+ config: {},
20
+ });
21
+
22
+ it('declares the right provider + capability triple', () => {
23
+ expect(ClamavSelfInfraProviderModule.provider).toBe(InfraProvider.CLAMAV_SELF);
24
+ expect(ClamavSelfInfraProviderModule.supportedCapabilities).toEqual([
25
+ InfraCapability.ANTIVIRUS_SCANNING,
26
+ ]);
27
+ });
28
+
29
+ it('contributes the clamav compose service + K8s manifest', () => {
30
+ expect(contribution.dockerComposeServices).toEqual(['clamav']);
31
+ expect(contribution.k8sResources).toEqual([
32
+ 'templates/infra/k8s/internal/clamav/deployment.yaml.hbs',
33
+ ]);
34
+ });
35
+
36
+ it('declares CLAMAV_HOST/PORT env (no auth — no secret env vars)', () => {
37
+ expect(contribution.platformAdmin?.envVars).toEqual({
38
+ CLAMAV_HOST: 'clamav',
39
+ CLAMAV_PORT: '3310',
40
+ });
41
+ expect(contribution.platformAdmin?.secretEnvVars).toBeUndefined();
42
+ expect(contribution.platformAdmin?.consumers).toEqual([
43
+ InfraPlatformAdminConsumer.APPS_MANAGER,
44
+ InfraPlatformAdminConsumer.COMPANION,
45
+ ]);
46
+ });
47
+
48
+ it('declares NO appRuntime contribution (stateless — apps connect by host:port)', () => {
49
+ expect(contribution.appRuntime).toBeUndefined();
50
+ });
51
+ });
52
+
53
+ describe('ClamavSelfInfraProviderModule stateless absence', () => {
54
+ it('does NOT declare mintAppCredentials (no auth, no per-app state)', () => {
55
+ expect(ClamavSelfInfraProviderModule.mintAppCredentials).toBeUndefined();
56
+ });
57
+
58
+ it('does NOT declare revokeAppCredentials', () => {
59
+ expect(ClamavSelfInfraProviderModule.revokeAppCredentials).toBeUndefined();
60
+ });
61
+
62
+ it('does NOT declare mintPlatformServiceCredentials', () => {
63
+ expect(ClamavSelfInfraProviderModule.mintPlatformServiceCredentials).toBeUndefined();
64
+ });
65
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * `ClamavSelfInfraProviderModule` — eighth concrete `InfraProviderModule`
3
+ * (Slice 5.2 of `infra-provider-planner.md`).
4
+ *
5
+ * Implements the `CLAMAV_SELF` `InfraProvider`. **Stateless**: ClamAV
6
+ * is a network virus-scanning service with no auth and no per-tenant
7
+ * state. Apps connect to the shared host:port and submit files for
8
+ * scanning over the ClamAV protocol; nothing is persisted, nothing
9
+ * is per-app. The module's only job is to declare the resource +
10
+ * env vars in `plan()`; there are no `mintAppCredentials`,
11
+ * `revokeAppCredentials`, or `mintPlatformServiceCredentials`
12
+ * methods because there are no credentials.
13
+ *
14
+ * **Apps consume**: `CLAMAV_HOST`, `CLAMAV_PORT` env vars (delivered
15
+ * via the bootstrap env contract). The engine's antivirus-scanning
16
+ * adapter (`saas-backend-lib/.../antivirus/...`) opens a TCP
17
+ * connection per scan request.
18
+ *
19
+ * **Why declare it as a module at all (vs. inline compose service)?**
20
+ * The module gives the lockfile a stable `planContributionDigest`
21
+ * for `ANTIVIRUS_SCANNING` so `wildo lock check` catches drift in
22
+ * the ClamAV resource shape (port change, env-var rename, K8s
23
+ * manifest move). Without a module, the digest would be the
24
+ * authored-entry digest (which is empty — ClamAV has no configurable
25
+ * `BackingService` shape today, just the `BackingServiceKind`
26
+ * enum value).
27
+ */
28
+
29
+ import {
30
+ InfraCapability,
31
+ InfraPlatformAdminConsumer,
32
+ InfraProvider,
33
+ InfraProviderModule,
34
+ InfraProviderPlanContribution,
35
+ } from '@wildo-ai/platform-config-lib';
36
+
37
+ const DEFAULT_CLAMAV_PORT = 3310;
38
+
39
+ export const ClamavSelfInfraProviderModule: InfraProviderModule = {
40
+ provider: InfraProvider.CLAMAV_SELF,
41
+ supportedCapabilities: [InfraCapability.ANTIVIRUS_SCANNING],
42
+
43
+ /**
44
+ * Stateless infra with no authentication and no per-app state — `plan()` only.
45
+ */
46
+ perPrincipalSecretBlobPaths: [],
47
+
48
+ plan(): InfraProviderPlanContribution {
49
+ return {
50
+ dockerComposeServices: ['clamav'],
51
+ k8sResources: ['templates/infra/k8s/internal/clamav/deployment.yaml.hbs'],
52
+ platformAdmin: {
53
+ envVars: {
54
+ CLAMAV_HOST: 'clamav',
55
+ CLAMAV_PORT: String(DEFAULT_CLAMAV_PORT),
56
+ },
57
+ // No secret env vars — ClamAV has no auth.
58
+ // Audience-1 declared so the planner emits the env vars to
59
+ // both apps-manager and companion (companion runs scans
60
+ // locally during dev for parity with prod behavior).
61
+ consumers: [
62
+ InfraPlatformAdminConsumer.APPS_MANAGER,
63
+ InfraPlatformAdminConsumer.COMPANION,
64
+ ],
65
+ },
66
+ // No `appRuntime` blob paths — apps connect via the same
67
+ // `CLAMAV_HOST:CLAMAV_PORT` regardless of app identity.
68
+ };
69
+ },
70
+
71
+ // No mint methods — ClamAV is stateless.
72
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Tests for `GotenbergSelfInfraProviderModule` (Slice 5.3 of
3
+ * `infra-provider-planner.md`).
4
+ */
5
+
6
+ import { describe, expect, it } from 'vitest';
7
+ import {
8
+ InfraCapability,
9
+ InfraPlatformAdminConsumer,
10
+ InfraProvider,
11
+ } from '@wildo-ai/platform-config-lib';
12
+ import { GotenbergSelfInfraProviderModule } from '../gotenberg-self.infra.module';
13
+
14
+ describe('GotenbergSelfInfraProviderModule.plan()', () => {
15
+ const contribution = GotenbergSelfInfraProviderModule.plan({
16
+ environment: 'local',
17
+ runtime: 'docker-compose',
18
+ backingServiceKey: 'gotenberg',
19
+ config: {},
20
+ });
21
+
22
+ it('declares the right provider + capability triple', () => {
23
+ expect(GotenbergSelfInfraProviderModule.provider).toBe(InfraProvider.GOTENBERG_SELF);
24
+ expect(GotenbergSelfInfraProviderModule.supportedCapabilities).toEqual([
25
+ InfraCapability.DOCUMENT_RENDERING,
26
+ ]);
27
+ });
28
+
29
+ it('contributes the gotenberg compose service + K8s manifest', () => {
30
+ expect(contribution.dockerComposeServices).toEqual(['gotenberg']);
31
+ expect(contribution.k8sResources).toEqual([
32
+ 'templates/infra/k8s/internal/gotenberg/deployment.yaml.hbs',
33
+ ]);
34
+ });
35
+
36
+ it('declares GOTENBERG_HOST/PORT env (no auth — no secret env vars)', () => {
37
+ expect(contribution.platformAdmin?.envVars).toEqual({
38
+ GOTENBERG_HOST: 'gotenberg',
39
+ GOTENBERG_PORT: '3000',
40
+ });
41
+ expect(contribution.platformAdmin?.secretEnvVars).toBeUndefined();
42
+ expect(contribution.platformAdmin?.consumers).toEqual([
43
+ InfraPlatformAdminConsumer.APPS_MANAGER,
44
+ InfraPlatformAdminConsumer.COMPANION,
45
+ ]);
46
+ });
47
+
48
+ it('declares NO appRuntime contribution (stateless — apps connect by host:port)', () => {
49
+ expect(contribution.appRuntime).toBeUndefined();
50
+ });
51
+ });
52
+
53
+ describe('GotenbergSelfInfraProviderModule stateless absence', () => {
54
+ it('does NOT declare mintAppCredentials (no auth, no per-app state)', () => {
55
+ expect(GotenbergSelfInfraProviderModule.mintAppCredentials).toBeUndefined();
56
+ });
57
+
58
+ it('does NOT declare revokeAppCredentials', () => {
59
+ expect(GotenbergSelfInfraProviderModule.revokeAppCredentials).toBeUndefined();
60
+ });
61
+
62
+ it('does NOT declare mintPlatformServiceCredentials', () => {
63
+ expect(GotenbergSelfInfraProviderModule.mintPlatformServiceCredentials).toBeUndefined();
64
+ });
65
+ });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `GotenbergSelfInfraProviderModule` — ninth concrete
3
+ * `InfraProviderModule` (Slice 5.3 of `infra-provider-planner.md`).
4
+ *
5
+ * Implements the `GOTENBERG_SELF` `InfraProvider`. **Stateless**:
6
+ * Gotenberg is a network HTML-to-PDF rendering service with no auth
7
+ * and no per-tenant state. Apps POST HTML/Office docs and receive
8
+ * rendered PDFs; nothing is persisted server-side. Same shape as
9
+ * `ClamavSelfInfraProviderModule` — `plan()` only, no mint methods.
10
+ *
11
+ * **Apps consume**: `GOTENBERG_HOST`, `GOTENBERG_PORT` env vars (TBD
12
+ * naming — current engine adapter reads these from
13
+ * `BackingServicesEnvConfig` via the catalog at
14
+ * `wildo-cli/.../service-catalog.schemas.ts:78`). The plan emits
15
+ * the env vars under those names.
16
+ *
17
+ * **Two ports** (Slice 5.3 design note): Gotenberg exposes both a
18
+ * public port (3000 by default) for HTTP API access and an internal
19
+ * port (3001) for health-check/metrics. The CLI's docker-compose
20
+ * generator already wires both. The module's `plan()` only emits
21
+ * the public port — the internal port is a docker-compose-only
22
+ * concern and isn't part of the customer-app env-var surface.
23
+ */
24
+
25
+ import {
26
+ InfraCapability,
27
+ InfraPlatformAdminConsumer,
28
+ InfraProvider,
29
+ InfraProviderModule,
30
+ InfraProviderPlanContribution,
31
+ } from '@wildo-ai/platform-config-lib';
32
+
33
+ const DEFAULT_GOTENBERG_PORT = 3000;
34
+
35
+ export const GotenbergSelfInfraProviderModule: InfraProviderModule = {
36
+ provider: InfraProvider.GOTENBERG_SELF,
37
+ supportedCapabilities: [InfraCapability.DOCUMENT_RENDERING],
38
+
39
+ /**
40
+ * Stateless infra with no authentication and no per-app state — `plan()` only.
41
+ */
42
+ perPrincipalSecretBlobPaths: [],
43
+
44
+ plan(): InfraProviderPlanContribution {
45
+ return {
46
+ dockerComposeServices: ['gotenberg'],
47
+ k8sResources: ['templates/infra/k8s/internal/gotenberg/deployment.yaml.hbs'],
48
+ platformAdmin: {
49
+ envVars: {
50
+ GOTENBERG_HOST: 'gotenberg',
51
+ GOTENBERG_PORT: String(DEFAULT_GOTENBERG_PORT),
52
+ },
53
+ // No secret env vars — Gotenberg has no auth.
54
+ consumers: [
55
+ InfraPlatformAdminConsumer.APPS_MANAGER,
56
+ InfraPlatformAdminConsumer.COMPANION,
57
+ ],
58
+ },
59
+ // No `appRuntime` blob paths — apps connect via the same
60
+ // `GOTENBERG_HOST:GOTENBERG_PORT` regardless of app identity.
61
+ };
62
+ },
63
+
64
+ // No mint methods — Gotenberg is stateless.
65
+ };
package/src/index.ts ADDED
@@ -0,0 +1,133 @@
1
+ /**
2
+ * `@wildo-ai/platform-infra-providers` — concrete `InfraProviderModule`
3
+ * catalog (Slice 2.5c-1 of `infra-provider-planner.md`).
4
+ *
5
+ * Tier-portable: the modules import only `@wildo-ai/platform-config-lib`
6
+ * (contracts) and have no DI / mongoose / axios runtime dependencies.
7
+ * This lets both `platform-apps-manager` (orchestrator) and
8
+ * `wildo-cli` (lockfile materializer) consume the same module
9
+ * objects — apps-manager passes a real admin-client implementation
10
+ * via `AppCredentialMintingContext.adminClient`; wildo-cli only
11
+ * needs `module.plan()` for the lockfile digest.
12
+ *
13
+ * Each module is exported by name (no intermediary barrels per
14
+ * platform/CLAUDE.md "no intermediary index.ts barrels"). The
15
+ * `INFRA_PROVIDER_MODULES` map below is the canonical catalog —
16
+ * consumers iterate it (or look up by `InfraProvider` value) instead
17
+ * of importing modules one by one.
18
+ */
19
+
20
+ import type { InfraProvider, InfraProviderModule } from '@wildo-ai/platform-config-lib';
21
+
22
+ import { RabbitmqSelfInfraProviderModule } from './rabbitmq-self/rabbitmq-self.infra.module';
23
+ import { RedisSelfInfraProviderModule } from './redis-self/redis-self.infra.module';
24
+ import { PostgresSelfInfraProviderModule } from './postgres-self/postgres-self.infra.module';
25
+ import { MongodbAtlasInfraProviderModule } from './mongodb-atlas/mongodb-atlas.infra.module';
26
+ import { MongodbSelfInfraProviderModule } from './mongodb-self/mongodb-self.infra.module';
27
+ // Slice 5 of `infra-provider-planner.md` — object-storage + stateless infra.
28
+ import { MinioSelfInfraProviderModule } from './minio-self/minio-self.infra.module';
29
+ import { AwsS3InfraProviderModule } from './aws-s3/aws-s3.infra.module';
30
+ import { ClamavSelfInfraProviderModule } from './clamav-self/clamav-self.infra.module';
31
+ import { GotenbergSelfInfraProviderModule } from './gotenberg-self/gotenberg-self.infra.module';
32
+
33
+ export { RabbitmqSelfInfraProviderModule } from './rabbitmq-self/rabbitmq-self.infra.module';
34
+ export { RedisSelfInfraProviderModule } from './redis-self/redis-self.infra.module';
35
+ export { PostgresSelfInfraProviderModule } from './postgres-self/postgres-self.infra.module';
36
+ export { MongodbAtlasInfraProviderModule } from './mongodb-atlas/mongodb-atlas.infra.module';
37
+ export { MongodbSelfInfraProviderModule } from './mongodb-self/mongodb-self.infra.module';
38
+ export { MinioSelfInfraProviderModule } from './minio-self/minio-self.infra.module';
39
+ export { AwsS3InfraProviderModule } from './aws-s3/aws-s3.infra.module';
40
+ export { ClamavSelfInfraProviderModule } from './clamav-self/clamav-self.infra.module';
41
+ export { GotenbergSelfInfraProviderModule } from './gotenberg-self/gotenberg-self.infra.module';
42
+
43
+ /**
44
+ * Canonical `InfraProvider` → `InfraProviderModule` catalog. Caller
45
+ * (CLI, apps-manager) builds a `Map<InfraProvider, InfraProviderModule>`
46
+ * from this constant when threading modules into
47
+ * `materializeInfraSection`.
48
+ *
49
+ * **Why a const array instead of a `Map` literal**: arrays serialize
50
+ * stably under tsc and roll up to a deterministic order. The
51
+ * downstream `Map` is built once at program start; the catalog
52
+ * itself is a value-level fixture.
53
+ *
54
+ * **Coverage** (Slice 5 baseline, 2026-05-10):
55
+ * - `RABBITMQ_SELF`, `REDIS_SELF`, `POSTGRES_SELF`, `MONGODB_SELF`,
56
+ * `MONGODB_ATLAS` — five stateful modules with mint/revoke
57
+ * methods.
58
+ * - `MINIO_SELF`, `AWS_S3` — object storage (shared-bucket model;
59
+ * no per-app credentials).
60
+ * - `CLAMAV_SELF`, `GOTENBERG_SELF` — stateless infra (no auth, no
61
+ * per-app state; `plan()` only).
62
+ *
63
+ * **NOT covered** (catalog entries without modules):
64
+ * - `POSTGRES_MANAGED`, `POSTGRES_AWS_RDS`, `POSTGRES_GCP_CLOUD_SQL`,
65
+ * `POSTGRES_AZURE_DATABASE`, `POSTGRES_SUPABASE`, `POSTGRES_NEON`,
66
+ * `POSTGRES_RENDER` — managed-Postgres brand catalog entries
67
+ * (Slice 4.B.0). No module yet; trigger = real customer.
68
+ *
69
+ * `materializeInfraSection` falls back to the authored-entry digest
70
+ * when a selected `InfraProvider` is not in the map (preserves the
71
+ * Slice 2.5b behavior for uncovered providers).
72
+ */
73
+ export const INFRA_PROVIDER_MODULES: ReadonlyArray<InfraProviderModule> = [
74
+ RabbitmqSelfInfraProviderModule,
75
+ RedisSelfInfraProviderModule,
76
+ PostgresSelfInfraProviderModule,
77
+ MongodbAtlasInfraProviderModule,
78
+ MongodbSelfInfraProviderModule,
79
+ MinioSelfInfraProviderModule,
80
+ AwsS3InfraProviderModule,
81
+ ClamavSelfInfraProviderModule,
82
+ GotenbergSelfInfraProviderModule,
83
+ ];
84
+
85
+ /**
86
+ * Every `Wildo_Application_Secrets` JSON path that ANY provider module can mint per runtime
87
+ * principal — the union of the catalog's own static declarations.
88
+ *
89
+ * ## Why this is derived rather than written down
90
+ *
91
+ * The consumer is the secret-DELIVERY path in apps-manager: it strips exactly this set from the
92
+ * application blob before overlaying a principal's own material, so a principal receives a
93
+ * credential only where one was minted FOR IT. A hand-maintained copy at that consumer would be a
94
+ * second source — add a provider that mints a new credential path and delivery keeps shipping the
95
+ * APPLICATION's value for it, silently. That is the defect class this repo has paid for twice: the
96
+ * party that CONSTRAINS is not the party that DECLARES.
97
+ *
98
+ * Derived from `INFRA_PROVIDER_MODULES`, so a new module is covered the day it enters the catalog.
99
+ *
100
+ * Deliberately the union across ALL modules, not the ones a given application selected: a principal
101
+ * must not receive an application-wide credential for a provider it does not use either, and the
102
+ * selected set is not knowable at the delivery call site without threading app config through it.
103
+ * Stripping a path no module minted for this app is harmless — it is absent from the blob anyway.
104
+ */
105
+ export function collectPerPrincipalSecretBlobPaths(): ReadonlySet<string> {
106
+ const paths = new Set<string>();
107
+ for (const module of INFRA_PROVIDER_MODULES) {
108
+ for (const path of module.perPrincipalSecretBlobPaths) {
109
+ paths.add(path);
110
+ }
111
+ }
112
+ return paths;
113
+ }
114
+
115
+ /**
116
+ * Build a `Map<InfraProvider, InfraProviderModule>` for the
117
+ * canonical catalog. Callers store the result once at startup and
118
+ * pass it to `materializeInfraSection` (digest derivation) or to
119
+ * orchestrator paths that look up modules by `InfraProvider`.
120
+ */
121
+ export function buildInfraProviderModuleMap(): ReadonlyMap<InfraProvider, InfraProviderModule> {
122
+ const map = new Map<InfraProvider, InfraProviderModule>();
123
+ for (const m of INFRA_PROVIDER_MODULES) {
124
+ if (map.has(m.provider)) {
125
+ throw new Error(
126
+ `INFRA_PROVIDER_MODULES contains duplicate provider '${m.provider}' — ` +
127
+ `each InfraProvider may have at most one module.`,
128
+ );
129
+ }
130
+ map.set(m.provider, m);
131
+ }
132
+ return map;
133
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Tests for `MinioSelfInfraProviderModule` (Slice 5.1 of
3
+ * `infra-provider-planner.md`).
4
+ */
5
+
6
+ import { describe, expect, it } from 'vitest';
7
+ import {
8
+ InfraCapability,
9
+ InfraPlatformAdminConsumer,
10
+ InfraProvider,
11
+ } from '@wildo-ai/platform-config-lib';
12
+ import { MinioSelfInfraProviderModule } from '../minio-self.infra.module';
13
+
14
+ describe('MinioSelfInfraProviderModule.plan()', () => {
15
+ const contribution = MinioSelfInfraProviderModule.plan({
16
+ environment: 'local',
17
+ runtime: 'docker-compose',
18
+ backingServiceKey: 's3',
19
+ config: {},
20
+ });
21
+
22
+ it('declares the right provider + capability triple', () => {
23
+ expect(MinioSelfInfraProviderModule.provider).toBe(InfraProvider.MINIO_SELF);
24
+ expect(MinioSelfInfraProviderModule.supportedCapabilities).toEqual([
25
+ InfraCapability.OBJECT_STORAGE,
26
+ ]);
27
+ });
28
+
29
+ it('contributes the minio compose service + K8s manifest', () => {
30
+ expect(contribution.dockerComposeServices).toEqual(['minio']);
31
+ expect(contribution.k8sResources).toEqual([
32
+ 'templates/infra/k8s/internal/minio/deployment.yaml.hbs',
33
+ ]);
34
+ });
35
+
36
+ it('declares STORAGE_PROVIDER=minio and the default endpoint topology', () => {
37
+ expect(contribution.platformAdmin?.envVars).toEqual({
38
+ STORAGE_PROVIDER: 'minio',
39
+ STORAGE_ENDPOINT: 'minio',
40
+ STORAGE_PORT: '9000',
41
+ STORAGE_USE_SSL: 'false',
42
+ });
43
+ expect(contribution.platformAdmin?.secretEnvVars).toEqual([
44
+ 'STORAGE_ACCESS_KEY',
45
+ 'STORAGE_SECRET_KEY',
46
+ ]);
47
+ expect(contribution.platformAdmin?.consumers).toEqual([
48
+ InfraPlatformAdminConsumer.APPS_MANAGER,
49
+ InfraPlatformAdminConsumer.COMPANION,
50
+ ]);
51
+ });
52
+
53
+ it('declares NO appRuntime contribution (shared-bucket model — no per-app state)', () => {
54
+ expect(contribution.appRuntime).toBeUndefined();
55
+ });
56
+ });
57
+
58
+ describe('MinioSelfInfraProviderModule shared-bucket absence (Slice 5.1)', () => {
59
+ it('does NOT declare mintAppCredentials (shared bucket — no per-app creds)', () => {
60
+ expect(MinioSelfInfraProviderModule.mintAppCredentials).toBeUndefined();
61
+ });
62
+
63
+ it('does NOT declare revokeAppCredentials', () => {
64
+ expect(MinioSelfInfraProviderModule.revokeAppCredentials).toBeUndefined();
65
+ });
66
+
67
+ it('does NOT declare mintPlatformServiceCredentials', () => {
68
+ expect(MinioSelfInfraProviderModule.mintPlatformServiceCredentials).toBeUndefined();
69
+ });
70
+ });