@fabrika/provider-zerops 0.8.1 → 0.9.0-dev.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 (102) hide show
  1. package/package.json +15 -14
  2. package/src/api/client.ts +6 -0
  3. package/src/api/types.ts +12 -0
  4. package/src/control.ts +26 -0
  5. package/src/execution.ts +147 -14
  6. package/src/installation/generated/platform-proxy-manifest.ts +20 -0
  7. package/src/manifest.ts +9 -5
  8. package/src/namespace/provisioning.ts +4 -2
  9. package/src/namespace/proxy-attempt.ts +29 -0
  10. package/src/plan.ts +1 -1
  11. package/src/provider.ts +40 -0
  12. package/types/src/api/client.d.ts +5 -0
  13. package/types/src/api/errors.d.ts +24 -0
  14. package/types/src/api/polling.d.ts +41 -0
  15. package/types/src/api/readers.d.ts +977 -0
  16. package/types/src/api/service-env-limit.d.ts +17 -0
  17. package/types/src/api/service-env-response.d.ts +23 -0
  18. package/types/src/api/types.d.ts +638 -0
  19. package/types/src/api.d.ts +6 -0
  20. package/types/src/authoring.d.ts +9 -0
  21. package/types/src/cli-args.d.ts +19 -0
  22. package/types/src/cli.d.ts +10 -0
  23. package/types/src/collaborators.d.ts +18 -0
  24. package/types/src/compile.d.ts +91 -0
  25. package/types/src/control.d.ts +41 -0
  26. package/types/src/database-budget.d.ts +38 -0
  27. package/types/src/descriptor.d.ts +28 -0
  28. package/types/src/execution.d.ts +137 -0
  29. package/types/src/exposure.d.ts +47 -0
  30. package/types/src/index.d.ts +31 -0
  31. package/types/src/installation/admin-options.d.ts +24 -0
  32. package/types/src/installation/admin.d.ts +35 -0
  33. package/types/src/installation/checker-release.d.ts +12 -0
  34. package/types/src/installation/deploy-options.d.ts +37 -0
  35. package/types/src/installation/deploy.d.ts +96 -0
  36. package/types/src/installation/generated/platform-console-schema.d.ts +2 -0
  37. package/types/src/installation/generated/platform-proxy-manifest.d.ts +2 -0
  38. package/types/src/installation/hosts.d.ts +38 -0
  39. package/types/src/installation/index.d.ts +9 -0
  40. package/types/src/installation/init-options.d.ts +38 -0
  41. package/types/src/installation/init.d.ts +60 -0
  42. package/types/src/installation/install-collaborators.d.ts +24 -0
  43. package/types/src/installation/install-environment.d.ts +70 -0
  44. package/types/src/installation/install-options.d.ts +49 -0
  45. package/types/src/installation/install-project.d.ts +13 -0
  46. package/types/src/installation/install-provisioning.d.ts +58 -0
  47. package/types/src/installation/install-secrets.d.ts +30 -0
  48. package/types/src/installation/install-token.d.ts +10 -0
  49. package/types/src/installation/install.d.ts +23 -0
  50. package/types/src/installation/installation-source.d.ts +8 -0
  51. package/types/src/installation/log.d.ts +44 -0
  52. package/types/src/installation/machine-key.d.ts +19 -0
  53. package/types/src/installation/mcp.d.ts +18 -0
  54. package/types/src/installation/placement-options.d.ts +11 -0
  55. package/types/src/installation/platform-archive.d.ts +7 -0
  56. package/types/src/installation/platform-source.d.ts +8 -0
  57. package/types/src/installation/platform-upload.d.ts +3 -0
  58. package/types/src/installation/proxy-manifest.d.ts +105 -0
  59. package/types/src/installation/secrets.d.ts +17 -0
  60. package/types/src/installation/sidecar.d.ts +51 -0
  61. package/types/src/installation/source-options.d.ts +1 -0
  62. package/types/src/installation/topology/artifacts.d.ts +8 -0
  63. package/types/src/installation/topology/index.d.ts +5 -0
  64. package/types/src/installation/topology/installation-schemas.d.ts +2242 -0
  65. package/types/src/installation/topology/invariants.d.ts +23 -0
  66. package/types/src/installation/topology/setups.d.ts +58 -0
  67. package/types/src/installation/topology/topology.d.ts +148 -0
  68. package/types/src/installation/topology/validate.d.ts +11 -0
  69. package/types/src/installation/upgrade-options.d.ts +26 -0
  70. package/types/src/installation/upgrade-output.d.ts +6 -0
  71. package/types/src/installation/upgrade.d.ts +88 -0
  72. package/types/src/managed-env.d.ts +57 -0
  73. package/types/src/manifest.d.ts +206 -0
  74. package/types/src/namespace/claims.d.ts +3 -0
  75. package/types/src/namespace/codec.d.ts +334 -0
  76. package/types/src/namespace/constants.d.ts +25 -0
  77. package/types/src/namespace/errors.d.ts +15 -0
  78. package/types/src/namespace/manifest.d.ts +4 -0
  79. package/types/src/namespace/operator.d.ts +12 -0
  80. package/types/src/namespace/provisioning.d.ts +4 -0
  81. package/types/src/namespace/proxy-attempt.d.ts +36 -0
  82. package/types/src/namespace/proxy-reads.d.ts +33 -0
  83. package/types/src/namespace/proxy-secrets.d.ts +8 -0
  84. package/types/src/namespace/proxy-source.d.ts +74 -0
  85. package/types/src/namespace/topology.d.ts +9 -0
  86. package/types/src/namespace/types.d.ts +99 -0
  87. package/types/src/namespace/update.d.ts +6 -0
  88. package/types/src/namespace/validation.d.ts +12 -0
  89. package/types/src/namespace.d.ts +9 -0
  90. package/types/src/plan.d.ts +12 -0
  91. package/types/src/provider.d.ts +65 -0
  92. package/types/src/proxy-manifest-carrier.d.ts +16 -0
  93. package/types/src/schema.generated.d.ts +376 -0
  94. package/types/src/service-names.d.ts +4 -0
  95. package/types/src/service-references.d.ts +13 -0
  96. package/types/src/source/constants.d.ts +6 -0
  97. package/types/src/source/port.d.ts +60 -0
  98. package/types/src/source/types.d.ts +4 -0
  99. package/types/src/source/validation.d.ts +19 -0
  100. package/types/src/source.d.ts +2 -0
  101. package/types/src/types.d.ts +121 -0
  102. package/types/src/version-attempt.d.ts +8 -0
@@ -0,0 +1,96 @@
1
+ import type { SchemaReconciler } from '@fabrika/provider-contract';
2
+ import { type Sleeper, type ZeropsApi, type ZeropsServiceEnv } from '../index';
3
+ import { type CheckerPublication } from './checker-release';
4
+ import type { PlatformDeployInput } from './deploy-options';
5
+ import { type PlatformSourcePreparer } from './installation-source';
6
+ import type { DeployLog } from './log';
7
+ import type { PreparedPlatformSource } from './platform-source';
8
+ import { type PlatformUploadFetch } from './platform-upload';
9
+ /**
10
+ * The four services this command touches, in the order it RESOLVES and WRITES them.
11
+ *
12
+ * It is no longer the order they are deployed in — that is `PLATFORM_CONCURRENT_DEPLOY` followed by
13
+ * `PLATFORM_SEQUENTIAL_DEPLOY`, and the two stages concatenate back to this list (pinned by
14
+ * `deploy-order.test.ts` in `@fabrika/platform-artifacts`). What this list still decides is which services must exist
15
+ * and which environment is written, both of which happen for ALL FOUR before any of them is deployed.
16
+ *
17
+ * Each name is a service hostname in the topology AND the `zerops.yaml` setup name the pipeline
18
+ * selects — the platform's own "setup name defaults to the hostname" rule.
19
+ * `deploy-order.test.ts` in `@fabrika/platform-artifacts` pins both against the declarations that own them.
20
+ */
21
+ export declare const PLATFORM_DEPLOY_ORDER: readonly ["iam", "operations", "proxy", "control"];
22
+ export type PlatformDeployService = (typeof PLATFORM_DEPLOY_ORDER)[number];
23
+ /**
24
+ * The two services built AT ONCE, because nothing orders them against each other.
25
+ *
26
+ * Neither calls a sibling at boot: IAM holds no sibling origin at all, and Operations only
27
+ * CONSTRUCTS its IAM client (the first request is the first call). Each readiness gate hits the
28
+ * service's own `/healthz`, and no deploy-written variable of one is read by another's BUILD — the
29
+ * only build-time read on the whole path is the proxy reading its own manifest. So the order among them was arbitrary, and paying for it sequentially cost about 3 min 45 s
30
+ * of the ~9 min 35 s a warm five-service roll took (two measured runs, 2026-08-21).
31
+ *
32
+ * What is NOT arbitrary stays: every environment write happens before any of these starts, the proxy
33
+ * is deployed after both, and control after the proxy (ADR-0022 — the enforcement point ahead of
34
+ * the service whose table just widened). `deploy-order.test.ts` in `@fabrika/platform-artifacts` and
35
+ * `__tests__/deploy.test.ts` fail if that sequence changes.
36
+ */
37
+ export declare const PLATFORM_CONCURRENT_DEPLOY: readonly ["iam", "operations"];
38
+ /** What is deployed after the concurrent stage, one at a time and in this order. */
39
+ export declare const PLATFORM_SEQUENTIAL_DEPLOY: readonly ["proxy", "control"];
40
+ /** The proxy's hostname. The only publicly routed service, and the one that carries the manifest. */
41
+ export declare const PLATFORM_PROXY_SERVICE = "proxy";
42
+ /**
43
+ * What deploying ONE service needs. Narrower than the whole command's collaborators so the bring-up
44
+ * (`install.ts`), which deploys the proxy alone before any manifest exists, can reuse `deployPlatformService`
45
+ * without inventing an IAM reconciler it has no use for.
46
+ */
47
+ export interface PlatformServiceDeployCollaborators {
48
+ readonly uploadFetch: PlatformUploadFetch;
49
+ readonly api: ZeropsApi;
50
+ readonly sleep: Sleeper;
51
+ readonly log: DeployLog;
52
+ readonly signal: AbortSignal;
53
+ }
54
+ /** Everything `deployPlatform` reaches the outside world through. */
55
+ export interface PlatformDeployCollaborators extends PlatformServiceDeployCollaborators {
56
+ readonly prepareSource: PlatformSourcePreparer;
57
+ readonly checkerPublished: CheckerPublication;
58
+ readonly reconcileSchema: SchemaReconciler;
59
+ }
60
+ /** The prepared snapshot shared by every build in an installation run. */
61
+ export interface PlatformServiceDeployInput {
62
+ readonly source: PreparedPlatformSource;
63
+ readonly dryRun: boolean;
64
+ /** The proxy's manifest, which its archive carries as the one data file (ADR-0045); the proxy is refused without it. */
65
+ readonly proxyManifest?: string;
66
+ }
67
+ /** One resolved platform service. */
68
+ export interface ResolvedService {
69
+ readonly hostname: PlatformDeployService;
70
+ readonly id: string;
71
+ }
72
+ export declare const serviceOf: (services: ReadonlyMap<PlatformDeployService, ResolvedService>, hostname: PlatformDeployService) => ResolvedService;
73
+ /** One service's environment by key. A sensitive record carries no value. Values are never logged. */
74
+ export type LiveServiceEnv = ReadonlyMap<string, ZeropsServiceEnv>;
75
+ /** Read one service's environment by key. */
76
+ export declare const readServiceEnv: (serviceId: string, { api, signal }: Pick<PlatformServiceDeployCollaborators, "api" | "signal">) => Promise<LiveServiceEnv>;
77
+ /**
78
+ * Deploy ONE service and wait for it to become the active version.
79
+ *
80
+ * A failed deploy throws, which stops the sequence: the platform keeps the previous version serving
81
+ * (`deploy.readinessCheck` is a real gate, live-verified), so a failure here leaves the installation
82
+ * consistent — old code behind the manifest that was written for it.
83
+ *
84
+ * Exported for the BRING-UP, which builds the proxy on its own before any manifest exists so that the
85
+ * service publishes the HTTP ports its public hostnames are named after (`install.ts`, pass 1). That is
86
+ * one service, not an order — the order below stays this file's.
87
+ */
88
+ export declare const deployPlatformService: (service: ResolvedService, input: PlatformServiceDeployInput, collaborators: PlatformServiceDeployCollaborators) => Promise<void>;
89
+ /**
90
+ * Bring one Zerops platform installation to this checkout, unattended.
91
+ *
92
+ * Every step is idempotent and the whole sequence fails closed: nothing is deployed until the manifest
93
+ * and the environment name have been applied, and nothing after a failed step runs.
94
+ */
95
+ export declare const deployPlatform: (input: PlatformDeployInput, collaborators: PlatformDeployCollaborators) => Promise<void>;
96
+ export declare const deployPreparedPlatform: (input: PlatformDeployInput, collaborators: PlatformDeployCollaborators, source: PreparedPlatformSource) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ import type { AppSchema } from '@fabrika/auth-core';
2
+ export declare const PLATFORM_CONSOLE_APP_SCHEMA: AppSchema;
@@ -0,0 +1,2 @@
1
+ import type { PlatformProxyManifestTemplate } from '../proxy-manifest';
2
+ export declare const PLATFORM_PROXY_MANIFEST_TEMPLATE: PlatformProxyManifestTemplate;
@@ -0,0 +1,38 @@
1
+ import type { PlatformProxyService } from './proxy-manifest';
2
+ /** The public hostname of each platform service, without a scheme and without a port. */
3
+ export type PlatformHosts = Readonly<Record<PlatformProxyService, string>>;
4
+ /** Everything the deploy needs to know about where this installation answers. */
5
+ export interface PlatformPlacement {
6
+ readonly hosts: PlatformHosts;
7
+ /** The scheme the BROWSER speaks. Configuration, never a header — see `ProxyApp.scheme`. */
8
+ readonly scheme: 'http' | 'https';
9
+ /**
10
+ * Whether the proxy's public entry point is Zerops' generated subdomain. True only on the derived
11
+ * path: a custom-domain installation must not additionally publish a `.zerops.app` address.
12
+ */
13
+ readonly zeropsSubdomain: boolean;
14
+ }
15
+ /** The Zerops variable naming one URL per published HTTP port. READ_ONLY, written by the platform. */
16
+ export declare const ZEROPS_SUBDOMAIN_VARIABLE = "zeropsSubdomain";
17
+ /**
18
+ * Map the proxy's generated subdomains by the listener port each one fronts.
19
+ *
20
+ * The host form is `<hostname>-<hash>-<port>.<region>.zerops.app`, one line per HTTP port. Read as
21
+ * URLs rather than by splitting on `-`, so a hostname that itself contains a digit-suffixed segment
22
+ * cannot be misread: the port is the trailing `-<digits>` of the FIRST label only.
23
+ */
24
+ export declare const parseZeropsSubdomains: (value: string) => Map<number, string>;
25
+ /** One platform app's listener, as the committed template assigns it. */
26
+ export interface PlatformListener {
27
+ readonly service: PlatformProxyService;
28
+ readonly port: number;
29
+ }
30
+ /**
31
+ * Bind every platform service to the generated subdomain of its listener.
32
+ *
33
+ * Throws naming every port it could not find, because the alternative — writing a manifest with a
34
+ * guessed or missing host — is a proxy that fronts nothing at an address the console then bounces to.
35
+ */
36
+ export declare const derivePlatformHosts: (subdomains: string, listeners: readonly PlatformListener[]) => PlatformHosts;
37
+ /** `https://<host>` — the origin a browser, a token's `iss` and a return-origin registry all use. */
38
+ export declare const platformOrigin: (scheme: "http" | "https", host: string) => string;
@@ -0,0 +1,9 @@
1
+ import type { InstallationCli } from '@fabrika/provider-contract/installation';
2
+ export { PLATFORM_CONCURRENT_DEPLOY, PLATFORM_DEPLOY_ORDER, PLATFORM_PROXY_SERVICE, PLATFORM_SEQUENTIAL_DEPLOY } from './deploy';
3
+ export { PLATFORM_CONSOLE_APP_SCHEMA } from './generated/platform-console-schema';
4
+ export { PLATFORM_PROXY_MANIFEST_TEMPLATE } from './generated/platform-proxy-manifest';
5
+ export { type IamOAuthConfig, type McpClient, parseMcpClientsJson, platformIamOAuthConfig } from './mcp';
6
+ export { type PlatformOAuthResourceTemplate, type PlatformProxyApp, platformProxyAppFor, type PlatformProxyComposition, type PlatformProxyManifest, type PlatformProxyManifestTemplate, type PlatformProxyPlacement, type PlatformProxyService, platformServiceTableDigest, resolvePlatformProxyManifest, } from './proxy-manifest';
7
+ /** The operator's guide of this release, shipped beside this module. */
8
+ export declare const readInstallationGuide: () => Promise<string>;
9
+ export declare const installationCli: InstallationCli;
@@ -0,0 +1,38 @@
1
+ import { type CommandEnv } from '@fabrika/installation-init';
2
+ /** Every input `runInit` takes from the operator. */
3
+ export interface InitInput {
4
+ /** Names the GitHub Environment, the default repository and the local checkout directory. */
5
+ readonly installation: string;
6
+ readonly repo: string;
7
+ /** The sidecar's local checkout: `--checkout`, or `fabrika-zerops-<installation>` in the working directory. */
8
+ readonly checkoutDir: string;
9
+ /** The published fabrika-platform tag the sidecar pins in `fabrika.ref`. */
10
+ readonly fabrikaRef: string;
11
+ readonly projectId: string;
12
+ /** The name this installation calls itself — written to every service as `ENVIRONMENT`. */
13
+ readonly environmentName: string;
14
+ /** All three or none: none derives them from the proxy's Zerops subdomains. */
15
+ readonly hosts?: {
16
+ readonly iam: string;
17
+ readonly control: string;
18
+ readonly operations: string;
19
+ };
20
+ /** Region API base, when the installation is not on the default one. */
21
+ readonly apiBaseUrl?: string;
22
+ /** The Zerops INTEGRATION token `platform install` minted. Environment only. */
23
+ readonly accessToken: string;
24
+ /** The `px_` key this installation's IAM was seeded with. Environment only. */
25
+ readonly provisioningKey: string;
26
+ /** `--yes`: answer every confirmation yes. */
27
+ readonly unattended: boolean;
28
+ }
29
+ /**
30
+ * The installation's environment name, refused when it claims to be local.
31
+ *
32
+ * The same refusal the services enforce at boot: `local` is the value `localDevLogin`, the ephemeral
33
+ * signing key and the credential-less caller path are all gated on, and a deployed installation is
34
+ * never a loopback address.
35
+ */
36
+ export declare const checkedEnvironmentName: (declared: string) => string;
37
+ /** Parse the command's arguments and environment into the one input `runInit` takes. */
38
+ export declare const parseInitArgs: (argv: readonly string[], env: CommandEnv, cwd?: string) => InitInput;
@@ -0,0 +1,60 @@
1
+ import { type Confirmations, type EnvironmentConfig, type SidecarScaffoldInput, type SidecarScaffoldResult } from '@fabrika/installation-init';
2
+ import { type ZeropsApi } from '../index';
3
+ import type { InitInput } from './init-options';
4
+ import type { InitLog } from './log';
5
+ /** Everything `runInit` does outside the operator's own disk. Every one of them is confirmed first. */
6
+ export interface InitEffects {
7
+ /** Only decides how the scaffold confirmation is phrased; the scaffold itself re-reads it. */
8
+ repositoryExists(repo: string): Promise<boolean>;
9
+ scaffold(input: SidecarScaffoldInput): Promise<SidecarScaffoldResult>;
10
+ configureEnvironment(config: EnvironmentConfig): Promise<void>;
11
+ triggerWorkflow(repo: string): Promise<void>;
12
+ /** Read one Zerops project's name — the cheapest proof that the token and the project id agree. */
13
+ describeProject(input: {
14
+ projectId: string;
15
+ accessToken: string;
16
+ apiBaseUrl?: string;
17
+ }): Promise<string>;
18
+ inspectPlatform(input: InspectPlatformInput): Promise<PlatformInspection>;
19
+ }
20
+ export interface InitCollaborators {
21
+ readonly log: InitLog;
22
+ readonly prompts: Confirmations;
23
+ readonly effects: InitEffects;
24
+ }
25
+ /** Where this installation answers, as far as `init` needs to know it. */
26
+ interface Placement {
27
+ readonly hosts?: {
28
+ readonly iam: string;
29
+ readonly control: string;
30
+ readonly operations: string;
31
+ };
32
+ }
33
+ export interface InspectPlatformInput {
34
+ readonly projectId: string;
35
+ readonly accessToken: string;
36
+ readonly apiBaseUrl?: string;
37
+ }
38
+ /** What `init` reads off the live installation to print origins it can verify. Values are never logged. */
39
+ export interface PlatformInspection {
40
+ readonly controlEnv: ReadonlyMap<string, string>;
41
+ readonly proxyEnv: ReadonlyMap<string, string>;
42
+ readonly proxyPublished: boolean;
43
+ }
44
+ /** Read `control` and `proxy` without writing anything. */
45
+ export declare const inspectPlatform: (projectId: string, api: ZeropsApi, signal: AbortSignal) => Promise<PlatformInspection>;
46
+ /**
47
+ * IAM's PUBLIC origin as the installation itself records it, or nothing when init would have to guess.
48
+ *
49
+ * `platform install` writes it to `control` as `FABRIKA_IAM_ISSUER`, so this reads the live value
50
+ * rather than re-deriving one — and refuses it when the operator named a different IAM host, because
51
+ * a printed origin an operator then pastes into `FABRIKA_IAM_RPC_URL` must be the one that answers.
52
+ */
53
+ export declare const platformIamOrigin: (placement: Placement, platform: PlatformInspection) => string | undefined;
54
+ /** Return a verified live console URL, or no URL when init would have to guess. */
55
+ export declare const sourceConnectionSettingsUrl: (placement: Placement, platform: PlatformInspection) => string | undefined;
56
+ /** Run the whole thing for `<installation>`. */
57
+ export declare const runInit: (input: InitInput, collaborators: InitCollaborators) => Promise<void>;
58
+ /** The real collaborators: a TTY, `gh`, git, and one read of the Zerops API. */
59
+ export declare const consoleInitCollaborators: (input: InitInput) => InitCollaborators;
60
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { Confirmations } from '@fabrika/installation-init';
2
+ import type { SchemaReconciler } from '@fabrika/provider-contract';
3
+ import type { Sleeper, ZeropsApi } from '../index';
4
+ import type { CheckerPublication } from './checker-release';
5
+ import type { PlatformSourcePreparer } from './installation-source';
6
+ import type { InitLog } from './log';
7
+ import type { PlatformUploadFetch } from './platform-upload';
8
+ /** The one question this command asks. Injected so the flow can be exercised without a TTY. */
9
+ export type InstallPrompts = Confirmations;
10
+ /** Everything `runInstall` reaches the outside world through. */
11
+ export interface InstallCollaborators {
12
+ readonly prepareSource: PlatformSourcePreparer;
13
+ readonly checkerPublished: CheckerPublication;
14
+ readonly uploadFetch: PlatformUploadFetch;
15
+ readonly api: ZeropsApi;
16
+ readonly reconcileSchema: SchemaReconciler;
17
+ readonly sleep: Sleeper;
18
+ readonly log: InitLog;
19
+ readonly prompts: InstallPrompts;
20
+ readonly signal: AbortSignal;
21
+ /** Create the local secrets file (`--secrets-file`) with this text. Never logged. */
22
+ readonly storeSecrets: (text: string) => Promise<void>;
23
+ }
24
+ export declare const assertRunning: (signal: AbortSignal) => void;
@@ -0,0 +1,70 @@
1
+ import type { InstallationEmail } from '@fabrika/installation-init';
2
+ import type { ZeropsNamespaceProxySource } from '../index';
3
+ import type { PlatformDeployService, ResolvedService } from './deploy';
4
+ import { type PlatformHosts } from './hosts';
5
+ import { type InstallCollaborators } from './install-collaborators';
6
+ import type { InstallationSecrets } from './secrets';
7
+ import type { PlatformTier } from './topology/topology';
8
+ /** The private address the proxy dials IAM at inside the project, and pass 1's placeholder issuer. */
9
+ export declare const PRIVATE_IAM_UPSTREAM = "iam:3000";
10
+ /**
11
+ * The three variables pass 1 writes on the proxy, and nothing else.
12
+ *
13
+ * `FABRIKA_IAM_ISSUER` is the private address rather than the public issuer because the public one is not known
14
+ * yet; `deployPlatform` corrects it in pass 2, before the manifest it belongs to is applied. The pass-1 manifest is
15
+ * `PASS_ONE_PROXY_MANIFEST`, which the proxy's archive carries.
16
+ */
17
+ export declare const passOneProxyEnv: (secrets: InstallationSecrets) => ReadonlyMap<string, string>;
18
+ /** EMPTY on purpose: the only legal document before the hosts exist. The proxy comes up answering 404. */
19
+ export declare const PASS_ONE_PROXY_MANIFEST: string;
20
+ /** What `installationServiceEnv` needs. Everything in it is settled before the first write. */
21
+ export interface InstallationEnvInput {
22
+ readonly environment: string;
23
+ readonly tier: PlatformTier;
24
+ readonly scheme: 'http' | 'https';
25
+ readonly hosts: PlatformHosts;
26
+ readonly clientId: string;
27
+ readonly proxySource: ZeropsNamespaceProxySource;
28
+ readonly apiBaseUrl?: string;
29
+ readonly secrets: InstallationSecrets;
30
+ /** The Zerops integration token minted for the control plane. Never logged, never printed. */
31
+ readonly zeropsAccessToken: string;
32
+ /** IAM sends its own mail with it, and Control hands it to applications whose job declares `mailer: 'installation'`. */
33
+ readonly email: InstallationEmail;
34
+ }
35
+ /**
36
+ * EVERY variable this command places, per service.
37
+ *
38
+ * Pure, and exported, because the matrix is the thing most worth pinning: a name that is right on
39
+ * Cloudflare and wrong on Bun is a silent no-op rather than a failure. Two spelling rules run through
40
+ * it, both verified against the runtimes rather than assumed:
41
+ *
42
+ * • IAM's Bun runtime reads `ISSUER` and `ENVIRONMENT` UNPREFIXED and everything else with the
43
+ * `FABRIKA_IAM_` prefix (`packages/iam/src/node/runtime.ts`). The prefixed spellings below are the
44
+ * ones that file actually reads; the unprefixed ones are the ones it actually reads too.
45
+ * • No key any service declares in its own `zerops.yaml` appears here — `PORT`,
46
+ * `FABRIKA_IAM_RPC_URL`, `FABRIKA_OPERATIONS_URL`, `FABRIKA_CONTROL_ASSETS_DIR`,
47
+ * `FABRIKA_CONTROL_URL`, `FABRIKA_PROXY_MANIFEST`, `FABRIKA_PROXY_PORT`. The env API refuses those
48
+ * (`provider-zerops/src/api.ts`), because the conflict cannot be resolved to a record id.
49
+ *
50
+ * The proxy manifest is deliberately absent: pass 1 writes an empty one and `deployPlatform` owns the
51
+ * real one, in the same run as the code it describes (ADR-0027).
52
+ */
53
+ export declare const installationServiceEnv: (input: InstallationEnvInput) => ReadonlyMap<PlatformDeployService, ReadonlyMap<string, string>>;
54
+ /**
55
+ * The keys whose VALUE this command generates, per service.
56
+ *
57
+ * Their presence on a live service is what "this project already holds an installation" means, and it is
58
+ * checked by KEY only — a value is never read back for comparison. `__tests__/install.test.ts` pins this
59
+ * against the matrix above, so a new generated secret cannot be added without appearing here.
60
+ */
61
+ export declare const GENERATED_SECRET_KEYS: ReadonlyMap<PlatformDeployService, readonly string[]>;
62
+ /**
63
+ * Write a set of variables, reporting the KEYS — never a value, not even in a debug line.
64
+ *
65
+ * Write-first and unconditional: a generated secret cannot be read back to compare, and `PUT
66
+ * /user-data/{id}` replaces in place, so a repeat costs a call and nothing else. Exactly the `sensitive`
67
+ * keys are written sensitive (the generated secrets and the operator's mail credentials); everything else
68
+ * stays readable, because `platform deploy` compares and merges it later.
69
+ */
70
+ export declare const writeServiceEnv: (service: ResolvedService, desired: ReadonlyMap<string, string>, { api, log, signal }: InstallCollaborators, sensitive: ReadonlySet<string>) => Promise<void>;
@@ -0,0 +1,49 @@
1
+ import { type InstallationEmail } from '@fabrika/installation-init';
2
+ import type { PlatformTier } from './topology/topology';
3
+ /**
4
+ * Where the installation's project comes from.
5
+ *
6
+ * A UNION rather than an optional id beside a boolean, because the two mistakes worth making
7
+ * unrepresentable are "neither" and "both": the parser is the only place that can tell them apart, and
8
+ * downstream code must not have to ask a second time.
9
+ */
10
+ export type InstallProjectSource =
11
+ /** One the operator created, empty, by hand. */
12
+ {
13
+ readonly kind: 'existing';
14
+ readonly projectId: string;
15
+ }
16
+ /** One this command creates, from the same declaration the provisioning import is compiled from. */
17
+ | {
18
+ readonly kind: 'create';
19
+ readonly projectName: string;
20
+ };
21
+ /** Every input `runInstall` takes from the operator. */
22
+ export interface PlatformInstallInput {
23
+ /** The project this installation is brought up in — named, or created here. */
24
+ readonly project: InstallProjectSource;
25
+ /** The client the project belongs to — required, because the control plane's token is minted on it. */
26
+ readonly clientId: string;
27
+ /** Zerops personal access token. Environment only. */
28
+ readonly accessToken: string;
29
+ /** Region API base, when the installation is not on the default one. */
30
+ readonly apiBaseUrl?: string;
31
+ /** The name this installation calls itself — written to every platform service as `ENVIRONMENT`. */
32
+ readonly environment: string;
33
+ /** The scheme a browser speaks to this installation. */
34
+ readonly scheme: 'http' | 'https';
35
+ readonly sourceDir: string;
36
+ /** Where every credential this run creates is written, mode 0600, before the first is placed. */
37
+ readonly secretsFile: string;
38
+ readonly tier: PlatformTier;
39
+ /** The installation's mail transport, written on `iam` and `control`; `none` when the operator named no provider. */
40
+ readonly email: InstallationEmail;
41
+ /** `--yes`: answer every confirmation yes, for a bring-up with nobody at the keyboard. */
42
+ readonly unattended: boolean;
43
+ }
44
+ /** The canonical public release repository used for namespace proxy source identities. */
45
+ export declare const FABRIKA_REPOSITORY_URL = "https://github.com/contember/fabrika-platform";
46
+ /** `--create-project`: bring the project up too, instead of naming one the operator made. */
47
+ export declare const CREATE_PROJECT_FLAG = "--create-project";
48
+ /** Parse the command's arguments and environment into the one input `runInstall` takes. */
49
+ export declare const parsePlatformInstallArgs: (argv: readonly string[], env: Record<string, string | undefined>) => PlatformInstallInput;
@@ -0,0 +1,13 @@
1
+ import { type InstallCollaborators } from './install-collaborators';
2
+ import type { PlatformInstallInput } from './install-options';
3
+ /**
4
+ * Settle which project this run installs into — the operator's, or one created here.
5
+ *
6
+ * ONE confirmation either way, and both cover the token check and the read that follows: a created project is read back
7
+ * through the same `resolveProject` an operator-made one goes through, so the core package is checked on
8
+ * the project that will actually be installed into rather than on the document that asked for it.
9
+ */
10
+ export declare const resolveInstallationProject: (input: PlatformInstallInput, environment: string, collaborators: InstallCollaborators) => Promise<{
11
+ id: string;
12
+ name: string;
13
+ }>;
@@ -0,0 +1,58 @@
1
+ import { type ZeropsService, type ZeropsServiceStatus } from '../index';
2
+ import { type PlatformDeployService, type ResolvedService } from './deploy';
3
+ import { type InstallCollaborators } from './install-collaborators';
4
+ import { type CompiledDocument, type CompiledTopology, type PlatformTier } from './topology/topology';
5
+ /** The topology this command installs for a tier, so the project document and the services document cannot drift. */
6
+ export declare const installationTopology: (environment: string, tier: PlatformTier) => CompiledTopology;
7
+ /**
8
+ * The services-only provisioning document (WU2), compiled from the declaration the committed artifact
9
+ * is rendered from.
10
+ *
11
+ * Not read off disk: `gen:check` already proves
12
+ * `packages/platform-artifacts/generated/platform{,-light}.services.provision.zerops-import.yaml` is this exact text,
13
+ * and a value needs no filesystem in a published package.
14
+ */
15
+ export declare const provisioningDocument: (environment: string, tier: PlatformTier) => CompiledDocument;
16
+ /** One service of the provisioning document that the project already has. */
17
+ interface ExistingService {
18
+ readonly id: string;
19
+ readonly status?: ZeropsServiceStatus;
20
+ }
21
+ /**
22
+ * Every service of the provisioning document that the project already has, by hostname.
23
+ *
24
+ * The STATUS travels with the id because `NEW` is not a service this command may touch: measured live,
25
+ * a service holds NO environment at all — not even the platform's generated keys — until it leaves that
26
+ * state, so both a read and a write against one are meaningless rather than merely early.
27
+ */
28
+ export declare const existingServices: (declared: readonly string[], services: readonly ZeropsService[]) => Map<string, ExistingService>;
29
+ /**
30
+ * Refuse a service the platform is still creating.
31
+ *
32
+ * Not a wait: this command holds no process id for a service some earlier run imported, and a sleep
33
+ * would be a guess at a number the platform never promised. The whole light topology settles in about a
34
+ * minute, so "run it again shortly" is both true and cheap.
35
+ */
36
+ export declare const assertNoServiceIsBeingCreated: (existing: ReadonlyMap<string, ExistingService>) => void;
37
+ /**
38
+ * Refuse a project that already carries this installation's generated secrets.
39
+ *
40
+ * By KEY, never by value. Re-running a bring-up would roll a fresh vault KEK — whose loss is
41
+ * unrecoverable by design — and fresh signing keys, invalidating every live token, so the safe answer to
42
+ * "was this already installed?" is to stop and say so. `platform deploy` is the command for an
43
+ * installation that exists.
44
+ */
45
+ export declare const assertNotAlreadyInstalled: (existing: ReadonlyMap<string, ExistingService>, collaborators: InstallCollaborators) => Promise<void>;
46
+ /**
47
+ * Import the topology, unless the project already has it.
48
+ *
49
+ * Three outcomes, and the middle one is the one that costs something to get wrong: re-applying a
50
+ * `startWithoutCode: true` document at a service that carries code activates an EMPTY app version and
51
+ * demotes the running one to `BACKUP` (`docs/reference/zerops-platform.md`). A PARTIAL set is refused
52
+ * rather than completed, because an import applies as one document and cannot skip the services that
53
+ * are already there.
54
+ */
55
+ export declare const provision: (projectId: string, document: CompiledDocument, existing: ReadonlyMap<string, ExistingService>, collaborators: InstallCollaborators) => Promise<void>;
56
+ /** Resolve the four platform services by hostname, naming the whole set that is missing rather than the first. */
57
+ export declare const resolvePlatformServices: (projectId: string, { api, signal }: InstallCollaborators) => Promise<Map<PlatformDeployService, ResolvedService>>;
58
+ export { serviceOf } from './deploy';
@@ -0,0 +1,30 @@
1
+ import type { InstallationSecrets } from './secrets';
2
+ /** `--secrets-file`: where the local copy goes. A new file, outside the source checkout. */
3
+ export declare const SECRETS_FILE_FLAG = "--secrets-file";
4
+ export declare const SECRETS_FILE_VARIABLE = "FABRIKA_PLATFORM_SECRETS_FILE";
5
+ /**
6
+ * Settle the path before anything is contacted.
7
+ *
8
+ * Refused when it exists, because overwriting one would destroy another installation's only copy, and
9
+ * when it is inside the source checkout, where it would dirty the checkout `platform deploy` uploads
10
+ * and could be committed.
11
+ */
12
+ export declare const checkedSecretsFilePath: (path: string, sourceDir: string) => string;
13
+ /** What the file records beside the credentials, so it identifies its installation on its own. */
14
+ export interface InstallationSecretsRecord {
15
+ readonly environment: string;
16
+ readonly projectId: string;
17
+ readonly clientId: string;
18
+ readonly secrets: InstallationSecrets;
19
+ /** The integration token minted for the control plane. */
20
+ readonly zeropsAccessToken: string;
21
+ }
22
+ /**
23
+ * The file's text: shell-sourceable assignments, named as the commands that read them name them.
24
+ *
25
+ * `FABRIKA_ZEROPS_ACCESS_TOKEN` is the INTEGRATION token, which is what `platform init` and
26
+ * `platform deploy` take; the personal token the install ran with is not written.
27
+ */
28
+ export declare const renderSecretsFile: (record: InstallationSecretsRecord) => string;
29
+ /** Create the file with mode 0600, failing rather than replacing one that appeared since the check. */
30
+ export declare const writeSecretsFile: (path: string, text: string) => Promise<void>;
@@ -0,0 +1,10 @@
1
+ import type { InstallCollaborators } from './install-collaborators';
2
+ /**
3
+ * Refuse an integration token before anything is created.
4
+ *
5
+ * The install mints an integration token at step 4, and an integration token is refused that mint. Without
6
+ * this read the mistake costs a billed project and six imported services first.
7
+ */
8
+ export declare const assertPersonalAccessToken: ({ api, signal }: InstallCollaborators) => Promise<void>;
9
+ /** Translate the mint's refusal of an integration token into the fix and the way back into this project. */
10
+ export declare const explainMintFailure: (failure: unknown, projectId: string) => unknown;
@@ -0,0 +1,23 @@
1
+ import type { SchemaReconciler } from '@fabrika/provider-contract';
2
+ import { type InstallCollaborators, type InstallPrompts } from './install-collaborators';
3
+ import type { PlatformInstallInput } from './install-options';
4
+ export type { InstallCollaborators, InstallPrompts } from './install-collaborators';
5
+ export { GENERATED_SECRET_KEYS, type InstallationEnvInput, installationServiceEnv } from './install-environment';
6
+ /**
7
+ * Bring up one Zerops platform installation, in a project the operator created empty or in one created
8
+ * here.
9
+ *
10
+ * Every step that leaves this machine is confirmed first, and declining any of them stops there with
11
+ * what to run instead. Nothing after the project step and before the import is a mutation, so a declined
12
+ * install leaves the project exactly as it was.
13
+ */
14
+ export declare const runInstall: (input: PlatformInstallInput, collaborators: InstallCollaborators) => Promise<void>;
15
+ /**
16
+ * The confirmations stand between a stray invocation and an installation: this command generates six
17
+ * credentials, mints a Zerops token on the account and deploys.
18
+ */
19
+ export declare const installPrompts: (input: PlatformInstallInput, stdin: {
20
+ readonly isTTY?: boolean;
21
+ }, announce: (line: string) => void) => InstallPrompts;
22
+ /** The real collaborators: a TTY for the confirmations, the Zerops API, and IAM's own schema reconciler. */
23
+ export declare const consoleInstallCollaborators: (input: PlatformInstallInput, reconcileSchema: SchemaReconciler) => InstallCollaborators;
@@ -0,0 +1,8 @@
1
+ import { type ZeropsNamespaceProxySource } from '../index';
2
+ import { type PreparedPlatformSource } from './platform-source';
3
+ export type PlatformSourcePreparer = (sourceDir: string, signal: AbortSignal) => Promise<PreparedPlatformSource>;
4
+ export declare const prepareInstallationSource: PlatformSourcePreparer;
5
+ export declare const validateInstallationSetups: (source: PreparedPlatformSource, setups: readonly string[]) => void;
6
+ export declare const FABRIKA_ZEROPS_PROXY_SOURCE_JSON = "FABRIKA_ZEROPS_PROXY_SOURCE_JSON";
7
+ export declare const serializeNamespaceProxySource: (source: ZeropsNamespaceProxySource) => string;
8
+ export declare const prepareNamespaceProxySource: (source: PreparedPlatformSource) => Promise<ZeropsNamespaceProxySource>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Progress reporting for `fabrika platform deploy --provider=zerops` and for `platform init`.
3
+ *
4
+ * HARD RULE, the same one `@fabrika/installation-init`'s `log.ts` states: **nothing here ever
5
+ * prints a secret VALUE, and there is deliberately no helper that accepts one.** Callers may pass a
6
+ * variable NAME, a count, or a "✓ written" confirmation. The Zerops access token and the IAM
7
+ * provisioning key are the two credentials this command holds; neither has a formatting path.
8
+ *
9
+ * A caught error is never passed through here either — `ZeropsApiError` (`src/api/errors.ts`)
10
+ * already redacts the platform's message on the calls that carry a value, but a raw error from
11
+ * anywhere else may quote a URL with an embedded token. Log a short sentence, never an error object.
12
+ *
13
+ * Only INTERFACES and test doubles live here. The console bindings live with the flow that uses them,
14
+ * so nothing on the deploy path imports the interactive `platform init` machinery.
15
+ */
16
+ /** Where the command's progress goes. Injected so a test can read the transcript instead of stdout. */
17
+ export interface DeployLog {
18
+ /** A numbered stage of the sequence. */
19
+ step(title: string): void;
20
+ /** A detail line under the current stage. */
21
+ info(message: string): void;
22
+ /** Something the operator should notice but which does not stop the deploy. */
23
+ warn(message: string): void;
24
+ }
25
+ /** The real console log. Stateful only in its step counter. */
26
+ export declare const consoleDeployLog: () => DeployLog;
27
+ /** A log that records every line, for tests and for `--dry-run` assertions. */
28
+ export declare const recordingDeployLog: () => DeployLog & {
29
+ readonly lines: readonly string[];
30
+ };
31
+ /**
32
+ * What `platform init` reports. Two lines more than a deploy: a confirmation of something that
33
+ * happened, and the boxed hand-off an operator must not scroll past.
34
+ */
35
+ export interface InitLog extends DeployLog {
36
+ /** Something outward-facing succeeded. Names a key or a repository, never a value. */
37
+ ok(message: string): void;
38
+ /** A boxed OPERATOR ACTION — what to run when a confirmation was declined, or what is left to do. */
39
+ action(title: string, lines: readonly string[]): void;
40
+ }
41
+ /** A log that records every line, so a test can assert on the transcript — including what is NOT in it. */
42
+ export declare const recordingInitLog: () => InitLog & {
43
+ readonly lines: readonly string[];
44
+ };
@@ -0,0 +1,19 @@
1
+ /** What the calling verb knows about this installation. Both fields are omitted when it would guess. */
2
+ export interface MachineKeyNextStep {
3
+ /** IAM's PUBLIC origin — the address `FABRIKA_IAM_RPC_URL` takes. */
4
+ readonly iamOrigin?: string;
5
+ /** The sidecar GitHub Environment that also holds the provisioning key, when its name is known. */
6
+ readonly sidecarEnvironment?: string;
7
+ }
8
+ /** The boxed hand-off's title. Exported so a test names it once. */
9
+ export declare const MACHINE_KEY_NEXT_STEP_TITLE = "NEXT \u2014 mint a machine key for `fabrika control`";
10
+ /** The command the block leads to, with the three flags `key issue` requires. */
11
+ export declare const MACHINE_KEY_ISSUE_COMMAND = "fabrika control key issue --label=<name> --permissions=<a,b,c> --expires-in=<seconds>";
12
+ /**
13
+ * The lines of the block, in the order an operator uses them.
14
+ *
15
+ * The origin is the one VALUE printed, because it is not a credential: it is where the installation
16
+ * answers, and the rebuild that motivated this block failed precisely because an operator guessed it.
17
+ * The two keys are named and located; a value for either would come from the environment anyway.
18
+ */
19
+ export declare const machineKeyNextStepLines: (step?: MachineKeyNextStep) => readonly string[];
@@ -0,0 +1,18 @@
1
+ import { type OAuthManagedClient, type OAuthResourceDto } from '@fabrika/iam-contract';
2
+ import { type PlatformProxyComposition, type PlatformProxyManifestTemplate } from './proxy-manifest';
3
+ /** One operator-registered MCP client: its CIMD identity, plus where IAM fetches it when that differs. */
4
+ export interface McpClient extends OAuthManagedClient {
5
+ readonly metadataUrl?: string;
6
+ }
7
+ /** The document IAM parses from `FABRIKA_IAM_OAUTH_CONFIG`. */
8
+ export interface IamOAuthConfig {
9
+ readonly clients: readonly McpClient[];
10
+ readonly resources: readonly OAuthResourceDto[];
11
+ }
12
+ /**
13
+ * Parse the operator's MCP client list — a JSON array. Anything malformed, an empty list or a repeated
14
+ * client id is refused here, before a deploy writes a value IAM would refuse to boot with.
15
+ */
16
+ export declare const parseMcpClientsJson: (raw: string) => readonly McpClient[];
17
+ /** IAM's OAuth configuration for one placed installation: the clients, and one resource per app that serves one. */
18
+ export declare const platformIamOAuthConfig: (template: PlatformProxyManifestTemplate, composition: PlatformProxyComposition, clients: readonly McpClient[]) => IamOAuthConfig;