@kici-dev/orchestrator 0.6.1 → 0.8.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.
- package/dist/agent/dispatcher.d.ts +10 -1
- package/dist/agent/host-roster.d.ts +8 -3
- package/dist/agent/registry.d.ts +60 -5
- package/dist/app.d.ts +10 -5
- package/dist/cache/dep-cache.d.ts +14 -6
- package/dist/cache/global-eval-round-cache.d.ts +1 -1
- package/dist/cache/index.d.ts +1 -1
- package/dist/cache/source-cache.d.ts +92 -15
- package/dist/cancel/cancel-run.d.ts +20 -2
- package/dist/cancel/sweep-stuck-cancelling.d.ts +30 -0
- package/dist/cli/api-client.d.ts +10 -0
- package/dist/cli/commands/agent-service/logs.d.ts +1 -1
- package/dist/cli/commands/agent-service/restart.d.ts +1 -1
- package/dist/cli/commands/agent-service/start.d.ts +1 -1
- package/dist/cli/commands/agent-service/status.d.ts +1 -1
- package/dist/cli/commands/agent-service/stop.d.ts +1 -1
- package/dist/cli/commands/agent-service/uninstall.d.ts +1 -1
- package/dist/cli/commands/agent.d.ts +25 -0
- package/dist/cli/commands/config.d.ts +16 -0
- package/dist/cli/commands/db-backup.d.ts +46 -0
- package/dist/cli/commands/held-run.d.ts +9 -0
- package/dist/cli/commands/orchestrator-service/drain.d.ts +2 -19
- package/dist/cli/commands/orchestrator-service/index.d.ts +9 -1
- package/dist/cli/commands/orchestrator-service/install-env.d.ts +43 -2
- package/dist/cli/commands/orchestrator-service/logs.d.ts +1 -1
- package/dist/cli/commands/orchestrator-service/restart.d.ts +1 -1
- package/dist/cli/commands/orchestrator-service/start.d.ts +1 -1
- package/dist/cli/commands/orchestrator-service/status.d.ts +17 -1
- package/dist/cli/commands/orchestrator-service/stop.d.ts +1 -1
- package/dist/cli/commands/orchestrator-service/uninstall.d.ts +1 -1
- package/dist/cli/commands/orchestrator-service/upgrade.d.ts +2 -1
- package/dist/cli/commands/rotate.d.ts +1 -1
- package/dist/cli/commands/shared/env-file-mode.d.ts +16 -0
- package/dist/cli/commands/shared/upgrade-hooks.d.ts +32 -0
- package/dist/cli/commands/shared/upgrade-safety.d.ts +81 -0
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +67 -12
- package/dist/cli/commands/source.d.ts +1 -0
- package/dist/cli/commands/token.d.ts +4 -0
- package/dist/cli/commands/trust-policy.d.ts +57 -1
- package/dist/cli/join.d.ts +1 -1
- package/dist/cli/service/backup-timer.d.ts +108 -0
- package/dist/cli/service/compose-path.d.ts +11 -0
- package/dist/cli/service/compose.d.ts +22 -0
- package/dist/cli/service/deploy-env.d.ts +10 -11
- package/dist/cli/service/env-assignment.d.ts +24 -0
- package/dist/cli/service/index.d.ts +2 -2
- package/dist/cli/service/instance/resolve.d.ts +71 -7
- package/dist/cli/service/instance/types.d.ts +15 -0
- package/dist/cli/service/launchd.d.ts +7 -0
- package/dist/cli/service/systemd.d.ts +1 -0
- package/dist/cli/service/types.d.ts +36 -0
- package/dist/cli/service/windows.d.ts +16 -0
- package/dist/cli/wizard/orchestrator-wizard.d.ts +12 -3
- package/dist/cli/wizard/prompts.d.ts +7 -0
- package/dist/cli.js +12544 -9299
- package/dist/cluster/cluster-settings-reader.d.ts +2 -2
- package/dist/cluster/coordinator.d.ts +11 -0
- package/dist/cluster/instance-heartbeat.d.ts +96 -0
- package/dist/cluster/join-client.d.ts +55 -5
- package/dist/cluster/join-handler.d.ts +1 -0
- package/dist/cluster/orphan-recovery.d.ts +16 -0
- package/dist/cluster/peer-registry.d.ts +10 -0
- package/dist/concurrency/dispatch-next-queued.d.ts +1 -13
- package/dist/concurrency/index.d.ts +1 -2
- package/dist/concurrency/queue-manager.d.ts +63 -18
- package/dist/config/env-overlay.d.ts +3 -2
- package/dist/config.d.ts +24 -0
- package/dist/contexts/release-queued-holds.d.ts +23 -1
- package/dist/dashboard/attestation-filters.d.ts +2 -0
- package/dist/dashboard/handler.d.ts +22 -2
- package/dist/db/deferred-indexes.d.ts +72 -0
- package/dist/db/migration-test-harness.d.ts +22 -0
- package/dist/db/migrations/131_execution_jobs_git_credentials.d.ts +30 -0
- package/dist/db/migrations/132_execution_runs_trigger_event.d.ts +23 -0
- package/dist/db/migrations/133_dispatch_queue_source_tar_digest.d.ts +23 -0
- package/dist/db/migrations/134_dispatch_queue_ownership.d.ts +4 -0
- package/dist/db/migrations/135_cluster_settings_retention_days.d.ts +4 -0
- package/dist/db/migrations/136_master_key_wrapped_tables_key_version.d.ts +4 -0
- package/dist/db/migrations/137_execution_runs_pr_head_context.d.ts +4 -0
- package/dist/db/migrations/138_execution_runs_subject_trigger_event.d.ts +4 -0
- package/dist/db/migrations/139_admin_token_subject.d.ts +4 -0
- package/dist/db/migrations/140_dispatch_queue_canonical_labels.d.ts +54 -0
- package/dist/db/migrations/141_event_log_shed.d.ts +15 -0
- package/dist/db/migrator.d.ts +47 -0
- package/dist/db/notify-listener.d.ts +60 -0
- package/dist/db/synthetic-job-ids.d.ts +24 -0
- package/dist/db/types.d.ts +169 -4
- package/dist/deployment/deployment-identity.d.ts +31 -1
- package/dist/diagnostics/checks/index.d.ts +2 -1
- package/dist/diagnostics/checks/retention.d.ts +24 -0
- package/dist/diagnostics/types.d.ts +7 -0
- package/dist/events/event-router.d.ts +1 -1
- package/dist/fault-injection-types.d.ts +53 -0
- package/dist/firecracker/host-network.d.ts +63 -8
- package/dist/git/credential-broker.d.ts +35 -2
- package/dist/git/inherited-secret.d.ts +7 -2
- package/dist/git/job-context.d.ts +7 -4
- package/dist/helpers/secure-write.d.ts +35 -0
- package/dist/index.js +196 -67
- package/dist/metrics/prometheus.d.ts +116 -1
- package/dist/oidc/db-signer.d.ts +27 -5
- package/dist/oidc/id-token-claims.d.ts +33 -2
- package/dist/oidc/oidc-mint-registration.d.ts +6 -0
- package/dist/oidc/orchestrator-mint.d.ts +8 -0
- package/dist/oidc/reconcile-signing-key.d.ts +37 -0
- package/dist/orchestrator-core.d.ts +9 -3
- package/dist/pipeline/decorating-secret-resolver.d.ts +1 -1
- package/dist/pipeline/dispatch-matched-workflow.d.ts +65 -14
- package/dist/pipeline/manual-schedule.d.ts +1 -0
- package/dist/pipeline/process-webhook.d.ts +20 -2
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +7 -0
- package/dist/pipeline/run-event-context.d.ts +39 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +1 -1
- package/dist/policy/dashboard-write-policy.d.ts +47 -1
- package/dist/provenance/dispatch-context.d.ts +46 -0
- package/dist/provenance/verify-deferred-capture.d.ts +39 -0
- package/dist/providers/github/auth.d.ts +9 -0
- package/dist/providers/github/deliveries.d.ts +118 -0
- package/dist/providers/local/lock-file-fetcher.d.ts +8 -0
- package/dist/providers/universal-git/config.d.ts +1 -1
- package/dist/queue/cleanup.d.ts +13 -0
- package/dist/queue/job-queue.d.ts +162 -14
- package/dist/queue/retention.d.ts +105 -0
- package/dist/reporting/execution-tracker.d.ts +87 -36
- package/dist/routes/admin-db.d.ts +5 -0
- package/dist/routes/admin-events.d.ts +13 -0
- package/dist/routes/admin-held-runs.d.ts +11 -1
- package/dist/routes/admin-org-settings.d.ts +9 -0
- package/dist/routes/admin-runs.d.ts +7 -4
- package/dist/routes/admin-sources.d.ts +15 -0
- package/dist/routes/admin-trust-policy.d.ts +11 -0
- package/dist/routes/admin.d.ts +25 -0
- package/dist/scaler/backend-factory.d.ts +16 -0
- package/dist/scaler/bare-metal-backend.d.ts +22 -4
- package/dist/scaler/claim-store.d.ts +0 -2
- package/dist/scaler/config.d.ts +6 -3
- package/dist/scaler/container-backend.d.ts +102 -3
- package/dist/scaler/container-hostconfig.d.ts +60 -0
- package/dist/scaler/event-backend.d.ts +7 -6
- package/dist/scaler/file-tail.d.ts +25 -2
- package/dist/scaler/firecracker-api.d.ts +9 -1
- package/dist/scaler/firecracker-backend.d.ts +135 -2
- package/dist/scaler/host-access.d.ts +59 -0
- package/dist/scaler/index.d.ts +1 -0
- package/dist/scaler/manager.d.ts +51 -2
- package/dist/scaler/scaler-state-store.d.ts +13 -0
- package/dist/scaler/types.d.ts +15 -7
- package/dist/scaler/warm-pool.d.ts +6 -4
- package/dist/secrets/config.d.ts +48 -0
- package/dist/secrets/dashboard-encryption-key.d.ts +19 -0
- package/dist/secrets/ephemeral-keys.d.ts +9 -3
- package/dist/secrets/index.d.ts +3 -1
- package/dist/secrets/job-secret-gate.d.ts +76 -0
- package/dist/secrets/master-key-rotation.d.ts +65 -0
- package/dist/secrets/secret-output-crypto.d.ts +17 -0
- package/dist/secrets/secret-resolver.d.ts +23 -6
- package/dist/secrets/source-credentials.d.ts +1 -1
- package/dist/secrets/token-manager.d.ts +4 -1
- package/dist/security/identity-link.d.ts +1 -1
- package/dist/security/reduced-privilege-note.d.ts +5 -0
- package/dist/security/trust-directory-store.d.ts +1 -1
- package/dist/security/trust-policy-gate.d.ts +70 -6
- package/dist/security/trust-tier.d.ts +31 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +10793 -5323
- package/dist/sources/source-manager.d.ts +1 -1
- package/dist/stale-detector/stale-dispatch-candidates.d.ts +43 -0
- package/dist/standalone.js +9385 -4160
- package/dist/storage/loopback-guard.d.ts +53 -6
- package/dist/webhook/event-log.d.ts +14 -1
- package/dist/webhook/generic-sources-listener.d.ts +1 -1
- package/dist/webhook/ingest-admission.d.ts +24 -0
- package/dist/webhook/ingest-overflow-replayer.d.ts +78 -14
- package/dist/webhook/ingest-overflow-types.d.ts +13 -1
- package/dist/webhook/register-source-bundle.d.ts +23 -0
- package/dist/webhook/relay-reinject.d.ts +11 -12
- package/dist/webhook/shed-breadcrumb.d.ts +15 -0
- package/dist/worker/in-memory-job-queue.d.ts +9 -0
- package/dist/ws/fleet-agent-collector.d.ts +7 -0
- package/dist/ws/git-credential-relay.d.ts +41 -12
- package/dist/ws/platform-client.d.ts +28 -5
- package/dist/ws/test-relay-handlers.d.ts +2 -0
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +53 -53
- package/dist/concurrency/group-tracker.d.ts +0 -51
- package/dist/scaler/nftables.d.ts +0 -111
package/dist/db/types.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export interface Database {
|
|
|
45
45
|
run_ephemeral_keys: RunEphemeralKeysTable;
|
|
46
46
|
run_secret_outputs: RunSecretOutputsTable;
|
|
47
47
|
concurrency_groups: ConcurrencyGroupsTable;
|
|
48
|
+
cluster_instances: ClusterInstancesTable;
|
|
48
49
|
sources: SourcesTable;
|
|
49
50
|
cluster_meta: ClusterMetaTable;
|
|
50
51
|
join_tokens: JoinTokenTable;
|
|
@@ -253,6 +254,7 @@ export interface DispatchQueueTable {
|
|
|
253
254
|
source_tar_url: string | null;
|
|
254
255
|
/** SHA-256 hash of the source tarball bytes for integrity verification. Nullable. */
|
|
255
256
|
source_tar_hash: string | null;
|
|
257
|
+
source_tar_digest: string | null;
|
|
256
258
|
/** Pre-built dependency tarball URL (from dep cache). Nullable. */
|
|
257
259
|
deps_url: string | null;
|
|
258
260
|
/** SHA-256 hash of the dependency tarball. Nullable. */
|
|
@@ -322,7 +324,53 @@ export interface DispatchQueueTable {
|
|
|
322
324
|
* NULL for pending rows and for rows dispatched before the column existed.
|
|
323
325
|
*/
|
|
324
326
|
agent_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
327
|
+
/**
|
|
328
|
+
* The coordinator watching this dispatch. Written at claim/dispatch from
|
|
329
|
+
* `config.instanceId`, cleared on requeue.
|
|
330
|
+
*
|
|
331
|
+
* `agent_id` above says which agent holds the job; this says which
|
|
332
|
+
* coordinator armed the timers for it, which is the question every
|
|
333
|
+
* cluster-wide sweep has to answer before it acts. A NULL reads as "unknown
|
|
334
|
+
* owner", never as "not mine" — the convention migration 119 established for
|
|
335
|
+
* the scaler plane — so an unknown-owner row is spared by recovery selection
|
|
336
|
+
* and reaped, if genuinely dead, by the stale detector's dispatch-time scan.
|
|
337
|
+
*/
|
|
338
|
+
owner_instance_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
339
|
+
/**
|
|
340
|
+
* When the row was handed to an agent, as distinct from `created_at`, which
|
|
341
|
+
* is when it was enqueued. A job that waited behind a busy fleet has an old
|
|
342
|
+
* `created_at` and a fresh `dispatched_at`, and only the second one measures
|
|
343
|
+
* "this dispatch has gone unacknowledged". NULL for pending rows and for rows
|
|
344
|
+
* dispatched before the column existed; readers fall back to `created_at`.
|
|
345
|
+
*/
|
|
346
|
+
dispatched_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Coordinator heartbeat table (cluster_instances)
|
|
350
|
+
*
|
|
351
|
+
* The liveness source every ownership predicate reads: an orchestrator upserts
|
|
352
|
+
* its own row on a fixed tick, and a row whose `last_heartbeat_at` is inside the
|
|
353
|
+
* grace window means that coordinator is alive. It is a DB heartbeat rather than
|
|
354
|
+
* the in-memory peer registry because the peer registry is empty at exactly the
|
|
355
|
+
* moment startup recovery runs — a freshly booted process has handshaken with
|
|
356
|
+
* nobody — and because it needs no peer connectivity, so it works the same in
|
|
357
|
+
* non-Raft multi-coordinator and standalone deployments. `host_roster` does the
|
|
358
|
+
* same job for agents.
|
|
359
|
+
*/
|
|
360
|
+
export interface ClusterInstancesTable {
|
|
361
|
+
/** The coordinator's instance id (`config.instanceId`). */
|
|
362
|
+
instance_id: string;
|
|
363
|
+
/** Raft role at the last heartbeat, or null when the node runs without Raft. */
|
|
364
|
+
role: string | null;
|
|
365
|
+
/** Orchestrator version at the last heartbeat. */
|
|
366
|
+
version: string | null;
|
|
367
|
+
/** When this instance last started. */
|
|
368
|
+
started_at: Generated<Date>;
|
|
369
|
+
/** Refreshed every tick; staleness past the grace window reads as dead. */
|
|
370
|
+
last_heartbeat_at: Generated<Date>;
|
|
325
371
|
}
|
|
372
|
+
export type ClusterInstance = Selectable<ClusterInstancesTable>;
|
|
373
|
+
export type NewClusterInstance = Insertable<ClusterInstancesTable>;
|
|
326
374
|
/**
|
|
327
375
|
* Deduplication cache table
|
|
328
376
|
* Tracks recently processed webhook delivery IDs to prevent reprocessing.
|
|
@@ -452,6 +500,13 @@ export interface ExecutionRunTable {
|
|
|
452
500
|
started_at: Generated<Date>;
|
|
453
501
|
/** When the run completed */
|
|
454
502
|
completed_at: Date | null;
|
|
503
|
+
/**
|
|
504
|
+
* When the run entered `cancelling`. Stamped once, on the transition, and
|
|
505
|
+
* never cleared: it is the clock the stuck-cancelling re-drive reads, and no
|
|
506
|
+
* other column records when the cancel started. NULL for a run that never
|
|
507
|
+
* cancelled and for one that entered `cancelling` before the column existed.
|
|
508
|
+
*/
|
|
509
|
+
cancelling_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
455
510
|
/** Total execution duration in milliseconds */
|
|
456
511
|
duration_ms: number | null;
|
|
457
512
|
/**
|
|
@@ -487,6 +542,54 @@ export interface ExecutionRunTable {
|
|
|
487
542
|
context: string | null;
|
|
488
543
|
/** Matched context id for this run (null if no/unresolved context). */
|
|
489
544
|
context_id: string | null;
|
|
545
|
+
/**
|
|
546
|
+
* The event type that started this run (`push`, `pr:open`, `schedule`, …).
|
|
547
|
+
*
|
|
548
|
+
* Persisted because the git credential relay evaluates a named context's
|
|
549
|
+
* protection rules against the run, and a context may restrict which trigger
|
|
550
|
+
* types may use it. NULL means the trigger was never recorded, which fails a
|
|
551
|
+
* `triggerTypeFilters` rule closed — a credential is withheld rather than
|
|
552
|
+
* granted on a rule nobody could check.
|
|
553
|
+
*/
|
|
554
|
+
trigger_event: string | null;
|
|
555
|
+
/**
|
|
556
|
+
* The event the OIDC token's SUBJECT is derived from, when that is not
|
|
557
|
+
* {@link ExecutionRunTable.trigger_event}.
|
|
558
|
+
*
|
|
559
|
+
* A re-run records `trigger_event: 'rerun'`, which is what the dashboard's
|
|
560
|
+
* trigger filter and the credential relay's `triggerTypeFilters` need to
|
|
561
|
+
* read, but it carries no pull-request dimension — so a re-run of a pull
|
|
562
|
+
* request presented the branch-shaped subject a push to the same base branch
|
|
563
|
+
* presents. A re-run copies the original run's event here so the subject
|
|
564
|
+
* survives the hop.
|
|
565
|
+
*
|
|
566
|
+
* `buildIdTokenSubject` is the ONLY reader. NULL means "use `trigger_event`",
|
|
567
|
+
* which is what every row written before this column existed says, so a
|
|
568
|
+
* legacy row keeps the subject it already mints.
|
|
569
|
+
*/
|
|
570
|
+
subject_trigger_event: string | null;
|
|
571
|
+
/**
|
|
572
|
+
* The pull-request HEAD branch — where a contributor's code actually lives.
|
|
573
|
+
* Distinct from {@link ExecutionRunTable.ref}, which is the base branch a PR
|
|
574
|
+
* targets. NULL for a non-PR run and for a row written before this column
|
|
575
|
+
* existed.
|
|
576
|
+
*/
|
|
577
|
+
head_ref: string | null;
|
|
578
|
+
/**
|
|
579
|
+
* `owner/repo` of the pull-request HEAD. Equals `repo_identifier` for a
|
|
580
|
+
* same-repo PR and names the contributor's fork for a fork PR. NULL for a
|
|
581
|
+
* non-PR run and for a legacy row.
|
|
582
|
+
*/
|
|
583
|
+
head_repository: string | null;
|
|
584
|
+
/**
|
|
585
|
+
* True when the pull request came from a fork (head repo != base repo).
|
|
586
|
+
* NULL means NOT RESOLVED — a legacy row, or one of the two reroute
|
|
587
|
+
* projections, which insert from a `job.reroute` message carrying no event
|
|
588
|
+
* context. Every claim derived from this column renders NULL as
|
|
589
|
+
* `'unresolved'` rather than `'false'`, so a trust policy that pins it fails
|
|
590
|
+
* closed.
|
|
591
|
+
*/
|
|
592
|
+
is_fork: boolean | null;
|
|
490
593
|
/** Trust tier of the contributor for PR runs (null for non-PR events) */
|
|
491
594
|
trust_tier: string | null;
|
|
492
595
|
/** Lock file source: 'head' or 'base' (null for non-PR events) */
|
|
@@ -679,6 +782,24 @@ export interface ExecutionJobTable {
|
|
|
679
782
|
* overwritten with the agent-resolved list for dynamic contexts.
|
|
680
783
|
*/
|
|
681
784
|
contexts: string | null;
|
|
785
|
+
/**
|
|
786
|
+
* The `gitCredentials` map this job's lock entry declared, as server truth
|
|
787
|
+
* the credential relay reads on every git network operation. JSONB: the
|
|
788
|
+
* driver returns a parsed object on SELECT, while writers pass a
|
|
789
|
+
* `JSON.stringify` string.
|
|
790
|
+
*
|
|
791
|
+
* Copied verbatim from the lock, because the relay compares a request against
|
|
792
|
+
* it byte for byte: a `<name>Secret` field is a secret NAME in qualified
|
|
793
|
+
* `<context>:<key>` form, and its `<name>Value` sibling is material for a
|
|
794
|
+
* credential with no store entry to name.
|
|
795
|
+
*
|
|
796
|
+
* This row is written after the dispatch loop, so NULL means either "the job
|
|
797
|
+
* declared none" or "the row has not caught up with the dispatch yet". The
|
|
798
|
+
* relay does not guess between them — `git/job-context.ts` reads the job's
|
|
799
|
+
* dispatch record, which carries the same declaration and is written before
|
|
800
|
+
* the agent is told about the job.
|
|
801
|
+
*/
|
|
802
|
+
git_credentials: ColumnType<Record<string, Record<string, string>> | null, string | null, string | null>;
|
|
682
803
|
/**
|
|
683
804
|
* Bound contexts skipped on a test/local run (non-test or unconfigured),
|
|
684
805
|
* JSON-encoded `string[]`. NULL = nothing skipped.
|
|
@@ -1194,6 +1315,14 @@ export interface AdminTokenTable {
|
|
|
1194
1315
|
role: string;
|
|
1195
1316
|
/** Routing key scope (null = all) */
|
|
1196
1317
|
routing_key: string | null;
|
|
1318
|
+
/**
|
|
1319
|
+
* The intended holder, as the operator stated it at `token create --subject`
|
|
1320
|
+
* (an OIDC `sub` or an email). Advisory metadata only: the orchestrator
|
|
1321
|
+
* cannot verify it and never reads it when authorizing a request. It is the
|
|
1322
|
+
* join key the Platform's RBAC drift report compares against org membership.
|
|
1323
|
+
* NULL means nobody recorded a holder — the report's `unlinked` finding.
|
|
1324
|
+
*/
|
|
1325
|
+
subject: string | null;
|
|
1197
1326
|
/** When this token was created */
|
|
1198
1327
|
created_at: Generated<Date>;
|
|
1199
1328
|
/** When this token expires (null = never) */
|
|
@@ -1555,6 +1684,8 @@ export interface RunEphemeralKeysTable {
|
|
|
1555
1684
|
encrypted_private_key: string;
|
|
1556
1685
|
/** Base64-encoded DER public key (X25519 SPKI format) */
|
|
1557
1686
|
public_key: string;
|
|
1687
|
+
/** Master-key generation this row is sealed under. Bumped by `rotate-key`. */
|
|
1688
|
+
key_version: Generated<number>;
|
|
1558
1689
|
/** When this key pair was created */
|
|
1559
1690
|
created_at: Generated<Date>;
|
|
1560
1691
|
}
|
|
@@ -1563,7 +1694,12 @@ export type NewRunEphemeralKey = Insertable<RunEphemeralKeysTable>;
|
|
|
1563
1694
|
/**
|
|
1564
1695
|
* Run secret outputs table (run_secret_outputs)
|
|
1565
1696
|
* Stores encrypted secret output values produced by jobs for cross-job consumption.
|
|
1566
|
-
*
|
|
1697
|
+
*
|
|
1698
|
+
* The agent seals a value to the run's ephemeral public key (ECDH + AES-256-GCM),
|
|
1699
|
+
* but that envelope is not what lands here: the orchestrator opens it with the
|
|
1700
|
+
* run private key and RE-ENCRYPTS the plaintext under the master key
|
|
1701
|
+
* (`KICI_SECRET_KEY`) before storing. So this is one of the master-key-wrapped
|
|
1702
|
+
* stores and moves with every key rotation.
|
|
1567
1703
|
*/
|
|
1568
1704
|
export interface RunSecretOutputsTable {
|
|
1569
1705
|
/** UUID primary key */
|
|
@@ -1574,8 +1710,14 @@ export interface RunSecretOutputsTable {
|
|
|
1574
1710
|
job_id: string;
|
|
1575
1711
|
/** Output key name */
|
|
1576
1712
|
output_key: string;
|
|
1577
|
-
/**
|
|
1713
|
+
/**
|
|
1714
|
+
* Base64-encoded value, AES-256-GCM sealed under the master key with AAD
|
|
1715
|
+
* `secret-output:<run_id>`. Read through `unsealSecretOutput`, which carries
|
|
1716
|
+
* the rotation grace window's old-key fallback.
|
|
1717
|
+
*/
|
|
1578
1718
|
encrypted_value: string;
|
|
1719
|
+
/** Master-key generation this row is sealed under. Bumped by `rotate-key`. */
|
|
1720
|
+
key_version: Generated<number>;
|
|
1579
1721
|
/** When this output was stored */
|
|
1580
1722
|
created_at: Generated<Date>;
|
|
1581
1723
|
}
|
|
@@ -1583,8 +1725,14 @@ export type RunSecretOutput = Selectable<RunSecretOutputsTable>;
|
|
|
1583
1725
|
export type NewRunSecretOutput = Insertable<RunSecretOutputsTable>;
|
|
1584
1726
|
/**
|
|
1585
1727
|
* Concurrency groups table (concurrency_groups)
|
|
1586
|
-
*
|
|
1587
|
-
*
|
|
1728
|
+
*
|
|
1729
|
+
* The arbiter of `concurrency: { group, max }`, not a record of a decision made
|
|
1730
|
+
* elsewhere: a slot is acquired by an advisory-locked transaction that counts
|
|
1731
|
+
* `status='active'` rows for the scope and inserts only when the count is below
|
|
1732
|
+
* `max`. That is what makes the cap hold across a restart and across every
|
|
1733
|
+
* coordinator in a cluster, which a per-process map cannot. A partial unique
|
|
1734
|
+
* index on `(routing_key, group_key, run_id) WHERE status='active'` makes a
|
|
1735
|
+
* re-acquire idempotent.
|
|
1588
1736
|
*/
|
|
1589
1737
|
export interface ConcurrencyGroupsTable {
|
|
1590
1738
|
/** UUID primary key */
|
|
@@ -1914,6 +2062,21 @@ export interface OrgPlanHeadroomTable {
|
|
|
1914
2062
|
* / seconds knobs are INTEGER (pg returns a number).
|
|
1915
2063
|
*/
|
|
1916
2064
|
export interface ClusterSettingsTable {
|
|
2065
|
+
/**
|
|
2066
|
+
* Database-side retention windows, in days. NULL means the orchestrator's
|
|
2067
|
+
* configured default; 0 disables that window. These prune the warm tables
|
|
2068
|
+
* directly, for the installs that run no object store — with the cold store
|
|
2069
|
+
* on, the archivers keep sole ownership of the tables they register.
|
|
2070
|
+
*/
|
|
2071
|
+
run_retention_days: number | null;
|
|
2072
|
+
audit_retention_days: number | null;
|
|
2073
|
+
provenance_retention_days: number | null;
|
|
2074
|
+
held_run_retention_days: number | null;
|
|
2075
|
+
/**
|
|
2076
|
+
* When the retention sweep first reported a non-zero window. Deletion starts
|
|
2077
|
+
* a week later, so an upgrade never removes history on its first night.
|
|
2078
|
+
*/
|
|
2079
|
+
retention_announced_at: Date | null;
|
|
1917
2080
|
id: ColumnType<string, string | undefined, never>;
|
|
1918
2081
|
max_github_payload_bytes: ColumnType<string | null, number | null | undefined, number | null>;
|
|
1919
2082
|
event_log_max_payload_bytes: ColumnType<string | null, number | null | undefined, number | null>;
|
|
@@ -2375,6 +2538,8 @@ export interface DashboardEncryptionKeysTable {
|
|
|
2375
2538
|
public_jwk: unknown;
|
|
2376
2539
|
/** AES-256-GCM-wrapped DER private key (master-key wrapped under KICI_SECRET_KEY). */
|
|
2377
2540
|
encrypted_private_key: string;
|
|
2541
|
+
/** Master-key generation this row is sealed under. Bumped by `rotate-key`. */
|
|
2542
|
+
key_version: Generated<number>;
|
|
2378
2543
|
/** Lifecycle status (`active` | `revoked`). */
|
|
2379
2544
|
status: Generated<string>;
|
|
2380
2545
|
revocation_reason: string | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DeploymentIdentity } from '@kici-dev/engine';
|
|
1
|
+
import { type DeploymentIdentity, type ConfigPaths } from '@kici-dev/engine';
|
|
2
2
|
/**
|
|
3
3
|
* The pinned `<node> <kici-admin>` invocation for a posix bare-metal unit, or
|
|
4
4
|
* `undefined` when the shim cannot be located.
|
|
@@ -91,4 +91,34 @@ export declare function resolveWindowsAdminPath(execPath: string, entryPath: str
|
|
|
91
91
|
* shells cannot share one quoting.
|
|
92
92
|
*/
|
|
93
93
|
export declare function readDeploymentIdentity(env?: NodeJS.ProcessEnv, execPath?: string, entryPath?: string | undefined, fileExists?: (p: string) => boolean): DeploymentIdentity;
|
|
94
|
+
/**
|
|
95
|
+
* Where this orchestrator's own configuration lives on its host.
|
|
96
|
+
*
|
|
97
|
+
* `envFile` and `composeFile` come from the env the installer injects
|
|
98
|
+
* (`KICI_DEPLOY_CONFIG_FILE` / `KICI_DEPLOY_COMPOSE_FILE`), because a running
|
|
99
|
+
* process cannot derive either: systemd's `EnvironmentFile=` delivers the
|
|
100
|
+
* variables and not the filename, and a compose container never sees the host
|
|
101
|
+
* path at all. `scalerConfig` needs no stamp — the orchestrator already holds
|
|
102
|
+
* it in its own config — so every orchestrator reports it, including hand-run
|
|
103
|
+
* ones and installs that predate the stamp.
|
|
104
|
+
*
|
|
105
|
+
* Values are trimmed for the same reason the deployment reader trims them:
|
|
106
|
+
* they arrive through an env file, where a hand edit or a writer that leaves
|
|
107
|
+
* the trailing byte on attaches a space or a newline. A known path carrying a
|
|
108
|
+
* stray space is still a known path, and dropping it would discard an answer
|
|
109
|
+
* we have.
|
|
110
|
+
*
|
|
111
|
+
* No path is checked for existence. On bare metal such a check would pass; in
|
|
112
|
+
* a compose container it would fail for every path, because these are HOST
|
|
113
|
+
* paths the container cannot see — blanking the value for exactly the
|
|
114
|
+
* deployment shape that needs it most.
|
|
115
|
+
*
|
|
116
|
+
* This is a standalone reader rather than a branch of `readDeploymentIdentity`
|
|
117
|
+
* because that function returns early for `compose`, and anything placed after
|
|
118
|
+
* that return does nothing for compose deployments.
|
|
119
|
+
*/
|
|
120
|
+
export declare function readConfigPaths(env?: NodeJS.ProcessEnv, scaler?: {
|
|
121
|
+
scalerConfigPath?: string;
|
|
122
|
+
scalerConfigDir?: string;
|
|
123
|
+
}): ConfigPaths;
|
|
94
124
|
//# sourceMappingURL=deployment-identity.d.ts.map
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { DiagnosticCheck } from '../types.js';
|
|
8
8
|
import { checkDbConnectivity } from './db.js';
|
|
9
9
|
import { checkBackupFreshness } from './backup.js';
|
|
10
|
+
import { checkRunHistoryRetention } from './retention.js';
|
|
10
11
|
import { checkWsToPlatform } from './ws.js';
|
|
11
12
|
import { checkAgentConnectivity } from './agents.js';
|
|
12
13
|
import { checkDiskSpace } from './disk.js';
|
|
@@ -16,5 +17,5 @@ import { checkScalerProvisioning } from './scaler.js';
|
|
|
16
17
|
import { checkFirecrackerNetwork } from './firecracker-network.js';
|
|
17
18
|
/** All diagnostic checks in display order. */
|
|
18
19
|
export declare const defaultChecks: DiagnosticCheck[];
|
|
19
|
-
export { checkDbConnectivity, checkBackupFreshness, checkWsToPlatform, checkAgentConnectivity, checkDiskSpace, checkConfigValidity, checkCertificateExpiry, checkScalerProvisioning, checkFirecrackerNetwork, };
|
|
20
|
+
export { checkDbConnectivity, checkBackupFreshness, checkRunHistoryRetention, checkWsToPlatform, checkAgentConnectivity, checkDiskSpace, checkConfigValidity, checkCertificateExpiry, checkScalerProvisioning, checkFirecrackerNetwork, };
|
|
20
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DiagnosticDeps, DiagnosticResult } from '../types.js';
|
|
2
|
+
import { type RetentionWindows } from '../../queue/retention.js';
|
|
3
|
+
/** Rows past which an unbounded `execution_runs` is worth warning about. */
|
|
4
|
+
export declare const RETENTION_WARN_ROW_THRESHOLD = 500000;
|
|
5
|
+
/** What the verdict needs to know. Gathered by the check, decided here. */
|
|
6
|
+
export interface RetentionVerdictInput {
|
|
7
|
+
windows: RetentionWindows;
|
|
8
|
+
coldStoreEnabled: boolean;
|
|
9
|
+
/** `execution_runs` row count. Only read when every window is disabled. */
|
|
10
|
+
rows: number;
|
|
11
|
+
announcedAt: Date | null;
|
|
12
|
+
now: Date;
|
|
13
|
+
rowThreshold?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Decide the check's outcome from already-gathered facts.
|
|
17
|
+
*
|
|
18
|
+
* Split from the check so the branches are testable without standing up a
|
|
19
|
+
* database — in particular the WARN branch, which needs a row count larger
|
|
20
|
+
* than any fixture would seed.
|
|
21
|
+
*/
|
|
22
|
+
export declare function retentionVerdict(input: RetentionVerdictInput): Pick<DiagnosticResult, 'status' | 'message' | 'details'>;
|
|
23
|
+
export declare function checkRunHistoryRetention(deps: DiagnosticDeps): Promise<DiagnosticResult>;
|
|
24
|
+
//# sourceMappingURL=retention.d.ts.map
|
|
@@ -8,6 +8,7 @@ import type { Kysely } from 'kysely';
|
|
|
8
8
|
import type { Database } from '../db/types.js';
|
|
9
9
|
import type { AgentRegistry } from '../agent/registry.js';
|
|
10
10
|
import type { ScalerManager } from '../scaler/manager.js';
|
|
11
|
+
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
11
12
|
/** Result of a single diagnostic check. */
|
|
12
13
|
export interface DiagnosticResult {
|
|
13
14
|
/** Human-readable check name (e.g., "Database connectivity"). */
|
|
@@ -35,6 +36,12 @@ export interface DiagnosticDeps {
|
|
|
35
36
|
tlsCertPath?: string;
|
|
36
37
|
/** Scaler manager for recent spawn-failure health (optional -- no scaler configured). */
|
|
37
38
|
scalerManager?: ScalerManager;
|
|
39
|
+
/**
|
|
40
|
+
* Fleet-wide settings reader. When present, a check reads the live
|
|
41
|
+
* `cluster_settings` value rather than only the configured default — so
|
|
42
|
+
* `kici-admin diagnose` reports what the cluster is actually doing.
|
|
43
|
+
*/
|
|
44
|
+
clusterSettings?: ClusterSettingsReader;
|
|
38
45
|
}
|
|
39
46
|
/** A diagnostic check function. May return one result or several (e.g. one per scaler backend). */
|
|
40
47
|
export type DiagnosticCheck = (deps: DiagnosticDeps) => Promise<DiagnosticResult | DiagnosticResult[]>;
|
|
@@ -137,7 +137,7 @@ export declare class EventRouter {
|
|
|
137
137
|
private stopped;
|
|
138
138
|
private readonly registrationIndex;
|
|
139
139
|
private readonly nodeId;
|
|
140
|
-
private
|
|
140
|
+
private listener;
|
|
141
141
|
private lastProcessedEventId;
|
|
142
142
|
constructor(options: EventRouterOptions);
|
|
143
143
|
/**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of the orchestrator's fault-injection policy.
|
|
3
|
+
*
|
|
4
|
+
* The policy's *implementation* is test-only and lives in `src/testing/`, which
|
|
5
|
+
* is excluded from the npm tarball, the container image and the public mirror.
|
|
6
|
+
* The type itself has to ship: three production modules (`app.ts`,
|
|
7
|
+
* `orchestrator-core.ts`, `server.ts`) declare an optional `faultInjection`
|
|
8
|
+
* field, so a projection without this file fails to typecheck with three
|
|
9
|
+
* TS2307s. Only the declaration lives here — its one dependency is a type from
|
|
10
|
+
* `@kici-dev/engine`, so nothing test-only is pulled along with it.
|
|
11
|
+
*/
|
|
12
|
+
import type { OrchCapabilities } from '@kici-dev/engine';
|
|
13
|
+
/**
|
|
14
|
+
* The set of synthetic faults a test-only entrypoint can inject into the
|
|
15
|
+
* orchestrator. Every field is optional: an absent field means "no fault at
|
|
16
|
+
* this seam". A whole `undefined` policy means the orchestrator runs with no
|
|
17
|
+
* fault injection at all.
|
|
18
|
+
*/
|
|
19
|
+
export interface OrchestratorFaultInjection {
|
|
20
|
+
/**
|
|
21
|
+
* Per-event-name fault map: while `event.attempts <= N`, the EventRouter
|
|
22
|
+
* throws a synthetic dispatch error to drive the retry / DLQ path.
|
|
23
|
+
*/
|
|
24
|
+
eventFailFirstN?: Record<string, number>;
|
|
25
|
+
/**
|
|
26
|
+
* Skip the S3 sentinel validation on cluster-identity bootstrap. Read
|
|
27
|
+
* independently of `KICI_TEST_MODE` (matching today's behavior).
|
|
28
|
+
*/
|
|
29
|
+
skipS3Sentinel?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Predicate over an OIDC `audience`: when true, the *initial* provenance
|
|
32
|
+
* mint fails transiently (defer), so an E2E can exercise the
|
|
33
|
+
* deferred-attestation retry + per-run serve path.
|
|
34
|
+
*/
|
|
35
|
+
initialMintFault?: (audience: string) => boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Predicate over an OIDC `audience`: when true, the retrier's later re-mint
|
|
38
|
+
* TERMINALLY REJECTS the audience, exercising the reject → gauge-exclusion →
|
|
39
|
+
* re-arm cycle.
|
|
40
|
+
*/
|
|
41
|
+
remintReject?: (audience: string) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Invoked by `handleRerunRequest` before `onRerun`, so an HA E2E can make
|
|
44
|
+
* the first coordinator slow enough that the Platform relay fails over.
|
|
45
|
+
*/
|
|
46
|
+
beforeRerun?: () => Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Transform the advertised capability manifest — used to reproduce an older
|
|
49
|
+
* / sourceless orchestrator that predates a given dashboard capability.
|
|
50
|
+
*/
|
|
51
|
+
capabilitiesTransform?: (c: OrchCapabilities) => OrchCapabilities;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=fault-injection-types.d.ts.map
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* isolation. One pure command-builder drives live provisioning, the rendered
|
|
8
8
|
* boot script, and (read-only) verification.
|
|
9
9
|
*
|
|
10
|
-
* This is HOST setup, distinct from the runtime per-VM isolation
|
|
11
|
-
*
|
|
12
|
-
* nft table name but have separate lifecycles;
|
|
13
|
-
* per-VM rules.
|
|
10
|
+
* This is HOST setup, distinct from the runtime per-VM isolation the rule
|
|
11
|
+
* builder in `@kici-dev/shared/net` performs (added at spawn / removed at
|
|
12
|
+
* destroy). The two share the nft table name but have separate lifecycles;
|
|
13
|
+
* this module never touches the per-VM rules.
|
|
14
14
|
*/
|
|
15
15
|
import { FIRECRACKER_NET_INTERFACES } from './net-interfaces.js';
|
|
16
16
|
export { FIRECRACKER_NET_INTERFACES };
|
|
@@ -42,10 +42,17 @@ export declare function cidrToNetwork(cidr: string): string;
|
|
|
42
42
|
* Pure — performs no I/O. `provisionBridge` executes these; `renderBootScript`
|
|
43
43
|
* serializes them.
|
|
44
44
|
*
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
|
49
|
+
* cloud-metadata reach, until it was destroyed. Instead the baseline lives in
|
|
50
|
+
* its own regular chain (`baseline`, see {@link BASELINE_CHAIN}) that is flushed
|
|
51
|
+
* and refilled, and the hooked `forward` chain keeps its per-VM rules
|
|
52
|
+
* untouched, reaching the baseline through a `jump` appended as its last rule.
|
|
53
|
+
*
|
|
54
|
+
* Every forward/postrouting/MSS rule is source-scoped to the bridge subnet so
|
|
55
|
+
* two tables on the shared hooks do not cross-drop each other's traffic.
|
|
49
56
|
*/
|
|
50
57
|
export declare function buildBridgeCommands(cfg: FirecrackerBridgeConfig): CommandSpec[];
|
|
51
58
|
export declare const NM_CONF_PATH = "/etc/NetworkManager/conf.d/90-kici-unmanaged.conf";
|
|
@@ -71,13 +78,61 @@ export interface BridgeHealth {
|
|
|
71
78
|
bridgeUp: boolean;
|
|
72
79
|
addrPresent: boolean;
|
|
73
80
|
tablePresent: boolean;
|
|
81
|
+
/** The regular baseline chain exists and `forward` ends in a jump to it. */
|
|
82
|
+
baselineChainPresent: boolean;
|
|
83
|
+
/** Every enslaved kici-* TAP carries bridge port isolation. */
|
|
84
|
+
tapIsolationPresent: boolean;
|
|
74
85
|
healthy: boolean;
|
|
75
86
|
detail: string;
|
|
76
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Delete every rule in the table's `forward` chain that no live VM owns.
|
|
90
|
+
*
|
|
91
|
+
* A per-VM rule matches one host address or one exact TAP name; everything else
|
|
92
|
+
* in that chain belongs to this provisioner — the tail `jump baseline`, and on
|
|
93
|
+
* a host provisioned by an older boot script, the six baseline rules that used
|
|
94
|
+
* to sit directly in `forward`. Removing only those makes re-provisioning
|
|
95
|
+
* idempotent and upgrades an old-shaped host in place, without the `delete
|
|
96
|
+
* table` that used to strip every running VM's isolation.
|
|
97
|
+
*
|
|
98
|
+
* One exception, and it is not this provisioner's: on a host that also runs a
|
|
99
|
+
* container-mode agent, the agent installs ONE drop set for its whole
|
|
100
|
+
* {@link JOB_NETWORK_SUBNET} rather than one per job container, so that a
|
|
101
|
+
* customer image's `ENTRYPOINT` cannot run ahead of its own rules. nft reports
|
|
102
|
+
* a subnet source as a prefix object, which is exactly what makes
|
|
103
|
+
* `listForwardRules` report no per-agent identifier — so those rules read as
|
|
104
|
+
* this provisioner's and would be reaped, leaving every job container already
|
|
105
|
+
* running at that moment with no egress filtering until the next job repairs
|
|
106
|
+
* it. They are spared by name.
|
|
107
|
+
*
|
|
108
|
+
* Best-effort: a chain that cannot be read yields no rules, so a fresh host
|
|
109
|
+
* sweeps nothing and provisioning proceeds. A chain whose TEXT cannot be read
|
|
110
|
+
* sweeps nothing either — the spared set is unknown at that point, and deleting
|
|
111
|
+
* an unclassified rule from a chain carrying a security control is the wrong
|
|
112
|
+
* direction to guess in.
|
|
113
|
+
*/
|
|
114
|
+
export declare function sweepUnownedForwardRules(cfg: FirecrackerBridgeConfig, opts?: ExecOptions): Promise<number>;
|
|
77
115
|
/** Provision (or heal) one Firecracker host bridge. Throws on any failure. */
|
|
78
116
|
export declare function provisionBridge(cfg: FirecrackerBridgeConfig, opts?: ExecOptions): Promise<void>;
|
|
79
117
|
/** Read-only health probe for one bridge. Never throws on a missing resource. */
|
|
80
118
|
export declare function verifyBridge(cfg: FirecrackerBridgeConfig, opts?: ExecOptions): Promise<BridgeHealth>;
|
|
119
|
+
/**
|
|
120
|
+
* True when the LAST rule of a `nft -j list chain … forward` listing is a jump
|
|
121
|
+
* to {@link BASELINE_CHAIN}.
|
|
122
|
+
*
|
|
123
|
+
* Position is the assertion, not mere presence: a jump that is not last would
|
|
124
|
+
* let the baseline's unconditional internet accept run before a per-VM
|
|
125
|
+
* `denyAll` drop.
|
|
126
|
+
*/
|
|
127
|
+
export declare function forwardEndsInBaselineJump(nftJson: string): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Names of the kici-* TAPs enslaved to the bridge that are NOT port-isolated,
|
|
130
|
+
* read from `ip -d -j link show master <bridge>`.
|
|
131
|
+
*
|
|
132
|
+
* Only kici-* TAPs are checked: an operator may legitimately enslave another
|
|
133
|
+
* interface, and isolating it is not this module's business.
|
|
134
|
+
*/
|
|
135
|
+
export declare function unisolatedTapNames(ipJson: string): string[];
|
|
81
136
|
/** Remove the bridge + its nft table. Leaves the host-scoped NM conf in place. */
|
|
82
137
|
export declare function teardownBridge(cfg: FirecrackerBridgeConfig, opts?: ExecOptions): Promise<void>;
|
|
83
138
|
/** Serialize the provisioning command list into a dependency-free boot script. */
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
*
|
|
19
19
|
* Knows nothing about git, clones, or agents.
|
|
20
20
|
*/
|
|
21
|
-
import type { GitCredentialRef, GitCredentialResult, ProviderGitAuth } from '@kici-dev/engine';
|
|
21
|
+
import type { GitCredentialRef, GitCredentialResult, ProviderGitAuth, TrustTier } from '@kici-dev/engine';
|
|
22
|
+
import type { ContextStore } from '../contexts/context-store.js';
|
|
23
|
+
import type { JobDispatchContext } from '../contexts/protection/pipeline.js';
|
|
22
24
|
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
23
25
|
import { type MintedToken } from './installation-token.js';
|
|
24
26
|
/** Injected so tests need no network and so the mint seam stays explicit. */
|
|
@@ -35,6 +37,19 @@ type MintFn = (args: {
|
|
|
35
37
|
* the decryption stays in the one place that already owns it.
|
|
36
38
|
*/
|
|
37
39
|
type SecretOutputFn = (runId: string, jobId: string, key: string) => Promise<string | null>;
|
|
40
|
+
/**
|
|
41
|
+
* What a qualified `<context>:<key>` reference is authorized against.
|
|
42
|
+
*
|
|
43
|
+
* Required, not optional: a caller that could omit it would silently get the
|
|
44
|
+
* ungated lookup back, which is the exact shape this class used to have. A
|
|
45
|
+
* caller with no job to authorize against — there is none today — would have to
|
|
46
|
+
* say so explicitly.
|
|
47
|
+
*/
|
|
48
|
+
export interface BrokerGateContext {
|
|
49
|
+
/** The dispatch facts the named context's protection rules evaluate against. */
|
|
50
|
+
dispatchCtx: JobDispatchContext;
|
|
51
|
+
trustTier: TrustTier | undefined;
|
|
52
|
+
}
|
|
38
53
|
export interface BrokerResolveArgs {
|
|
39
54
|
orgId: string;
|
|
40
55
|
repositories: readonly string[];
|
|
@@ -43,14 +58,23 @@ export interface BrokerResolveArgs {
|
|
|
43
58
|
permissions?: Readonly<Record<string, string>>;
|
|
44
59
|
runId?: string;
|
|
45
60
|
jobId?: string;
|
|
61
|
+
/** Server truth a `<context>:<key>` reference is gated on. */
|
|
62
|
+
gate: BrokerGateContext;
|
|
46
63
|
}
|
|
47
64
|
export declare class GitCredentialBroker {
|
|
48
65
|
private readonly secretResolver;
|
|
66
|
+
private readonly contextStore;
|
|
49
67
|
private readonly sourceAuth;
|
|
50
68
|
private readonly mint;
|
|
51
69
|
private readonly secretOutputs;
|
|
52
70
|
constructor(deps: {
|
|
53
71
|
secretResolver: SecretResolverApi;
|
|
72
|
+
/**
|
|
73
|
+
* Reads the context a qualified reference names, so its protection rules
|
|
74
|
+
* can run. Required: without it there is no gate, and a broker with no gate
|
|
75
|
+
* resolves any secret in the org.
|
|
76
|
+
*/
|
|
77
|
+
contextStore: ContextStore;
|
|
54
78
|
/**
|
|
55
79
|
* Yields the source credential for a repository — normally
|
|
56
80
|
* `bundle.cloneTokenProvider.issueGitAuth(...)`. Injected rather than
|
|
@@ -79,7 +103,16 @@ export declare class GitCredentialBroker {
|
|
|
79
103
|
* `isQualifiedSecretRef` in `packages/sdk/src/workflow.ts`.
|
|
80
104
|
*/
|
|
81
105
|
private sourced;
|
|
82
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Route a qualified reference to the store that owns it.
|
|
108
|
+
*
|
|
109
|
+
* A store-backed reference goes through the job secret gate, which refuses a
|
|
110
|
+
* reserved namespace, refuses an untrusted contributor, and runs the NAMED
|
|
111
|
+
* context's protection rules before reading anything. The reserved `needs:`
|
|
112
|
+
* context keeps its own path: it reads no secret store at all, only a
|
|
113
|
+
* run-scoped output bound to `(runId, jobId)` and written by an upstream this
|
|
114
|
+
* job declared in `needs` — so the `needs` edge is already the authorization.
|
|
115
|
+
*/
|
|
83
116
|
private resolveQualified;
|
|
84
117
|
/**
|
|
85
118
|
* Resolve a secret output this job inherited from a `needs` upstream.
|
|
@@ -6,13 +6,18 @@
|
|
|
6
6
|
* `run_secret_outputs`, and is deleted when the run completes — so this reads
|
|
7
7
|
* the store that already exists rather than adding a second one.
|
|
8
8
|
*/
|
|
9
|
+
import type { ResolvedMasterKeys } from '../secrets/config.js';
|
|
9
10
|
import type { SecretOutputStore } from '../secrets/secret-output-store.js';
|
|
10
11
|
export interface InheritedSecretDeps {
|
|
11
12
|
secretOutputStore: SecretOutputStore;
|
|
12
13
|
/** Upstream job ids for a job, in the same shape the run merge path uses. */
|
|
13
14
|
upstreamJobIds: (runId: string, jobId: string) => Promise<string[]>;
|
|
14
|
-
/**
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The orchestrator's master key, plus the previous generation during a
|
|
17
|
+
* rotation grace window — a stored output sealed under the old key still
|
|
18
|
+
* unseals until `rotate-key` has swept the table.
|
|
19
|
+
*/
|
|
20
|
+
masterKeys: ResolvedMasterKeys;
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
18
23
|
* Build the `secretOutputs` lookup the credential broker takes.
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-job facts the git credential relay needs, read from server truth.
|
|
3
3
|
*
|
|
4
|
-
* The relay never takes
|
|
5
|
-
*
|
|
6
|
-
* orchestrator itself wrote at dispatch
|
|
4
|
+
* The relay never takes any of these from request params — an agent could name
|
|
5
|
+
* any repository, declare any credential, and claim any trust tier it liked.
|
|
6
|
+
* Every field comes from rows the orchestrator itself wrote at dispatch: the
|
|
7
|
+
* run row for the org, the repository, the branch, the trigger and the
|
|
8
|
+
* contributor's trust tier, and the dispatch record for the credentials the
|
|
9
|
+
* job's lock entry declared.
|
|
7
10
|
*/
|
|
8
11
|
import type { Kysely } from 'kysely';
|
|
9
12
|
import type { Database } from '../db/types.js';
|
|
10
13
|
import type { JobCredentialContext } from '../ws/git-credential-relay.js';
|
|
11
14
|
/** Build the `jobContext` lookup the git credential handler takes. */
|
|
12
|
-
export declare function createJobCredentialContextReader(db: Kysely<Database>): (runId: string) => Promise<JobCredentialContext | null>;
|
|
15
|
+
export declare function createJobCredentialContextReader(db: Kysely<Database>): (runId: string, jobId: string) => Promise<JobCredentialContext | null>;
|
|
13
16
|
//# sourceMappingURL=job-context.d.ts.map
|