@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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing a credential file without ever exposing it.
|
|
3
|
+
*
|
|
4
|
+
* `writeFile(path, content, { mode })` applies the mode only when it *creates*
|
|
5
|
+
* the file: writing over a destination that already sits at 0644 leaves the
|
|
6
|
+
* secret world-readable for as long as it takes the following `chmod` to run.
|
|
7
|
+
* `copyFile` is worse — it reproduces the source file's mode, so a 0644 source
|
|
8
|
+
* yields a 0644 destination.
|
|
9
|
+
*
|
|
10
|
+
* These helpers write a sibling temporary file at the target mode and rename it
|
|
11
|
+
* over the destination. `rename(2)` is atomic within a directory and carries
|
|
12
|
+
* the temporary file's mode with it, so the destination never exists with
|
|
13
|
+
* permissions looser than the content requires — not for a window, not at all.
|
|
14
|
+
*
|
|
15
|
+
* A mode is only ever narrowed by the process umask, so the created file is at
|
|
16
|
+
* most as permissive as `mode`; the explicit `chmod` on the temporary file then
|
|
17
|
+
* restores exactly `mode` under an unusually restrictive umask.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Write `content` to `filePath` at `mode`, atomically and never more permissive.
|
|
21
|
+
*
|
|
22
|
+
* `wx` refuses a pre-existing temporary path, so a planted file or symlink at
|
|
23
|
+
* the staging name is an error rather than a write through it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeFileSecurelySync(filePath: string, content: string, mode: number): void;
|
|
26
|
+
/** Promise-returning {@link writeFileSecurelySync}. */
|
|
27
|
+
export declare function writeFileSecurely(filePath: string, content: string, mode: number): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Copy `sourcePath` to `filePath` at `mode`, never inheriting the source's.
|
|
30
|
+
*
|
|
31
|
+
* Reads the source into memory: an env file is a handful of lines, and the
|
|
32
|
+
* alternative (copy then chmod) is the window this module exists to close.
|
|
33
|
+
*/
|
|
34
|
+
export declare function copyFileSecurelySync(sourcePath: string, filePath: string, mode: number): void;
|
|
35
|
+
//# sourceMappingURL=secure-write.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -599,67 +599,175 @@ function createCacheStorage(config) {
|
|
|
599
599
|
/**
|
|
600
600
|
* Source-tarball cache layer wrapping CacheStorage.
|
|
601
601
|
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
602
|
+
* Stores customer-workflow source tarballs — the `.kici/` directory minus
|
|
603
|
+
* `node_modules/`, packed by the agent's build job via `source-packer.ts` —
|
|
604
|
+
* under the tarball's OWN content hash, with a small pointer resolving an org's
|
|
605
|
+
* workflow `contentHash` to that hash. Tarballs are platform-independent: raw
|
|
606
|
+
* TypeScript source is identical across architectures. Refreshes TTL on reads
|
|
606
607
|
* (touch-on-read) so actively used sources stay in cache longer.
|
|
607
608
|
*
|
|
608
|
-
*
|
|
609
|
+
* Keys: `source/v2/{orgId}/{sourceTarDigest}.tar.gz` (immutable) and
|
|
610
|
+
* `source/v2/{orgId}/{contentHash}.hash` (the pointer) — the shape `dep-cache.ts`
|
|
611
|
+
* already uses, for the same reasons — content addressing and org scoping.
|
|
609
612
|
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
613
|
+
* **Content addressing** makes a mismatched pair unrepresentable. At
|
|
614
|
+
* `source/{contentHash}.tar.gz` the object's key was derived from something
|
|
615
|
+
* other than its bytes, so nothing a reader fetched could be verified against
|
|
616
|
+
* anything, and the agent restored whatever the signed URL returned.
|
|
617
|
+
*
|
|
618
|
+
* **Org scoping** makes the key a tenant's key. `contentHash` alone is a
|
|
619
|
+
* property of a `.kici/` tree, so two repositories scaffolded from the same
|
|
620
|
+
* `kici init` template — or a fork and its upstream — produced one object, and
|
|
621
|
+
* whichever built first won for the life of that object. On an orchestrator
|
|
622
|
+
* serving more than one org that is a cross-tenant read of `.kici/` contents.
|
|
623
|
+
* The org is the tenant boundary and the segment the user cache already scopes
|
|
624
|
+
* by; inside one org, sharing a key now means the two trees are byte-identical,
|
|
625
|
+
* so a monorepo or a same-org fork keeps its warm cache and pays nothing.
|
|
626
|
+
*
|
|
627
|
+
* Legacy `source/{contentHash}.tar.gz` objects are **not read**. Only a
|
|
628
|
+
* `compileSchemaVersion: 5` lock can name one, and that `contentHash` covers
|
|
629
|
+
* the workflow entry file alone rather than the tree the tarball holds — the
|
|
630
|
+
* defect this layout closes — so serving it would serve the bug. For a v6 lock
|
|
631
|
+
* the fallback is unreachable anyway, since the schema bump moves every hash.
|
|
632
|
+
* Old objects are orphaned and aged out by the normal cache TTL.
|
|
614
633
|
*/
|
|
615
634
|
const logger$3 = createLogger({ prefix: "source-cache" });
|
|
616
|
-
/**
|
|
617
|
-
|
|
618
|
-
|
|
635
|
+
/**
|
|
636
|
+
* Cache key for a source tarball, addressed by the tarball's OWN content hash:
|
|
637
|
+
* `source/v2/{orgId}/{sourceTarDigest}.tar.gz`.
|
|
638
|
+
*
|
|
639
|
+
* Naming the object by its own hash means every pair a reader can observe is
|
|
640
|
+
* self-consistent, and the bytes at a given key can never change after a URL is
|
|
641
|
+
* signed for it.
|
|
642
|
+
*/
|
|
643
|
+
function sourceTarballKey(orgId, sourceTarDigest) {
|
|
644
|
+
return `source/v2/${orgId}/${sourceTarDigest}.tar.gz`;
|
|
619
645
|
}
|
|
620
|
-
|
|
646
|
+
/**
|
|
647
|
+
* Cache key for the pointer that resolves a workflow `contentHash` to the
|
|
648
|
+
* content hash of the tarball built from it: `source/v2/{orgId}/{contentHash}.hash`.
|
|
649
|
+
*
|
|
650
|
+
* This is the one mutable object in the scheme. A concurrent write replaces a
|
|
651
|
+
* pointer wholesale — there is no half-written window — so the worst a racing
|
|
652
|
+
* pair of builders can do is leave whichever pointer landed last, and both
|
|
653
|
+
* tarballs stay valid and fetchable.
|
|
654
|
+
*/
|
|
655
|
+
function sourcePointerKey(orgId, contentHash) {
|
|
656
|
+
return `source/v2/${orgId}/${contentHash}.hash`;
|
|
657
|
+
}
|
|
658
|
+
var SourceCache = class SourceCache {
|
|
621
659
|
storage;
|
|
622
660
|
constructor(options) {
|
|
623
661
|
this.storage = options.storage;
|
|
624
662
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
663
|
+
/** The single place the pointer indirection is read. */
|
|
664
|
+
async resolvePointer(orgId, contentHash) {
|
|
665
|
+
return (await this.storage.get(sourcePointerKey(orgId, contentHash)))?.toString("utf-8").trim() || null;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Whether a source tarball for this org's `contentHash` exists.
|
|
669
|
+
*
|
|
670
|
+
* Both halves must be present: a pointer whose tarball has aged out is a
|
|
671
|
+
* miss, not a hit, or the caller skips a rebuild and dispatches a URL that
|
|
672
|
+
* 404s on the agent.
|
|
673
|
+
*/
|
|
674
|
+
async has(orgId, contentHash) {
|
|
675
|
+
const digest = await this.resolvePointer(orgId, contentHash);
|
|
676
|
+
if (!digest) {
|
|
677
|
+
logger$3.debug(`has(${contentHash}): false (no pointer)`, { orgId });
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
const exists = await this.storage.has(sourceTarballKey(orgId, digest));
|
|
681
|
+
logger$3.debug(`has(${contentHash}): ${exists}`, { orgId });
|
|
629
682
|
return exists;
|
|
630
683
|
}
|
|
631
|
-
async get(contentHash) {
|
|
632
|
-
const
|
|
684
|
+
async get(orgId, contentHash) {
|
|
685
|
+
const digest = await this.resolvePointer(orgId, contentHash);
|
|
686
|
+
if (!digest) {
|
|
687
|
+
logger$3.debug(`get(${contentHash}): miss (no pointer)`, { orgId });
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
const key = sourceTarballKey(orgId, digest);
|
|
633
691
|
const data = await this.storage.get(key);
|
|
634
692
|
if (data) {
|
|
635
693
|
await this.storage.touch(key);
|
|
636
|
-
logger$3.debug(`get(${contentHash}): hit (${data.length} bytes)
|
|
637
|
-
} else logger$3.debug(`get(${contentHash}): miss
|
|
694
|
+
logger$3.debug(`get(${contentHash}): hit (${data.length} bytes)`, { orgId });
|
|
695
|
+
} else logger$3.debug(`get(${contentHash}): miss (dangling pointer)`, { orgId });
|
|
638
696
|
return data;
|
|
639
697
|
}
|
|
640
|
-
|
|
641
|
-
|
|
698
|
+
/**
|
|
699
|
+
* A pre-signed download URL and the tarball's own digest.
|
|
700
|
+
*
|
|
701
|
+
* The pointer is resolved FIRST: it names the content hash, which is also the
|
|
702
|
+
* tarball's key, so the url and the digest returned here are read from a
|
|
703
|
+
* single source of truth and cannot disagree. Returns null on a miss —
|
|
704
|
+
* including a pointer with no tarball behind it, which is unverifiable and so
|
|
705
|
+
* is deliberately not served.
|
|
706
|
+
*/
|
|
707
|
+
async getUrlAndDigest(orgId, contentHash) {
|
|
708
|
+
const pointerKey = sourcePointerKey(orgId, contentHash);
|
|
709
|
+
const digest = (await this.storage.get(pointerKey))?.toString("utf-8").trim() || null;
|
|
710
|
+
if (!digest) {
|
|
711
|
+
logger$3.debug(`getUrlAndDigest(${contentHash}): miss (no pointer)`, { orgId });
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
const key = sourceTarballKey(orgId, digest);
|
|
642
715
|
const url = await this.storage.getUrl(key);
|
|
643
|
-
if (url) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
716
|
+
if (!url) {
|
|
717
|
+
logger$3.debug(`getUrlAndDigest(${contentHash}): miss (dangling pointer)`, { orgId });
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
await this.storage.touch(key);
|
|
721
|
+
await this.storage.touch(pointerKey);
|
|
722
|
+
logger$3.debug(`getUrlAndDigest(${contentHash}): hit`, { orgId });
|
|
723
|
+
return {
|
|
724
|
+
url,
|
|
725
|
+
digest
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
async getUrl(orgId, contentHash) {
|
|
729
|
+
return (await this.getUrlAndDigest(orgId, contentHash))?.url ?? null;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* A pre-signed upload URL for direct agent-to-storage upload.
|
|
733
|
+
*
|
|
734
|
+
* Takes the tarball's OWN digest, not the workflow `contentHash`: the agent
|
|
735
|
+
* computes it before asking for a URL, and the object is named by it.
|
|
736
|
+
*/
|
|
737
|
+
async getUploadUrl(orgId, sourceTarDigest) {
|
|
738
|
+
return this.storage.getUploadUrl(sourceTarballKey(orgId, sourceTarDigest));
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Publish the pointer that makes an uploaded tarball discoverable by
|
|
742
|
+
* `contentHash`. Called only after the agent confirms its upload completed —
|
|
743
|
+
* publishing before the bytes land would let a reader follow the pointer to a
|
|
744
|
+
* missing object.
|
|
745
|
+
*/
|
|
746
|
+
async publishPointer(orgId, contentHash, sourceTarDigest) {
|
|
747
|
+
await this.storage.put(sourcePointerKey(orgId, contentHash), sourceTarDigest);
|
|
748
|
+
logger$3.info(`publishPointer(${contentHash}) -> ${sourceTarDigest.slice(0, 12)}`, { orgId });
|
|
749
|
+
}
|
|
750
|
+
/** Store a tarball and the pointer that names it. */
|
|
751
|
+
async store(orgId, contentHash, tarball) {
|
|
752
|
+
const data = typeof tarball === "string" ? Buffer.from(tarball) : tarball;
|
|
753
|
+
const digest = SourceCache.computeDigest(data);
|
|
754
|
+
await this.storage.put(sourceTarballKey(orgId, digest), data);
|
|
755
|
+
await this.storage.put(sourcePointerKey(orgId, contentHash), digest);
|
|
756
|
+
logger$3.info(`store(${contentHash}): stored (${data.length} bytes)`, {
|
|
757
|
+
orgId,
|
|
758
|
+
digest: digest.slice(0, 12)
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
/** SHA-256 of a tarball's bytes. The agent computes the same value. */
|
|
762
|
+
static computeDigest(data) {
|
|
763
|
+
return sha256(data);
|
|
764
|
+
}
|
|
765
|
+
/** Remove a source tarball and its pointer. */
|
|
766
|
+
async remove(orgId, contentHash) {
|
|
767
|
+
const digest = await this.resolvePointer(orgId, contentHash);
|
|
768
|
+
await this.storage.delete(sourcePointerKey(orgId, contentHash));
|
|
769
|
+
const removed = digest ? await this.storage.delete(sourceTarballKey(orgId, digest)) : false;
|
|
770
|
+
logger$3.info(`remove(${contentHash}): ${removed ? "removed" : "not found"}`, { orgId });
|
|
663
771
|
return removed;
|
|
664
772
|
}
|
|
665
773
|
};
|
|
@@ -700,15 +808,23 @@ function depTarballKey(depsHash, platform, arch) {
|
|
|
700
808
|
}
|
|
701
809
|
/**
|
|
702
810
|
* Cache key for the pointer that resolves a lockfile to the content hash of the
|
|
703
|
-
* tarball built from it: `deps/{platform}-{arch}/{lockfileHash}.hash
|
|
811
|
+
* tarball built from it: `deps/{platform}-{arch}/{lockfileHash}.hash`, or
|
|
812
|
+
* `…/{lockfileHash}-{siblingsDigest}.hash` when the lock carries one.
|
|
813
|
+
*
|
|
814
|
+
* `siblingsDigest` is part of the key because the deps tarball carries in-repo
|
|
815
|
+
* `workspace:` sibling directories WITH their built output, and editing a
|
|
816
|
+
* sibling's source moves no package-manager lock file — so a `lockfileHash`-only
|
|
817
|
+
* key restored the sibling's stale build over the fresh clone on every run. A
|
|
818
|
+
* lock with no in-repo sibling carries no digest and keys exactly as before, so
|
|
819
|
+
* existing pointers stay live.
|
|
704
820
|
*
|
|
705
821
|
* This is the one mutable object in the scheme. A concurrent write replaces a
|
|
706
822
|
* pointer wholesale — there is no window in which it is half-written — so the
|
|
707
823
|
* worst a racing pair of builders can do is leave whichever pointer landed last,
|
|
708
824
|
* and both tarballs remain valid and fetchable.
|
|
709
825
|
*/
|
|
710
|
-
function depPointerKey(lockfileHash, platform, arch) {
|
|
711
|
-
return `deps/${platform}-${arch}/${lockfileHash}.hash`;
|
|
826
|
+
function depPointerKey(lockfileHash, platform, arch, siblingsDigest) {
|
|
827
|
+
return `deps/${platform}-${arch}/${siblingsDigest ? `${lockfileHash}-${siblingsDigest}` : lockfileHash}.hash`;
|
|
712
828
|
}
|
|
713
829
|
var DepCache = class DepCache {
|
|
714
830
|
storage;
|
|
@@ -736,8 +852,8 @@ var DepCache = class DepCache {
|
|
|
736
852
|
* Resolve a lockfile to the content hash of the tarball built from it, or
|
|
737
853
|
* null when no pointer exists. The single place the indirection is read.
|
|
738
854
|
*/
|
|
739
|
-
async resolvePointer(lockfileHash, platform, arch, ttlMsOverride) {
|
|
740
|
-
return (await this.storage.get(depPointerKey(lockfileHash, platform, arch), ttlMsOverride))?.toString("utf-8").trim() || null;
|
|
855
|
+
async resolvePointer(lockfileHash, platform, arch, ttlMsOverride, siblingsDigest) {
|
|
856
|
+
return (await this.storage.get(depPointerKey(lockfileHash, platform, arch, siblingsDigest), ttlMsOverride))?.toString("utf-8").trim() || null;
|
|
741
857
|
}
|
|
742
858
|
/**
|
|
743
859
|
* Check if a dep tarball exists in cache.
|
|
@@ -745,9 +861,9 @@ var DepCache = class DepCache {
|
|
|
745
861
|
* Both halves must be present: a pointer whose tarball has aged out is a miss,
|
|
746
862
|
* not a hit, or the caller skips a rebuild and dispatches a URL that 404s.
|
|
747
863
|
*/
|
|
748
|
-
async has(lockfileHash, platform, arch) {
|
|
864
|
+
async has(lockfileHash, platform, arch, siblingsDigest) {
|
|
749
865
|
const ttlMsOverride = await this.resolveTtlMsOverride();
|
|
750
|
-
const depsHash = await this.resolvePointer(lockfileHash, platform, arch, ttlMsOverride);
|
|
866
|
+
const depsHash = await this.resolvePointer(lockfileHash, platform, arch, ttlMsOverride, siblingsDigest);
|
|
751
867
|
if (!depsHash) {
|
|
752
868
|
logger$2.debug(`has(${lockfileHash}): false (no pointer)`, {
|
|
753
869
|
platform,
|
|
@@ -766,8 +882,8 @@ var DepCache = class DepCache {
|
|
|
766
882
|
* Get a pre-signed download URL for the dep tarball (for agent delivery).
|
|
767
883
|
* Refreshes TTL on hit.
|
|
768
884
|
*/
|
|
769
|
-
async getUrl(lockfileHash, platform, arch) {
|
|
770
|
-
return (await this.getUrlAndHash(lockfileHash, platform, arch))?.url ?? null;
|
|
885
|
+
async getUrl(lockfileHash, platform, arch, siblingsDigest) {
|
|
886
|
+
return (await this.getUrlAndHash(lockfileHash, platform, arch, siblingsDigest))?.url ?? null;
|
|
771
887
|
}
|
|
772
888
|
/**
|
|
773
889
|
* Get a pre-signed download URL and the tarball content hash.
|
|
@@ -775,9 +891,9 @@ var DepCache = class DepCache {
|
|
|
775
891
|
* Returns null on cache miss — including an entry with no pointer, which is
|
|
776
892
|
* unverifiable and so is deliberately not served.
|
|
777
893
|
*/
|
|
778
|
-
async getUrlAndHash(lockfileHash, platform, arch) {
|
|
894
|
+
async getUrlAndHash(lockfileHash, platform, arch, siblingsDigest) {
|
|
779
895
|
const ttlMsOverride = await this.resolveTtlMsOverride();
|
|
780
|
-
const pointerKey = depPointerKey(lockfileHash, platform, arch);
|
|
896
|
+
const pointerKey = depPointerKey(lockfileHash, platform, arch, siblingsDigest);
|
|
781
897
|
const hash = (await this.storage.get(pointerKey, ttlMsOverride))?.toString("utf-8").trim() || void 0;
|
|
782
898
|
if (!hash) {
|
|
783
899
|
logger$2.debug(`getUrlAndHash(${lockfileHash}): miss (no pointer)`, {
|
|
@@ -819,8 +935,8 @@ var DepCache = class DepCache {
|
|
|
819
935
|
* Called only after the agent confirms its upload completed. Publishing before
|
|
820
936
|
* the bytes land would let a reader follow the pointer to a missing object.
|
|
821
937
|
*/
|
|
822
|
-
async publishPointer(lockfileHash, platform, arch, depsHash) {
|
|
823
|
-
await this.storage.put(depPointerKey(lockfileHash, platform, arch), depsHash);
|
|
938
|
+
async publishPointer(lockfileHash, platform, arch, depsHash, siblingsDigest) {
|
|
939
|
+
await this.storage.put(depPointerKey(lockfileHash, platform, arch, siblingsDigest), depsHash);
|
|
824
940
|
logger$2.info(`publishPointer(${lockfileHash}) -> ${depsHash.slice(0, 12)}`, {
|
|
825
941
|
platform,
|
|
826
942
|
arch
|
|
@@ -944,7 +1060,7 @@ const UNAMBIGUOUS_LEGACY_KEY = /^[a-z0-9_][a-z0-9._-]*$/;
|
|
|
944
1060
|
* - a trailing `.` is stripped by NTFS, and a reserved device name cannot be
|
|
945
1061
|
* created there at all.
|
|
946
1062
|
*
|
|
947
|
-
* A key failing any clause
|
|
1063
|
+
* A key failing any clause gets no legacy fallback — it is re-fetched
|
|
948
1064
|
* and re-saved under the discriminated key, which is always correct.
|
|
949
1065
|
*/
|
|
950
1066
|
function unambiguousLegacyKey(key) {
|
|
@@ -2077,6 +2193,11 @@ Object.freeze({
|
|
|
2077
2193
|
approvalExpiryHours: DEFAULT_APPROVAL_EXPIRY_HOURS,
|
|
2078
2194
|
approvalExpirySeconds: DEFAULT_APPROVAL_EXPIRY_SECONDS
|
|
2079
2195
|
});
|
|
2196
|
+
z.enum([
|
|
2197
|
+
"stored",
|
|
2198
|
+
"unconfigured",
|
|
2199
|
+
"read-failure"
|
|
2200
|
+
]);
|
|
2080
2201
|
//#endregion
|
|
2081
2202
|
//#region src/security/trust-policy-store.ts
|
|
2082
2203
|
/**
|
|
@@ -2744,6 +2865,23 @@ var AdminApiClient = class {
|
|
|
2744
2865
|
* Uses GitHub App installation tokens for repository access.
|
|
2745
2866
|
*/
|
|
2746
2867
|
/**
|
|
2868
|
+
* Create an Octokit instance authenticated as the GitHub App itself (a JWT
|
|
2869
|
+
* signed with the App private key), not as one of its installations.
|
|
2870
|
+
*
|
|
2871
|
+
* App-level endpoints — `GET /app`, `GET /app/installations`, and the webhook
|
|
2872
|
+
* delivery endpoints under `/app/hook/deliveries` — accept only this
|
|
2873
|
+
* credential. Installation tokens are rejected there.
|
|
2874
|
+
*/
|
|
2875
|
+
function createAppOctokit(config) {
|
|
2876
|
+
return new Octokit({
|
|
2877
|
+
authStrategy: createAppAuth,
|
|
2878
|
+
auth: {
|
|
2879
|
+
appId: config.appId,
|
|
2880
|
+
privateKey: config.privateKey
|
|
2881
|
+
}
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
/**
|
|
2747
2885
|
* Create an Octokit instance authenticated as a GitHub App installation.
|
|
2748
2886
|
*
|
|
2749
2887
|
* Uses @octokit/auth-app to handle:
|
|
@@ -2842,22 +2980,13 @@ async function convertManifestCode(code, deps = {}) {
|
|
|
2842
2980
|
htmlUrl: d.html_url
|
|
2843
2981
|
};
|
|
2844
2982
|
}
|
|
2845
|
-
function appOctokitFor(creds) {
|
|
2846
|
-
return new Octokit({
|
|
2847
|
-
authStrategy: createAppAuth,
|
|
2848
|
-
auth: {
|
|
2849
|
-
appId: creds.appId,
|
|
2850
|
-
privateKey: creds.privateKey
|
|
2851
|
-
}
|
|
2852
|
-
});
|
|
2853
|
-
}
|
|
2854
2983
|
/**
|
|
2855
2984
|
* Poll GitHub (as the App, via a JWT) until at least one installation exists,
|
|
2856
2985
|
* returning the first installation's id + account login. Throws on timeout.
|
|
2857
2986
|
*/
|
|
2858
2987
|
async function waitForInstallation(creds, opts) {
|
|
2859
2988
|
const now = opts.now ?? Date.now;
|
|
2860
|
-
const octokit = opts.appOctokit ??
|
|
2989
|
+
const octokit = opts.appOctokit ?? createAppOctokit(creds);
|
|
2861
2990
|
const deadline = now() + opts.timeoutMs;
|
|
2862
2991
|
for (;;) {
|
|
2863
2992
|
const { data } = await octokit.request("GET /app/installations", { per_page: 1 });
|
|
@@ -15,6 +15,39 @@ export declare function setPendingAttestations(value: number): void;
|
|
|
15
15
|
export declare function setPendingAttestationOldestAgeSeconds(value: number): void;
|
|
16
16
|
/** Set the current number of terminally-rejected deferred attestations. */
|
|
17
17
|
export declare function setRejectedAttestations(value: number): void;
|
|
18
|
+
/**
|
|
19
|
+
* Why a deferred attestation was refused at capture, BEFORE it reached the
|
|
20
|
+
* outbox. Distinct from `kici_orch_rejected_attestations_current`, which counts
|
|
21
|
+
* rows the retrier later gave up on: this counts statements the orchestrator
|
|
22
|
+
* declined to sign anything about, so no row was ever written.
|
|
23
|
+
*
|
|
24
|
+
* `build_context_mismatch` is the reason an agent too old to receive the job's
|
|
25
|
+
* canonical build context produces, so it also counts an upgrade the fleet has
|
|
26
|
+
* not taken yet. Every other reason means a job produced a statement that
|
|
27
|
+
* contradicts the orchestrator's own run row — investigate the job, not the
|
|
28
|
+
* orchestrator.
|
|
29
|
+
*/
|
|
30
|
+
export declare const DeferredAttestationRejectReason: {
|
|
31
|
+
/** The DSSE payload did not decode, or did not parse as a KiCI statement. */
|
|
32
|
+
readonly Unparseable: 'unparseable';
|
|
33
|
+
/** The agent-supplied `statementHash` did not match the envelope's bytes. */
|
|
34
|
+
readonly StatementHashMismatch: 'statement_hash_mismatch';
|
|
35
|
+
/** The frozen statement disagrees with the run row on a build fact. */
|
|
36
|
+
readonly BuildContextMismatch: 'build_context_mismatch';
|
|
37
|
+
/** The reported subject digest appears in no subject of the statement. */
|
|
38
|
+
readonly SubjectDigestMismatch: 'subject_digest_mismatch';
|
|
39
|
+
/** The run or job named by the capture does not exist. */
|
|
40
|
+
readonly RunNotFound: 'run_not_found';
|
|
41
|
+
};
|
|
42
|
+
export type DeferredAttestationRejectReason = (typeof DeferredAttestationRejectReason)[keyof typeof DeferredAttestationRejectReason];
|
|
43
|
+
/**
|
|
44
|
+
* Deferred provenance attestations refused at capture.
|
|
45
|
+
* Labels:
|
|
46
|
+
* - reason: one of `DeferredAttestationRejectReason`
|
|
47
|
+
*/
|
|
48
|
+
export declare const deferredAttestationRejectedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
49
|
+
/** Record one refused deferred-attestation capture. */
|
|
50
|
+
export declare function incDeferredAttestationRejected(reason: DeferredAttestationRejectReason): void;
|
|
18
51
|
/** Record the collation-drift flag (1 = drift, 0 = clean) for a database. */
|
|
19
52
|
export declare function setDbCollationDrift(database: string, drifted: boolean): void;
|
|
20
53
|
interface ScalerUsageRow {
|
|
@@ -193,8 +226,21 @@ export declare function resetIngestOverflowMetricState(): void;
|
|
|
193
226
|
export declare const ingestOverflowCapturedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
194
227
|
/** Buffered deliveries successfully replayed through normal ingest. */
|
|
195
228
|
export declare const ingestOverflowReplayedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
196
|
-
/** Buffered deliveries permanently dropped. Labels: reason (cap_full|max_attempts). */
|
|
229
|
+
/** Buffered deliveries permanently dropped. Labels: reason (cap_full|max_attempts|max_age). */
|
|
197
230
|
export declare const ingestOverflowDroppedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
231
|
+
/**
|
|
232
|
+
* Replay reservations the admission controller refused. This is the drain's own
|
|
233
|
+
* backpressure signal, kept off `kici_orch_ingest_shed_total` because that one
|
|
234
|
+
* means "a sender was refused" and a background drain deciding not to start is
|
|
235
|
+
* not that.
|
|
236
|
+
*
|
|
237
|
+
* It is also the evidence that the gate can still stop the replayer: a drain
|
|
238
|
+
* that converges proves the mechanism works, while a non-zero count here during
|
|
239
|
+
* that drain proves the reservation is refusable rather than an exempt lane.
|
|
240
|
+
*
|
|
241
|
+
* Labels: reason (loop_overload|queue_full).
|
|
242
|
+
*/
|
|
243
|
+
export declare const ingestOverflowReplayRefusedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
198
244
|
/**
|
|
199
245
|
* Total webhooks received by the orchestrator.
|
|
200
246
|
* Labels:
|
|
@@ -232,6 +278,15 @@ export declare const trustPolicyDecisionsTotal: import("@opentelemetry/api").Cou
|
|
|
232
278
|
* shows as a divergence between the two rather than as silence.
|
|
233
279
|
*/
|
|
234
280
|
export declare const forkEventsIgnoredTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
281
|
+
/**
|
|
282
|
+
* Record when the approval directory this orchestrator authorizes against was
|
|
283
|
+
* last refreshed — the cached row's write time at boot, and the arrival time on
|
|
284
|
+
* every later Platform push.
|
|
285
|
+
*
|
|
286
|
+
* Never called on an orchestrator that has received no directory, which is what
|
|
287
|
+
* leaves the gauge below unset rather than reporting an age of zero.
|
|
288
|
+
*/
|
|
289
|
+
export declare function setTrustDirectoryUpdatedAt(updatedAt: Date): void;
|
|
235
290
|
/**
|
|
236
291
|
* Duration of trigger matching operations (lock file evaluation).
|
|
237
292
|
* Buckets: 1ms to 5s covering fast rejects through complex evaluations.
|
|
@@ -249,6 +304,22 @@ export declare const dedupHitsTotal: import("@opentelemetry/api").Counter<import
|
|
|
249
304
|
* - source: idle-pool | client
|
|
250
305
|
*/
|
|
251
306
|
export declare const pgPoolClientErrorsTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
307
|
+
/**
|
|
308
|
+
* Post-boot `CREATE INDEX CONCURRENTLY` builds, which run outside the
|
|
309
|
+
* migration set so an index on a volume-scaling table never locks that table
|
|
310
|
+
* during an upgrade.
|
|
311
|
+
* Labels:
|
|
312
|
+
* - name: index name
|
|
313
|
+
* - outcome: ok | error
|
|
314
|
+
*/
|
|
315
|
+
export declare const deferredIndexBuildsTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
316
|
+
/**
|
|
317
|
+
* Rows removed by the database-side retention sweep, which prunes the warm
|
|
318
|
+
* tables directly for installs that run no object store.
|
|
319
|
+
* Labels:
|
|
320
|
+
* - table: the table the rows came from
|
|
321
|
+
*/
|
|
322
|
+
export declare const retentionRowsDeletedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
252
323
|
/**
|
|
253
324
|
* Total source cache hits (`.kici/` source tarball found for content hash).
|
|
254
325
|
*/
|
|
@@ -557,6 +628,19 @@ export declare const installSecretsRegistryUsedTotal: import("@opentelemetry/api
|
|
|
557
628
|
* - trust_tier: unknown | known (the `trusted` tier does not strip and therefore never fires this counter)
|
|
558
629
|
*/
|
|
559
630
|
export declare const installSecretsContributorStrippedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
631
|
+
/**
|
|
632
|
+
* Dispatches where a job's container-registry credentials were withheld because
|
|
633
|
+
* the contributor tier was not `trusted`.
|
|
634
|
+
*
|
|
635
|
+
* The sibling of `installSecretsContributorStrippedTotal`, for the other class
|
|
636
|
+
* of credential a dispatch resolves orchestrator-side. Counted separately
|
|
637
|
+
* because the two fire on different jobs: install secrets are workflow-level
|
|
638
|
+
* and registry auth is per-job, so one run can strip both, either, or neither.
|
|
639
|
+
*
|
|
640
|
+
* Labels:
|
|
641
|
+
* - trust_tier: unknown | known (the `trusted` tier does not strip and therefore never fires this counter)
|
|
642
|
+
*/
|
|
643
|
+
export declare const containerRegistryAuthContributorStrippedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
560
644
|
/**
|
|
561
645
|
* Per-environment latency of `secretResolver.resolveForJob` calls in the
|
|
562
646
|
* install-secrets path. Buckets mirror `triggerMatchDurationSeconds`:
|
|
@@ -566,6 +650,29 @@ export declare const installSecretsContributorStrippedTotal: import("@openteleme
|
|
|
566
650
|
* - environment: the environment name referenced in the qualified `<environment>:<secret>` ref (per-org count is typically <10)
|
|
567
651
|
*/
|
|
568
652
|
export declare const installSecretsTokenResolutionDurationSeconds: import("@opentelemetry/api").Histogram<import("@opentelemetry/api").Attributes>;
|
|
653
|
+
/**
|
|
654
|
+
* Recovery, ack-requeue and cancel actions this coordinator declined to take
|
|
655
|
+
* because the DB says the row is not its to act on — owned by a live sibling,
|
|
656
|
+
* already reclaimed, or already acked elsewhere.
|
|
657
|
+
*
|
|
658
|
+
* Labels:
|
|
659
|
+
* - action: startup_recovery | recovery_expiry | ack_expiry | ack_sweep
|
|
660
|
+
* - reason: owned_elsewhere | unknown_owner | already_resolved
|
|
661
|
+
*/
|
|
662
|
+
export declare const dispatchOwnershipSkippedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
663
|
+
/**
|
|
664
|
+
* `execution_jobs` status writes the monotonicity guard rejected where the
|
|
665
|
+
* stored row contradicts the frame. A non-zero rate means an agent is still
|
|
666
|
+
* reporting on a job this orchestrator already reaped, with a different verdict.
|
|
667
|
+
*
|
|
668
|
+
* A rejected write whose stored row AGREES with the frame is not counted: an
|
|
669
|
+
* orchestrator-side writer reached the same verdict first, which is a race the
|
|
670
|
+
* cancel path takes deliberately, not a divergence.
|
|
671
|
+
*
|
|
672
|
+
* Labels:
|
|
673
|
+
* - rejected_status: the status the late frame carried
|
|
674
|
+
*/
|
|
675
|
+
export declare const nonMonotonicJobStatusWritesTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
569
676
|
/**
|
|
570
677
|
* Terminal dispatch_queue rows deleted by the retention sweep (the cleanup
|
|
571
678
|
* tick's `pruneTerminalDispatchRows`).
|
|
@@ -610,5 +717,13 @@ export declare const wsPlatformCapabilityGapTotal: import("@opentelemetry/api").
|
|
|
610
717
|
* Platform push. Idempotent: repeat calls are no-ops.
|
|
611
718
|
*/
|
|
612
719
|
export declare function registerOrchestratorMetrics(): void;
|
|
720
|
+
/** Record whether the LISTEN subscription on `channel` currently holds a client. */
|
|
721
|
+
export declare function setPgListenConnected(channel: string, connected: boolean): void;
|
|
722
|
+
/**
|
|
723
|
+
* LISTEN subscriptions re-established after losing their connection.
|
|
724
|
+
* Labels:
|
|
725
|
+
* - channel: the LISTEN channel name
|
|
726
|
+
*/
|
|
727
|
+
export declare const pgListenReconnectsTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
613
728
|
export {};
|
|
614
729
|
//# sourceMappingURL=prometheus.d.ts.map
|
package/dist/oidc/db-signer.d.ts
CHANGED
|
@@ -16,8 +16,27 @@ import type { Signer } from './signer.js';
|
|
|
16
16
|
export declare const SIGNING_KEY_AAD = "orchestrator-signing-key";
|
|
17
17
|
/** Encrypt a private JWK with the master key. Returns the base64 wrapped blob. */
|
|
18
18
|
export declare function wrapPrivateJwk(privateJwk: JWK, secretKey: string): string;
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Decrypt a wrapped private JWK with the master key, falling back to the old
|
|
21
|
+
* key during a rotation grace window. Same dual-key pattern as
|
|
22
|
+
* `PgSecretStore.decryptWithFallback` and `decryptConfigFields`. Throws on
|
|
23
|
+
* wrong key(s) / tamper.
|
|
24
|
+
*
|
|
25
|
+
* The fallback is what keeps `rotate-key` from bricking the next boot: the
|
|
26
|
+
* signing key is master-key-wrapped, so a rotation that re-encrypts it leaves
|
|
27
|
+
* every already-running node reading it under the old key, and a rotation that
|
|
28
|
+
* does not still leaves this row sealed under the previous generation.
|
|
29
|
+
*/
|
|
30
|
+
export declare function unwrapPrivateJwk(encrypted: string, secretKey: string, oldSecretKey?: string): JWK;
|
|
31
|
+
/**
|
|
32
|
+
* Unwrap a private JWK and report which key opened it, so a caller that can
|
|
33
|
+
* re-seal the row (the boot self-heal) knows the row is stranded under the old
|
|
34
|
+
* key rather than current.
|
|
35
|
+
*/
|
|
36
|
+
export declare function unwrapPrivateJwkWithKeyAge(encrypted: string, secretKey: string, oldSecretKey?: string): {
|
|
37
|
+
privateJwk: JWK;
|
|
38
|
+
usedOldKey: boolean;
|
|
39
|
+
};
|
|
21
40
|
export declare class DbSigner implements Signer {
|
|
22
41
|
private readonly privateKey;
|
|
23
42
|
private readonly publicJwk;
|
|
@@ -26,7 +45,7 @@ export declare class DbSigner implements Signer {
|
|
|
26
45
|
readonly keyRef: null;
|
|
27
46
|
private constructor();
|
|
28
47
|
/** Import an EC P-256 private JWK NON-EXTRACTABLE and derive its public half. */
|
|
29
|
-
|
|
48
|
+
static fromPrivateJwk(privateJwk: JWK): Promise<DbSigner>;
|
|
30
49
|
/**
|
|
31
50
|
* Generate a fresh ES256 keypair, wrap the private half with the master key,
|
|
32
51
|
* and return the signer + the artifacts to persist. The generated private key
|
|
@@ -39,8 +58,11 @@ export declare class DbSigner implements Signer {
|
|
|
39
58
|
encryptedPrivateJwk: string;
|
|
40
59
|
kid: string;
|
|
41
60
|
}>;
|
|
42
|
-
/**
|
|
43
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Reconstruct a signer from a wrapped private JWK, accepting the old master
|
|
63
|
+
* key during a rotation grace window.
|
|
64
|
+
*/
|
|
65
|
+
static fromEncrypted(encryptedPrivateJwk: string, secretKey: string, oldSecretKey?: string): Promise<DbSigner>;
|
|
44
66
|
sign(data: Uint8Array): Promise<Uint8Array>;
|
|
45
67
|
getPublicJwk(): Promise<JWK>;
|
|
46
68
|
getKid(): Promise<string>;
|