@kici-dev/orchestrator 0.10.0 → 0.12.0

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 (141) hide show
  1. package/dist/__test-helpers__/mock-db-query.d.ts +4 -5
  2. package/dist/agent/agent-version.d.ts +5 -5
  3. package/dist/agent/dispatcher.d.ts +26 -0
  4. package/dist/agent/registry.d.ts +7 -9
  5. package/dist/agent-packaging/node-runtime-members.d.ts +14 -0
  6. package/dist/app.d.ts +3 -3
  7. package/dist/approvals/apply-decision.d.ts +1 -1
  8. package/dist/audit/access-log.d.ts +2 -3
  9. package/dist/cli/api-client.d.ts +20 -1
  10. package/dist/cli/commands/access-log.d.ts +3 -3
  11. package/dist/cli/commands/agent-service/agent-env.d.ts +20 -0
  12. package/dist/cli/commands/agent-service/status.d.ts +3 -3
  13. package/dist/cli/commands/attestations.d.ts +10 -0
  14. package/dist/cli/commands/cold-store-impl.d.ts +3 -3
  15. package/dist/cli/commands/cold-store.d.ts +6 -3
  16. package/dist/cli/commands/context.d.ts +5 -3
  17. package/dist/cli/commands/dashboard-encryption-key.d.ts +18 -0
  18. package/dist/cli/commands/event-dlq.d.ts +5 -6
  19. package/dist/cli/commands/event-log.d.ts +2 -3
  20. package/dist/cli/commands/execution.d.ts +1 -3
  21. package/dist/cli/commands/orchestrator-service/status.d.ts +10 -3
  22. package/dist/cli/commands/queue.d.ts +1 -2
  23. package/dist/cli/commands/registration.d.ts +8 -4
  24. package/dist/cli/commands/runs.d.ts +2 -3
  25. package/dist/cli/commands/secret.d.ts +1 -0
  26. package/dist/cli/commands/service-health.d.ts +68 -0
  27. package/dist/cli/commands/shared/confirm.d.ts +19 -0
  28. package/dist/cli/commands/shared/env-file-mode.d.ts +3 -2
  29. package/dist/cli/commands/shared/upgrade-safety.d.ts +11 -5
  30. package/dist/cli/commands/shared/versioned-upgrade.d.ts +119 -7
  31. package/dist/cli/commands/workflow.d.ts +3 -4
  32. package/dist/cli/open-browser.d.ts +1 -1
  33. package/dist/cli/service/entrypoint.d.ts +18 -0
  34. package/dist/cli/service/image-digests.d.ts +10 -0
  35. package/dist/cli/service/index.d.ts +4 -2
  36. package/dist/cli/service/instance/manifest.d.ts +39 -0
  37. package/dist/cli/service/launchd.d.ts +45 -10
  38. package/dist/cli/service/shutdown-grace.d.ts +35 -0
  39. package/dist/cli/service/types.d.ts +9 -0
  40. package/dist/cli/service/windows-acl.d.ts +40 -0
  41. package/dist/cli/service/windows-env-file.d.ts +45 -0
  42. package/dist/cli/service/windows.d.ts +13 -0
  43. package/dist/cli.js +1869 -707
  44. package/dist/cluster/cluster-identity.d.ts +4 -4
  45. package/dist/cluster/join-token.d.ts +2 -2
  46. package/dist/cluster/leader-gated-scheduler.d.ts +7 -1
  47. package/dist/cluster/peer-credentials.d.ts +1 -1
  48. package/dist/cluster/raft-state.d.ts +6 -0
  49. package/dist/cluster/raft.d.ts +16 -2
  50. package/dist/cold-store/load-access-log-range.d.ts +3 -3
  51. package/dist/cold-store/load-event-log-range.d.ts +1 -1
  52. package/dist/cold-store/load-secret-audit-log-range.d.ts +2 -2
  53. package/dist/cold-store/orchestrator-cold-store.d.ts +9 -11
  54. package/dist/cold-store/tables/access-log.d.ts +2 -2
  55. package/dist/cold-store/tables/event-log.d.ts +4 -10
  56. package/dist/cold-store/tables/execution-jobs.d.ts +1 -1
  57. package/dist/cold-store/tables/execution-runs.d.ts +4 -4
  58. package/dist/cold-store/tables/execution-steps.d.ts +1 -1
  59. package/dist/cold-store/tables/secret-audit-log.d.ts +3 -3
  60. package/dist/config.d.ts +3 -1
  61. package/dist/contexts/held-runs.d.ts +2 -2
  62. package/dist/contexts/variable-store.d.ts +2 -0
  63. package/dist/dashboard/attestation-filters.d.ts +1 -1
  64. package/dist/dashboard/handler.d.ts +8 -8
  65. package/dist/db/deferred-indexes.d.ts +2 -2
  66. package/dist/db/migrations/005_cold_store_chunk_counter.d.ts +2 -4
  67. package/dist/db/migrations/006_runs_jobs_steps_archived_at.d.ts +4 -4
  68. package/dist/db/migrations/007_audit_logs_archived_at.d.ts +6 -9
  69. package/dist/db/migrations/008_event_log_archived_at.d.ts +4 -6
  70. package/dist/db/migrations/010_cold_store_chunks.d.ts +2 -2
  71. package/dist/db/migrations/012_peer_credentials_active_uniq.d.ts +2 -4
  72. package/dist/db/migrations/016_org_settings_allow_http_npm.d.ts +2 -2
  73. package/dist/db/migrations/045_host_properties.d.ts +1 -1
  74. package/dist/db/migrations/046_join_token_consumed_by_instance.d.ts +1 -1
  75. package/dist/db/migrations/048_host_reboot_pending.d.ts +1 -1
  76. package/dist/db/migrations/049_held_runs_payload.d.ts +1 -1
  77. package/dist/db/migrations/050_sources_slug.d.ts +1 -1
  78. package/dist/db/migrations/051_binding_host_pattern.d.ts +1 -1
  79. package/dist/db/migrations/052_host_reach_metadata.d.ts +1 -1
  80. package/dist/db/migrations/053_agent_token_single_use.d.ts +1 -1
  81. package/dist/db/migrations/054_local_working_tree.d.ts +1 -1
  82. package/dist/db/migrations/055_agent_token_mandatory_labels.d.ts +1 -1
  83. package/dist/db/migrations/056_execution_jobs_environments.d.ts +1 -1
  84. package/dist/db/migrations/064_execution_jobs_skipped_environments.d.ts +1 -1
  85. package/dist/db/types.d.ts +12 -13
  86. package/dist/events/types.d.ts +1 -1
  87. package/dist/fault-injection-types.d.ts +2 -2
  88. package/dist/firecracker/host-network.d.ts +21 -3
  89. package/dist/firecracker/net-interfaces.d.ts +1 -1
  90. package/dist/helpers/run-detached.d.ts +12 -0
  91. package/dist/index.js +76 -14
  92. package/dist/oidc/db-signer.d.ts +1 -1
  93. package/dist/pipeline/global-eval-round.d.ts +2 -2
  94. package/dist/pipeline/processor.d.ts +2 -2
  95. package/dist/provenance/retrier-mint.d.ts +1 -1
  96. package/dist/provenance/trust-root.d.ts +3 -3
  97. package/dist/providers/universal-git/clone-token.d.ts +9 -13
  98. package/dist/providers/universal-git/index.d.ts +1 -2
  99. package/dist/providers/universal-git/normalizer.d.ts +2 -3
  100. package/dist/providers/universal-git/ssh-auth.d.ts +4 -4
  101. package/dist/queue/cleanup.d.ts +2 -2
  102. package/dist/queue/job-queue.d.ts +6 -6
  103. package/dist/queue/scheduled-job.d.ts +5 -7
  104. package/dist/registration/extractor.d.ts +1 -1
  105. package/dist/registration/registration-index.d.ts +1 -1
  106. package/dist/reporting/execution-tracker.d.ts +3 -3
  107. package/dist/routes/admin-access-log.d.ts +1 -1
  108. package/dist/routes/admin-auth.d.ts +2 -2
  109. package/dist/routes/admin-contexts.d.ts +6 -0
  110. package/dist/routes/admin-event-log.d.ts +2 -2
  111. package/dist/routes/admin-registrations.d.ts +2 -2
  112. package/dist/routes/admin-runs.d.ts +3 -3
  113. package/dist/routes/admin-scheduled-jobs.d.ts +2 -3
  114. package/dist/routes/health.d.ts +9 -0
  115. package/dist/scaler/container-backend.d.ts +1 -1
  116. package/dist/scaler/firecracker-backend.d.ts +15 -10
  117. package/dist/scaler/firecracker-extra-hosts.d.ts +20 -0
  118. package/dist/scaler/machine-ledger.d.ts +3 -4
  119. package/dist/scaler/scaler-state-store.d.ts +1 -1
  120. package/dist/scaler/types.d.ts +4 -1
  121. package/dist/secrets/audit-logger.d.ts +3 -3
  122. package/dist/security/reduced-privilege-note.d.ts +2 -3
  123. package/dist/security/trust-policy-store.d.ts +3 -3
  124. package/dist/server.d.ts +2 -1
  125. package/dist/server.js +2885 -2211
  126. package/dist/stale-detector/stale-run-detector.d.ts +1 -1
  127. package/dist/standalone.d.ts +1 -1
  128. package/dist/standalone.js +2254 -1629
  129. package/dist/storage/filesystem.d.ts +1 -1
  130. package/dist/storage/index.d.ts +1 -1
  131. package/dist/storage/sign-url.d.ts +2 -1
  132. package/dist/storage/types.d.ts +2 -2
  133. package/dist/ws/agent-handler.d.ts +34 -3
  134. package/dist/ws/agent-heartbeat.d.ts +2 -3
  135. package/dist/ws/dashboard-context-handler.d.ts +1 -1
  136. package/dist/ws/platform-client.d.ts +44 -32
  137. package/dist/ws/server-options.d.ts +1 -1
  138. package/dist/ws/test-relay-handlers.d.ts +1 -1
  139. package/installer-image-digests.json +3 -3
  140. package/package.json +4 -4
  141. package/sbom.spdx.json +53 -53
@@ -10,7 +10,7 @@ import { type Kysely } from 'kysely';
10
10
  * join token already in its env — no operator action, no cluster redeploy.
11
11
  *
12
12
  * Idempotent (`ADD COLUMN IF NOT EXISTS`): re-running on a DB that already has
13
- * the column is a no-op. Staging data is preserved (additive nullable column).
13
+ * the column is a no-op. Existing data is preserved (additive nullable column).
14
14
  */
15
15
  export declare function up(db: Kysely<unknown>): Promise<void>;
16
16
  export declare function down(db: Kysely<unknown>): Promise<void>;
@@ -11,7 +11,7 @@ import { type Kysely } from 'kysely';
11
11
  * (3) clears on the next reconnect (down-then-up), releasing the held job. NULL
12
12
  * = no reboot pending.
13
13
  *
14
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
14
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so existing data is
15
15
  * preserved.
16
16
  */
17
17
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -6,7 +6,7 @@ import { type Kysely } from 'kysely';
6
6
  * the dashboard approval queue and the CLI render the computed diff the
7
7
  * operator approves. NULL for every non-drift hold.
8
8
  *
9
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
9
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so existing data is
10
10
  * preserved.
11
11
  */
12
12
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -9,7 +9,7 @@ import { type Kysely } from 'kysely';
9
9
  * hasn't populated it yet (manual `--app-id` flow whose initial fetch failed,
10
10
  * or a row created before the rollout).
11
11
  *
12
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
12
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so existing data is
13
13
  * preserved.
14
14
  */
15
15
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -11,7 +11,7 @@ import { type Kysely } from 'kysely';
11
11
  * distinct per-host selectors.
12
12
  *
13
13
  * Idempotent: re-running on a DB that already has the column / index is a no-op.
14
- * Additive — staging data is preserved.
14
+ * Additive — existing data is preserved.
15
15
  */
16
16
  export declare function up(db: Kysely<unknown>): Promise<void>;
17
17
  export declare function down(db: Kysely<unknown>): Promise<void>;
@@ -12,7 +12,7 @@ import { type Kysely } from 'kysely';
12
12
  *
13
13
  * All nullable: a host with no reach metadata simply cannot be bootstrapped and
14
14
  * behaves exactly as before. Idempotent (`ADD COLUMN IF NOT EXISTS`); additive,
15
- * so staging data is preserved.
15
+ * so existing data is preserved.
16
16
  */
17
17
  export declare function up(db: Kysely<unknown>): Promise<void>;
18
18
  export declare function down(db: Kysely<unknown>): Promise<void>;
@@ -9,7 +9,7 @@ import { type Kysely } from 'kysely';
9
9
  * default for every existing static / ephemeral token, which stay reusable
10
10
  * until expiry).
11
11
  *
12
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
12
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so existing data is
13
13
  * preserved.
14
14
  */
15
15
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -7,7 +7,7 @@ import { type Kysely } from 'kysely';
7
7
  * lock file). The dashboard renders a "Local machine" badge for these runs
8
8
  * and avoids building an external repository link from the repo identifier.
9
9
  *
10
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive with a default, so staging
10
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive with a default, so existing
11
11
  * data is preserved.
12
12
  */
13
13
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -10,7 +10,7 @@ import { type Kysely } from 'kysely';
10
10
  * agent accepts any job its advertised labels match), so every existing token
11
11
  * stays unconfined until re-minted.
12
12
  *
13
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
13
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so existing data is
14
14
  * preserved.
15
15
  */
16
16
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -9,7 +9,7 @@ import { type Kysely } from 'kysely';
9
9
  * fully-resolved list when a deferred-init agent eval resolves dynamic
10
10
  * elements. NULL = the job binds no environment.
11
11
  *
12
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
12
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so existing data is
13
13
  * preserved.
14
14
  */
15
15
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -9,7 +9,7 @@ import { type Kysely } from 'kysely';
9
9
  * - `execution_jobs.env_warning text NULL` — the user-visible warning naming the
10
10
  * skipped environments, surfaced on the dashboard run view. NULL = no warning.
11
11
  *
12
- * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so staging data is
12
+ * Idempotent (`ADD COLUMN IF NOT EXISTS`); additive, so existing data is
13
13
  * preserved.
14
14
  */
15
15
  export declare function up(db: Kysely<unknown>): Promise<void>;
@@ -714,8 +714,8 @@ export interface ExecutionRunTable {
714
714
  check_mode: string | null;
715
715
  /**
716
716
  * Set inside the cold-store archive transaction before the row is
717
- * DELETEd. Survivors carry NULL. Exists so a future
718
- * "promote-chunk-back-into-PG" path (Phase F) can restore rows with
717
+ * DELETEd. Survivors carry NULL. Exists so the
718
+ * "promote-chunk-back-into-PG" path can restore rows with
719
719
  * their original archive pointer.
720
720
  */
721
721
  archived_at: Date | null;
@@ -1301,7 +1301,7 @@ export interface SecretAuditLogTable {
1301
1301
  metadata: string | null;
1302
1302
  /**
1303
1303
  * Set inside the archive transaction before the row is DELETEd.
1304
- * Survivors carry NULL. See cold-storage Phase D notes.
1304
+ * Survivors carry NULL.
1305
1305
  */
1306
1306
  archived_at: Date | null;
1307
1307
  /** S3 object key of the chunk that carried this row; see `archived_at`. */
@@ -1338,9 +1338,8 @@ export interface AccessLogTable {
1338
1338
  created_at: Generated<Date>;
1339
1339
  /**
1340
1340
  * Set inside the archive transaction before the row is DELETEd.
1341
- * Survivors carry NULL. See cold-storage Phase D notes.
1342
- * Phase D removed the previous `expires_at`-based TTL — rows older
1343
- * than 30 days are archived to S3 instead of hard-deleted.
1341
+ * Survivors carry NULL. The table has no `expires_at`-based TTL — rows
1342
+ * older than 30 days are archived to S3 instead of hard-deleted.
1344
1343
  */
1345
1344
  archived_at: Date | null;
1346
1345
  /** S3 object key of the chunk that carried this row; see `archived_at`. */
@@ -2403,9 +2402,9 @@ export interface EventLogTable {
2403
2402
  error_message: string | null;
2404
2403
  /** When the delivery was received */
2405
2404
  received_at: Generated<Date>;
2406
- /** Phase E cold-store: set inside the archive transaction; NULL for hot rows. */
2405
+ /** Cold-store: set inside the archive transaction; NULL for hot rows. */
2407
2406
  archived_at: Date | null;
2408
- /** Phase E cold-store: chunk object key when row is archived. */
2407
+ /** Cold-store: chunk object key when row is archived. */
2409
2408
  archive_object_key: string | null;
2410
2409
  }
2411
2410
  export type EventLogRow = Selectable<EventLogTable>;
@@ -2415,8 +2414,8 @@ export type EventLogRowUpdate = Updateable<EventLogTable>;
2415
2414
  * Cold-store chunk counts table (cold_store_chunk_counts).
2416
2415
  * Tracks per-(db, table, tenant) archived-chunk metadata so the
2417
2416
  * `cold_store_chunks_total` gauge and the `kici-admin cold-store list-chunks`
2418
- * CLI can report totals without S3 LIST calls. Phase A creates the table
2419
- * empty; Phase B+ populates it transactionally on each chunk write.
2417
+ * CLI can report totals without S3 LIST calls. The cold-store archiver
2418
+ * populates it transactionally on each chunk write.
2420
2419
  */
2421
2420
  export interface ColdStoreChunkCountsTable {
2422
2421
  /** DbKind identifier ('orchestrator' on this side). */
@@ -2438,12 +2437,12 @@ export type ColdStoreChunkCountsRow = Selectable<ColdStoreChunkCountsTable>;
2438
2437
  export type NewColdStoreChunkCountsRow = Insertable<ColdStoreChunkCountsTable>;
2439
2438
  export type ColdStoreChunkCountsUpdate = Updateable<ColdStoreChunkCountsTable>;
2440
2439
  /**
2441
- * Cold-store chunk index (cold_store_chunks). Phase 2.
2440
+ * Cold-store chunk index (cold_store_chunks).
2442
2441
  *
2443
2442
  * One row per archived chunk that the GC sweep can later purge from S3.
2444
2443
  * Inserted inside `markArchivedAndDelete`'s transaction by adapters that
2445
- * opt into the per-bucket layout via `coldTtlDays(row)`. Pre-Phase-2
2446
- * (v1) chunks are NOT in this table — they're treated as
2444
+ * opt into the per-bucket layout via `coldTtlDays(row)`. V1
2445
+ * chunks are NOT in this table — they're treated as
2447
2446
  * `'forever'` and never purged.
2448
2447
  */
2449
2448
  export interface ColdStoreChunksTable {
@@ -31,7 +31,7 @@ export interface EventRouterConfig {
31
31
  /**
32
32
  * **Test-only.** Per-event-name fault injection: when `attempts <= N`,
33
33
  * the EventRouter throws a synthetic dispatch error to drive the retry /
34
- * DLQ path. Used by the fault-injection E2E to prove the lease + retry
34
+ * DLQ path. Used by fault-injection tests to prove the lease + retry
35
35
  * loop dispatches a real run when the inner dispatch eventually
36
36
  * succeeds, and lands the row in the DLQ when N exceeds
37
37
  * `maxDispatchAttempts`.
@@ -29,7 +29,7 @@ export interface OrchestratorFaultInjection {
29
29
  skipS3Sentinel?: boolean;
30
30
  /**
31
31
  * Predicate over an OIDC `audience`: when true, the *initial* provenance
32
- * mint fails transiently (defer), so an E2E can exercise the
32
+ * mint fails transiently (defer), so a test can exercise the
33
33
  * deferred-attestation retry + per-run serve path.
34
34
  */
35
35
  initialMintFault?: (audience: string) => boolean;
@@ -40,7 +40,7 @@ export interface OrchestratorFaultInjection {
40
40
  */
41
41
  remintReject?: (audience: string) => boolean;
42
42
  /**
43
- * Invoked by `handleRerunRequest` before `onRerun`, so an HA E2E can make
43
+ * Invoked by `handleRerunRequest` before `onRerun`, so an HA test can make
44
44
  * the first coordinator slow enough that the Platform relay fails over.
45
45
  */
46
46
  beforeRerun?: () => Promise<void>;
@@ -43,9 +43,9 @@ export declare function cidrToNetwork(cidr: string): string;
43
43
  * serializes them.
44
44
  *
45
45
  * The command list NEVER deletes the table. It is a self-heal — `ensureHostReady`
46
- * runs it whenever `verifyBridge` reports unhealthy, which happens on any of
47
- * four conditions none of which knows how many VMs are live — so dropping the
48
- * table would leave every running VM fail-open, with full RFC1918 and
46
+ * runs it whenever `verifyBridge` reports unhealthy, and none of the
47
+ * conditions it checks says how many VMs are live — so dropping the table
48
+ * would leave every running VM fail-open, with full RFC1918 and
49
49
  * cloud-metadata reach, until it was destroyed. Instead the baseline lives in
50
50
  * its own regular chain (`baseline`, see {@link BASELINE_CHAIN}) that is flushed
51
51
  * and refilled, and the hooked `forward` chain keeps its per-VM rules
@@ -80,6 +80,10 @@ export interface BridgeHealth {
80
80
  tablePresent: boolean;
81
81
  /** The regular baseline chain exists and `forward` ends in a jump to it. */
82
82
  baselineChainPresent: boolean;
83
+ /** The table's `postrouting` chain masquerades the bridge subnet — the guests' internet egress. */
84
+ natPresent: boolean;
85
+ /** The baseline chain holds the bridge subnet's rules, in the order the provisioner writes them. */
86
+ baselineRulesPresent: boolean;
83
87
  /** Every enslaved kici-* TAP carries bridge port isolation. */
84
88
  tapIsolationPresent: boolean;
85
89
  healthy: boolean;
@@ -125,6 +129,20 @@ export declare function verifyBridge(cfg: FirecrackerBridgeConfig, opts?: ExecOp
125
129
  * `denyAll` drop.
126
130
  */
127
131
  export declare function forwardEndsInBaselineJump(nftJson: string): boolean;
132
+ /**
133
+ * True when a `nft -j list chain … postrouting` listing masquerades traffic
134
+ * whose source is `subnet` (the network CIDR, e.g. `10.0.0.0/24`).
135
+ */
136
+ export declare function postroutingMasqueradesSubnet(nftJson: string, subnet: string): boolean;
137
+ /**
138
+ * True when a `nft -j list chain … baseline` listing holds this bridge's
139
+ * subnet-scoped accepts and drops exactly as {@link buildBridgeCommands} writes
140
+ * them, in the same order.
141
+ *
142
+ * Order is part of the check: the internet accept is terminal, so ahead of the
143
+ * RFC1918 and metadata drops it would let the guests reach every private range.
144
+ */
145
+ export declare function baselineHoldsBridgeRules(nftJson: string, cfg: FirecrackerBridgeConfig): boolean;
128
146
  /**
129
147
  * Names of the kici-* TAPs enslaved to the bridge that are NOT port-isolated,
130
148
  * read from `ip -d -j link show master <bridge>`.
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * A leaf module on purpose: it imports nothing, so an out-of-package consumer
6
6
  * can read the constant without pulling the orchestrator runtime graph in with
7
- * it. The NetworkManager drift watchdog (`hack/lib/nm-watchdog.ts`) derives its
7
+ * it. A NetworkManager drift watchdog derives its
8
8
  * expected-pattern set from here rather than re-typing the patterns — a second
9
9
  * copy desyncs silently the moment a pattern is added. `host-network.ts`
10
10
  * re-exports it and renders `NM_CONF_CONTENT` from it, so the drop-in and the
@@ -0,0 +1,12 @@
1
+ /** The part of a logger that `runDetached` writes a failure to. */
2
+ export interface DetachedFailureLogger {
3
+ error(message: string, meta?: Record<string, unknown>): unknown;
4
+ }
5
+ /**
6
+ * Run `work` without waiting for it. A throw, or a rejection of the promise it
7
+ * returns, is logged at error level as `<task> failed` with `context` and the
8
+ * error message, and goes no further. `context` is read when the failure is
9
+ * logged, so the work may fill it in while it runs.
10
+ */
11
+ export declare function runDetached(logger: DetachedFailureLogger, task: string, work: () => unknown, context?: Record<string, unknown>): void;
12
+ //# sourceMappingURL=run-detached.d.ts.map
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import "node:module";
2
2
  import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand } from "@aws-sdk/client-s3";
3
3
  import { Upload } from "@aws-sdk/lib-storage";
4
4
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
5
- import { createLogger, createS3Client, sha256 } from "@kici-dev/shared";
5
+ import { createLogger, createS3Client, sha256, toErrorMessage } from "@kici-dev/shared";
6
6
  import * as fs from "node:fs";
7
7
  import { mkdtempSync, promises, writeFileSync } from "node:fs";
8
8
  import { dirname, join } from "node:path";
@@ -299,7 +299,8 @@ var S3CacheStorage = class {
299
299
  * Signature input: `${method}:${key}:${expiresUnixMs}` with HMAC-SHA256 keyed
300
300
  * by a per-orchestrator-process secret (random 32-byte hex at boot, persists
301
301
  * for the lifetime of the process). Tokens become invalid on orchestrator
302
- * restart — fine for the filesystem backend's E2E / single-host use case.
302
+ * restart — fine for the filesystem backend's single-host / test-sandbox use
303
+ * case.
303
304
  */
304
305
  /** Default URL lifetime: 1 hour (matches S3 backend's PUT-URL lifetime). */
305
306
  const DEFAULT_SIGN_URL_TTL_MS = 36e5;
@@ -328,7 +329,7 @@ function signToken(secret, method, key, ttlMs = DEFAULT_SIGN_URL_TTL_MS) {
328
329
  * are replaced by HMAC-signed `http://<orchUrl>/api/v1/cache/blob/...` URLs
329
330
  * — see `sign-url.ts` for the token mechanics and `app.ts` for the route.
330
331
  *
331
- * Intended for single-host deployments and E2E sandboxes where standing up
332
+ * Intended for single-host deployments and test sandboxes where standing up
332
333
  * an S3-compatible service is overkill. Production should still use S3.
333
334
  *
334
335
  * Metadata is co-located as a sibling JSON file (`<key>.meta.json`) so the
@@ -1461,7 +1462,7 @@ async function readCredentialFile(filePath) {
1461
1462
  * store plus a disposer that closes the underlying pool. Lets tests
1462
1463
  * exercise the real Kysely-backed store without importing pg/kysely.
1463
1464
  *
1464
- * Intended for e2e coverage of the store's own CRUD methods — production
1465
+ * Intended for tests of the store's own CRUD methods — production
1465
1466
  * code constructs the store with an already-allocated Kysely instance
1466
1467
  * shared with the orchestrator.
1467
1468
  */
@@ -1479,6 +1480,41 @@ async function createPeerCredentialStoreFromUrl(databaseUrl, opts) {
1479
1480
  };
1480
1481
  }
1481
1482
  //#endregion
1483
+ //#region src/helpers/run-detached.ts
1484
+ /**
1485
+ * Running work that nothing awaits.
1486
+ *
1487
+ * A promise that nobody awaits and nobody catches becomes an unhandled
1488
+ * rejection when it fails, and the orchestrator's shutdown hook treats an
1489
+ * unhandled rejection as fatal: one failed query in a timer tick or a message
1490
+ * handler would stop the whole process. `runDetached` runs such work and logs
1491
+ * its failure instead. ESLint's `no-floating-promises` (with `ignoreVoid:
1492
+ * false`) refuses a bare or `void`-ed promise anywhere in this package, so
1493
+ * detached work runs through here or carries its own `.catch`.
1494
+ */
1495
+ /**
1496
+ * Run `work` without waiting for it. A throw, or a rejection of the promise it
1497
+ * returns, is logged at error level as `<task> failed` with `context` and the
1498
+ * error message, and goes no further. `context` is read when the failure is
1499
+ * logged, so the work may fill it in while it runs.
1500
+ */
1501
+ function runDetached(logger, task, work, context = {}) {
1502
+ const fail = (err) => {
1503
+ logger.error(`${task} failed`, {
1504
+ ...context,
1505
+ error: toErrorMessage(err)
1506
+ });
1507
+ };
1508
+ let outcome;
1509
+ try {
1510
+ outcome = work();
1511
+ } catch (err) {
1512
+ fail(err);
1513
+ return;
1514
+ }
1515
+ Promise.resolve(outcome).catch(fail);
1516
+ }
1517
+ //#endregion
1482
1518
  //#region src/cluster/peer-auth-coordinator.ts
1483
1519
  /**
1484
1520
  * Per-orchestrator coordinator that owns the shared peer credential file.
@@ -1566,11 +1602,11 @@ var PeerAuthCoordinator = class {
1566
1602
  }
1567
1603
  makeComplete(join) {
1568
1604
  return (issued) => {
1569
- this.withLock(async () => {
1605
+ runDetached(logger, "Peer token-join completion", () => this.withLock(async () => {
1570
1606
  if (issued) await writeCredentialFile(this.credentialFile, issued);
1571
1607
  if (this.inFlightJoin === join) this.inFlightJoin = null;
1572
1608
  join.resolve(issued);
1573
- });
1609
+ }), { instanceId: this.instanceId });
1574
1610
  };
1575
1611
  }
1576
1612
  async awaitInFlightJoin() {
@@ -1737,8 +1773,8 @@ var JoinTokenManager = class {
1737
1773
  /**
1738
1774
  * Build a JoinTokenManager backed by its own connection pool to the given
1739
1775
  * orchestrator database URL. Mirrors `createPeerCredentialStoreFromUrl`;
1740
- * consumed by E2E tests that need to exercise token validation/reuse against
1741
- * the real cluster DB.
1776
+ * used by tests that need to exercise token validation/reuse against a real
1777
+ * cluster DB.
1742
1778
  */
1743
1779
  function createJoinTokenManagerFromUrl(databaseUrl, opts) {
1744
1780
  const pool = new pg.Pool({
@@ -2137,8 +2173,8 @@ var HeldRunStore = class {
2137
2173
  /**
2138
2174
  * Build a HeldRunStore backed by its own connection pool to the given
2139
2175
  * orchestrator database URL. Mirrors `createPeerCredentialStoreFromUrl` /
2140
- * `createJoinTokenManagerFromUrl`; consumed by E2E tests that exercise the
2141
- * PR-scoped hold selection against the real deployed orchestrator DB.
2176
+ * `createJoinTokenManagerFromUrl`; used by tests that exercise the PR-scoped
2177
+ * hold selection against a real orchestrator DB.
2142
2178
  */
2143
2179
  function createHeldRunStoreFromUrl(databaseUrl, opts) {
2144
2180
  const pool = new pg.Pool({
@@ -2303,9 +2339,9 @@ var TrustPolicyStore = class {
2303
2339
  };
2304
2340
  /**
2305
2341
  * Build a TrustPolicyStore backed by its own connection pool to the given
2306
- * orchestrator database URL. Mirrors `createHeldRunStoreFromUrl`; consumed by
2307
- * E2E tests that assert the pushed policy is persisted in the real deployed
2308
- * orchestrator DB rather than held in process memory.
2342
+ * orchestrator database URL. Mirrors `createHeldRunStoreFromUrl`; exported for
2343
+ * tests that assert the pushed policy is persisted in a real orchestrator DB
2344
+ * rather than held in process memory.
2309
2345
  */
2310
2346
  function createTrustPolicyStoreFromUrl(databaseUrl, opts) {
2311
2347
  const pool = new pg.Pool({
@@ -2509,6 +2545,32 @@ var AdminApiClient = class {
2509
2545
  return this.request("DELETE", `/api/v1/admin/secrets/${encodeURIComponent(orgId)}/${encodeURIComponent(scope)}/${encodeURIComponent(key)}`);
2510
2546
  }
2511
2547
  /**
2548
+ * Create an empty secret scope. A `<backend>:` qualifier selects the
2549
+ * backend; an unqualified scope targets the PG backend. Creating a scope
2550
+ * that already exists succeeds and changes nothing.
2551
+ */
2552
+ async createScope(orgId, scope) {
2553
+ return this.request("POST", "/api/v1/admin/secrets/scopes", {
2554
+ orgId,
2555
+ scope
2556
+ });
2557
+ }
2558
+ /**
2559
+ * Rename a secret scope inside its backend. The route refuses a move between
2560
+ * backends and a rename onto a scope that already exists.
2561
+ */
2562
+ async renameScope(orgId, oldScope, newScope) {
2563
+ return this.request("PUT", "/api/v1/admin/secrets/scopes/rename", {
2564
+ orgId,
2565
+ oldScope,
2566
+ newScope
2567
+ });
2568
+ }
2569
+ /** Delete a secret scope and every secret in it. */
2570
+ async deleteScope(orgId, scope) {
2571
+ return this.request("DELETE", `/api/v1/admin/secrets/scopes/${encodeURIComponent(orgId)}/${encodeURIComponent(scope)}`);
2572
+ }
2573
+ /**
2512
2574
  * Create (or upsert) a deployment context. `allowLocalExecution` lets
2513
2575
  * CLI-initiated / local runs resolve secrets through this context.
2514
2576
  */
@@ -3081,7 +3143,7 @@ function startManifestLoopback(opts) {
3081
3143
  * blocked launcher) just falls back to copy-paste.
3082
3144
  *
3083
3145
  * `KICI_BROWSER_CMD` overrides the launcher: `none` suppresses it entirely
3084
- * (E2E / headless capture), any other value is run with `{url}` substituted —
3146
+ * (automated tests / headless capture), any other value is run with `{url}` substituted —
3085
3147
  * mirroring the `kici login` convention.
3086
3148
  */
3087
3149
  function openBrowserBestEffort(url) {
@@ -8,7 +8,7 @@
8
8
  * The in-memory private key is imported NON-EXTRACTABLE (`extractable: false`),
9
9
  * so once loaded the raw private material can never be read back out of the
10
10
  * process — the only export path is the public JWK. This is what makes the
11
- * private key "non-exportable by design" (design spec § A / § H).
11
+ * private key "non-exportable by design".
12
12
  */
13
13
  import { type JWK } from 'jose';
14
14
  import type { Signer } from './signer.js';
@@ -156,8 +156,8 @@ export interface GlobalEvalAgentRegistry {
156
156
  * fails outright or reports success without the `globalEvalComplete` the
157
157
  * orchestrator is waiting for. Either way the round never settles.
158
158
  *
159
- * Customers upgrade their orchestrator and their agents on their own schedule
160
- * (`.claude/rules/compatibility.md`), so an orchestrator ahead of its fleet is
159
+ * Customers upgrade their orchestrator and their agents on their own schedule,
160
+ * so an orchestrator ahead of its fleet is
161
161
  * a supported state and not an error — but it is one this module has to
162
162
  * recognise, because the damage is not confined to the new feature. A global
163
163
  * workflow that merely *contains* a generator now routes through the round, so
@@ -156,8 +156,8 @@ export declare function isRootJob(lockJob: LockJob): boolean;
156
156
  * Why this exists
157
157
  * ----------------
158
158
  * The webhook pipeline binds `lockFileFetcher` to the inbound webhook's
159
- * provider bundle. When a local-sourced webhook (e.g., the staging
160
- * stg-ha-smoke failover-dispatch test) arrives for a repo whose lock file
159
+ * provider bundle. When a local-sourced webhook (e.g., a failover-dispatch
160
+ * push sent through the internal ingress) arrives for a repo whose lock file
161
161
  * is only accessible via a different provider (e.g., github), the inbound
162
162
  * fetcher returns null and trigger matching silently drops the webhook.
163
163
  * This resolver lets the pipeline consult OTHER bundles whose registrations
@@ -24,7 +24,7 @@ export interface RetrierMintDeps {
24
24
  } | undefined;
25
25
  /**
26
26
  * Test-only fault injection: the build-time test double supplies a predicate
27
- * over the audience that forces a TERMINAL rejection, so an E2E can exercise
27
+ * over the audience that forces a TERMINAL rejection, so a test can exercise
28
28
  * the markRejected → gauge-exclusion → `--include-rejected` re-arm cycle with
29
29
  * a real deferred row. It returns before the real mint, so the signing choke
30
30
  * point is preserved. The shipped orchestrator leaves it undefined.
@@ -1,7 +1,7 @@
1
1
  import type { JSONWebKeySet } from 'jose';
2
2
  /**
3
3
  * Derive the JWKS URI for a provenance OIDC issuer. Identical to the Platform's
4
- * own derivation (`packages/platform/src/dashboard/routes/runs.ts`) so a bundle
4
+ * own derivation so a bundle
5
5
  * verified client-side and at ingest resolve the same key set.
6
6
  */
7
7
  export declare function deriveJwksUri(issuer: string): string;
@@ -17,8 +17,8 @@ export interface ProvenanceTrustRoot {
17
17
  setIssuer(issuer: string | null): void;
18
18
  }
19
19
  /**
20
- * A trust root backed by the orchestrator's OWN signing keys (Phase 1
21
- * orchestrator-owned attestations). Verify-at-ingest resolves the key set from
20
+ * A trust root backed by the orchestrator's OWN signing keys
21
+ * (orchestrator-owned attestations). Verify-at-ingest resolves the key set from
22
22
  * the `orchestrator_signing_keys` table so fresh rotations / revocations are
23
23
  * reflected immediately, and the issuer is the orchestrator's own configured
24
24
  * provenance issuer. The key set is read fresh from the DB each resolve (a cheap
@@ -11,14 +11,11 @@
11
11
  * The value returned here is the raw secret material:
12
12
  * - `credentialType === 'pat' | 'basic'`: the PAT / password string
13
13
  * - `credentialType === 'ssh'`: the PEM-encoded private key (the agent
14
- * materializes it to a tempfile and wires `GIT_SSH_COMMAND` — see
15
- * Phase 4 dispatch protocol + agent work)
14
+ * materializes it to a tempfile and wires `GIT_SSH_COMMAND`)
16
15
  *
17
16
  * The token's "kind" (basic vs ssh) is NOT carried through this interface;
18
- * it is propagated via `QueuedJobInput.gitAuth` in Phase 4. For Phase 2 we
19
- * expose a richer `issueGitAuth()` method on this class that the dispatcher
20
- * can call directly once Phase 4 adds `sourceAuth`/`workflowAuth` to the
21
- * dispatch schema.
17
+ * the richer `issueGitAuth()` method on this class returns it, and the
18
+ * dispatcher carries it in the dispatch's `sourceAuth`/`workflowAuth` fields.
22
19
  */
23
20
  import type { CloneTokenProvider, ProviderGitAuth } from '@kici-dev/engine';
24
21
  import type { SecretResolver } from '../../secrets/secret-resolver.js';
@@ -26,7 +23,7 @@ import type { UniversalGitConfig } from './config.js';
26
23
  /**
27
24
  * Structured auth material the agent needs to clone a universal-git repo.
28
25
  *
29
- * Phase 4: serialized into the new `sourceAuth` / `workflowAuth` fields on
26
+ * Serialized into the `sourceAuth` / `workflowAuth` fields on
30
27
  * `jobDispatchSchema`. The shape matches `ProviderGitAuth` from
31
28
  * `@kici-dev/engine` with additional SSH fields pulled from the source
32
29
  * config (`sshHostKeyPolicy`, `sshKnownHostsPem`).
@@ -52,11 +49,10 @@ export declare class UniversalGitCloneTokenProvider implements CloneTokenProvide
52
49
  /**
53
50
  * Create a clone token — the raw secret material (PAT / password / SSH key).
54
51
  *
55
- * The orchestrator dispatch pipeline currently threads this string into
56
- * `jobDispatch.token`, which the agent uses as HTTPS Basic-auth password
57
- * via `http.extraHeader`. SSH support requires the Phase 4 dispatch-schema
58
- * split (sourceAuth.kind = 'ssh') — which is where the agent gains the
59
- * ability to materialize the PEM key and wire `GIT_SSH_COMMAND`.
52
+ * A bare token reaches `jobDispatch.token`, which the agent uses as HTTPS
53
+ * Basic-auth password via `http.extraHeader`. SSH needs the structured
54
+ * `sourceAuth` (`kind: 'ssh'`) from `issueGitAuth()` — which is where the
55
+ * agent materializes the PEM key and wires `GIT_SSH_COMMAND`.
60
56
  *
61
57
  * @returns The resolved secret string, or `null` when the secret is
62
58
  * missing (source misconfigured — caller treats this as a clone failure).
@@ -64,7 +60,7 @@ export declare class UniversalGitCloneTokenProvider implements CloneTokenProvide
64
60
  createCloneToken(_repoIdentifier: string, _credentials: unknown): Promise<string | null>;
65
61
  /**
66
62
  * Richer variant that returns the full `UniversalGitAuth` shape instead
67
- * of just the secret string. The Phase 4 dispatcher calls this via the
63
+ * of just the secret string. The dispatcher calls this via the
68
64
  * `CloneTokenProvider.issueGitAuth?` interface hook so the agent
69
65
  * receives the auth `kind` + `user` + SSH host-key policy alongside the
70
66
  * secret.
@@ -18,8 +18,7 @@
18
18
  * - `UniversalGitRepoUrlBuilder` — substitutes `{owner}`/`{name}`/`{repo}`
19
19
  * in the source's `gitUrlTemplate`.
20
20
  *
21
- * No `CheckStatusPoster` is wired for v1 — forge API support for it is uneven
22
- * and adding it is a separate phase.
21
+ * No `CheckStatusPoster` is wired — forge API support for it is uneven.
23
22
  *
24
23
  * The bundle also leaves `hasForkModel` unset. The normalizer does report an
25
24
  * `isForkPR`, but it compares two repo names that `extractRepoFullName` reads
@@ -79,9 +79,8 @@ export declare class UniversalGitWebhookNormalizer implements WebhookNormalizer
79
79
  /**
80
80
  * Extension hook used by the processor's `isDefaultBranchPush` check so it
81
81
  * does not need to hardcode `payload.repository.default_branch`. The
82
- * engine interface does not yet declare this method; it is consumed via
83
- * an optional chain (`normalizer.extractDefaultBranch?.(payload)`), so it
84
- * is safe to ship ahead of the interface change in Phase 3.
82
+ * engine interface declares it optional, so it is consumed via
83
+ * an optional chain (`normalizer.extractDefaultBranch?.(payload)`).
85
84
  */
86
85
  extractDefaultBranch(payload: unknown): string | null;
87
86
  /**
@@ -10,9 +10,9 @@
10
10
  *
11
11
  * This module provides the orchestrator-side primitives for preparing those
12
12
  * artefacts. It is used in two contexts:
13
- * - **Agent-side** (Phase 4+): the agent receives the PEM + host-key policy
13
+ * - **Agent-side**: the agent receives the PEM + host-key policy
14
14
  * in the dispatch message and materializes them before cloning.
15
- * - **Lock-file fetcher** (Phase 2, this module's immediate consumer):
15
+ * - **Lock-file fetcher**:
16
16
  * the orchestrator shallow-clones the source repo itself at registration
17
17
  * time, using the same SSH materialization path.
18
18
  *
@@ -82,8 +82,8 @@ export declare function prepareSshAuthSync(opts: PrepareSshAuthOptions): SshAuth
82
82
  * mismatches against cached keys). The per-call file is wiped by `cleanup()`.
83
83
  *
84
84
  * IdentitiesOnly=yes ensures ssh ignores any agent-forwarded keys and only
85
- * uses the one we provided; this is critical in staging where the runner
86
- * user may have a populated ssh-agent.
85
+ * uses the one we provided; this is critical on any host where the
86
+ * orchestrator's runtime user may have a populated ssh-agent.
87
87
  */
88
88
  export declare function composeGitSshCommand(privateKeyPath: string, policy: SshHostKeyPolicy, knownHostsPath: string): string;
89
89
  /**
@@ -94,8 +94,8 @@ export interface CleanupExtras {
94
94
  * as terminal status updates so the Platform execution_jobs projection
95
95
  * stays in sync.
96
96
  *
97
- * Event-log row + payload retention used to live here too, but Phase E
98
- * replaced the 30-day hard-delete with cold-store archive-then-delete
97
+ * Event-log row + payload retention is not here: event_log rows are
98
+ * archived to cold-store and then deleted
99
99
  * (see `packages/orchestrator/src/cold-store/tables/event-log.ts`).
100
100
  *
101
101
  * @returns Counts of cleaned entries.