@kici-dev/orchestrator 0.1.24 → 0.1.25
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/app.d.ts +24 -0
- package/dist/audit/access-log.d.ts +4 -0
- package/dist/cancel/cancel-run.d.ts +2 -0
- package/dist/cli/api-client.d.ts +2 -0
- package/dist/cli/commands/attestations-list.d.ts +49 -0
- package/dist/cli/commands/attestations-retry.d.ts +28 -0
- package/dist/cli/commands/attestations.d.ts +2 -1
- package/dist/cli/commands/cluster.d.ts +3 -0
- package/dist/cli/local-github-ingress-url.d.ts +8 -0
- package/dist/cli.js +1021 -404
- package/dist/cluster/cluster-identity.d.ts +11 -0
- package/dist/cluster/reconcile-identity.d.ts +50 -0
- package/dist/cold-store/load-access-log-range.d.ts +4 -0
- package/dist/config.d.ts +2 -0
- package/dist/dashboard/attestation-filters.d.ts +62 -1
- package/dist/dashboard/handler.d.ts +24 -4
- package/dist/db/migrations/061_execution_runs_environment_id.d.ts +4 -0
- package/dist/db/migrations/062_execution_runs_agent_label.d.ts +4 -0
- package/dist/db/migrations/063_access_log_agent_label_index.d.ts +10 -0
- package/dist/db/migrations/064_execution_jobs_skipped_environments.d.ts +17 -0
- package/dist/db/migrations/065_pending_attestations.d.ts +22 -0
- package/dist/db/migrations/066_pending_attestations_rejected.d.ts +13 -0
- package/dist/db/types.d.ts +58 -0
- package/dist/environments/protection/satisfiability.d.ts +14 -9
- package/dist/events/circuit-breaker.d.ts +8 -5
- package/dist/events/event-router.d.ts +12 -4
- package/dist/events/event-store.d.ts +21 -3
- package/dist/events/trust-store.d.ts +6 -1
- package/dist/events/types.d.ts +6 -1
- package/dist/helpers/rate-limiter.d.ts +17 -2
- package/dist/index.js +2 -1
- package/dist/metrics/prometheus.d.ts +6 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +6 -0
- package/dist/pipeline/manual-schedule.d.ts +1 -1
- package/dist/pipeline/process-webhook.d.ts +14 -1
- package/dist/pipeline/rerun.d.ts +1 -1
- package/dist/pipeline/test-pipeline.d.ts +6 -0
- package/dist/provenance/attestation-retrier.d.ts +92 -0
- package/dist/provenance/backfill-run.d.ts +41 -0
- package/dist/provenance/pending-attestations-repo.d.ts +64 -0
- package/dist/provenance/verify-at-ingest.d.ts +10 -3
- package/dist/reporting/execution-tracker.d.ts +15 -3
- package/dist/reporting/run-aggregator.d.ts +5 -1
- package/dist/routes/admin-access-log.d.ts +2 -1
- package/dist/routes/admin.d.ts +14 -0
- package/dist/routes/github-webhook.d.ts +37 -0
- package/dist/routes/health.d.ts +9 -0
- package/dist/routes/webhooks.d.ts +2 -4
- package/dist/server.js +3462 -1886
- package/dist/sources/source-store.d.ts +6 -0
- package/dist/stale-detector/workflow-deadline-detector.d.ts +25 -0
- package/dist/standalone.js +19335 -18258
- package/dist/webhook/dedup.d.ts +14 -0
- package/dist/ws/agent-handler.d.ts +23 -0
- package/dist/ws/oidc-token-relay.d.ts +31 -21
- package/dist/ws/orch-rpc.d.ts +9 -0
- package/dist/ws/platform-client.d.ts +18 -1
- package/dist/ws/test-relay-handlers.d.ts +2 -0
- package/package.json +5 -5
- package/sbom.spdx.json +55 -50
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sliding-window rate limiter.
|
|
3
3
|
*
|
|
4
|
-
* Tracks timestamps of recent events per key and rejects new events
|
|
5
|
-
*
|
|
4
|
+
* Tracks timestamps of recent events per key and rejects new events once the
|
|
5
|
+
* count within the window exceeds the configured maximum.
|
|
6
|
+
*
|
|
7
|
+
* The internal Map stays bounded to the recently-active key set. A checked key
|
|
8
|
+
* whose window has fully expired is dropped before this call decides whether to
|
|
9
|
+
* record a new timestamp, so a call that records nothing (for example when the
|
|
10
|
+
* limit is zero) leaves no residual bucket. Keys that are never checked again
|
|
11
|
+
* are reclaimed by an amortized sweep that runs at most once per window, so the
|
|
12
|
+
* per-call hot path stays O(1) amortized even though a single sweep is O(n).
|
|
6
13
|
*/
|
|
7
14
|
export declare class SlidingWindowRateLimiter {
|
|
8
15
|
private readonly maxPerWindow;
|
|
9
16
|
private readonly windowMs;
|
|
10
17
|
private state;
|
|
18
|
+
private lastSweepAt;
|
|
11
19
|
constructor(maxPerWindow: number, windowMs?: number);
|
|
12
20
|
/**
|
|
13
21
|
* Check if a new event is allowed for the given key.
|
|
@@ -18,6 +26,13 @@ export declare class SlidingWindowRateLimiter {
|
|
|
18
26
|
allowed: boolean;
|
|
19
27
|
retryAfterMs?: number;
|
|
20
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Evict buckets whose newest timestamp is outside the window. Runs at most
|
|
31
|
+
* once per window so the amortized cost per check() stays O(1).
|
|
32
|
+
*/
|
|
33
|
+
private maybeSweep;
|
|
34
|
+
/** Number of tracked keys currently held in the internal map. */
|
|
35
|
+
get size(): number;
|
|
21
36
|
reset(): void;
|
|
22
37
|
}
|
|
23
38
|
//# sourceMappingURL=rate-limiter.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,6 @@ import { Octokit } from "@octokit/rest";
|
|
|
16
16
|
import { createAppAuth } from "@octokit/auth-app";
|
|
17
17
|
import { createServer } from "node:http";
|
|
18
18
|
import { exec } from "node:child_process";
|
|
19
|
-
import.meta.url;
|
|
20
19
|
//#endregion
|
|
21
20
|
//#region src/storage/s3.ts
|
|
22
21
|
/**
|
|
@@ -1842,6 +1841,8 @@ var AdminApiClient = class {
|
|
|
1842
1841
|
if (opts?.from) params.set("from", opts.from);
|
|
1843
1842
|
if (opts?.to) params.set("to", opts.to);
|
|
1844
1843
|
if (opts?.q) params.set("q", opts.q);
|
|
1844
|
+
if (opts?.agentLabel) params.set("agentLabel", opts.agentLabel);
|
|
1845
|
+
if (opts?.agentOnly) params.set("agentOnly", "true");
|
|
1845
1846
|
if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
|
|
1846
1847
|
if (opts?.cursor) params.set("cursor", opts.cursor);
|
|
1847
1848
|
const qs = params.toString();
|
|
@@ -6,6 +6,12 @@ export declare function setConfigVersion(value: number): void;
|
|
|
6
6
|
export declare function setDeclaredHostsUnreachable(value: number): void;
|
|
7
7
|
/** Set the current number of stale runs detected. */
|
|
8
8
|
export declare function setStaleRunsCurrent(value: number): void;
|
|
9
|
+
/** Set the current number of deferred attestations awaiting a later mint. */
|
|
10
|
+
export declare function setPendingAttestations(value: number): void;
|
|
11
|
+
/** Set the age (seconds) of the oldest deferred attestation in the outbox. */
|
|
12
|
+
export declare function setPendingAttestationOldestAgeSeconds(value: number): void;
|
|
13
|
+
/** Set the current number of terminally-rejected deferred attestations. */
|
|
14
|
+
export declare function setRejectedAttestations(value: number): void;
|
|
9
15
|
interface ScalerUsageRow {
|
|
10
16
|
scaler: string;
|
|
11
17
|
/** Backend type for this scaler (rollup dimension). `__global__` for the orchestrator-wide row. */
|
|
@@ -138,6 +138,12 @@ export interface DispatchMatchedWorkflowResult {
|
|
|
138
138
|
dispatchedJobIds: string[];
|
|
139
139
|
/** True when the workflow install gate paused the dispatch (held run). */
|
|
140
140
|
held?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* User-visible warnings aggregated from dispatched jobs — today, one per job
|
|
143
|
+
* whose bound test-run environment(s) were unavailable and skipped. Surfaced
|
|
144
|
+
* on the accepted trigger response so the CLI can print them.
|
|
145
|
+
*/
|
|
146
|
+
envWarnings?: string[];
|
|
141
147
|
}
|
|
142
148
|
/** Options controlling a (re-)dispatch of a matched workflow. */
|
|
143
149
|
export interface DispatchMatchedWorkflowOptions {
|
|
@@ -15,7 +15,7 @@ import type { RegistrationIndex } from '../registration/registration-index.js';
|
|
|
15
15
|
interface ManualScheduleDeps extends RerunDeps {
|
|
16
16
|
registrationIndex: RegistrationIndex;
|
|
17
17
|
}
|
|
18
|
-
export declare function handleManualSchedule(registrationId: string, triggeredBy: string | null, deps: ManualScheduleDeps): Promise<{
|
|
18
|
+
export declare function handleManualSchedule(registrationId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null, deps: ManualScheduleDeps): Promise<{
|
|
19
19
|
newRunId: string;
|
|
20
20
|
}>;
|
|
21
21
|
export declare function buildManualJobConfig(workflow: LockWorkflow, mat: MaterializedJob): {
|
|
@@ -16,11 +16,24 @@
|
|
|
16
16
|
* Internal helpers are pure phase functions returning typed results; the only
|
|
17
17
|
* top-level export is `processWebhook`, callable from server.ts / app.ts.
|
|
18
18
|
*/
|
|
19
|
+
import { z } from 'zod';
|
|
19
20
|
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
20
21
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
21
22
|
import type { ProviderBundle } from '../provider-registry.js';
|
|
22
23
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
23
24
|
import { type ProcessingDeps } from './processor.js';
|
|
25
|
+
/**
|
|
26
|
+
* Outcome of a single inbound-webhook ingestion. `duplicate` lets a direct-
|
|
27
|
+
* ingress route return `{ duplicate: true }` to GitHub's Recent Deliveries
|
|
28
|
+
* panel; `skipped` covers unknown provider / unknown event / no-repo paths;
|
|
29
|
+
* `processed` means the pipeline matched and dispatched (or recorded a run).
|
|
30
|
+
*/
|
|
31
|
+
export declare const WebhookIngestOutcome: z.ZodEnum<{
|
|
32
|
+
processed: "processed";
|
|
33
|
+
skipped: "skipped";
|
|
34
|
+
duplicate: "duplicate";
|
|
35
|
+
}>;
|
|
36
|
+
export type WebhookIngestOutcome = z.infer<typeof WebhookIngestOutcome>;
|
|
24
37
|
interface TrustOutcome {
|
|
25
38
|
trustResolution: TrustResolution | undefined;
|
|
26
39
|
/** Default 'base' for PR events; trust resolution may override to 'head'. */
|
|
@@ -52,6 +65,6 @@ export declare function resolveTrustForPR(args: {
|
|
|
52
65
|
* 9. Match + dispatch global workflows for OTHER repos
|
|
53
66
|
* 10. Forward Platform trace + record event log
|
|
54
67
|
*/
|
|
55
|
-
export declare function processWebhook(info: WebhookInfo, deps: ProcessingDeps): Promise<
|
|
68
|
+
export declare function processWebhook(info: WebhookInfo, deps: ProcessingDeps): Promise<WebhookIngestOutcome>;
|
|
56
69
|
export {};
|
|
57
70
|
//# sourceMappingURL=process-webhook.d.ts.map
|
package/dist/pipeline/rerun.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export interface RerunDeps {
|
|
|
67
67
|
*/
|
|
68
68
|
coldStore: ColdStore | null;
|
|
69
69
|
}
|
|
70
|
-
export declare function handleRerun(originalRunId: string, triggeredBy: string | null, deps: RerunDeps,
|
|
70
|
+
export declare function handleRerun(originalRunId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null, deps: RerunDeps,
|
|
71
71
|
/**
|
|
72
72
|
* Phase F — routing key for the original run, forwarded by Platform
|
|
73
73
|
* via the WS `run.rerun.request` payload. Required to address the
|
|
@@ -92,6 +92,12 @@ interface TestTriggerResult {
|
|
|
92
92
|
reason?: string;
|
|
93
93
|
/** Dispatched job IDs. */
|
|
94
94
|
jobIds: string[];
|
|
95
|
+
/**
|
|
96
|
+
* User-visible warnings on an accepted run — today, one per job whose bound
|
|
97
|
+
* test-run environment(s) were unavailable (non-test or unconfigured) and
|
|
98
|
+
* skipped. Printed by the CLI on acceptance.
|
|
99
|
+
*/
|
|
100
|
+
warnings?: string[];
|
|
95
101
|
}
|
|
96
102
|
/**
|
|
97
103
|
* Repo identity for an inline-lock (local working tree) run. Derived from the
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { PendingAttestationsRepo, PendingAttestationRow } from './pending-attestations-repo.js';
|
|
2
|
+
/** A minted token, a still-transient deferral, or a terminal rejection. */
|
|
3
|
+
export type RetrierMintResult = {
|
|
4
|
+
token: string;
|
|
5
|
+
expiresIn: number;
|
|
6
|
+
jti: string;
|
|
7
|
+
} | {
|
|
8
|
+
deferred: true;
|
|
9
|
+
code: string;
|
|
10
|
+
} | {
|
|
11
|
+
rejected: true;
|
|
12
|
+
reason: string;
|
|
13
|
+
};
|
|
14
|
+
export interface AttestationRetrierDeps {
|
|
15
|
+
repo: PendingAttestationsRepo;
|
|
16
|
+
/** Mint the deferred token (the OIDC relay with deferred params, Task 5). */
|
|
17
|
+
requestMint: (args: {
|
|
18
|
+
orchestratorId: string;
|
|
19
|
+
runId: string;
|
|
20
|
+
jobId: string;
|
|
21
|
+
audience: string;
|
|
22
|
+
deferred: {
|
|
23
|
+
statementHash: string;
|
|
24
|
+
origin: 'deferred' | 'offline-backfill';
|
|
25
|
+
};
|
|
26
|
+
}) => Promise<RetrierMintResult>;
|
|
27
|
+
/** Assemble + upload the bundle to object storage (initMeta included). */
|
|
28
|
+
uploadBundle: (args: {
|
|
29
|
+
runId: string;
|
|
30
|
+
jobId: string;
|
|
31
|
+
subjectDigest: string;
|
|
32
|
+
bundle: Record<string, unknown>;
|
|
33
|
+
storageKey: string;
|
|
34
|
+
}) => Promise<void>;
|
|
35
|
+
computeVerdict: (storageKey: string) => Promise<{
|
|
36
|
+
verifyStatus: string;
|
|
37
|
+
verifyReason: string | null;
|
|
38
|
+
verifiedAt: Date | null;
|
|
39
|
+
}>;
|
|
40
|
+
recordAttestation: (args: {
|
|
41
|
+
runId: string;
|
|
42
|
+
jobId: string;
|
|
43
|
+
subjectName: string;
|
|
44
|
+
subjectDigest: string;
|
|
45
|
+
storageKey: string;
|
|
46
|
+
mediaType: string;
|
|
47
|
+
verifyStatus: string;
|
|
48
|
+
verifyReason: string | null;
|
|
49
|
+
verifiedAt: Date | null;
|
|
50
|
+
}) => Promise<void>;
|
|
51
|
+
/** Replay the run/job rows the Platform missed (offline-backfill only). */
|
|
52
|
+
backfillRun: (runId: string) => Promise<void>;
|
|
53
|
+
setMetrics: (count: number, oldestCreatedAt: Date | null, rejected: number) => void;
|
|
54
|
+
isLeader: () => boolean;
|
|
55
|
+
orchestratorId: string;
|
|
56
|
+
intervalMs: number;
|
|
57
|
+
provenanceStorageKey: (runId: string, jobId: string, subjectDigest: string) => string;
|
|
58
|
+
}
|
|
59
|
+
export declare class AttestationRetrier {
|
|
60
|
+
private readonly deps;
|
|
61
|
+
private interval;
|
|
62
|
+
private running;
|
|
63
|
+
constructor(deps: AttestationRetrierDeps);
|
|
64
|
+
/**
|
|
65
|
+
* Start the periodic timer. Each tick self-gates on `isLeader()`, so it is
|
|
66
|
+
* safe to start on every instance — only the current Raft leader acts, and
|
|
67
|
+
* the `attestations` unique index makes fulfilment idempotent regardless.
|
|
68
|
+
*/
|
|
69
|
+
start(): void;
|
|
70
|
+
onBecomeLeader(): void;
|
|
71
|
+
onLoseLeadership(): void;
|
|
72
|
+
stop(): void;
|
|
73
|
+
/** On-reconnect trigger (Platform WS re-authenticated). Leader-gated inside tick. */
|
|
74
|
+
triggerNow(): void;
|
|
75
|
+
tick(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Fulfil the targeted pending rows once and report counts. Used by the manual
|
|
78
|
+
* `kici-admin attestations retry` path (via the orchestrator admin API): the
|
|
79
|
+
* operator explicitly requested a drain, so this is NOT leader-gated — the
|
|
80
|
+
* `attestations` unique index keeps a concurrent leader tick idempotent.
|
|
81
|
+
*/
|
|
82
|
+
runOnce(opts?: {
|
|
83
|
+
runId?: string;
|
|
84
|
+
includeRejected?: boolean;
|
|
85
|
+
}): Promise<{
|
|
86
|
+
minted: number;
|
|
87
|
+
stillPending: number;
|
|
88
|
+
rejected: number;
|
|
89
|
+
}>;
|
|
90
|
+
fulfilOne(row: PendingAttestationRow): Promise<'minted' | 'rejected' | 'deferred'>;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=attestation-retrier.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { OrchestratorToPlatformMessage } from '@kici-dev/engine';
|
|
2
|
+
/** The local `execution_runs` fields the backfill needs (production reads via Kysely). */
|
|
3
|
+
export interface BackfillRunRow {
|
|
4
|
+
run_id: string;
|
|
5
|
+
workflow_name: string;
|
|
6
|
+
status: string;
|
|
7
|
+
repo_identifier: string | null;
|
|
8
|
+
provider: string | null;
|
|
9
|
+
local_working_tree: boolean | null;
|
|
10
|
+
sha: string | null;
|
|
11
|
+
ref: string | null;
|
|
12
|
+
job_count: number | null;
|
|
13
|
+
started_at: Date | null;
|
|
14
|
+
completed_at: Date | null;
|
|
15
|
+
duration_ms: number | null;
|
|
16
|
+
}
|
|
17
|
+
/** The local `execution_jobs` fields the backfill needs. */
|
|
18
|
+
export interface BackfillJobRow {
|
|
19
|
+
run_id: string;
|
|
20
|
+
job_id: string;
|
|
21
|
+
job_name: string;
|
|
22
|
+
status: string;
|
|
23
|
+
started_at: Date | null;
|
|
24
|
+
completed_at: Date | null;
|
|
25
|
+
agent_id: string | null;
|
|
26
|
+
orchestrator_id: string | null;
|
|
27
|
+
}
|
|
28
|
+
export interface BackfillRunDeps {
|
|
29
|
+
send: (message: OrchestratorToPlatformMessage) => void;
|
|
30
|
+
loadRun: (runId: string) => Promise<BackfillRunRow | null>;
|
|
31
|
+
loadJobs: (runId: string) => Promise<BackfillJobRow[]>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Send one `execution.status` (terminal) followed by one `job.status.forward`
|
|
35
|
+
* per job, populating exactly the fields the Platform's `handler.ts` upserts.
|
|
36
|
+
* Ordered execution.status FIRST so the run row exists before the job rows
|
|
37
|
+
* reference it. A no-op (throws) when the run is not found locally — the caller
|
|
38
|
+
* leaves the pending row `deferred` with a clear error.
|
|
39
|
+
*/
|
|
40
|
+
export declare function backfillRunToPlatform(deps: BackfillRunDeps, runId: string): Promise<void>;
|
|
41
|
+
//# sourceMappingURL=backfill-run.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
import type { AttestationOrigin } from '@kici-dev/engine';
|
|
3
|
+
import type { Database, PendingAttestationRow } from '../db/types.js';
|
|
4
|
+
/** The non-`live` AttestationOrigin values a pending row can carry. */
|
|
5
|
+
export type PendingAttestationOrigin = Exclude<AttestationOrigin, 'live'>;
|
|
6
|
+
export interface PendingAttestationInput {
|
|
7
|
+
id: string;
|
|
8
|
+
runId: string;
|
|
9
|
+
jobId: string;
|
|
10
|
+
subjectName: string;
|
|
11
|
+
subjectDigest: string;
|
|
12
|
+
audience: string;
|
|
13
|
+
dsseEnvelope: unknown;
|
|
14
|
+
publicKey: unknown;
|
|
15
|
+
mediaType: string;
|
|
16
|
+
statementHash: string;
|
|
17
|
+
originKind: PendingAttestationOrigin;
|
|
18
|
+
}
|
|
19
|
+
export type { PendingAttestationRow } from '../db/types.js';
|
|
20
|
+
/** Typed CRUD over the deferred-attestation outbox (shared orchestrator DB). */
|
|
21
|
+
export declare class PendingAttestationsRepo {
|
|
22
|
+
private readonly db;
|
|
23
|
+
constructor(db: Kysely<Database>);
|
|
24
|
+
/** Insert a pending row; re-deferring the same subject is a no-op. */
|
|
25
|
+
insert(row: PendingAttestationInput): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* List pending rows oldest-first, optionally scoped to a run. Terminally
|
|
28
|
+
* rejected rows (`rejected_at IS NOT NULL`) are excluded — the retrier never
|
|
29
|
+
* re-picks a row the Platform definitively cannot mint.
|
|
30
|
+
*/
|
|
31
|
+
list(opts?: {
|
|
32
|
+
runId?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
}): Promise<PendingAttestationRow[]>;
|
|
35
|
+
/** Bump the attempt counter + record the last error on a still-failing retry. */
|
|
36
|
+
recordAttempt(id: string, lastError: string | null): Promise<void>;
|
|
37
|
+
/** Remove a fulfilled pending row. */
|
|
38
|
+
delete(id: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Outbox depth + oldest entry, feeding the metrics gauges. Terminally rejected
|
|
41
|
+
* rows are excluded so the pending gauge reflects only truly-pending rows.
|
|
42
|
+
*/
|
|
43
|
+
countAndOldest(): Promise<{
|
|
44
|
+
count: number;
|
|
45
|
+
oldestCreatedAt: Date | null;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Terminally reject a pending row: the Platform definitively cannot mint it
|
|
49
|
+
* (run/job absent). Stamps rejected_at, records the reason, and bumps the
|
|
50
|
+
* attempt counter. The row stays for audit but is skipped by list()/counts.
|
|
51
|
+
*/
|
|
52
|
+
markRejected(id: string, reason: string): Promise<void>;
|
|
53
|
+
/** Count terminally-rejected rows (feeds the rejected-attestations gauge). */
|
|
54
|
+
countRejected(): Promise<number>;
|
|
55
|
+
/**
|
|
56
|
+
* Re-arm terminally-rejected rows so the next drain re-attempts them (used
|
|
57
|
+
* after an operator fixes the Platform-side run/job). Scoped to a run when
|
|
58
|
+
* runId is given, else clears every rejected row. Returns the count re-armed.
|
|
59
|
+
*/
|
|
60
|
+
clearRejected(opts?: {
|
|
61
|
+
runId?: string;
|
|
62
|
+
}): Promise<number>;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=pending-attestations-repo.d.ts.map
|
|
@@ -9,11 +9,18 @@ export interface AttestationVerdict {
|
|
|
9
9
|
/**
|
|
10
10
|
* Compute the verification verdict for a stored provenance bundle at ingest.
|
|
11
11
|
*
|
|
12
|
+
* Reads the bundle, selects the signing-key set by the identity token's own
|
|
13
|
+
* `kid` (so a kid-miss triggers the trust root's single refetch — a token
|
|
14
|
+
* minted with a freshly-rotated key still verifies against a briefly-stale
|
|
15
|
+
* cached JWKS), then verifies offline.
|
|
16
|
+
*
|
|
12
17
|
* Fail-closed: a missing trust root, unfetchable JWKS, missing storage, or an
|
|
13
18
|
* unreadable bundle all yield `unverifiable` (never silently `verified`). A
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
19
|
+
* signing key that is absent from the published JWKS even after the refetch
|
|
20
|
+
* yields `unverifiable` (`signing_key_not_published`) — "couldn't verify", not
|
|
21
|
+
* "proved bad". A bundle that verifies false yields `failed` with the first
|
|
22
|
+
* failure code. Any thrown error is caught and recorded as `unverifiable` —
|
|
23
|
+
* verification never fails the upload.
|
|
17
24
|
*/
|
|
18
25
|
export declare function computeAttestationVerdict(opts: {
|
|
19
26
|
trustRoot: ProvenanceTrustRoot | undefined;
|
|
@@ -40,6 +40,8 @@ export interface ExecutionContext {
|
|
|
40
40
|
originalRunId?: string | null;
|
|
41
41
|
/** User identity that triggered this re-run (null/undefined for webhook-triggered). */
|
|
42
42
|
triggeredBy?: string | null;
|
|
43
|
+
/** Agent provenance label when triggered through an agent credential. */
|
|
44
|
+
triggeredByAgentLabel?: string | null;
|
|
43
45
|
/**
|
|
44
46
|
* Provider login of the person who triggered the run (pusher / PR author).
|
|
45
47
|
* Captured for all event types; forwarded to the Platform run projection and
|
|
@@ -221,6 +223,8 @@ export declare class ExecutionTracker {
|
|
|
221
223
|
waveMaxParallel?: number;
|
|
222
224
|
waveFailFast?: boolean;
|
|
223
225
|
environments?: string[];
|
|
226
|
+
skippedEnvironments?: string[];
|
|
227
|
+
envWarning?: string;
|
|
224
228
|
}>, routingKey?: string,
|
|
225
229
|
/** Secret context names dispatched with jobs (for context-disable job lookup). */
|
|
226
230
|
dispatchedContexts?: string[],
|
|
@@ -248,11 +252,16 @@ export declare class ExecutionTracker {
|
|
|
248
252
|
/** Provider login of the triggering actor (pusher / PR author). */
|
|
249
253
|
triggerActorUsername?: string | null,
|
|
250
254
|
/** Immutable provider user id of the triggering actor. */
|
|
251
|
-
triggerActorUserId?: string | null
|
|
255
|
+
triggerActorUserId?: string | null,
|
|
256
|
+
/** Agent provenance label when triggered through an agent credential. */
|
|
257
|
+
triggeredByAgentLabel?: string | null): Promise<void>;
|
|
252
258
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
259
|
+
* Upsert one execution_jobs row per dispatched job (idempotent on
|
|
260
|
+
* (run_id, job_id) to tolerate a race with an early `onJobStatus`). The
|
|
261
|
+
* `routing_key` is denormalized (see migration 006) so cold-store archival
|
|
262
|
+
* partitions by it without joining execution_runs.
|
|
255
263
|
*/
|
|
264
|
+
private insertTrackedJobRows;
|
|
256
265
|
/**
|
|
257
266
|
* Find the synthetic needs-pending job ID for a given job name in a run.
|
|
258
267
|
* Used by dispatchReadyJob to locate the placeholder entry before replacing it.
|
|
@@ -303,6 +312,8 @@ export declare class ExecutionTracker {
|
|
|
303
312
|
variantKind?: string;
|
|
304
313
|
variantLabel?: string;
|
|
305
314
|
environments?: string[];
|
|
315
|
+
skippedEnvironments?: string[];
|
|
316
|
+
envWarning?: string;
|
|
306
317
|
}>, dispatchedContexts?: string[],
|
|
307
318
|
/** Synthetic job ID to replace (e.g. needs-pending-deploy-{uuid}). */
|
|
308
319
|
replaceSyntheticId?: string): Promise<void>;
|
|
@@ -571,6 +582,7 @@ export declare class ExecutionTracker {
|
|
|
571
582
|
parentRunId?: string | null;
|
|
572
583
|
originalRunId?: string | null;
|
|
573
584
|
triggeredBy?: string | null;
|
|
585
|
+
triggeredByAgentLabel?: string | null;
|
|
574
586
|
failureReason?: string;
|
|
575
587
|
jobCount: number;
|
|
576
588
|
startedAt: number;
|
|
@@ -65,6 +65,8 @@ export interface RunDetailJobRow {
|
|
|
65
65
|
error_message: string | null;
|
|
66
66
|
runs_on_labels: unknown;
|
|
67
67
|
environments: unknown;
|
|
68
|
+
skipped_environments: unknown;
|
|
69
|
+
env_warning: string | null;
|
|
68
70
|
outputs: unknown;
|
|
69
71
|
init_failure: unknown;
|
|
70
72
|
}
|
|
@@ -100,7 +102,7 @@ export declare function buildRunDetailJobs(jobs: RunDetailJobRow[], lookups: Run
|
|
|
100
102
|
errorMessage: string | null;
|
|
101
103
|
}[];
|
|
102
104
|
initFailure?: {
|
|
103
|
-
scope: "
|
|
105
|
+
scope: "job" | "run";
|
|
104
106
|
category: "secret_resolution" | "install_secrets" | "lock_resolution" | "build_coordination" | "environment_rules" | "dynamic_eval" | "no_agent" | "matrix_expansion";
|
|
105
107
|
message: string;
|
|
106
108
|
jobName?: string | undefined;
|
|
@@ -120,6 +122,8 @@ export declare function buildRunDetailJobs(jobs: RunDetailJobRow[], lookups: Run
|
|
|
120
122
|
errorMessage: string | null;
|
|
121
123
|
runsOnLabels: string[] | null;
|
|
122
124
|
environments: string[] | null;
|
|
125
|
+
skippedEnvironments: string[] | null;
|
|
126
|
+
envWarning: string | null;
|
|
123
127
|
outputs: any;
|
|
124
128
|
secretOutputKeys: string[] | null;
|
|
125
129
|
}[];
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* GET /api/v1/admin/access-log
|
|
10
10
|
* Filters: orgId, actorType, actorId, action, source, outcome,
|
|
11
|
-
* targetType, targetId, from, to,
|
|
11
|
+
* targetType, targetId, from, to, q, agentLabel, agentOnly,
|
|
12
|
+
* limit, cursor
|
|
12
13
|
* Requires: access_log.read
|
|
13
14
|
*
|
|
14
15
|
* GET /api/v1/admin/access-log/:id
|
package/dist/routes/admin.d.ts
CHANGED
|
@@ -113,6 +113,20 @@ export interface AdminRouteDeps {
|
|
|
113
113
|
* write is best-effort, never gating.
|
|
114
114
|
*/
|
|
115
115
|
accessLog?: AccessLogWriter;
|
|
116
|
+
/**
|
|
117
|
+
* Optional -- fulfil deferred attestations on demand (mints in the running
|
|
118
|
+
* orchestrator process, which owns the Platform WS). Backs
|
|
119
|
+
* `POST /api/v1/admin/attestations/retry` (the `kici-admin attestations retry`
|
|
120
|
+
* command). Unset on a WS-only / non-coordinator admin.
|
|
121
|
+
*/
|
|
122
|
+
retryAttestations?: (opts: {
|
|
123
|
+
runId?: string;
|
|
124
|
+
includeRejected?: boolean;
|
|
125
|
+
}) => Promise<{
|
|
126
|
+
minted: number;
|
|
127
|
+
stillPending: number;
|
|
128
|
+
rejected: number;
|
|
129
|
+
}>;
|
|
116
130
|
}
|
|
117
131
|
/** Hono env type for admin routes with context variables. */
|
|
118
132
|
type AdminEnv = {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { WebhookInfo } from '../webhook/handler.js';
|
|
3
|
+
import type { SourceStore } from '../sources/source-store.js';
|
|
4
|
+
import { type VerifyInboundDeps } from '../webhook/verify-inbound.js';
|
|
5
|
+
import { WebhookIngestOutcome } from '../pipeline/process-webhook.js';
|
|
6
|
+
/** Dependencies for the direct GitHub webhook ingress route. */
|
|
7
|
+
export interface GithubWebhookRoutesDeps {
|
|
8
|
+
/** Local source lookup (by UUID). */
|
|
9
|
+
sourceStore: SourceStore;
|
|
10
|
+
/** Inbound verification deps (db + secret store + generic source manager). */
|
|
11
|
+
verifyDeps: VerifyInboundDeps;
|
|
12
|
+
/** Pipeline entry — owns the atomic dedup claim + dispatch. */
|
|
13
|
+
onWebhook: (info: WebhookInfo) => Promise<WebhookIngestOutcome>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether the orchestrator serves the direct GitHub ingress route for a given
|
|
17
|
+
* operating mode. Hybrid + independent run local source config; platform mode
|
|
18
|
+
* is relay-only (no local GitHub source to serve).
|
|
19
|
+
*/
|
|
20
|
+
export declare function shouldServeGithubIngress(mode: 'platform' | 'hybrid' | 'independent'): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Direct GitHub-App webhook ingress: `POST /webhook/:orgId/github/:sourceId`.
|
|
23
|
+
*
|
|
24
|
+
* Bypasses the Platform relay. Handles both operator topologies on one route:
|
|
25
|
+
* - App-level repoint: the App's single webhook URL points here, so GitHub
|
|
26
|
+
* sends `X-GitHub-Hook-Installation-Target-Type: integration` +
|
|
27
|
+
* `-Target-ID: <appId>` — validated against the source's routing key.
|
|
28
|
+
* - Classic per-repo webhook: a repo-level hook points here with no App
|
|
29
|
+
* target headers — `:sourceId` already identifies the source, so the
|
|
30
|
+
* App-header check is skipped.
|
|
31
|
+
*
|
|
32
|
+
* Verification is the existing local `verify-inbound.ts` GitHub path (secret
|
|
33
|
+
* read from the orchestrator secret store; rotation-aware). Dedup + dispatch
|
|
34
|
+
* are the universal `processWebhook` pipeline via `onWebhook`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createGithubWebhookRoutes(deps: GithubWebhookRoutesDeps): Hono;
|
|
37
|
+
//# sourceMappingURL=github-webhook.d.ts.map
|
package/dist/routes/health.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import type { Database } from '../db/types.js';
|
|
|
3
3
|
export interface HealthRoutesDeps {
|
|
4
4
|
/** Optional DB instance for readiness checks */
|
|
5
5
|
db?: Kysely<Database>;
|
|
6
|
+
/**
|
|
7
|
+
* Optional warmth latch. Returns `true` only once the orchestrator boot
|
|
8
|
+
* sequence has finished (all subsystems started, HTTP server serving). When
|
|
9
|
+
* provided, `/ready` returns `503` until it flips `true`, so a caller can
|
|
10
|
+
* gate on the orchestrator being ready to serve rather than merely live.
|
|
11
|
+
* Absent → warm defaults to `true` (callers that don't wire the latch are
|
|
12
|
+
* unaffected).
|
|
13
|
+
*/
|
|
14
|
+
isWarm?: () => boolean;
|
|
6
15
|
}
|
|
7
16
|
/**
|
|
8
17
|
* Create orchestrator health routes with database readiness check.
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import type { DedupCache } from '../webhook/dedup.js';
|
|
3
2
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
4
3
|
import type { GenericSourceManager } from '../webhook/generic-sources.js';
|
|
4
|
+
import { WebhookIngestOutcome } from '../pipeline/process-webhook.js';
|
|
5
5
|
/**
|
|
6
6
|
* Dependencies for generic webhook routes.
|
|
7
7
|
*/
|
|
8
8
|
export interface GenericWebhookRoutesDeps {
|
|
9
9
|
/** Generic source manager for source lookup and validation */
|
|
10
10
|
sourceManager: GenericSourceManager;
|
|
11
|
-
/** Delivery ID deduplication cache */
|
|
12
|
-
dedup: DedupCache;
|
|
13
11
|
/** Processing callback -- connects to the trigger matching pipeline */
|
|
14
|
-
onWebhook: (info: WebhookInfo) => Promise<
|
|
12
|
+
onWebhook: (info: WebhookInfo) => Promise<WebhookIngestOutcome>;
|
|
15
13
|
}
|
|
16
14
|
/**
|
|
17
15
|
* Create generic webhook routes for non-GitHub webhook ingestion.
|