@wtfalch/foundry 0.15.0 → 0.16.1
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/bootstrap/declare.d.ts +2 -2
- package/dist/bootstrap/declare.js +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/migrations/0034_owners.sql +42 -0
- package/dist/migrations/0035_chat_on_by_default.sql +10 -0
- package/dist/org.d.ts +80 -17
- package/dist/org.js +221 -18
- package/dist/plan/agora.d.ts +19 -1
- package/dist/plan/agora.js +37 -2
- package/dist/plan/chat-matrix.d.ts +99 -0
- package/dist/plan/chat-matrix.js +194 -0
- package/dist/plan/diff.d.ts +5 -3
- package/dist/plan/diff.js +143 -83
- package/dist/plan/entry-ai-key.d.ts +58 -0
- package/dist/plan/entry-ai-key.js +209 -0
- package/dist/plan/entry-files-key.d.ts +82 -0
- package/dist/plan/entry-files-key.js +327 -0
- package/dist/plan/executor.d.ts +244 -16
- package/dist/plan/executor.js +866 -84
- package/dist/plan/hostnames.d.ts +14 -1
- package/dist/plan/hostnames.js +26 -1
- package/dist/plan/index.d.ts +1 -0
- package/dist/plan/index.js +1 -0
- package/dist/plan/plan-apply.d.ts +2 -0
- package/dist/plan/plan-apply.js +42 -2
- package/dist/plan/platform-org.d.ts +17 -5
- package/dist/plan/platform-org.js +15 -7
- package/dist/plan/platform-providers.js +4 -3
- package/dist/plan/proxy-labels.d.ts +42 -0
- package/dist/plan/proxy-labels.js +73 -0
- package/dist/plan/zitadel-machine-user.d.ts +5 -1
- package/dist/plan/zitadel-machine-user.js +5 -1
- package/dist/plan/zitadel-project-roles.d.ts +25 -0
- package/dist/plan/zitadel-project-roles.js +104 -0
- package/dist/store/postgres.js +4 -17
- package/dist/store/schema.d.ts +13 -45
- package/dist/store/schema.js +5 -8
- package/package.json +1 -1
package/dist/plan/executor.d.ts
CHANGED
|
@@ -84,6 +84,7 @@ export interface CoolifyPort {
|
|
|
84
84
|
getApplication(uuid: string): Promise<{
|
|
85
85
|
uuid: string;
|
|
86
86
|
status?: string;
|
|
87
|
+
custom_labels?: string | null;
|
|
87
88
|
}>;
|
|
88
89
|
createGithubAppApplication(input: {
|
|
89
90
|
project_uuid: string;
|
|
@@ -457,6 +458,8 @@ export interface CloudflarePort {
|
|
|
457
458
|
content: string;
|
|
458
459
|
proxied?: boolean;
|
|
459
460
|
ttl?: number;
|
|
461
|
+
/** Required by Cloudflare for an `MX` record. */
|
|
462
|
+
priority?: number;
|
|
460
463
|
}): Promise<{
|
|
461
464
|
id: string;
|
|
462
465
|
name: string;
|
|
@@ -468,6 +471,7 @@ export interface CloudflarePort {
|
|
|
468
471
|
content: string;
|
|
469
472
|
proxied?: boolean;
|
|
470
473
|
ttl?: number;
|
|
474
|
+
priority?: number;
|
|
471
475
|
}): Promise<{
|
|
472
476
|
id: string;
|
|
473
477
|
name: string;
|
|
@@ -800,6 +804,24 @@ export interface ZitadelPort {
|
|
|
800
804
|
userId: string;
|
|
801
805
|
created: boolean;
|
|
802
806
|
}>;
|
|
807
|
+
/**
|
|
808
|
+
* Adds a role to a project. Idempotent: a role that already exists is not
|
|
809
|
+
* an error. `@wtfalch/providers`' `ZitadelClient` has no project-role API,
|
|
810
|
+
* so `zitadel-project-roles.ts` is the real implementation.
|
|
811
|
+
*/
|
|
812
|
+
ensureProjectRole(orgId: string, projectId: string, role: {
|
|
813
|
+
key: string;
|
|
814
|
+
displayName: string;
|
|
815
|
+
}): Promise<{
|
|
816
|
+
created: boolean;
|
|
817
|
+
}>;
|
|
818
|
+
/**
|
|
819
|
+
* Grants a user (in `orgId`) project roles. Idempotent: roles the user
|
|
820
|
+
* already holds on the project are kept and the missing ones added.
|
|
821
|
+
*/
|
|
822
|
+
grantProjectRoles(orgId: string, userId: string, projectId: string, roleKeys: string[]): Promise<{
|
|
823
|
+
changed: boolean;
|
|
824
|
+
}>;
|
|
803
825
|
/** Lets anyone sign up to the org (plan gap A9). Idempotent. */
|
|
804
826
|
allowSelfRegistration(orgId: string): Promise<{
|
|
805
827
|
changed: boolean;
|
|
@@ -1008,6 +1030,9 @@ export interface FilesOrganisationPort {
|
|
|
1008
1030
|
ceilingBytes: string | null;
|
|
1009
1031
|
ceilingMicros: string | null;
|
|
1010
1032
|
shareHost?: string | null;
|
|
1033
|
+
/** F6b: with `owner`, files creates the organisation's owner membership, which its own key is minted below. */
|
|
1034
|
+
name?: string;
|
|
1035
|
+
owner?: string;
|
|
1011
1036
|
}): Promise<{
|
|
1012
1037
|
organisationId: string;
|
|
1013
1038
|
ceilingBytes: string | null;
|
|
@@ -1018,10 +1043,65 @@ export interface FilesOrganisationPort {
|
|
|
1018
1043
|
suspended: boolean;
|
|
1019
1044
|
shareHost: string | null;
|
|
1020
1045
|
created: boolean;
|
|
1046
|
+
/**
|
|
1047
|
+
* F6b: files' service key for Agora, returned once on a 201 that passed
|
|
1048
|
+
* `name` and `owner` (the SDK's `agoraKey`). `filesOrganisation` delivers it.
|
|
1049
|
+
*/
|
|
1050
|
+
agoraKey?: {
|
|
1051
|
+
id: string;
|
|
1052
|
+
secret: string;
|
|
1053
|
+
};
|
|
1021
1054
|
}>;
|
|
1022
1055
|
/** #27, #177: the org's own spend limit on Files -- see this interface's own doc comment for why `organisationId` is explicit here. */
|
|
1023
1056
|
setQuota(organisationId: string, limitBytes: string | null): Promise<unknown>;
|
|
1024
1057
|
setBudget(organisationId: string, limitMicros: string | null): Promise<unknown>;
|
|
1058
|
+
/**
|
|
1059
|
+
* F6b: mints the organisation's own key, matching files'
|
|
1060
|
+
* `POST /v1/manage/organisations/:organisationId/keys` (SDK
|
|
1061
|
+
* `issueOrganisationKey`, wtfalch/files#121). `issuer` is a live human
|
|
1062
|
+
* member the key's permissions are copied from. The secret comes back
|
|
1063
|
+
* once; `filesRootKey` hands it to `deliverSecret` and returns only the id.
|
|
1064
|
+
*/
|
|
1065
|
+
issueOrganisationKey(organisationId: string, input: {
|
|
1066
|
+
name: string;
|
|
1067
|
+
kind: 'api_key' | 'agent' | 'service';
|
|
1068
|
+
issuer: string;
|
|
1069
|
+
permissions: string[];
|
|
1070
|
+
expiresDays: number;
|
|
1071
|
+
}): Promise<{
|
|
1072
|
+
id: string;
|
|
1073
|
+
secret: string;
|
|
1074
|
+
expiresAt: string;
|
|
1075
|
+
}>;
|
|
1076
|
+
/**
|
|
1077
|
+
* F6b: mints an entry's key below the organisation's own key without that
|
|
1078
|
+
* key's secret, matching `POST /v1/manage/organisations/:organisationId/
|
|
1079
|
+
* keys/:minterId/keys` (SDK `issueKeyBelow`, wtfalch/files#127).
|
|
1080
|
+
* `permissions` is a subset of the minter's, and files refuses an expiry
|
|
1081
|
+
* past the minter's. The secret comes back once; `filesEntryKey` hands it
|
|
1082
|
+
* to `bulkUpsertEnvs` and returns only the id.
|
|
1083
|
+
*/
|
|
1084
|
+
issueKeyBelow(organisationId: string, minterId: string, input: {
|
|
1085
|
+
name: string;
|
|
1086
|
+
kind: 'api_key' | 'agent' | 'service';
|
|
1087
|
+
permissions: string[];
|
|
1088
|
+
expiresDays: number;
|
|
1089
|
+
}): Promise<{
|
|
1090
|
+
id: string;
|
|
1091
|
+
secret: string;
|
|
1092
|
+
expiresAt: string;
|
|
1093
|
+
}>;
|
|
1094
|
+
/** F6b: revokes the key and every key it minted (SDK `revokeOrganisationKey`); a second call is a no-op. */
|
|
1095
|
+
revokeOrganisationKey(organisationId: string, keyId: string): Promise<{
|
|
1096
|
+
id: string;
|
|
1097
|
+
revoked: string[];
|
|
1098
|
+
}>;
|
|
1099
|
+
/** F6b: the organisation's collection of that name, created when missing (SDK `ensureCollection`, wtfalch/files#127). */
|
|
1100
|
+
ensureCollection(organisationId: string, name: string): Promise<{
|
|
1101
|
+
id: string;
|
|
1102
|
+
name: string;
|
|
1103
|
+
created: boolean;
|
|
1104
|
+
}>;
|
|
1025
1105
|
}
|
|
1026
1106
|
/**
|
|
1027
1107
|
* Provisions one org's organisation on AI and switches its AI use on (#80),
|
|
@@ -1077,6 +1157,45 @@ export interface AiOrganisationPort {
|
|
|
1077
1157
|
id: string;
|
|
1078
1158
|
secret: string;
|
|
1079
1159
|
}>;
|
|
1160
|
+
/**
|
|
1161
|
+
* F6a: mints an entry's key below the org's own key, matching ai's
|
|
1162
|
+
* `POST /v1/keys` (wtfalch/ai#162, SDK `mintKey`), which the management key
|
|
1163
|
+
* calls with `minter` set to `minterKeyId` (the recorded id of
|
|
1164
|
+
* `ai:key:<orgId>`). `permissions` is a subset of the minter's. ai clamps
|
|
1165
|
+
* the expiry to the minter's, so the returned `expiresAt` can be sooner
|
|
1166
|
+
* than `expiresDays`; `aiEntryKey` records that value. The child's issuer
|
|
1167
|
+
* is the minter, so revoking the org key reaches it. The secret comes back
|
|
1168
|
+
* once; `aiEntryKey` hands it to `bulkUpsertEnvs` and returns only the id.
|
|
1169
|
+
*/
|
|
1170
|
+
mintNestedKey(input: {
|
|
1171
|
+
organisationId: string;
|
|
1172
|
+
minterKeyId: string;
|
|
1173
|
+
name: string;
|
|
1174
|
+
expiresDays: number;
|
|
1175
|
+
permissions: string[];
|
|
1176
|
+
}): Promise<{
|
|
1177
|
+
organisationId: string;
|
|
1178
|
+
id: string;
|
|
1179
|
+
secret: string;
|
|
1180
|
+
expiresAt: string;
|
|
1181
|
+
}>;
|
|
1182
|
+
/**
|
|
1183
|
+
* F6a: registers `address` as the org's webhook, matching `PUT /v1/webhooks`
|
|
1184
|
+
* (`setWebhookAddress`). `secret` is present only when ai created the
|
|
1185
|
+
* webhook; an address change on an existing one returns none.
|
|
1186
|
+
*/
|
|
1187
|
+
setWebhookAddress(organisationId: string, address: string): Promise<{
|
|
1188
|
+
address: string;
|
|
1189
|
+
secret?: string;
|
|
1190
|
+
}>;
|
|
1191
|
+
/**
|
|
1192
|
+
* REVOKE SEAM (wtfalch/ai#157, A1b, not built): revokes one key and its
|
|
1193
|
+
* credential row. Optional until that route exists; while a port has none,
|
|
1194
|
+
* a replaced entry key stays live until it expires and its id is recorded
|
|
1195
|
+
* under `entryAiUnrevokedScopeKey` for the plan to list. Nothing else
|
|
1196
|
+
* depends on it.
|
|
1197
|
+
*/
|
|
1198
|
+
revokeKey?(organisationId: string, keyId: string): Promise<void>;
|
|
1080
1199
|
/**
|
|
1081
1200
|
* #27, #177: the org's own budget on AI, matching
|
|
1082
1201
|
* `ai/packages/sdk/src/client.ts:94-101`'s `updateBudget` (`UpdateBudget`
|
|
@@ -1207,8 +1326,8 @@ export interface FoundryStepExecutorPorts {
|
|
|
1207
1326
|
}
|
|
1208
1327
|
/**
|
|
1209
1328
|
* Thrown by name for a step this executor deliberately does not implement:
|
|
1210
|
-
* chat
|
|
1211
|
-
*
|
|
1329
|
+
* moving chat or the issuer onto or off the org's domain (chat's re-key is
|
|
1330
|
+
* not built; the issuer's is built, but not in v1 — domain verification and passkey
|
|
1212
1331
|
* invalidation need their own design). Never carries a token, secret or key
|
|
1213
1332
|
* (task brief: "errors carry the step and the provider's status only").
|
|
1214
1333
|
*/
|
|
@@ -1278,7 +1397,7 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1278
1397
|
* `plan/diff.ts`'s `planOwner`: invites the org's owner into its ZITADEL
|
|
1279
1398
|
* org by email. Never logs, returns or throws the email itself -- only the
|
|
1280
1399
|
* invited user id, handed back as `resourceId` so `runSteps` records it
|
|
1281
|
-
* under `zitadel:owner:<orgId>`, which is exactly what makes the next
|
|
1400
|
+
* under `zitadel:owner:<orgId>:<email>`, which is exactly what makes the next
|
|
1282
1401
|
* `plan()` call idempotent (`planOwner` plans nothing once that key is
|
|
1283
1402
|
* recorded).
|
|
1284
1403
|
*/
|
|
@@ -1441,9 +1560,9 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1441
1560
|
* `zitadel:issuer` (`AUTH_ORGANIZATION_ID`), `zitadel:owner`
|
|
1442
1561
|
* (`OWNER_USER_ID`, gap P3) and every `zitadel:app` step
|
|
1443
1562
|
* (`AUTH_CLIENT_ID`), all ordered before `coolify:app` for exactly this
|
|
1444
|
-
* reason. `AUTH_APP_KEY`
|
|
1445
|
-
*
|
|
1446
|
-
*
|
|
1563
|
+
* reason. `AUTH_APP_KEY` is pushed here too, for an entry that declares
|
|
1564
|
+
* `uses: [auth]` (`ensureAuthAppKey`); the host's `BrokerPort` decides which
|
|
1565
|
+
* broker the org's app registers with (#206). `DATABASE_URL_OWNER`/
|
|
1447
1566
|
* `DATABASE_URL` are pushed by `pushDatabaseUrl` instead, not here --
|
|
1448
1567
|
* that call runs only once the `app` entry's `database` is declared and
|
|
1449
1568
|
* provisioned, where this one is unconditional (#239).
|
|
@@ -1753,6 +1872,31 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1753
1872
|
* so an existing one is always re-written.
|
|
1754
1873
|
*/
|
|
1755
1874
|
private ensureCallsFileStorage;
|
|
1875
|
+
/**
|
|
1876
|
+
* Creates the `chat.admin` role on the org's messaging project and grants
|
|
1877
|
+
* it to each owner whose invite is recorded (`zitadel:owner:<orgId>:<email>`,
|
|
1878
|
+
* email lowercased).
|
|
1879
|
+
*/
|
|
1880
|
+
private ensureChatAdmins;
|
|
1881
|
+
/**
|
|
1882
|
+
* `plan/diff.ts`'s `planChat`: the org's chat stack at level 1 -- three
|
|
1883
|
+
* Coolify-managed Postgres databases (Synapse, MAS, chat), Synapse with the
|
|
1884
|
+
* `wtfalch_chat_gate` module and MAS (both with a config file this step
|
|
1885
|
+
* writes, `chat-matrix.ts`), and the chat service. All four apps are
|
|
1886
|
+
* internal: Agora is the one public origin and proxies to them
|
|
1887
|
+
* (`AGORA_CHAT_UPSTREAM`, `AGORA_MATRIX_UPSTREAM`, `AGORA_MAS_UPSTREAM`).
|
|
1888
|
+
* It also finds or creates the org's ZITADEL messaging project, the
|
|
1889
|
+
* audience the chat service requires, and records its id for Agora.
|
|
1890
|
+
*
|
|
1891
|
+
* Idempotent: every resource is found by name or recorded id before it is
|
|
1892
|
+
* created, minted secrets are read back from the MAS app's envs, and an app
|
|
1893
|
+
* is deployed when it is new, its config was freshly minted, or it is not running. A
|
|
1894
|
+
* recorded chat resource means the stack already exists, so a second
|
|
1895
|
+
* `coolify:chat` step for it is a re-key (onto the company domain, or back)
|
|
1896
|
+
* and neither is built: both are refused by name, before anything is touched.
|
|
1897
|
+
* No secret reaches an error: errors name the org and the missing input only.
|
|
1898
|
+
*/
|
|
1899
|
+
private coolifyChat;
|
|
1756
1900
|
/**
|
|
1757
1901
|
* `plan/diff.ts`'s `planCallsService`: the org's own calls service --
|
|
1758
1902
|
* `wtfalch/calls`'s `packages/service`, wired to the calls Matrix stack
|
|
@@ -1821,10 +1965,12 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1821
1965
|
* `AGORA_FILES_UPSTREAM` are pushed once the platform's own mail/files
|
|
1822
1966
|
* hosts are on and applied (#177's `platform-org.ts`'s
|
|
1823
1967
|
* `agoraHostValuesFromPlatform`, read by `plan-apply.ts` when no host
|
|
1824
|
-
* `agora` port overrides it) -- absent otherwise.
|
|
1825
|
-
*
|
|
1826
|
-
*
|
|
1827
|
-
*
|
|
1968
|
+
* `agora` port overrides it) -- absent otherwise. `AGORA_CHAT_UPSTREAM`,
|
|
1969
|
+
* `AGORA_MATRIX_UPSTREAM` and `AGORA_MAS_UPSTREAM` are pushed together once
|
|
1970
|
+
* chat is on and `coolifyChat` has recorded that its apps carry their
|
|
1971
|
+
* internal names (`CHAT_SERVICE_URL_SCOPE_KEY`); absent otherwise, and Agora
|
|
1972
|
+
* answers 502 on those routes rather than serving anything wrong.
|
|
1973
|
+
* `messagingProjectId` falls back to the project `coolifyChat` recorded. `AGORA_CALLS_UPSTREAM` is
|
|
1828
1974
|
* pushed once calls are on and this org's calls service app carries its
|
|
1829
1975
|
* internal name (`desiredAgoraEnv` reads that marker from the recorded ids,
|
|
1830
1976
|
* not the host values).
|
|
@@ -2018,7 +2164,7 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2018
2164
|
* when the org has no owner yet, when no address is declared yet
|
|
2019
2165
|
* (`plan/diff.ts`'s `planMail` reports that case as blocked, not planned,
|
|
2020
2166
|
* so this step's own mode/domain work still runs), or once
|
|
2021
|
-
*
|
|
2167
|
+
* `mail_owner_mailbox` is already recorded -- checked last
|
|
2022
2168
|
* so a second apply never calls `MailPort.createMailbox` again, rather
|
|
2023
2169
|
* than relying on that port method's own duplicate refusal.
|
|
2024
2170
|
*/
|
|
@@ -2045,13 +2191,32 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2045
2191
|
*/
|
|
2046
2192
|
private deleteStaleLettermintDomain;
|
|
2047
2193
|
/**
|
|
2048
|
-
* Upserts each DNS record `applyOrgMail` says foundry must publish
|
|
2049
|
-
*
|
|
2050
|
-
*
|
|
2051
|
-
*
|
|
2052
|
-
* mints the key on a background task; the next apply publishes it.
|
|
2194
|
+
* Upserts each DNS record `applyOrgMail` says foundry must publish through
|
|
2195
|
+
* {@link upsertMailDnsRecord}. A `DKIM` record can be absent on the first
|
|
2196
|
+
* call, since Stalwart mints the key on a background task; the next apply
|
|
2197
|
+
* publishes it.
|
|
2053
2198
|
*/
|
|
2054
2199
|
private publishMailDnsRecords;
|
|
2200
|
+
/**
|
|
2201
|
+
* Creates or updates one mail record (Stalwart's or Lettermint's).
|
|
2202
|
+
*
|
|
2203
|
+
* Always DNS-only: Cloudflare proxies only `A`, `AAAA` and `CNAME`, and a
|
|
2204
|
+
* proxied `CNAME` answers with Cloudflare's own addresses instead of its
|
|
2205
|
+
* target, so Lettermint's return-path and DKIM names would stop resolving
|
|
2206
|
+
* to Lettermint. `@wtfalch/providers`' `createDnsRecord` proxies by
|
|
2207
|
+
* default, so `proxied: false` is sent on every write, update included, so
|
|
2208
|
+
* a record someone proxied by hand is put back. An `MX` record carries the
|
|
2209
|
+
* priority it was given, which Cloudflare requires.
|
|
2210
|
+
*
|
|
2211
|
+
* Found by an exact name+type match (`CloudflarePort.findDnsRecord`'s own
|
|
2212
|
+
* doc comment) so a stale record of a different type is never taken for a
|
|
2213
|
+
* match. A name can hold several `TXT` records (an apex often holds a site
|
|
2214
|
+
* verification beside SPF), so a `TXT` record only replaces one that
|
|
2215
|
+
* starts with the same tag (`v=spf1`, `v=DKIM1`); with none, it is added
|
|
2216
|
+
* beside them. Before this, the first `TXT` on the name was overwritten,
|
|
2217
|
+
* whatever it was.
|
|
2218
|
+
*/
|
|
2219
|
+
private upsertMailDnsRecord;
|
|
2055
2220
|
/**
|
|
2056
2221
|
* Parking mail (2026-09-28 lettermint-org-mail swap, "Core rule": "Parking
|
|
2057
2222
|
* mail deletes the org's projects and then the domain itself, so no
|
|
@@ -2096,6 +2261,14 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2096
2261
|
* idempotent by find-or-create, not by checking `recordedIds` first.
|
|
2097
2262
|
*/
|
|
2098
2263
|
private filesOrganisation;
|
|
2264
|
+
/**
|
|
2265
|
+
* Files onboards Agora when it provisions an organisation with an owner and
|
|
2266
|
+
* returns Agora's service key once. Foundry holds no other files key for
|
|
2267
|
+
* Agora, so this is the only one: its secret goes straight to
|
|
2268
|
+
* `deliverSecret`, its id is recorded as undelivered first, and a delivery
|
|
2269
|
+
* that fails revokes the key again instead of leaving a live key nobody holds.
|
|
2270
|
+
*/
|
|
2271
|
+
private deliverFilesAgoraKey;
|
|
2099
2272
|
private filesStorage;
|
|
2100
2273
|
/**
|
|
2101
2274
|
* #80: provisions the org's own organisation on AI and switches its AI use
|
|
@@ -2116,6 +2289,61 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2116
2289
|
* way to check for an already-minted key.
|
|
2117
2290
|
*/
|
|
2118
2291
|
private aiKey;
|
|
2292
|
+
/**
|
|
2293
|
+
* F6a: mints, renews, finishes, re-writes or retires one entry's ai key.
|
|
2294
|
+
*
|
|
2295
|
+
* Order keeps the app on a valid key throughout: the new key is minted below
|
|
2296
|
+
* the org's key, its id is recorded and persisted as undelivered, the env is
|
|
2297
|
+
* written (up to three tries), that write is recorded, the app is restarted,
|
|
2298
|
+
* and only then is the old key revoked. A failed write keeps the old key in
|
|
2299
|
+
* use and the planner blocks instead of minting again. A failed restart
|
|
2300
|
+
* after the write plans `finish`, which restarts and records, minting
|
|
2301
|
+
* nothing. The webhook secret, which ai shows once, is written the same way
|
|
2302
|
+
* before the mint. A secret reaches `bulkUpsertEnvs` and nothing else:
|
|
2303
|
+
* errors name ids, never the cause, which can echo one.
|
|
2304
|
+
*/
|
|
2305
|
+
private aiEntryKey;
|
|
2306
|
+
/**
|
|
2307
|
+
* REVOKE SEAM (ai#157): revokes through `revokeKey` when the port has one.
|
|
2308
|
+
* Without it, or when it fails, the id joins the entry's list of keys left
|
|
2309
|
+
* live, which the plan shows, and the step goes on: the app already has its
|
|
2310
|
+
* new key.
|
|
2311
|
+
*/
|
|
2312
|
+
private revokeEntryKey;
|
|
2313
|
+
/**
|
|
2314
|
+
* F6b: mints or renews the org's own files key, or revokes the ones a
|
|
2315
|
+
* renewal replaced. The key is minted below the files organisation's owner
|
|
2316
|
+
* (the member `files_org_owner` records) and its one-time secret goes
|
|
2317
|
+
* straight to {@link FoundryStepExecutorPorts.deliverSecret}. The id is
|
|
2318
|
+
* recorded as undelivered first, so a delivery that fails never leads to a
|
|
2319
|
+
* second mint. Entries are minted below it with `issueKeyBelow`, which needs
|
|
2320
|
+
* no secret.
|
|
2321
|
+
*
|
|
2322
|
+
* A renewal keeps the old key live: its id goes to `files_key_previous`, the
|
|
2323
|
+
* entries are re-minted below the new key by their own steps, and
|
|
2324
|
+
* `revoke-previous` revokes the old key only when every entry holds a key
|
|
2325
|
+
* below the new one. A failed revoke is listed, not retried.
|
|
2326
|
+
*/
|
|
2327
|
+
private filesRootKey;
|
|
2328
|
+
/**
|
|
2329
|
+
* F6b: mints, renews, finishes, re-writes or retires one entry's files key.
|
|
2330
|
+
*
|
|
2331
|
+
* Same order as the ai entry key: the collections are ensured first (they
|
|
2332
|
+
* hold no secret and can be repeated), the new key is minted below the
|
|
2333
|
+
* org's own key, its id is recorded and persisted as undelivered, the env
|
|
2334
|
+
* is written (up to three tries), that write is recorded, the app is
|
|
2335
|
+
* restarted, and only then is the old key revoked. A failed write keeps the
|
|
2336
|
+
* old key in use and the planner blocks instead of minting again. A failed
|
|
2337
|
+
* restart after the write plans `finish`, which restarts and records,
|
|
2338
|
+
* minting nothing. A secret reaches `bulkUpsertEnvs` and nothing else.
|
|
2339
|
+
*/
|
|
2340
|
+
private filesEntryKey;
|
|
2341
|
+
/**
|
|
2342
|
+
* Revokes one entry key. When the call fails the id joins the entry's list
|
|
2343
|
+
* of keys left live, which the plan shows, and the step goes on: the app
|
|
2344
|
+
* already has its new key.
|
|
2345
|
+
*/
|
|
2346
|
+
private revokeFilesEntryKey;
|
|
2119
2347
|
/**
|
|
2120
2348
|
* {@link StepDestroyer.destroy}: deletes the one resource
|
|
2121
2349
|
* `FoundryPlanApply.destroy` names. A provider 404 (already gone) counts as
|