@kici-dev/orchestrator 0.6.1 → 0.7.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 +12525 -9298
- 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/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/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/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 +33 -0
- package/dist/scaler/scaler-state-store.d.ts +6 -0
- package/dist/scaler/types.d.ts +5 -7
- 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/reduced-privilege-note.d.ts +5 -0
- 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 +10661 -5282
- package/dist/sources/source-manager.d.ts +1 -1
- package/dist/stale-detector/stale-dispatch-candidates.d.ts +43 -0
- package/dist/standalone.js +9258 -4122
- 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/ws/git-credential-relay.d.ts +41 -12
- package/dist/ws/platform-client.d.ts +28 -5
- 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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* differs only in the local sentinel issuer (`kici-local`) and the org anchor
|
|
9
9
|
* (`__default__`, the plane's default org).
|
|
10
10
|
*/
|
|
11
|
-
import { AttestationOrigin, SourceOrigin } from '@kici-dev/engine';
|
|
11
|
+
import { AttestationOrigin, SourceOrigin, type EventClaims } from '@kici-dev/engine';
|
|
12
12
|
/** The subset of an execution_runs row the claims need. */
|
|
13
13
|
export interface RunClaimSource {
|
|
14
14
|
run_id: string;
|
|
@@ -20,6 +20,24 @@ export interface RunClaimSource {
|
|
|
20
20
|
provider: string | null;
|
|
21
21
|
/** True when the run executed an uploaded local working tree (`kici run remote`). */
|
|
22
22
|
local_working_tree: boolean;
|
|
23
|
+
/** Normalized event that started the run (`push`, `pull_request:opened`, …). */
|
|
24
|
+
trigger_event?: string | null;
|
|
25
|
+
/**
|
|
26
|
+
* The event the SUBJECT is derived from, when that is not `trigger_event`.
|
|
27
|
+
* A re-run carries the original run's event here. NULL means "use
|
|
28
|
+
* `trigger_event`". Read only by `buildIdTokenSubject`.
|
|
29
|
+
*/
|
|
30
|
+
subject_trigger_event?: string | null;
|
|
31
|
+
/** Pull-request HEAD branch. */
|
|
32
|
+
head_ref?: string | null;
|
|
33
|
+
/** `owner/repo` of the pull-request HEAD. */
|
|
34
|
+
head_repository?: string | null;
|
|
35
|
+
/** True for a fork pull request; NULL when the run resolved no answer. */
|
|
36
|
+
is_fork?: boolean | null;
|
|
37
|
+
/** Resolved trust tier for the triggering actor. */
|
|
38
|
+
trust_tier?: string | null;
|
|
39
|
+
/** Provider login of the triggering actor. */
|
|
40
|
+
trigger_actor_username?: string | null;
|
|
23
41
|
}
|
|
24
42
|
/** The subset of an execution_jobs row the claims need. */
|
|
25
43
|
export interface JobClaimSource {
|
|
@@ -43,8 +61,21 @@ export interface BuildClaimsOpts {
|
|
|
43
61
|
statementHash: string;
|
|
44
62
|
origin: 'deferred' | 'offline-backfill';
|
|
45
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* Restore the pre-split pull-request `sub` — the branch-shaped form that
|
|
66
|
+
* collides with a push to the same base branch. An escape hatch for an
|
|
67
|
+
* operator whose cloud trust policy breaks mid-migration, set by
|
|
68
|
+
* `KICI_OIDC_LEGACY_PR_SUB`.
|
|
69
|
+
*
|
|
70
|
+
* @deprecated Removed at v1.0.0. Move the policy to `repo:<repo>:pull_request`.
|
|
71
|
+
*/
|
|
72
|
+
legacyPullRequestSubject?: boolean;
|
|
46
73
|
}
|
|
47
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Extends {@link EventClaims}, so every token carries the event-context claims
|
|
76
|
+
* a cloud trust policy needs to tell a fork pull request from a push.
|
|
77
|
+
*/
|
|
78
|
+
export interface IdTokenClaims extends EventClaims {
|
|
48
79
|
iss: string;
|
|
49
80
|
sub: string;
|
|
50
81
|
aud: string;
|
|
@@ -56,6 +56,12 @@ export interface OidcMintRegistrationInput {
|
|
|
56
56
|
};
|
|
57
57
|
db: Kysely<Database>;
|
|
58
58
|
orchestratorId: string;
|
|
59
|
+
/**
|
|
60
|
+
* `KICI_OIDC_LEGACY_PR_SUB` — restore the pre-split pull-request `sub`.
|
|
61
|
+
*
|
|
62
|
+
* @deprecated Removed at v1.0.0.
|
|
63
|
+
*/
|
|
64
|
+
legacyPullRequestSubject?: boolean;
|
|
59
65
|
/**
|
|
60
66
|
* Test-only fault-injection predicate forwarded to the relay mint handler,
|
|
61
67
|
* supplied only by the build-time test double. Only ever defers — the
|
|
@@ -36,6 +36,14 @@ export interface OrchestratorMintDeps {
|
|
|
36
36
|
ttlSeconds?: number;
|
|
37
37
|
/** Injectable clock (seconds). Defaults to wall-clock. */
|
|
38
38
|
nowSeconds?: () => number;
|
|
39
|
+
/**
|
|
40
|
+
* Restore the pre-split pull-request `sub`, from `KICI_OIDC_LEGACY_PR_SUB`.
|
|
41
|
+
* An escape hatch for an operator whose cloud trust policy breaks mid-
|
|
42
|
+
* migration; leaves the collision with a same-branch push in place.
|
|
43
|
+
*
|
|
44
|
+
* @deprecated Removed at v1.0.0.
|
|
45
|
+
*/
|
|
46
|
+
legacyPullRequestSubject?: boolean;
|
|
39
47
|
}
|
|
40
48
|
export interface OrchestratorMintInput {
|
|
41
49
|
runId: string;
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot reconcile for the orchestrator's provenance signing key. Ensures exactly
|
|
3
|
+
* one `active` key exists and returns a live `Signer` bound to it.
|
|
4
|
+
*
|
|
5
|
+
* - `db` custody: the private key is a random keypair generated + persisted
|
|
6
|
+
* (master-key-wrapped) in the DB. Generation is LEADER-GATED so an HA cluster
|
|
7
|
+
* never races two active keys; every node then loads the one active row. A
|
|
8
|
+
* non-leader that finds no active row yet returns null this tick and is
|
|
9
|
+
* retried on the next leadership/boot cycle.
|
|
10
|
+
* - `aws-kms` / `command` custody: the key lives outside KiCI, so every node
|
|
11
|
+
* resolves the SAME kid and `upsertActive` is idempotent across the fleet.
|
|
12
|
+
*/
|
|
13
|
+
import type { JWK } from 'jose';
|
|
1
14
|
import { type OrchestratorSignerConfig } from './orchestrator-signer-factory.js';
|
|
2
15
|
import type { Signer } from './signer.js';
|
|
3
16
|
import type { OrchestratorSigningKeyRepo } from '../db/repos/signing-keys-repo.js';
|
|
@@ -7,6 +20,30 @@ export interface ReconcileSigningKeyDeps {
|
|
|
7
20
|
isLeader: () => boolean;
|
|
8
21
|
/** The orchestrator master key (`KICI_SECRET_KEY`), for `db` custody wrapping. */
|
|
9
22
|
secretKey: string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The previous master key (`KICI_SECRET_KEY_OLD`), during a rotation grace
|
|
25
|
+
* window. The signing key is master-key wrapped, so a rotation that has not
|
|
26
|
+
* yet swept `orchestrator_signing_keys` leaves the row sealed under the old
|
|
27
|
+
* key — without this fallback the boot path throws and provenance signing is
|
|
28
|
+
* dead until the old key is restored.
|
|
29
|
+
*/
|
|
30
|
+
oldSecretKey?: string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Re-seal a row that opened only under the OLD master key, back under the
|
|
33
|
+
* current one. Optional: when unset the row is loaded and left as it is, so
|
|
34
|
+
* signing works but stays stranded. Best-effort — a failure is warned and
|
|
35
|
+
* never blocks the boot.
|
|
36
|
+
*
|
|
37
|
+
* This is the recovery path for a deployment stranded by a rotation that ran
|
|
38
|
+
* before `orchestrator_signing_keys` was part of the sweep: restore the old
|
|
39
|
+
* key once, restart, and the row moves to the current key on its own.
|
|
40
|
+
*/
|
|
41
|
+
selfHeal?: (row: {
|
|
42
|
+
kid: string;
|
|
43
|
+
key_version: number;
|
|
44
|
+
}, privateJwk: JWK) => Promise<void>;
|
|
45
|
+
/** Structured warn sink for the self-heal notice. */
|
|
46
|
+
logWarn?: (message: string, meta: Record<string, unknown>) => void;
|
|
10
47
|
/** Called once when a NEW kid is first activated (audit log, system actor). */
|
|
11
48
|
audit: (info: {
|
|
12
49
|
kid: string;
|
|
@@ -47,7 +47,7 @@ import { LogWriter } from './reporting/log-writer.js';
|
|
|
47
47
|
import { type NormalizedLogChunk } from './reporting/log-chunk-sink.js';
|
|
48
48
|
import { PeerRegistry, PeerClient, createPeerHandler, RaftNode, OrphanRecovery, RunCoordinator, PlanHeadroomStore } from './cluster/index.js';
|
|
49
49
|
import { SourceStore, SourceManager } from './sources/index.js';
|
|
50
|
-
import { PgSecretStore, AuditLogger, SecretResolver } from './secrets/index.js';
|
|
50
|
+
import { type ResolvedMasterKeys, PgSecretStore, AuditLogger, SecretResolver } from './secrets/index.js';
|
|
51
51
|
import type { SecretStore } from '@kici-dev/engine';
|
|
52
52
|
import type { AdminRouteDeps } from './routes/admin.js';
|
|
53
53
|
import { AgentTokenStore } from './agent/token-store.js';
|
|
@@ -56,7 +56,7 @@ import { EventStore } from './events/event-store.js';
|
|
|
56
56
|
import { EventRouter, type EventMatchContext } from './events/event-router.js';
|
|
57
57
|
import { EventEmitter } from './events/event-emitter.js';
|
|
58
58
|
import { TrustStore } from './events/trust-store.js';
|
|
59
|
-
import type { OrchestratorFaultInjection } from './
|
|
59
|
+
import type { OrchestratorFaultInjection } from './fault-injection-types.js';
|
|
60
60
|
import { GenericSourceManager } from './webhook/generic-sources.js';
|
|
61
61
|
import { GenericSourcesChangeListener } from './webhook/generic-sources-listener.js';
|
|
62
62
|
import { DashboardWritePolicyChangeListener } from './policy/dashboard-write-policy-listener.js';
|
|
@@ -392,7 +392,7 @@ export declare function buildMatrixOutputsEnvelope(baseName: string, children: A
|
|
|
392
392
|
* upstream-outputs read doesn't block dispatch — the agent will still get
|
|
393
393
|
* its declared secrets, just without the merged upstream additions.
|
|
394
394
|
*/
|
|
395
|
-
export declare function mergeUpstreamOutputs(db: Kysely<Database>, runId: string, jobName: string, needs: unknown, dispatchSecrets: Record<string, string> | undefined,
|
|
395
|
+
export declare function mergeUpstreamOutputs(db: Kysely<Database>, runId: string, jobName: string, needs: unknown, dispatchSecrets: Record<string, string> | undefined, masterKeys: ResolvedMasterKeys | null): Promise<{
|
|
396
396
|
mergedSecrets: Record<string, string> | undefined;
|
|
397
397
|
upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined;
|
|
398
398
|
upstreamJobStatuses: Record<string, ExecutionJobStatus> | undefined;
|
|
@@ -434,5 +434,11 @@ export declare function bootstrapOrchestrator(config: AppConfig, hooks: Orchestr
|
|
|
434
434
|
* drive the seams directly.
|
|
435
435
|
*/
|
|
436
436
|
faultInjection?: OrchestratorFaultInjection;
|
|
437
|
+
/**
|
|
438
|
+
* This build's version, recorded on the coordinator heartbeat row. Read by
|
|
439
|
+
* an operator during a rolling upgrade to see which coordinators have
|
|
440
|
+
* already moved. Undefined leaves the column NULL.
|
|
441
|
+
*/
|
|
442
|
+
orchestratorVersion?: string;
|
|
437
443
|
}): Promise<void>;
|
|
438
444
|
//# sourceMappingURL=orchestrator-core.d.ts.map
|
|
@@ -23,7 +23,7 @@ export declare class DecoratingSecretResolver implements SecretResolverApi {
|
|
|
23
23
|
private readonly cli;
|
|
24
24
|
constructor(base: SecretResolverApi, cli: CliSecrets);
|
|
25
25
|
resolveForJob(orgId: string, contextName: string, hostCtx?: HostFacts): Promise<Record<string, string>>;
|
|
26
|
-
|
|
26
|
+
resolveNamedInternal(orgId: string, scope: string, key: string, opts?: {
|
|
27
27
|
store?: string;
|
|
28
28
|
runId?: string;
|
|
29
29
|
jobId?: string;
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
* results through the pipeline.
|
|
15
15
|
*/
|
|
16
16
|
import { ExecutionJobStatus, InitFailureCategory, CacheRefScope, HoldScope, TriggerSource } from '@kici-dev/engine';
|
|
17
|
-
import type { LabelMatcher, LockWorkflow, LockJob, ApprovalRequirement, HostTargetSelector, SimulatedEvent, WorkflowDecision, MaterializedJob, ResolvedHostAgent, HostFacts } from '@kici-dev/engine';
|
|
17
|
+
import type { LabelMatcher, LockWorkflow, LockJob, ApprovalRequirement, HostTargetSelector, SimulatedEvent, WorkflowDecision, MaterializedJob, ResolvedHostAgent, UpstreamSnapshot, HostFacts } from '@kici-dev/engine';
|
|
18
18
|
import { type HostRosterStore } from '../agent/host-roster.js';
|
|
19
|
+
import { NEEDS_PENDING_JOB_ID_PREFIX } from '../db/synthetic-job-ids.js';
|
|
19
20
|
import { gatherInvokeResults } from '../orchestrator-core.js';
|
|
20
21
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
21
22
|
import type { ProviderBundle } from '../provider-registry.js';
|
|
@@ -24,6 +25,7 @@ import type { QueuedJobInput } from '../queue/job-queue.js';
|
|
|
24
25
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
25
26
|
import type { TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
26
27
|
import type { CreateHeldRunData } from '../contexts/held-runs.js';
|
|
28
|
+
import { type NpmRegistrySpec } from './install-secrets-resolver.js';
|
|
27
29
|
import { type ProcessingDeps } from './processor.js';
|
|
28
30
|
/**
|
|
29
31
|
* Trusted refs (write+ contributor, default-branch) get the org-shared cache
|
|
@@ -82,26 +84,34 @@ export interface WorkflowDispatchContext {
|
|
|
82
84
|
fullLockFile: {
|
|
83
85
|
workflows: readonly LockWorkflow[];
|
|
84
86
|
lockfileHash?: string;
|
|
87
|
+
/** In-repo `workspace:` sibling closure digest; part of the dep pointer key. */
|
|
88
|
+
siblingsDigest?: string;
|
|
85
89
|
source: {
|
|
86
90
|
file: string;
|
|
87
91
|
};
|
|
88
92
|
};
|
|
89
93
|
resolvedOrgId: string;
|
|
90
94
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
95
|
+
* The NAMES of the jobs this dispatch pass has admitted against a context
|
|
96
|
+
* concurrency limit, keyed by {@link concurrencyAdmissionKey}.
|
|
93
97
|
*
|
|
94
|
-
* The
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
+
* The database count cannot see a job this pass has only just admitted, so
|
|
99
|
+
* without this tally every child of a fan-out is evaluated against that one
|
|
100
|
+
* blind value and all N are admitted against a single slot.
|
|
101
|
+
*
|
|
102
|
+
* Names rather than a counter, because the two terms have to be DISJOINT: the
|
|
103
|
+
* count query excludes exactly the names held here (see
|
|
104
|
+
* {@link countOccupyingJobs}), so a job whose row has already landed
|
|
105
|
+
* contributes its tally entry and nothing else. A counter cannot express that
|
|
106
|
+
* exclusion, and summing it with a count that already sees the row is how the
|
|
107
|
+
* deferred-init path double-counts a sibling and spuriously holds a job.
|
|
98
108
|
*
|
|
99
109
|
* Created lazily on first use and dropped with the ctx. It is per-pass state,
|
|
100
|
-
* never persisted and never shared between passes; two
|
|
101
|
-
* still race
|
|
102
|
-
* isolation boundary.
|
|
110
|
+
* never persisted and never shared between passes; two actors arriving in the
|
|
111
|
+
* same millisecond still race between the count and the insert, which is
|
|
112
|
+
* accepted — this is a throughput control, not an isolation boundary.
|
|
103
113
|
*/
|
|
104
|
-
concurrencyAdmissions?: Map<string,
|
|
114
|
+
concurrencyAdmissions?: Map<string, Set<string>>;
|
|
105
115
|
workflow: LockWorkflow;
|
|
106
116
|
decision: WorkflowDecision;
|
|
107
117
|
runId: string;
|
|
@@ -373,6 +383,8 @@ interface DispatchSetup {
|
|
|
373
383
|
interface BuildPrepResult {
|
|
374
384
|
sourceTarUrl: string | undefined;
|
|
375
385
|
sourceTarHash: string | undefined;
|
|
386
|
+
/** SHA-256 of the source tarball's own bytes; the agent verifies against it. */
|
|
387
|
+
sourceTarDigest: string | undefined;
|
|
376
388
|
depsUrl: string | undefined;
|
|
377
389
|
depsHash: string | undefined;
|
|
378
390
|
contentHash: string | undefined;
|
|
@@ -482,8 +494,11 @@ interface JobEnvEvalResult {
|
|
|
482
494
|
* keys on it, so it is load-bearing rather than cosmetic — which is what makes
|
|
483
495
|
* it a sound way to recover the gated set without threading a parallel list
|
|
484
496
|
* through both the single-orchestrator and cluster dispatch paths.
|
|
497
|
+
*
|
|
498
|
+
* Re-exported from `db/synthetic-job-ids.ts`, which owns it so the concurrency
|
|
499
|
+
* sweep in `contexts/` can exclude these rows without importing this module.
|
|
485
500
|
*/
|
|
486
|
-
export
|
|
501
|
+
export { NEEDS_PENDING_JOB_ID_PREFIX };
|
|
487
502
|
interface RejectedJob {
|
|
488
503
|
jobId: string;
|
|
489
504
|
jobName: string;
|
|
@@ -510,7 +525,7 @@ interface RejectedJob {
|
|
|
510
525
|
* cloud-hosted one-shot agent, an air-gapped runner) could not run the job at
|
|
511
526
|
* all. The `__build__` job already carries `buildSourceNeeded` /
|
|
512
527
|
* `buildDepsNeeded` separately and the agent already honors both, so a
|
|
513
|
-
* deps-only build was implemented and
|
|
528
|
+
* deps-only build was implemented and unreachable.
|
|
514
529
|
*
|
|
515
530
|
* A hash is required alongside its miss: with no hash there is no cache key to
|
|
516
531
|
* write, so building would produce an artifact nothing could ever look up.
|
|
@@ -563,6 +578,16 @@ interface JobRoutingSelectors {
|
|
|
563
578
|
runsOnPatterns: LabelMatcher[];
|
|
564
579
|
excludeLabels: string[];
|
|
565
580
|
excludePatterns: LabelMatcher[];
|
|
581
|
+
/**
|
|
582
|
+
* The `runsOn` exact values exactly as the lock file spells them, before the
|
|
583
|
+
* canonical fold `partitionMatchers` applies.
|
|
584
|
+
*
|
|
585
|
+
* Only {@link resolveRosterAgentPin} reads this. A bare agent id in `runsOn`
|
|
586
|
+
* is the documented inventory fan-out pattern (`runsOn: [h.agentId]`), and an
|
|
587
|
+
* agent id is not a label: `host_roster.agent_id` is case-sensitive, so the
|
|
588
|
+
* folded form misses every host whose id carries a capital.
|
|
589
|
+
*/
|
|
590
|
+
runsOnExactRaw: string[];
|
|
566
591
|
}
|
|
567
592
|
/**
|
|
568
593
|
* Partition a lock job's runsOn / excludeLabels matchers into exact labels (SQL
|
|
@@ -606,9 +631,15 @@ export declare function requiredRuntimeLabelsFor(container: unknown): string[];
|
|
|
606
631
|
* cross-cluster reroute) — exact parity with how `runsOnAll` children pin. Any
|
|
607
632
|
* other shape (multi-label, a regex pattern, a non-roster label, or no roster
|
|
608
633
|
* store) returns null and the caller keeps normal label routing.
|
|
634
|
+
*
|
|
635
|
+
* `runsOnExact` takes the values the lock file spells, NOT the canonical labels
|
|
636
|
+
* every other routing path uses: this looks the value up as an agent id, and
|
|
637
|
+
* agent ids are case-sensitive. Handing it folded labels strands every host
|
|
638
|
+
* whose id carries a capital on the normal label path, where a bare agent id
|
|
639
|
+
* matches nothing.
|
|
609
640
|
*/
|
|
610
641
|
export declare function resolveRosterAgentPin(args: {
|
|
611
|
-
|
|
642
|
+
runsOnExact: string[];
|
|
612
643
|
runsOnPatterns: LabelMatcher[];
|
|
613
644
|
hostRosterStore: HostRosterStore | undefined;
|
|
614
645
|
}): Promise<{
|
|
@@ -865,6 +896,12 @@ export interface GeneratedJobConfig {
|
|
|
865
896
|
connectedInstanceId?: string | null;
|
|
866
897
|
/** The matrix combination for this child; absent for non-matrix generated jobs. */
|
|
867
898
|
matrixValues?: Record<string, unknown>;
|
|
899
|
+
/**
|
|
900
|
+
* The `gitCredentials` map the generator's LOCK entry declared, inherited by
|
|
901
|
+
* this child. Persisted to `execution_jobs.git_credentials` at every dispatch
|
|
902
|
+
* site below, which is what the credential relay authorizes a request against.
|
|
903
|
+
*/
|
|
904
|
+
gitCredentials?: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
868
905
|
}
|
|
869
906
|
/**
|
|
870
907
|
* Split generated configs into pinned (host-pin dispatch) and unpinned (normal
|
|
@@ -876,6 +913,20 @@ export declare function partitionGeneratedConfigsByPin(configs: readonly Generat
|
|
|
876
913
|
pinnedConfigs: GeneratedJobConfig[];
|
|
877
914
|
unpinnedConfigs: GeneratedJobConfig[];
|
|
878
915
|
};
|
|
916
|
+
export declare function resolveGeneratedJobConfigs(args: {
|
|
917
|
+
ctx: WorkflowDispatchContext;
|
|
918
|
+
workflow: LockWorkflow;
|
|
919
|
+
fullLockFile: WorkflowDispatchContext['fullLockFile'];
|
|
920
|
+
resolvedSecrets: Record<string, string> | undefined;
|
|
921
|
+
resolvedNamespacedSecrets: Record<string, Record<string, string>> | undefined;
|
|
922
|
+
runPublicKeyBase64: string | undefined;
|
|
923
|
+
npmRegistries: NpmRegistrySpec[] | undefined;
|
|
924
|
+
installEnvSecrets: Record<string, string> | undefined;
|
|
925
|
+
generatedJobs: LockJob[];
|
|
926
|
+
dynamicEntry: BuildPrepResult['dynamicEntries'][number];
|
|
927
|
+
/** Frozen upstream snapshot threaded into each generated job's dynamicSource for re-eval. */
|
|
928
|
+
upstreamSnapshot?: UpstreamSnapshot;
|
|
929
|
+
}): Promise<GeneratedJobConfig[]>;
|
|
879
930
|
/**
|
|
880
931
|
* Recompute the needs gate for `jobNames` and act on whatever became ready:
|
|
881
932
|
* open the gate for a satisfied job, terminalize a job an upstream's status
|
|
@@ -49,6 +49,7 @@ export declare function buildManualJobConfig(workflow: LockWorkflow, mat: Materi
|
|
|
49
49
|
runOn: ("cancelled" | "cancelling" | "drift_dropped" | "failed" | "pending" | "queued" | "recovering" | "running" | "skipped" | "success" | "timed_out_stale" | "unroutable")[];
|
|
50
50
|
})[];
|
|
51
51
|
rules: readonly import("@kici-dev/engine").LockRule[] | undefined;
|
|
52
|
+
gitCredentials?: Readonly<Record<string, Readonly<Record<string, string>>>> | undefined;
|
|
52
53
|
dispatchInputs?: Record<string, unknown> | undefined;
|
|
53
54
|
contentHash?: string | undefined;
|
|
54
55
|
resolvedHashFiles?: string[] | undefined;
|
|
@@ -22,7 +22,7 @@ import type { OrchestratorMode } from '@kici-dev/engine';
|
|
|
22
22
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
23
23
|
import type { ProviderBundle } from '../provider-registry.js';
|
|
24
24
|
import { type TrustResolution } from '../security/trust-resolver.js';
|
|
25
|
-
import { type TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
25
|
+
import { type ForkSwitchContext, type TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
26
26
|
import { type WorkflowModification } from '../security/workflow-diff.js';
|
|
27
27
|
import type { IdentityLink, PermissionLevel } from '../security/identity-link.js';
|
|
28
28
|
import { type ProcessingDeps } from './processor.js';
|
|
@@ -143,6 +143,20 @@ export declare function applyWorkflowModificationsAndSecurityHold(args: {
|
|
|
143
143
|
ref: string;
|
|
144
144
|
credentials: Record<string, unknown>;
|
|
145
145
|
}): SecurityState;
|
|
146
|
+
/**
|
|
147
|
+
* A trust-policy verdict plus the fork switch it was decided under.
|
|
148
|
+
*
|
|
149
|
+
* The first arm is the short-circuit: a non-PR event, or a provider with no
|
|
150
|
+
* fork model, decides `pass` before any policy is read, so there is no switch
|
|
151
|
+
* to report. Every other verdict comes from an actual policy and carries it —
|
|
152
|
+
* which is what lets the drop site read `forkSwitch` without a fallback that
|
|
153
|
+
* would have to invent a source it never observed.
|
|
154
|
+
*/
|
|
155
|
+
export type SecurityPolicyDecision = {
|
|
156
|
+
action: 'pass';
|
|
157
|
+
} | (TrustPolicyOutcome & {
|
|
158
|
+
forkSwitch: ForkSwitchContext;
|
|
159
|
+
});
|
|
146
160
|
/**
|
|
147
161
|
* Evaluate the org trust policy for a PR event.
|
|
148
162
|
*
|
|
@@ -157,6 +171,10 @@ export declare function applyWorkflowModificationsAndSecurityHold(args: {
|
|
|
157
171
|
* The policy read has two distinct failure shapes, and they get distinct
|
|
158
172
|
* answers: no stored row is the unconfigured default (`FAIL_CLOSED_POLICY`),
|
|
159
173
|
* while a read that THREW gets `READ_FAILURE_POLICY` — see there for why.
|
|
174
|
+
*
|
|
175
|
+
* The verdict carries the fork switch it was decided under, so the drop site
|
|
176
|
+
* can explain itself. It is absent on the two short-circuit `pass` arms below,
|
|
177
|
+
* which return before any policy is read.
|
|
160
178
|
*/
|
|
161
179
|
export declare function evaluateSecurityPolicy(args: {
|
|
162
180
|
deps: ProcessingDeps;
|
|
@@ -166,7 +184,7 @@ export declare function evaluateSecurityPolicy(args: {
|
|
|
166
184
|
mode: OrchestratorMode;
|
|
167
185
|
trustResolution: TrustResolution | undefined;
|
|
168
186
|
isForkPR: boolean;
|
|
169
|
-
}): Promise<
|
|
187
|
+
}): Promise<SecurityPolicyDecision>;
|
|
170
188
|
/**
|
|
171
189
|
* Phase J — After per-repo workflows are dispatched, query the global index
|
|
172
190
|
* for workflows authored in OTHER repos that target this event type via
|
|
@@ -34,6 +34,13 @@ export interface DispatchedJobEntry {
|
|
|
34
34
|
jobKind?: JobKind;
|
|
35
35
|
/** For a gate job, its wall-clock timeout in ms (orchestrator-swept). */
|
|
36
36
|
timeoutMs?: number;
|
|
37
|
+
/**
|
|
38
|
+
* The `gitCredentials` map this job's lock entry declared, verbatim.
|
|
39
|
+
* Persisted to `execution_jobs.git_credentials`, which the credential relay
|
|
40
|
+
* authorizes a workflow-supplied ref against. Dropping it here would refuse
|
|
41
|
+
* every declared credential on a job this path registered.
|
|
42
|
+
*/
|
|
43
|
+
gitCredentials?: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
37
44
|
}
|
|
38
45
|
export interface RejectedJobEntry {
|
|
39
46
|
jobId: string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the pull-request head context an `execution_runs` row records.
|
|
3
|
+
*
|
|
4
|
+
* `execution_runs.ref` is the branch a run PRESENTS — the BASE branch for a
|
|
5
|
+
* pull request — so it says which branch a run targeted and nothing about where
|
|
6
|
+
* the code came from. These three fields are the missing half, and the OIDC
|
|
7
|
+
* mint reads them: without them a fork pull request and a trusted push to the
|
|
8
|
+
* same base branch mint an identical identity.
|
|
9
|
+
*/
|
|
10
|
+
import { type SimulatedEvent } from '@kici-dev/engine';
|
|
11
|
+
export interface RunEventContext {
|
|
12
|
+
headRef: string | null;
|
|
13
|
+
headRepository: string | null;
|
|
14
|
+
isFork: boolean | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Derive the head context from a normalized event.
|
|
18
|
+
*
|
|
19
|
+
* `isFork` is resolved from the event TYPE, not from the presence of the
|
|
20
|
+
* optional flag. A push, tag or schedule has no fork dimension, so `false` is
|
|
21
|
+
* its true resolved value — not a stand-in. Leaving those unresolved would make
|
|
22
|
+
* every `is_fork = 'false'` cloud trust policy reject legitimate pushes, and the
|
|
23
|
+
* customer's fix for that (accepting `'unresolved'` too) would re-open the hole
|
|
24
|
+
* the claim exists to close.
|
|
25
|
+
*
|
|
26
|
+
* NULL survives for exactly one case: a PR-family event whose payload carries
|
|
27
|
+
* no head repository, so the fork question genuinely has no answer. A lost
|
|
28
|
+
* write leaves all three NULL, which every claim renders as unresolved and a
|
|
29
|
+
* policy that pins them therefore fails closed.
|
|
30
|
+
*
|
|
31
|
+
* The head repository is what decides that case, NOT the presence of the
|
|
32
|
+
* `isForkPR` flag. Every normalizer derives that flag as `head !== base`, which
|
|
33
|
+
* collapses to `false` when the payload names no head repository — a deleted
|
|
34
|
+
* fork, or a trimmed payload. Reading the flag alone would therefore publish
|
|
35
|
+
* `is_fork = 'false'` for a pull request nobody resolved, which is the fail-OPEN
|
|
36
|
+
* direction this claim exists to remove.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveRunEventContext(event: Pick<SimulatedEvent, 'type' | 'sourceBranch' | 'headRepo' | 'isForkPR'>): RunEventContext;
|
|
39
|
+
//# sourceMappingURL=run-event-context.d.ts.map
|
|
@@ -47,7 +47,7 @@ export interface DashboardWritePolicyChangeListenerOptions {
|
|
|
47
47
|
}
|
|
48
48
|
export declare class DashboardWritePolicyChangeListener {
|
|
49
49
|
private readonly opts;
|
|
50
|
-
private
|
|
50
|
+
private listener;
|
|
51
51
|
private readonly pending;
|
|
52
52
|
private debounceTimer;
|
|
53
53
|
private readonly debounceMs;
|
|
@@ -22,7 +22,7 @@ import { EventEmitter } from 'node:events';
|
|
|
22
22
|
import { type Kysely } from 'kysely';
|
|
23
23
|
import { DashboardWriteOperation, type DashboardWritePolicyMap, type DashboardWritePolicyState } from '@kici-dev/engine/protocol/dashboard-write-operations';
|
|
24
24
|
import type { Database } from '../db/types.js';
|
|
25
|
-
import type
|
|
25
|
+
import { type ActorPrincipal, type OrchestratorMode } from '@kici-dev/engine';
|
|
26
26
|
/**
|
|
27
27
|
* Postgres `LISTEN/NOTIFY` channel carrying policy changes across the
|
|
28
28
|
* cluster. The payload is the `customer_id` alone — every coordinator
|
|
@@ -54,6 +54,18 @@ export declare class DashboardWritePolicyDisabledError extends Error {
|
|
|
54
54
|
readonly code: 'operation_disabled';
|
|
55
55
|
constructor(operation: DashboardWriteOperation, cliEquivalent: string);
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Error thrown by `setDashboardWritePolicy` when a write would leave a context
|
|
59
|
+
* or reviewer hold with no surface that can answer it. Translated into a 409 by
|
|
60
|
+
* the admin route.
|
|
61
|
+
*/
|
|
62
|
+
export declare class HeldRunLockoutRefusedError extends Error {
|
|
63
|
+
readonly operations: DashboardWriteOperation[];
|
|
64
|
+
readonly code: 'held_run_lockout_refused';
|
|
65
|
+
constructor(operations: DashboardWriteOperation[]);
|
|
66
|
+
}
|
|
67
|
+
/** Say which operations were refused, and why there is no fallback for them. */
|
|
68
|
+
export declare function heldRunLockoutMessage(operations: readonly DashboardWriteOperation[]): string;
|
|
57
69
|
/**
|
|
58
70
|
* Structured response envelope sent back to Platform when a mutating
|
|
59
71
|
* dashboard.* handler is rejected by the policy gate. Mirrors the
|
|
@@ -104,9 +116,16 @@ export declare function assertDashboardWriteAllowed(db: Kysely<Database>, custom
|
|
|
104
116
|
*
|
|
105
117
|
* Emits `'changed'` on `dashboardWritePolicyEvents` with the new map
|
|
106
118
|
* so the WS publisher can rebroadcast capabilities.
|
|
119
|
+
*
|
|
120
|
+
* Throws {@link HeldRunLockoutRefusedError} when the write would disable a
|
|
121
|
+
* held-run operation on a Platform-attached orchestrator, where no other surface
|
|
122
|
+
* can answer a hold. The check lives here rather than in the admin route so a
|
|
123
|
+
* caller hitting the API directly is refused too.
|
|
107
124
|
*/
|
|
108
125
|
export declare function setDashboardWritePolicy(db: Kysely<Database>, customerId: string, updates: DashboardWritePolicyMap, options: {
|
|
109
126
|
actor: ActorPrincipal;
|
|
127
|
+
/** This orchestrator's mode; decides whether a held-run disable is coherent. */
|
|
128
|
+
mode: OrchestratorMode;
|
|
110
129
|
onChange?: (event: PolicyChangeEvent) => Promise<void>;
|
|
111
130
|
}): Promise<DashboardWritePolicyMap>;
|
|
112
131
|
/**
|
|
@@ -128,7 +147,34 @@ export interface PolicyChangeEvent {
|
|
|
128
147
|
*/
|
|
129
148
|
export declare function resetDashboardWritePolicy(db: Kysely<Database>, customerId: string, options: {
|
|
130
149
|
actor: ActorPrincipal;
|
|
150
|
+
mode: OrchestratorMode;
|
|
131
151
|
onChange?: (event: PolicyChangeEvent) => Promise<void>;
|
|
132
152
|
}): Promise<DashboardWritePolicyMap>;
|
|
153
|
+
/**
|
|
154
|
+
* Report every org whose stored policy already leaves its context and reviewer
|
|
155
|
+
* holds unanswerable.
|
|
156
|
+
*
|
|
157
|
+
* The refusal above only guards FUTURE writes, and the operator hardening
|
|
158
|
+
* postures documented before it existed recommended exactly this disable — so a
|
|
159
|
+
* deployment that followed them is locked out right now and would learn nothing
|
|
160
|
+
* from a check it never trips.
|
|
161
|
+
*
|
|
162
|
+
* Deliberately read-only. Silently re-enabling a write an operator disabled on
|
|
163
|
+
* purpose is a worse failure than the one being reported: the operator chose
|
|
164
|
+
* that posture, and only they can decide whether to keep the lockout, re-enable
|
|
165
|
+
* the operation, or move the orchestrator to independent mode.
|
|
166
|
+
*/
|
|
167
|
+
export declare function findHeldRunLockouts(db: Kysely<Database>, mode: OrchestratorMode): Promise<Array<{
|
|
168
|
+
customerId: string;
|
|
169
|
+
operations: DashboardWriteOperation[];
|
|
170
|
+
}>>;
|
|
171
|
+
/**
|
|
172
|
+
* Log one warning per org already in that state, at boot.
|
|
173
|
+
*
|
|
174
|
+
* Swallows its own failure: a policy read that cannot answer must not stop an
|
|
175
|
+
* orchestrator from starting, and the condition it reports is a standing one
|
|
176
|
+
* that the next boot reports again.
|
|
177
|
+
*/
|
|
178
|
+
export declare function warnOnHeldRunLockouts(db: Kysely<Database>, mode: OrchestratorMode): Promise<void>;
|
|
133
179
|
export { resolveFullPolicyView } from '@kici-dev/engine/protocol/dashboard-write-operations';
|
|
134
180
|
//# sourceMappingURL=dashboard-write-policy.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical build context the orchestrator sends with a job, so the agent
|
|
3
|
+
* can freeze a provenance statement that says what a live mint would have said.
|
|
4
|
+
*
|
|
5
|
+
* A deferred attestation is signed BEFORE its identity token exists, so the
|
|
6
|
+
* agent used to build the frozen statement from facts it held locally — and
|
|
7
|
+
* those are not the facts the mint reads:
|
|
8
|
+
*
|
|
9
|
+
* - the job's checkout `ref` is the PR HEAD branch, while the token's `ref`
|
|
10
|
+
* claim is `execution_runs.ref`, the BASE branch;
|
|
11
|
+
* - `request.workflowRef` is the git ref used to CLONE a global workflow's
|
|
12
|
+
* repository, while the token's `workflow_ref` claim is
|
|
13
|
+
* `<workflow_name>@<sha>`. Those two are never equal;
|
|
14
|
+
* - `provenanceIssuer` is declared and read but written by no dispatch path,
|
|
15
|
+
* so every frozen statement recorded `builder.id` as `/orchestrator/unknown`.
|
|
16
|
+
*
|
|
17
|
+
* So a field-by-field cross-check would have failed on every legitimate
|
|
18
|
+
* deferred attestation — which is exactly why the verifier's deferred branch
|
|
19
|
+
* was written as a hash equality instead. Sending the mint's own view of the
|
|
20
|
+
* run makes the cross-check meaningful, and only then can it be switched on.
|
|
21
|
+
*/
|
|
22
|
+
import type { Kysely } from 'kysely';
|
|
23
|
+
import { type ProvenanceContext } from '@kici-dev/engine';
|
|
24
|
+
import type { Database } from '../db/types.js';
|
|
25
|
+
/**
|
|
26
|
+
* Load the provenance context for a (run, job) from the same rows and the same
|
|
27
|
+
* derivations `buildIdTokenClaims` uses.
|
|
28
|
+
*
|
|
29
|
+
* Returns undefined only when the run row is absent. In particular this does
|
|
30
|
+
* NOT gate on a configured provenance issuer: the cross-check reads
|
|
31
|
+
* `repository` / `ref` / `sha` / `workflowRef` / `runId` / `jobId`, none of
|
|
32
|
+
* which depends on who signs the token. Only the statement's `builder.id`
|
|
33
|
+
* needs an issuer, and an unset one reproduces the `/orchestrator/unknown`
|
|
34
|
+
* builder the local guess has always recorded.
|
|
35
|
+
*
|
|
36
|
+
* Gating on the issuer here dropped the context for every orchestrator still
|
|
37
|
+
* on the deprecated Platform-relay mint, so its agent fell back to the local
|
|
38
|
+
* guess — `repository: 'unknown/unknown'` for a `file://` clone, and a bare
|
|
39
|
+
* workflow name where the claim is `<name>@<sha>` — and the capture check
|
|
40
|
+
* then refused every one of its deferred attestations.
|
|
41
|
+
*/
|
|
42
|
+
export declare function loadProvenanceContext(db: Kysely<Database>, runId: string, jobId: string, opts: {
|
|
43
|
+
issuer: string | undefined;
|
|
44
|
+
orchestratorId: string;
|
|
45
|
+
}): Promise<ProvenanceContext | undefined>;
|
|
46
|
+
//# sourceMappingURL=dispatch-context.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { Database } from '../db/types.js';
|
|
3
|
+
import { DeferredAttestationRejectReason } from '../metrics/prometheus.js';
|
|
4
|
+
/** What the capture handler hands over, before anything is persisted. */
|
|
5
|
+
export interface DeferredCaptureRecord {
|
|
6
|
+
runId: string;
|
|
7
|
+
jobId: string;
|
|
8
|
+
subjectDigest: string;
|
|
9
|
+
statementHash: string;
|
|
10
|
+
dsseEnvelope: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Accepted, with the RECOMPUTED statement hash — never the agent-supplied one,
|
|
14
|
+
* even when they agree. The later mint binds the token to this value.
|
|
15
|
+
*/
|
|
16
|
+
export type DeferredCaptureVerdict = {
|
|
17
|
+
ok: true;
|
|
18
|
+
statementHash: string;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
reason: DeferredAttestationRejectReason;
|
|
22
|
+
detail: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Check a deferred capture against the orchestrator's own rows, fail closed.
|
|
26
|
+
*
|
|
27
|
+
* A rejection is a DROP: the job stays green and has no attestation.
|
|
28
|
+
* That is a degradation, whereas storing an unchecked statement re-introduces
|
|
29
|
+
* the forgery primitive — every gate that lets one through is the hole again.
|
|
30
|
+
* An agent too old to receive `provenanceContext` freezes the legacy statement
|
|
31
|
+
* shape and is dropped for the same reason, with the mismatched field named so
|
|
32
|
+
* the operator can see the upgrade is what fixes it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkDeferredCapture(deps: {
|
|
35
|
+
db: Kysely<Database>;
|
|
36
|
+
record: DeferredCaptureRecord;
|
|
37
|
+
orchestratorId: string;
|
|
38
|
+
}): Promise<DeferredCaptureVerdict>;
|
|
39
|
+
//# sourceMappingURL=verify-deferred-capture.d.ts.map
|
|
@@ -15,6 +15,15 @@ export interface GitHubAppConfig {
|
|
|
15
15
|
/** GitHub App private key (PEM format) */
|
|
16
16
|
privateKey: string;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Create an Octokit instance authenticated as the GitHub App itself (a JWT
|
|
20
|
+
* signed with the App private key), not as one of its installations.
|
|
21
|
+
*
|
|
22
|
+
* App-level endpoints — `GET /app`, `GET /app/installations`, and the webhook
|
|
23
|
+
* delivery endpoints under `/app/hook/deliveries` — accept only this
|
|
24
|
+
* credential. Installation tokens are rejected there.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createAppOctokit(config: GitHubAppConfig): Octokit;
|
|
18
27
|
/**
|
|
19
28
|
* Credentials for GitHub operations requiring installation-level access.
|
|
20
29
|
*/
|