@kici-dev/orchestrator 0.17.0 → 0.18.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/agent-version.d.ts +12 -21
- package/dist/agent/dispatcher.d.ts +283 -437
- package/dist/agent/host-roster-reaper.d.ts +8 -10
- package/dist/agent/host-roster.d.ts +77 -100
- package/dist/agent/ownership-refusal.d.ts +3 -5
- package/dist/agent/ownership-tracker.d.ts +42 -76
- package/dist/agent/registry.d.ts +121 -233
- package/dist/agent/token-store.d.ts +34 -77
- package/dist/agent-packaging/availability.d.ts +6 -9
- package/dist/agent-packaging/download.d.ts +6 -9
- package/dist/agent-packaging/install-closure.d.ts +9 -12
- package/dist/agent-packaging/node-binary.d.ts +6 -10
- package/dist/agent-packaging/node-runtime-members.d.ts +3 -5
- package/dist/agent-packaging/refresh.d.ts +12 -16
- package/dist/agent-packaging/store.d.ts +5 -6
- package/dist/agent-packaging/upload.d.ts +3 -4
- package/dist/app.d.ts +0 -3
- package/dist/approvals/apply-decision.d.ts +35 -52
- package/dist/approvals/approval-resolver.d.ts +11 -23
- package/dist/approvals/held-run-release-wiring.d.ts +14 -21
- package/dist/approvals/independent-wiring.d.ts +13 -20
- package/dist/approvals/resume-router.d.ts +1 -1
- package/dist/approvals/step-approval-bridge.d.ts +11 -21
- package/dist/approvals/triggerer-subject.d.ts +8 -10
- package/dist/artifacts/artifact-store.d.ts +55 -95
- package/dist/artifacts/failure-messages.d.ts +15 -20
- package/dist/audit/access-log.d.ts +27 -45
- package/dist/audit/sampling-rate-limiter.d.ts +11 -23
- package/dist/cache/agent-job-failed-error.d.ts +2 -3
- package/dist/cache/build-coordinator.d.ts +5 -28
- package/dist/cache/cache-upload-settle.d.ts +11 -11
- package/dist/cache/dep-cache.d.ts +32 -60
- package/dist/cache/dispatch-cache-ref-tracker.d.ts +15 -23
- package/dist/cache/global-eval-round-cache.d.ts +33 -41
- package/dist/cache/pending-builds.d.ts +2 -6
- package/dist/cache/pending-dynamics.d.ts +2 -7
- package/dist/cache/pending-global-evals.d.ts +9 -12
- package/dist/cache/pending-inits.d.ts +11 -22
- package/dist/cache/pending-precursor-db-watcher.d.ts +24 -32
- package/dist/cache/pending-tracker.d.ts +17 -45
- package/dist/cache/precursor-result.d.ts +17 -30
- package/dist/cache/source-cache.d.ts +36 -65
- package/dist/cache/user-cache.d.ts +21 -25
- package/dist/cancel/cancel-held-run.d.ts +13 -16
- package/dist/cancel/cancel-route-answer.d.ts +1 -2
- package/dist/cancel/cancel-run.d.ts +26 -50
- package/dist/cancel/dashboard-cancel-handler.d.ts +2 -2
- package/dist/cancel/peer-job-cancel.d.ts +11 -14
- package/dist/cancel/sweep-stuck-cancelling.d.ts +9 -13
- package/dist/cli/api-client.d.ts +25 -76
- package/dist/cli/commands/access-log.d.ts +4 -24
- package/dist/cli/commands/agent-package.d.ts +7 -15
- package/dist/cli/commands/agent-service/agent-env.d.ts +3 -6
- package/dist/cli/commands/agent-service/index.d.ts +4 -10
- package/dist/cli/commands/agent-service/install.d.ts +4 -11
- package/dist/cli/commands/agent-service/status.d.ts +4 -9
- package/dist/cli/commands/agent-service/upgrade.d.ts +4 -16
- package/dist/cli/commands/agent.d.ts +12 -25
- package/dist/cli/commands/api-key.d.ts +5 -12
- package/dist/cli/commands/attestations-list.d.ts +7 -17
- package/dist/cli/commands/attestations-retry.d.ts +5 -11
- package/dist/cli/commands/attestations-reverify.d.ts +4 -5
- package/dist/cli/commands/attestations.d.ts +3 -8
- package/dist/cli/commands/audit.d.ts +1 -5
- package/dist/cli/commands/backend.d.ts +2 -11
- package/dist/cli/commands/check-run.d.ts +7 -18
- package/dist/cli/commands/cluster-name.d.ts +5 -12
- package/dist/cli/commands/cluster-settings.d.ts +28 -53
- package/dist/cli/commands/cold-store.d.ts +7 -15
- package/dist/cli/commands/config.d.ts +9 -20
- package/dist/cli/commands/context.d.ts +8 -20
- package/dist/cli/commands/dashboard-encryption-key.d.ts +9 -14
- package/dist/cli/commands/db-backup.d.ts +17 -32
- package/dist/cli/commands/db.d.ts +3 -4
- package/dist/cli/commands/debug-bundle.d.ts +3 -10
- package/dist/cli/commands/diagnose.d.ts +3 -14
- package/dist/cli/commands/event-dlq.d.ts +4 -12
- package/dist/cli/commands/event-log.d.ts +2 -20
- package/dist/cli/commands/execution.d.ts +3 -9
- package/dist/cli/commands/firecracker/index.d.ts +3 -6
- package/dist/cli/commands/held-run.d.ts +23 -37
- package/dist/cli/commands/host.d.ts +4 -11
- package/dist/cli/commands/inspect-bundle.d.ts +3 -11
- package/dist/cli/commands/local-hook.d.ts +2 -10
- package/dist/cli/commands/local-trigger.d.ts +2 -2
- package/dist/cli/commands/orchestrator-service/drain.d.ts +3 -6
- package/dist/cli/commands/orchestrator-service/index.d.ts +6 -14
- package/dist/cli/commands/orchestrator-service/install-env.d.ts +25 -49
- package/dist/cli/commands/orchestrator-service/install.d.ts +2 -5
- package/dist/cli/commands/orchestrator-service/status.d.ts +10 -22
- package/dist/cli/commands/orchestrator-service/upgrade.d.ts +6 -15
- package/dist/cli/commands/org-settings.d.ts +5 -11
- package/dist/cli/commands/peer.d.ts +5 -10
- package/dist/cli/commands/queue.d.ts +3 -9
- package/dist/cli/commands/registration.d.ts +4 -8
- package/dist/cli/commands/remote-source.d.ts +6 -12
- package/dist/cli/commands/rotate.d.ts +1 -5
- package/dist/cli/commands/runs.d.ts +1 -30
- package/dist/cli/commands/scaler.d.ts +5 -12
- package/dist/cli/commands/secret.d.ts +1 -9
- package/dist/cli/commands/service-health.d.ts +5 -10
- package/dist/cli/commands/shared/admin-cli-access-log.d.ts +10 -19
- package/dist/cli/commands/shared/bare-metal-binary-check.d.ts +86 -0
- package/dist/cli/commands/shared/cli-action.d.ts +1 -4
- package/dist/cli/commands/shared/confirm.d.ts +5 -10
- package/dist/cli/commands/shared/db.d.ts +1 -12
- package/dist/cli/commands/shared/env-file-mode.d.ts +6 -9
- package/dist/cli/commands/shared/secret-input.d.ts +7 -21
- package/dist/cli/commands/shared/service-verbs.d.ts +6 -12
- package/dist/cli/commands/shared/unbound-context-warning.d.ts +2 -4
- package/dist/cli/commands/shared/upgrade-hooks.d.ts +7 -16
- package/dist/cli/commands/shared/upgrade-safety.d.ts +20 -31
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +83 -161
- package/dist/cli/commands/signing-key.d.ts +4 -9
- package/dist/cli/commands/source-manifest.d.ts +11 -14
- package/dist/cli/commands/source.d.ts +7 -32
- package/dist/cli/commands/token.d.ts +4 -15
- package/dist/cli/commands/trust-policy.d.ts +35 -88
- package/dist/cli/commands/variable.d.ts +3 -5
- package/dist/cli/commands/workflow.d.ts +3 -24
- package/dist/cli/join.d.ts +3 -6
- package/dist/cli/kici-admin.d.ts +11 -29
- package/dist/cli/lazy-deps/cache.d.ts +1 -14
- package/dist/cli/lazy-deps/downloader.d.ts +4 -27
- package/dist/cli/lazy-deps/registry.d.ts +5 -25
- package/dist/cli/local-github-ingress-url.d.ts +5 -7
- package/dist/cli/loopback-callback.d.ts +6 -7
- package/dist/cli/open-browser.d.ts +4 -8
- package/dist/cli/root-option-routing.d.ts +17 -29
- package/dist/cli/service/backup-timer.d.ts +16 -39
- package/dist/cli/service/compose-path.d.ts +3 -7
- package/dist/cli/service/compose.d.ts +12 -30
- package/dist/cli/service/deploy-env.d.ts +6 -18
- package/dist/cli/service/entrypoint.d.ts +14 -25
- package/dist/cli/service/env-assignment.d.ts +6 -17
- package/dist/cli/service/image-digests.d.ts +6 -15
- package/dist/cli/service/index.d.ts +2 -18
- package/dist/cli/service/instance/index-file.d.ts +9 -16
- package/dist/cli/service/instance/manifest.d.ts +33 -59
- package/dist/cli/service/instance/resolve.d.ts +32 -68
- package/dist/cli/service/instance/types.d.ts +12 -29
- package/dist/cli/service/launchd.d.ts +29 -60
- package/dist/cli/service/platform-detect.d.ts +13 -68
- package/dist/cli/service/privilege.d.ts +12 -25
- package/dist/cli/service/shutdown-grace.d.ts +11 -20
- package/dist/cli/service/systemd.d.ts +7 -20
- package/dist/cli/service/types.d.ts +63 -114
- package/dist/cli/service/windows-acl.d.ts +14 -21
- package/dist/cli/service/windows-env-file.d.ts +10 -28
- package/dist/cli/service/windows.d.ts +15 -31
- package/dist/cli/webhook-url-notes.d.ts +4 -6
- package/dist/cli/wizard/agent-wizard.d.ts +3 -14
- package/dist/cli/wizard/orchestrator-wizard.d.ts +12 -37
- package/dist/cli/wizard/prompts.d.ts +6 -16
- package/dist/cli.js +4568 -6571
- package/dist/cluster/cluster-identity.d.ts +20 -32
- package/dist/cluster/cluster-settings-reader.d.ts +50 -77
- package/dist/cluster/coordinator-credential.d.ts +4 -5
- package/dist/cluster/coordinator.d.ts +76 -148
- package/dist/cluster/health-api.d.ts +2 -10
- package/dist/cluster/index.d.ts +1 -6
- package/dist/cluster/instance-heartbeat.d.ts +26 -42
- package/dist/cluster/join-client.d.ts +4 -7
- package/dist/cluster/join-handler.d.ts +3 -7
- package/dist/cluster/join-token.d.ts +13 -27
- package/dist/cluster/leader-gated-scheduler.d.ts +8 -17
- package/dist/cluster/orphan-recovery.d.ts +13 -35
- package/dist/cluster/peer-auth-coordinator.d.ts +2 -8
- package/dist/cluster/peer-client.d.ts +46 -76
- package/dist/cluster/peer-credentials.d.ts +24 -70
- package/dist/cluster/peer-crypto.d.ts +5 -26
- package/dist/cluster/peer-forget.d.ts +3 -3
- package/dist/cluster/peer-handler.d.ts +47 -71
- package/dist/cluster/peer-registry.d.ts +37 -106
- package/dist/cluster/plan-headroom-store.d.ts +4 -6
- package/dist/cluster/raft-state.d.ts +4 -13
- package/dist/cluster/raft.d.ts +18 -53
- package/dist/cluster/reconcile-identity.d.ts +8 -11
- package/dist/cluster/rerouted-job-guard.d.ts +7 -11
- package/dist/cluster/verified-issuer.d.ts +2 -3
- package/dist/cluster/worker-cluster-settings.d.ts +7 -10
- package/dist/cluster/worker-eviction.d.ts +16 -26
- package/dist/cold-store/load-access-log-range.d.ts +15 -31
- package/dist/cold-store/load-event-log-range.d.ts +13 -27
- package/dist/cold-store/load-secret-audit-log-range.d.ts +8 -12
- package/dist/cold-store/orchestrator-cold-store.d.ts +8 -28
- package/dist/cold-store/tables/access-log.d.ts +12 -14
- package/dist/cold-store/tables/event-log.d.ts +4 -8
- package/dist/cold-store/tables/execution-runs.d.ts +6 -11
- package/dist/cold-store/tables/secret-audit-log.d.ts +5 -12
- package/dist/concurrency/dispatch-next-queued.d.ts +13 -25
- package/dist/concurrency/queue-manager.d.ts +22 -54
- package/dist/concurrency/waiters.d.ts +15 -31
- package/dist/config/encryption.d.ts +0 -2
- package/dist/config/loader.d.ts +0 -1
- package/dist/config/reload.d.ts +0 -6
- package/dist/config/resolver.d.ts +0 -4
- package/dist/config/shared-store.d.ts +0 -2
- package/dist/config/types.d.ts +0 -3
- package/dist/content-requirements-cache.d.ts +0 -1
- package/dist/contexts/context-store.d.ts +0 -2
- package/dist/contexts/held-runs.d.ts +0 -2
- package/dist/contexts/variable-store.d.ts +0 -2
- package/dist/dashboard/attestation-filters.d.ts +13 -18
- package/dist/dashboard/handler.d.ts +143 -307
- package/dist/db/client.d.ts +2 -6
- package/dist/db/deferred-index-build.d.ts +8 -10
- package/dist/db/deferred-indexes.d.ts +15 -22
- package/dist/db/migration-provider.d.ts +2 -4
- package/dist/db/migration-test-harness.d.ts +26 -44
- package/dist/db/migrations/001_initial.d.ts +1 -4
- package/dist/db/migrations/002_config_versions_key_version.d.ts +5 -8
- package/dist/db/migrations/003_access_log.d.ts +10 -15
- package/dist/db/migrations/004_rename_bundle_to_source.d.ts +7 -9
- package/dist/db/migrations/005_cold_store_chunk_counter.d.ts +4 -10
- package/dist/db/migrations/006_runs_jobs_steps_archived_at.d.ts +9 -29
- package/dist/db/migrations/007_audit_logs_archived_at.d.ts +11 -30
- package/dist/db/migrations/008_event_log_archived_at.d.ts +14 -29
- package/dist/db/migrations/009_access_log_trigram.d.ts +4 -8
- package/dist/db/migrations/010_cold_store_chunks.d.ts +16 -26
- package/dist/db/migrations/011_drop_source_secrets_notify.d.ts +6 -14
- package/dist/db/migrations/012_peer_credentials_active_uniq.d.ts +9 -14
- package/dist/db/migrations/013_execution_log_bytes.d.ts +5 -13
- package/dist/db/migrations/014_kici_events_lease_retry.d.ts +14 -27
- package/dist/db/migrations/015_org_settings_customer_scoped.d.ts +12 -17
- package/dist/db/migrations/016_org_settings_allow_http_npm.d.ts +4 -6
- package/dist/db/migrations/019_generic_sources_change_notify.d.ts +13 -17
- package/dist/db/migrations/020_org_settings_dashboard_write_policy.d.ts +5 -8
- package/dist/db/migrations/021_check_run_tracking.d.ts +5 -17
- package/dist/db/migrations/022_scaler_manager_state.d.ts +10 -20
- package/dist/db/migrations/023_dispatch_queue_recovery_deadline.d.ts +7 -18
- package/dist/db/migrations/024_dispatch_queue_provisioning_error.d.ts +5 -11
- package/dist/db/migrations/025_init_failure.d.ts +5 -9
- package/dist/db/migrations/027_workflow_timeout.d.ts +5 -13
- package/dist/db/migrations/029_dispatch_queue_attempts.d.ts +4 -10
- package/dist/db/migrations/030_held_runs_env_set_null.d.ts +5 -6
- package/dist/db/migrations/031_dispatch_queue_ack_deadline.d.ts +6 -12
- package/dist/db/migrations/033_org_settings_approval.d.ts +7 -10
- package/dist/db/migrations/034_held_runs_generalize.d.ts +10 -18
- package/dist/db/migrations/035_pending_workflow_contexts.d.ts +4 -5
- package/dist/db/migrations/037_generic_sources_provider_type_local.d.ts +5 -6
- package/dist/db/migrations/038_remote_sources.d.ts +5 -7
- package/dist/db/migrations/045_host_properties.d.ts +5 -9
- package/dist/db/migrations/046_join_token_consumed_by_instance.d.ts +5 -10
- package/dist/db/migrations/048_host_reboot_pending.d.ts +6 -12
- package/dist/db/migrations/049_held_runs_payload.d.ts +3 -8
- package/dist/db/migrations/050_sources_slug.d.ts +4 -5
- package/dist/db/migrations/051_binding_host_pattern.d.ts +7 -10
- package/dist/db/migrations/052_host_reach_metadata.d.ts +8 -11
- package/dist/db/migrations/053_agent_token_single_use.d.ts +6 -10
- package/dist/db/migrations/054_local_working_tree.d.ts +4 -6
- package/dist/db/migrations/055_agent_token_mandatory_labels.d.ts +6 -11
- package/dist/db/migrations/056_execution_jobs_environments.d.ts +4 -11
- package/dist/db/migrations/064_execution_jobs_skipped_environments.d.ts +6 -9
- package/dist/db/migrations/065_pending_attestations.d.ts +9 -12
- package/dist/db/migrations/066_pending_attestations_rejected.d.ts +4 -7
- package/dist/db/migrations/068_request_idempotency.d.ts +7 -9
- package/dist/db/migrations/069_reroute_tunables.d.ts +6 -14
- package/dist/db/migrations/071_batch_accumulation.d.ts +8 -16
- package/dist/db/migrations/072_dispatch_queue_run_id_index.d.ts +3 -9
- package/dist/db/migrations/074_normalize_zero_concurrency_limit.d.ts +4 -7
- package/dist/db/migrations/076_artifacts.d.ts +7 -8
- package/dist/db/migrations/078_org_settings_backup_staleness.d.ts +3 -6
- package/dist/db/migrations/079_org_settings_scaler_spawn_timeout.d.ts +4 -8
- package/dist/db/migrations/080_cluster_settings.d.ts +6 -10
- package/dist/db/migrations/081_org_settings_queue_timeout.d.ts +7 -11
- package/dist/db/migrations/082_host_s3_reachable.d.ts +5 -10
- package/dist/db/migrations/083_org_settings_artifact_caps.d.ts +6 -8
- package/dist/db/migrations/084_orchestrator_signing_keys.d.ts +8 -13
- package/dist/db/migrations/085_cluster_settings_reroute_flap_grace_ms.d.ts +4 -5
- package/dist/db/migrations/086_cluster_settings_max_fanout_hosts.d.ts +3 -3
- package/dist/db/migrations/087_cluster_settings_event_router_rate_limit.d.ts +5 -3
- package/dist/db/migrations/088_cluster_settings_cache_max_tarball_bytes.d.ts +3 -3
- package/dist/db/migrations/089_cluster_settings_cache_ttl_days.d.ts +3 -5
- package/dist/db/migrations/090_cluster_settings_concurrency_wait_timeout_ms.d.ts +3 -6
- package/dist/db/migrations/091_cluster_settings_agent_token_ttl_ms.d.ts +3 -6
- package/dist/db/migrations/092_cluster_settings_version.d.ts +5 -9
- package/dist/db/migrations/094_dashboard_encryption_keys.d.ts +9 -14
- package/dist/db/migrations/095_dashboard_write_policy_tristate.d.ts +2 -6
- package/dist/db/migrations/099_cluster_settings_dashboard_verified_issuer.d.ts +8 -11
- package/dist/db/migrations/101_contexts_hold_expiry_drop_default.d.ts +9 -15
- package/dist/db/migrations/102_dispatch_queue_agent_id.d.ts +6 -11
- package/dist/db/migrations/103_cluster_settings_ownership_db_check_timeout_ms.d.ts +6 -12
- package/dist/db/migrations/104_check_run_terminal_sent.d.ts +8 -14
- package/dist/db/migrations/105_org_trust_policy.d.ts +5 -7
- package/dist/db/migrations/106_cluster_settings_check_run_tracking_ttl_days.d.ts +4 -12
- package/dist/db/migrations/107_check_run_tracking_updated_at_index.d.ts +8 -14
- package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +10 -19
- package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +2 -3
- package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +7 -8
- package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +9 -16
- package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +8 -16
- package/dist/db/migrations/125_org_trust_directory.d.ts +13 -21
- package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +10 -16
- package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +8 -16
- package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +6 -11
- package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +8 -11
- package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +13 -20
- package/dist/db/migrations/131_execution_jobs_git_credentials.d.ts +12 -22
- package/dist/db/migrations/132_execution_runs_trigger_event.d.ts +6 -16
- package/dist/db/migrations/133_dispatch_queue_source_tar_digest.d.ts +6 -9
- package/dist/db/migrations/140_dispatch_queue_canonical_labels.d.ts +22 -40
- package/dist/db/migrations/143_execution_jobs_precursor_result.d.ts +9 -18
- package/dist/db/migrations/144_execution_runs_registration_window.d.ts +12 -20
- package/dist/db/migrations/147_sealed_job_secrets.d.ts +5 -9
- package/dist/db/migrations/148_cluster_settings_sealed_secrets_retry_backoff_ms.d.ts +7 -13
- package/dist/db/migrations/150_execution_runs_status_epoch.d.ts +6 -11
- package/dist/db/migrations/152_org_settings_cache_upload_settle_timeout.d.ts +5 -8
- package/dist/db/migrations/157_host_roster_identity_source.d.ts +9 -13
- package/dist/db/migrations/159_org_id_widen_to_source_width.d.ts +5 -0
- package/dist/db/migrator.d.ts +17 -25
- package/dist/db/notify-listener.d.ts +8 -11
- package/dist/db/repos/dashboard-encryption-keys-repo.d.ts +9 -10
- package/dist/db/synthetic-job-ids.d.ts +6 -8
- package/dist/db/types.d.ts +531 -1058
- package/dist/deployment/deployment-identity.d.ts +42 -60
- package/dist/diagnostics/bundle-reader.d.ts +3 -15
- package/dist/diagnostics/bundle-writer.d.ts +5 -12
- package/dist/diagnostics/checks/agents.d.ts +1 -7
- package/dist/diagnostics/checks/backup.d.ts +7 -10
- package/dist/diagnostics/checks/certs.d.ts +1 -6
- package/dist/diagnostics/checks/config.d.ts +2 -5
- package/dist/diagnostics/checks/db.d.ts +1 -1
- package/dist/diagnostics/checks/disk.d.ts +1 -6
- package/dist/diagnostics/checks/firecracker-network.d.ts +3 -3
- package/dist/diagnostics/checks/index.d.ts +0 -6
- package/dist/diagnostics/checks/retention.d.ts +3 -6
- package/dist/diagnostics/checks/scaler.d.ts +3 -7
- package/dist/diagnostics/checks/ws.d.ts +1 -6
- package/dist/diagnostics/fleet-collector.d.ts +11 -14
- package/dist/diagnostics/fleet-constants.d.ts +1 -3
- package/dist/diagnostics/fleet-selection.d.ts +3 -7
- package/dist/diagnostics/fleet-topology.d.ts +2 -6
- package/dist/diagnostics/fleet-wiring.d.ts +6 -12
- package/dist/diagnostics/index.d.ts +0 -5
- package/dist/diagnostics/runner.d.ts +4 -8
- package/dist/diagnostics/types.d.ts +10 -21
- package/dist/drain/drain-controller.d.ts +16 -22
- package/dist/entry-helpers.d.ts +0 -2
- package/dist/events/circuit-breaker.d.ts +0 -3
- package/dist/events/event-router.d.ts +1 -2
- package/dist/events/event-store.d.ts +0 -9
- package/dist/events/trust-store.d.ts +0 -3
- package/dist/events/types.d.ts +0 -9
- package/dist/firecracker/host-network.d.ts +24 -30
- package/dist/firecracker/net-interfaces.d.ts +4 -7
- package/dist/helpers/rate-limiter.d.ts +0 -1
- package/dist/index.js +240 -482
- package/dist/metrics/agent-metrics-aggregator.d.ts +24 -67
- package/dist/metrics/metrics-reporter.d.ts +4 -13
- package/dist/metrics/prometheus.d.ts +47 -81
- package/dist/orchestrator-core.d.ts +3 -7
- package/dist/pipeline/content-filter.d.ts +20 -30
- package/dist/pipeline/cross-source-tally.d.ts +7 -8
- package/dist/pipeline/decorating-secret-resolver.d.ts +4 -8
- package/dist/pipeline/degraded-reason.d.ts +3 -4
- package/dist/pipeline/direct-ingress-deps.d.ts +15 -24
- package/dist/pipeline/dispatch-matched-workflow.d.ts +289 -525
- package/dist/pipeline/flatten-lock-steps.d.ts +4 -6
- package/dist/pipeline/global-dispatch-identity.d.ts +5 -10
- package/dist/pipeline/global-eval-round.d.ts +65 -151
- package/dist/pipeline/global-round-hold.d.ts +5 -7
- package/dist/pipeline/held-context-data.d.ts +5 -8
- package/dist/pipeline/held-round-registrations.d.ts +8 -11
- package/dist/pipeline/install-gate.d.ts +15 -25
- package/dist/pipeline/install-secrets-resolver.d.ts +24 -34
- package/dist/pipeline/internal-event-pipeline.d.ts +45 -62
- package/dist/pipeline/invoke-gate.d.ts +27 -40
- package/dist/pipeline/job-contexts.d.ts +19 -36
- package/dist/pipeline/manual-schedule.d.ts +7 -13
- package/dist/pipeline/needs-scheduler.d.ts +29 -46
- package/dist/pipeline/pending-workflow-context.d.ts +14 -28
- package/dist/pipeline/process-webhook.d.ts +88 -123
- package/dist/pipeline/processor.d.ts +209 -355
- package/dist/pipeline/remote-source-store.d.ts +5 -8
- package/dist/pipeline/request-idempotency.d.ts +10 -13
- package/dist/pipeline/rerun-global.d.ts +6 -7
- package/dist/pipeline/rerun-inputs.d.ts +23 -37
- package/dist/pipeline/rerun.d.ts +34 -57
- package/dist/pipeline/resolve-sandbox-grant.d.ts +5 -10
- package/dist/pipeline/resume-workflow.d.ts +28 -34
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +19 -30
- package/dist/pipeline/run-event-context.d.ts +16 -21
- package/dist/pipeline/sandbox-allowlist-reader.d.ts +7 -11
- package/dist/pipeline/security-hold-check.d.ts +56 -80
- package/dist/pipeline/test-pipeline.d.ts +20 -46
- package/dist/pipeline/unbound-job-context.d.ts +6 -9
- package/dist/pipeline/undispatched-hold-checks.d.ts +4 -6
- package/dist/pipeline/wave-scheduler.d.ts +15 -22
- package/dist/pipeline/webhook-payload-store.d.ts +3 -10
- package/dist/pipeline/workflow-lock-at-sha.d.ts +14 -20
- package/dist/policy/dashboard-write-policy.d.ts +1 -1
- package/dist/provider-registry.d.ts +0 -19
- package/dist/providers/generic/index.d.ts +0 -2
- package/dist/providers/generic/normalizer.d.ts +0 -1
- package/dist/providers/generic/verification.d.ts +0 -8
- package/dist/providers/github/auth.d.ts +0 -1
- package/dist/providers/github/changed-files.d.ts +2 -1
- package/dist/providers/github/file-contents.d.ts +0 -4
- package/dist/providers/github/lock-file.d.ts +0 -4
- package/dist/providers/github/normalizer.d.ts +1 -9
- package/dist/providers/local/index.d.ts +1 -2
- package/dist/providers/local/lock-file-fetcher.d.ts +0 -4
- package/dist/providers/local/normalizer.d.ts +0 -10
- package/dist/providers/local/repo-url-builder.d.ts +0 -2
- package/dist/providers/universal-git/clone-token.d.ts +0 -2
- package/dist/providers/universal-git/lock-file.d.ts +0 -2
- package/dist/providers/universal-git/normalizer.d.ts +0 -2
- package/dist/queue/bootstrap.d.ts +3 -15
- package/dist/queue/cleanup.d.ts +29 -50
- package/dist/queue/depth-refresher.d.ts +21 -39
- package/dist/queue/job-queue.d.ts +112 -163
- package/dist/queue/retention.d.ts +23 -36
- package/dist/queue/scheduled-job.d.ts +17 -39
- package/dist/queue/terminalize-unroutable.d.ts +16 -18
- package/dist/queue/unroutable-probe.d.ts +15 -22
- package/dist/registration/extractor.d.ts +0 -6
- package/dist/registration/registration-index.d.ts +0 -6
- package/dist/registration/registration-store.d.ts +0 -9
- package/dist/reporting/agent-failure-category.d.ts +3 -5
- package/dist/reporting/check-run-reporter.d.ts +142 -316
- package/dist/reporting/check-run-summary.d.ts +16 -35
- package/dist/reporting/check-run-tracking-store.d.ts +54 -107
- package/dist/reporting/execution-tracker.d.ts +356 -526
- package/dist/reporting/fs-log-storage.d.ts +31 -33
- package/dist/reporting/job-check-run-completion.d.ts +14 -22
- package/dist/reporting/log-chunk-sink.d.ts +11 -16
- package/dist/reporting/log-pull-handler.d.ts +4 -13
- package/dist/reporting/log-retention.d.ts +3 -4
- package/dist/reporting/log-storage.d.ts +24 -52
- package/dist/reporting/log-writer.d.ts +28 -56
- package/dist/reporting/peer-log-normalize.d.ts +4 -8
- package/dist/reporting/run-aggregator.d.ts +6 -11
- package/dist/reporting/run-summary-fields.d.ts +5 -8
- package/dist/reporting/s3-log-storage.d.ts +31 -50
- package/dist/reporting/step-display-order.d.ts +11 -21
- package/dist/reporting/step-log-buffer.d.ts +7 -28
- package/dist/reporting/step-log-reader.d.ts +11 -19
- package/dist/routes/admin-access-log.d.ts +4 -6
- package/dist/routes/admin-auth.d.ts +17 -38
- package/dist/routes/admin-backends.d.ts +3 -14
- package/dist/routes/admin-cluster-name.d.ts +13 -21
- package/dist/routes/admin-cluster-settings.d.ts +6 -9
- package/dist/routes/admin-config.d.ts +7 -19
- package/dist/routes/admin-contexts.d.ts +6 -13
- package/dist/routes/admin-db.d.ts +5 -10
- package/dist/routes/admin-event-dlq.d.ts +3 -5
- package/dist/routes/admin-event-inspect.d.ts +3 -4
- package/dist/routes/admin-event-log.d.ts +3 -7
- package/dist/routes/admin-events.d.ts +16 -30
- package/dist/routes/admin-held-runs.d.ts +52 -76
- package/dist/routes/admin-maintenance.d.ts +3 -6
- package/dist/routes/admin-orchestrator-drain.d.ts +0 -2
- package/dist/routes/admin-org-settings.d.ts +8 -14
- package/dist/routes/admin-peer-forget.d.ts +0 -1
- package/dist/routes/admin-queue-execution.d.ts +3 -3
- package/dist/routes/admin-registrations.d.ts +3 -12
- package/dist/routes/admin-runs.d.ts +16 -29
- package/dist/routes/admin-scaler-orphans.d.ts +4 -4
- package/dist/routes/admin-scaler-reload.d.ts +5 -8
- package/dist/routes/admin-scheduled-jobs.d.ts +3 -4
- package/dist/routes/admin-sources.d.ts +20 -29
- package/dist/routes/admin-trust-policy.d.ts +31 -48
- package/dist/routes/admin.d.ts +67 -94
- package/dist/routes/caller-identity.d.ts +5 -12
- package/dist/routes/capabilities.d.ts +8 -13
- package/dist/routes/diagnostics.d.ts +2 -10
- package/dist/routes/fleet.d.ts +1 -2
- package/dist/routes/github-webhook.d.ts +2 -5
- package/dist/routes/health.d.ts +6 -19
- package/dist/routes/provenance-oidc.d.ts +3 -3
- package/dist/routes/uploads.d.ts +5 -11
- package/dist/routes/webhooks.d.ts +2 -24
- package/dist/scaler/agent-binary-location.d.ts +77 -0
- package/dist/scaler/agent-fit.d.ts +2 -2
- package/dist/scaler/backend-factory.d.ts +10 -18
- package/dist/scaler/bare-metal-backend.d.ts +28 -45
- package/dist/scaler/claim-store.d.ts +20 -43
- package/dist/scaler/config.d.ts +10 -37
- package/dist/scaler/container-backend.d.ts +22 -35
- package/dist/scaler/container-hostconfig.d.ts +10 -16
- package/dist/scaler/container-routing.d.ts +10 -13
- package/dist/scaler/disk-guard.d.ts +2 -3
- package/dist/scaler/event-backend.d.ts +33 -58
- package/dist/scaler/event-provision-reaper.d.ts +61 -84
- package/dist/scaler/failure-tracker.d.ts +6 -13
- package/dist/scaler/file-tail.d.ts +6 -19
- package/dist/scaler/firecracker-api.d.ts +19 -63
- package/dist/scaler/firecracker-backend.d.ts +142 -258
- package/dist/scaler/host-access.d.ts +7 -10
- package/dist/scaler/index.d.ts +3 -6
- package/dist/scaler/ip-allocator.d.ts +30 -82
- package/dist/scaler/label-matcher.d.ts +14 -35
- package/dist/scaler/log-forwarder.d.ts +6 -25
- package/dist/scaler/machine-ledger.d.ts +39 -86
- package/dist/scaler/manager.d.ts +517 -787
- package/dist/scaler/pending-scale-sweeper.d.ts +8 -11
- package/dist/scaler/reap-orphans.d.ts +8 -12
- package/dist/scaler/resolve-container-auth.d.ts +12 -21
- package/dist/scaler/scaler-events.d.ts +2 -5
- package/dist/scaler/scaler-state-store.d.ts +127 -189
- package/dist/scaler/spawn-errors.d.ts +1 -4
- package/dist/scaler/startup-orphan-sweep.d.ts +3 -4
- package/dist/scaler/startup-tools-check.d.ts +21 -0
- package/dist/scaler/types.d.ts +142 -255
- package/dist/scaler/vm-job-bindings.d.ts +5 -9
- package/dist/scaler/warm-pool.d.ts +46 -93
- package/dist/secrets/audit-logger.d.ts +0 -3
- package/dist/secrets/backend-health.d.ts +0 -9
- package/dist/secrets/backend-registry.d.ts +0 -9
- package/dist/secrets/cleanup.d.ts +0 -1
- package/dist/secrets/config.d.ts +0 -3
- package/dist/secrets/ephemeral-keys.d.ts +0 -3
- package/dist/secrets/pg-secret-store.d.ts +0 -3
- package/dist/secrets/rbac.d.ts +0 -6
- package/dist/secrets/secret-output-store.d.ts +0 -6
- package/dist/secrets/secret-resolver.d.ts +2 -6
- package/dist/secrets/token-manager.d.ts +0 -4
- package/dist/secrets/vault-secret-store.d.ts +0 -13
- package/dist/security/comment-handler.d.ts +0 -2
- package/dist/security/workflow-diff.d.ts +1 -1
- package/dist/server.js +7500 -12629
- package/dist/sources/source-manager.d.ts +0 -3
- package/dist/sources/source-store.d.ts +0 -9
- package/dist/sources/source-validator.d.ts +0 -3
- package/dist/stale-detector/all-terminal-runs.d.ts +8 -11
- package/dist/stale-detector/gate-deadline-detector.d.ts +14 -19
- package/dist/stale-detector/reboot-deadline-sweep.d.ts +7 -11
- package/dist/stale-detector/stale-dispatch-candidates.d.ts +13 -20
- package/dist/stale-detector/stale-run-detector.d.ts +74 -121
- package/dist/stale-detector/workflow-deadline-detector.d.ts +15 -43
- package/dist/standalone.js +7141 -11957
- package/dist/storage/blob-routes.d.ts +10 -19
- package/dist/storage/filesystem.d.ts +19 -29
- package/dist/storage/index.d.ts +2 -10
- package/dist/storage/key-discriminator.d.ts +8 -10
- package/dist/storage/loopback-guard.d.ts +27 -45
- package/dist/storage/s3.d.ts +25 -52
- package/dist/storage/sign-url.d.ts +7 -22
- package/dist/storage/types.d.ts +44 -70
- package/dist/webhook/event-log.d.ts +0 -2
- package/dist/webhook/generic-sources.d.ts +0 -21
- package/dist/webhook/ingest-admission.d.ts +0 -1
- package/dist/webhook/relay-buffer.d.ts +2 -2
- package/dist/webhook/verify-inbound.d.ts +0 -1
- package/dist/worker/in-memory-execution-tracker.d.ts +9 -31
- package/dist/worker/in-memory-job-queue.d.ts +32 -72
- package/dist/worker/peer-outbox.d.ts +5 -8
- package/dist/worker/reroute-dispatch.d.ts +4 -6
- package/dist/worker/static-agent-token-store.d.ts +6 -27
- package/dist/worker/worker-outbox-relay.d.ts +2 -2
- package/dist/worker/worker-status.d.ts +3 -20
- package/dist/ws/agent-api-registry.d.ts +8 -27
- package/dist/ws/agent-handler.d.ts +63 -110
- package/dist/ws/agent-heartbeat.d.ts +5 -12
- package/dist/ws/bringup-api.d.ts +31 -38
- package/dist/ws/dashboard-backends-handler.d.ts +12 -22
- package/dist/ws/dashboard-context-handler.d.ts +37 -80
- package/dist/ws/dashboard-diagnostics-handler.d.ts +8 -25
- package/dist/ws/dashboard-dispatch-guard.d.ts +3 -4
- package/dist/ws/dashboard-fleet-handler.d.ts +9 -13
- package/dist/ws/dashboard-fleet-write-handler.d.ts +7 -21
- package/dist/ws/dashboard-global-workflows-handler.d.ts +22 -44
- package/dist/ws/dashboard-registrations-handler.d.ts +10 -27
- package/dist/ws/dashboard-run-state-handler.d.ts +10 -13
- package/dist/ws/event-buffer.d.ts +3 -6
- package/dist/ws/execution-status-frame.d.ts +7 -10
- package/dist/ws/failure-messages.d.ts +9 -14
- package/dist/ws/fleet-agent-collector.d.ts +6 -12
- package/dist/ws/fleet-runs-on-all.d.ts +1 -6
- package/dist/ws/git-credential-relay.d.ts +12 -20
- package/dist/ws/inventory-api.d.ts +4 -5
- package/dist/ws/observer-registry.d.ts +14 -59
- package/dist/ws/ownership-gate.d.ts +1 -1
- package/dist/ws/platform-client.d.ts +21 -30
- package/dist/ws/replay-chunker.d.ts +7 -10
- package/dist/ws/server-options.d.ts +5 -9
- package/dist/ws/test-relay-handlers.d.ts +36 -60
- package/installer-image-digests.json +3 -3
- package/package.json +6 -5
- package/sbom.spdx.json +53 -53
- package/dist/__test-helpers__/fake-scaler-state-store.d.ts +0 -24
- package/dist/__test-helpers__/mock-db-query.d.ts +0 -129
- package/dist/__test-helpers__/mock-db.d.ts +0 -134
- package/dist/__test-helpers__/mock-ws.d.ts +0 -13
- package/dist/__test-helpers__/peer-mutual-auth.d.ts +0 -42
- package/dist/__test-helpers__/scratch-db.d.ts +0 -16
- package/dist/__test-helpers__/test-db.d.ts +0 -34
- package/dist/pipeline/dispatch-matched-workflow.test-helpers.d.ts +0 -121
package/dist/index.js
CHANGED
|
@@ -22,17 +22,13 @@ import { exec } from "node:child_process";
|
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/storage/s3.ts
|
|
24
24
|
/**
|
|
25
|
-
* S3-backed cache storage.
|
|
25
|
+
* S3-backed cache storage; AWS SDK v3 modules load only when S3 storage is configured.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
* Uses AWS SDK v3 modular imports -- only loaded when S3 storage is configured.
|
|
29
|
-
*
|
|
30
|
-
* Metadata storage:
|
|
27
|
+
* TTL metadata headers:
|
|
31
28
|
* x-amz-meta-created-at -- ISO timestamp of creation
|
|
32
29
|
* x-amz-meta-last-accessed-at -- ISO timestamp of last access
|
|
33
30
|
*
|
|
34
31
|
* TTL is enforced lazily: expired items are deleted on access.
|
|
35
|
-
* Pre-signed URLs are generated for getUrl() with 15-minute expiry.
|
|
36
32
|
*/
|
|
37
33
|
const logger$5 = createLogger({ prefix: "s3-cache" });
|
|
38
34
|
/** Pre-signed download URL expiry in seconds (15 minutes) */
|
|
@@ -41,17 +37,14 @@ const PRESIGNED_URL_EXPIRY_SECONDS = 900;
|
|
|
41
37
|
const UPLOAD_URL_EXPIRY_SECONDS = 1800;
|
|
42
38
|
var S3CacheStorage = class {
|
|
43
39
|
client;
|
|
44
|
-
/**
|
|
40
|
+
/** Pre-signing client: externalEndpoint when configured. */
|
|
45
41
|
presignClient;
|
|
46
42
|
/** Client for the developer upload URL: uploadEndpoint, else the presign client. */
|
|
47
43
|
uploadPresignClient;
|
|
48
44
|
bucket;
|
|
49
45
|
prefix;
|
|
50
46
|
ttlMs;
|
|
51
|
-
/**
|
|
52
|
-
* False once the backend answered a conditional self-copy with
|
|
53
|
-
* `501 NotImplemented`. From then on `updateMeta` copies unconditionally.
|
|
54
|
-
*/
|
|
47
|
+
/** False once the backend answered a conditional self-copy with `501 NotImplemented`. */
|
|
55
48
|
conditionalCopySupported = true;
|
|
56
49
|
constructor(options) {
|
|
57
50
|
this.bucket = options.bucket;
|
|
@@ -68,7 +61,6 @@ var S3CacheStorage = class {
|
|
|
68
61
|
endpoint: options.uploadEndpoint
|
|
69
62
|
}) : this.presignClient;
|
|
70
63
|
}
|
|
71
|
-
/** Build the full S3 object key from a cache key. */
|
|
72
64
|
objectKey(key) {
|
|
73
65
|
return `${this.prefix}${key}`;
|
|
74
66
|
}
|
|
@@ -78,17 +70,11 @@ var S3CacheStorage = class {
|
|
|
78
70
|
const lastAccessed = new Date(meta.lastAccessedAt).getTime();
|
|
79
71
|
return Date.now() - lastAccessed > ttl;
|
|
80
72
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Read metadata from an S3 object's custom headers.
|
|
83
|
-
* Returns null if the object doesn't exist (NoSuchKey/NotFound).
|
|
84
|
-
*/
|
|
73
|
+
/** Null when the object does not exist (NoSuchKey/NotFound). */
|
|
85
74
|
async readMeta(key) {
|
|
86
75
|
return (await this.readHead(key))?.meta ?? null;
|
|
87
76
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Read an object's TTL metadata together with the ETag of the version it
|
|
90
|
-
* belongs to. Returns null if the object doesn't exist (NoSuchKey/NotFound).
|
|
91
|
-
*/
|
|
77
|
+
/** TTL metadata plus the ETag of its version; null when the object does not exist. */
|
|
92
78
|
async readHead(key) {
|
|
93
79
|
try {
|
|
94
80
|
const head = await this.client.send(new HeadObjectCommand({
|
|
@@ -111,25 +97,16 @@ var S3CacheStorage = class {
|
|
|
111
97
|
}
|
|
112
98
|
}
|
|
113
99
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* read. A self-copy is a whole-object write: without the condition, one that
|
|
119
|
-
* races a `put()` of the same key can land after it and write the older body
|
|
120
|
-
* back. Cache pointers are rewritten in place while builds publish them, so a
|
|
121
|
-
* touch-on-read racing a publish reverted the pointer to a tarball that no
|
|
122
|
-
* longer exists. `CopySourceIfMatch` pins the bytes copied to that version and
|
|
123
|
-
* `IfMatch` refuses the write unless the key still holds it.
|
|
100
|
+
* Updates metadata by self-copy, conditional on `etag` (the version the caller read). A
|
|
101
|
+
* self-copy is a whole-object write: unconditioned, one racing a `put()` of the key can write
|
|
102
|
+
* the older body back, reverting a cache pointer to a deleted tarball. `CopySourceIfMatch`
|
|
103
|
+
* pins the copied bytes; `IfMatch` refuses the write unless the key still holds that version.
|
|
124
104
|
*
|
|
125
|
-
* Returns false when the object was replaced since the read (`412`)
|
|
126
|
-
*
|
|
127
|
-
* write stamps metadata of its own, so there is nothing left to refresh.
|
|
105
|
+
* Returns false when the object was replaced since the read (`412`) or is being replaced
|
|
106
|
+
* (`409 ConditionalRequestConflict`): the newer write stamps its own metadata.
|
|
128
107
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* condition, and later copies skip it, as on a backend that ignores the
|
|
132
|
-
* headers.
|
|
108
|
+
* A backend without conditional copies answers `501 NotImplemented`; the copy reruns
|
|
109
|
+
* unconditionally, and later copies skip the condition.
|
|
133
110
|
*/
|
|
134
111
|
async updateMeta(key, meta, etag) {
|
|
135
112
|
const condition = this.conditionalCopySupported ? etag : void 0;
|
|
@@ -164,15 +141,15 @@ var S3CacheStorage = class {
|
|
|
164
141
|
}
|
|
165
142
|
}));
|
|
166
143
|
}
|
|
167
|
-
/**
|
|
144
|
+
/** A failed conditional request (PreconditionFailed, 412). */
|
|
168
145
|
isPreconditionFailedError(err) {
|
|
169
146
|
return this.hasErrorShape(err, "PreconditionFailed", 412);
|
|
170
147
|
}
|
|
171
|
-
/**
|
|
148
|
+
/** A conditional write that raced another write (ConditionalRequestConflict, 409). */
|
|
172
149
|
isConditionalConflictError(err) {
|
|
173
150
|
return this.hasErrorShape(err, "ConditionalRequestConflict", 409);
|
|
174
151
|
}
|
|
175
|
-
/**
|
|
152
|
+
/** A request the backend does not implement (NotImplemented, 501). */
|
|
176
153
|
isNotImplementedError(err) {
|
|
177
154
|
return this.hasErrorShape(err, "NotImplemented", 501);
|
|
178
155
|
}
|
|
@@ -181,7 +158,7 @@ var S3CacheStorage = class {
|
|
|
181
158
|
if (err.name === name) return true;
|
|
182
159
|
return err.$metadata?.httpStatusCode === status;
|
|
183
160
|
}
|
|
184
|
-
/**
|
|
161
|
+
/** NoSuchKey, NotFound or 404. */
|
|
185
162
|
isNotFoundError(err) {
|
|
186
163
|
if (typeof err !== "object" || err === null) return false;
|
|
187
164
|
const name = err.name;
|
|
@@ -359,30 +336,21 @@ var S3CacheStorage = class {
|
|
|
359
336
|
//#endregion
|
|
360
337
|
//#region src/storage/sign-url.ts
|
|
361
338
|
/**
|
|
362
|
-
* HMAC-
|
|
363
|
-
*
|
|
364
|
-
* Pre-signed S3 URLs are not available for filesystem storage, so the
|
|
365
|
-
* filesystem backend mints HMAC-SHA256 tokens that the orchestrator's HTTP
|
|
366
|
-
* blob route verifies before serving / writing a file. Token shape:
|
|
339
|
+
* HMAC-SHA256 URL tokens for the filesystem cache backend (which has no pre-signed S3 URLs);
|
|
340
|
+
* the blob route verifies them before serving / writing a file. Token shape:
|
|
367
341
|
*
|
|
368
342
|
* `${expiresUnixMs}.${hexSig}`
|
|
369
343
|
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* for the lifetime of the process). Tokens become invalid on orchestrator
|
|
373
|
-
* restart — fine for the filesystem backend's single-host / test-sandbox use
|
|
374
|
-
* case.
|
|
344
|
+
* Signs `${method}:${key}:${expiresUnixMs}` with a per-process secret (random 32 bytes at
|
|
345
|
+
* boot), so tokens die on orchestrator restart — fine for single-host / test-sandbox use.
|
|
375
346
|
*/
|
|
376
|
-
/** Default URL lifetime: 1 hour
|
|
347
|
+
/** Default URL lifetime: 1 hour. */
|
|
377
348
|
const DEFAULT_SIGN_URL_TTL_MS = 36e5;
|
|
378
349
|
function computeSig(secret, method, key, expiresMs) {
|
|
379
350
|
const hmac = createHmac("sha256", secret);
|
|
380
351
|
hmac.update(`${method}:${key}:${expiresMs}`);
|
|
381
352
|
return hmac.digest("hex");
|
|
382
353
|
}
|
|
383
|
-
/**
|
|
384
|
-
* Mint a signed token for the given (method, key, expiry) triple.
|
|
385
|
-
*/
|
|
386
354
|
function signToken(secret, method, key, ttlMs = DEFAULT_SIGN_URL_TTL_MS) {
|
|
387
355
|
const expiresMs = Date.now() + ttlMs;
|
|
388
356
|
return {
|
|
@@ -393,19 +361,15 @@ function signToken(secret, method, key, ttlMs = DEFAULT_SIGN_URL_TTL_MS) {
|
|
|
393
361
|
//#endregion
|
|
394
362
|
//#region src/storage/filesystem.ts
|
|
395
363
|
/**
|
|
396
|
-
* Filesystem-backed
|
|
397
|
-
*
|
|
398
|
-
* Mirrors the `CacheStorage` interface that `S3CacheStorage` provides, but
|
|
399
|
-
* stores blobs as files under a configured base directory. Pre-signed URLs
|
|
400
|
-
* are replaced by HMAC-signed `http://<orchUrl>/api/v1/cache/blob/...` URLs
|
|
401
|
-
* — see `sign-url.ts` for the token mechanics and `app.ts` for the route.
|
|
364
|
+
* Filesystem-backed `CacheStorage`, the counterpart of `S3CacheStorage`, for
|
|
365
|
+
* single-host deployments and test sandboxes. Production should still use S3.
|
|
402
366
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
367
|
+
* Pre-signed URLs become HMAC-signed `http://<orchUrl>/api/v1/cache/blob/...`
|
|
368
|
+
* URLs (token mechanics: `sign-url.ts`; route: `app.ts`).
|
|
405
369
|
*
|
|
406
|
-
* Metadata
|
|
407
|
-
*
|
|
408
|
-
*
|
|
370
|
+
* Metadata sits in a sibling `<key>.meta.json`, so the data file is
|
|
371
|
+
* byte-identical to what the agent receives over HTTP. TTL is enforced lazily
|
|
372
|
+
* on access, as in the S3 backend.
|
|
409
373
|
*/
|
|
410
374
|
const DEFAULT_ROUTE_PREFIX = "/api/v1/cache/blob/";
|
|
411
375
|
var FilesystemCacheStorage = class {
|
|
@@ -422,9 +386,9 @@ var FilesystemCacheStorage = class {
|
|
|
422
386
|
this.routePrefix = (options.routePrefix ?? DEFAULT_ROUTE_PREFIX).replace(/\/?$/, "/");
|
|
423
387
|
}
|
|
424
388
|
/**
|
|
425
|
-
* Map a cache key to its on-disk path
|
|
426
|
-
*
|
|
427
|
-
*
|
|
389
|
+
* Map a cache key to its on-disk path (`/` becomes a directory separator).
|
|
390
|
+
* Rejects `..` segments and NUL bytes against path traversal, as the route
|
|
391
|
+
* layer also does.
|
|
428
392
|
*/
|
|
429
393
|
resolvePath(key) {
|
|
430
394
|
if (key.includes("\0") || key.split("/").some((seg) => seg === ".." || seg === "")) throw new Error(`Invalid cache key: ${JSON.stringify(key)}`);
|
|
@@ -534,19 +498,14 @@ var FilesystemCacheStorage = class {
|
|
|
534
498
|
});
|
|
535
499
|
}
|
|
536
500
|
/**
|
|
537
|
-
* List cache keys under a prefix
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
* `restoreKeys` fallback lists on `${prefix}${seg(restoreKey)}`, a partial
|
|
541
|
-
* object name. Two shapes, and both must work the way the S3 backend does:
|
|
501
|
+
* List cache keys under a **key prefix**, which need not be a directory:
|
|
502
|
+
* `UserCache`'s `restoreKeys` fallback lists on `${prefix}${seg(restoreKey)}`,
|
|
503
|
+
* a partial object name. Both shapes must behave as on S3:
|
|
542
504
|
*
|
|
543
505
|
* - trailing `/` — a directory. Walk it directly; this is the hot path.
|
|
544
|
-
* - no trailing `/` — a partial name. Walk the parent
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
* Reading the prefix as a directory in the second case is what made the
|
|
548
|
-
* `restoreKeys` fallback silently never hit here (`ENOENT` → `[]`) or throw
|
|
549
|
-
* (`ENOTDIR`, when a real object carried that exact name).
|
|
506
|
+
* - no trailing `/` — a partial name. Walk the parent and filter by the
|
|
507
|
+
* prefix, which may name a file or nothing at all. Reading it as a
|
|
508
|
+
* directory made `restoreKeys` never hit (`ENOENT`) or throw (`ENOTDIR`).
|
|
550
509
|
*/
|
|
551
510
|
async list(subPrefix) {
|
|
552
511
|
const isDirPrefix = subPrefix.endsWith("/");
|
|
@@ -587,10 +546,9 @@ var FilesystemCacheStorage = class {
|
|
|
587
546
|
});
|
|
588
547
|
}
|
|
589
548
|
/**
|
|
590
|
-
* Recursively collect data files under `dir
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
* `list()` only surfaces real cache objects.
|
|
549
|
+
* Recursively collect data files under `dir` as `basePath`-relative keys with
|
|
550
|
+
* their mtime, skipping `*.meta.json` sidecars and `*.tmp-*` atomic-write
|
|
551
|
+
* temp files so `list()` surfaces only real cache objects.
|
|
594
552
|
*/
|
|
595
553
|
async walkDataFiles(dir, keyPrefix) {
|
|
596
554
|
const out = [];
|
|
@@ -627,9 +585,8 @@ function isNotFoundFsError(err) {
|
|
|
627
585
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
628
586
|
}
|
|
629
587
|
/**
|
|
630
|
-
* Write
|
|
631
|
-
* partial
|
|
632
|
-
* randomised so concurrent writers don't collide on the same temp name.
|
|
588
|
+
* Write atomically (temp sibling, fsync, rename) so a crash mid-write leaves no
|
|
589
|
+
* partial file. The random temp suffix keeps concurrent writers apart.
|
|
633
590
|
*/
|
|
634
591
|
async function writeFileAtomic(path, data) {
|
|
635
592
|
const tmp = `${path}.tmp-${randomBytes(6).toString("hex")}`;
|
|
@@ -644,9 +601,6 @@ async function writeFileAtomic(path, data) {
|
|
|
644
601
|
}
|
|
645
602
|
//#endregion
|
|
646
603
|
//#region src/storage/index.ts
|
|
647
|
-
/**
|
|
648
|
-
* Create a cache storage backend from configuration.
|
|
649
|
-
*/
|
|
650
604
|
function createCacheStorage(config) {
|
|
651
605
|
if (config.type === "s3") return new S3CacheStorage({
|
|
652
606
|
bucket: config.bucket,
|
|
@@ -669,61 +623,44 @@ function createCacheStorage(config) {
|
|
|
669
623
|
//#endregion
|
|
670
624
|
//#region src/cache/source-cache.ts
|
|
671
625
|
/**
|
|
672
|
-
* Source-tarball cache
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
* workflow `contentHash` to that hash. Tarballs are platform-independent: raw
|
|
678
|
-
* TypeScript source is identical across architectures. Refreshes TTL on reads
|
|
679
|
-
* (touch-on-read) so actively used sources stay in cache longer.
|
|
626
|
+
* Source-tarball cache over CacheStorage: the `.kici/` directory minus
|
|
627
|
+
* `node_modules/` (packed by the agent's build job via `source-packer.ts`),
|
|
628
|
+
* stored under the tarball's OWN content hash, with a pointer resolving an
|
|
629
|
+
* org's workflow `contentHash` to it. Tarballs are platform-independent (raw
|
|
630
|
+
* TypeScript source). Reads touch the TTL so active sources stay cached.
|
|
680
631
|
*
|
|
681
632
|
* Keys: `source/v2/{orgId}/{sourceTarDigest}.tar.gz` (immutable) and
|
|
682
|
-
* `source/v2/{orgId}/{contentHash}.hash` (the pointer) — the
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
* **Content addressing** makes a mismatched pair unrepresentable. At
|
|
686
|
-
* `source/{contentHash}.tar.gz` the object's key was derived from something
|
|
687
|
-
* other than its bytes, so nothing a reader fetched could be verified against
|
|
688
|
-
* anything, and the agent restored whatever the signed URL returned.
|
|
633
|
+
* `source/v2/{orgId}/{contentHash}.hash` (the pointer) — the `dep-cache.ts`
|
|
634
|
+
* shape, for the same reasons:
|
|
689
635
|
*
|
|
690
|
-
* **
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
* by;
|
|
697
|
-
* so a monorepo or
|
|
636
|
+
* - **Content addressing** makes a mismatched pair unrepresentable: a key not
|
|
637
|
+
* derived from the bytes lets nothing a reader fetches be verified.
|
|
638
|
+
* - **Org scoping** makes the key a tenant's key. `contentHash` alone is a
|
|
639
|
+
* property of a `.kici/` tree, so two repositories from the same `kici init`
|
|
640
|
+
* template, or a fork and its upstream, would share one object — a
|
|
641
|
+
* cross-tenant read on a multi-org orchestrator. The org is the tenant
|
|
642
|
+
* boundary the user cache scopes by too; within one org a shared key means
|
|
643
|
+
* byte-identical trees, so a monorepo or same-org fork keeps its warm cache.
|
|
698
644
|
*
|
|
699
|
-
*
|
|
700
|
-
* `compileSchemaVersion: 5` lock
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
* Old objects are orphaned. The cache TTL is enforced lazily on access, so
|
|
705
|
-
* nothing here removes them.
|
|
645
|
+
* Retired `source/{contentHash}.tar.gz` objects are **not read**: only a
|
|
646
|
+
* `compileSchemaVersion: 5` lock names one, and its `contentHash` covers the
|
|
647
|
+
* entry file alone rather than the packed tree, so serving it would serve the
|
|
648
|
+
* bug. A v6 lock cannot reach it (the bump moves every hash). The lazy TTL never
|
|
649
|
+
* removes them (docs/operator/orchestrator/storage-layout.md § "Retired layouts").
|
|
706
650
|
*/
|
|
707
651
|
const logger$4 = createLogger({ prefix: "source-cache" });
|
|
708
652
|
/**
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
*
|
|
712
|
-
* Naming the object by its own hash means every pair a reader can observe is
|
|
713
|
-
* self-consistent, and the bytes at a given key can never change after a URL is
|
|
714
|
-
* signed for it.
|
|
653
|
+
* Named by its own hash, so every pair a reader observes is self-consistent and
|
|
654
|
+
* the bytes at a key never change after a URL is signed for it.
|
|
715
655
|
*/
|
|
716
656
|
function sourceTarballKey(orgId, sourceTarDigest) {
|
|
717
657
|
return `source/v2/${orgId}/${sourceTarDigest}.tar.gz`;
|
|
718
658
|
}
|
|
719
659
|
/**
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
* pointer wholesale — there is no half-written window — so the worst a racing
|
|
725
|
-
* pair of builders can do is leave whichever pointer landed last, and both
|
|
726
|
-
* tarballs stay valid and fetchable.
|
|
660
|
+
* The pointer from a workflow `contentHash` to its tarball's hash: the one
|
|
661
|
+
* mutable object. A write replaces it wholesale (no half-written window), so
|
|
662
|
+
* racing builders at worst leave the last-landed pointer, and both tarballs
|
|
663
|
+
* stay valid and fetchable.
|
|
727
664
|
*/
|
|
728
665
|
function sourcePointerKey(orgId, contentHash) {
|
|
729
666
|
return `source/v2/${orgId}/${contentHash}.hash`;
|
|
@@ -733,16 +670,13 @@ var SourceCache = class SourceCache {
|
|
|
733
670
|
constructor(options) {
|
|
734
671
|
this.storage = options.storage;
|
|
735
672
|
}
|
|
736
|
-
/**
|
|
673
|
+
/** Resolves the pointer indirection (`getUrlAndDigest` reads it inline to touch it). */
|
|
737
674
|
async resolvePointer(orgId, contentHash) {
|
|
738
675
|
return (await this.storage.get(sourcePointerKey(orgId, contentHash)))?.toString("utf-8").trim() || null;
|
|
739
676
|
}
|
|
740
677
|
/**
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
* Both halves must be present: a pointer whose tarball has aged out is a
|
|
744
|
-
* miss, not a hit, or the caller skips a rebuild and dispatches a URL that
|
|
745
|
-
* 404s on the agent.
|
|
678
|
+
* Both halves must exist: a pointer whose tarball aged out is a miss, or the
|
|
679
|
+
* caller skips a rebuild and dispatches a URL that 404s on the agent.
|
|
746
680
|
*/
|
|
747
681
|
async has(orgId, contentHash) {
|
|
748
682
|
const digest = await this.resolvePointer(orgId, contentHash);
|
|
@@ -769,13 +703,9 @@ var SourceCache = class SourceCache {
|
|
|
769
703
|
return data;
|
|
770
704
|
}
|
|
771
705
|
/**
|
|
772
|
-
* A pre-signed download URL and the tarball's own digest.
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
* tarball's key, so the url and the digest returned here are read from a
|
|
776
|
-
* single source of truth and cannot disagree. Returns null on a miss —
|
|
777
|
-
* including a pointer with no tarball behind it, which is unverifiable and so
|
|
778
|
-
* is deliberately not served.
|
|
706
|
+
* A pre-signed download URL and the tarball's own digest. The pointer is
|
|
707
|
+
* resolved FIRST and names both, so url and digest cannot disagree. Null on a
|
|
708
|
+
* miss, including a pointer with no tarball (unverifiable, so not served).
|
|
779
709
|
*/
|
|
780
710
|
async getUrlAndDigest(orgId, contentHash) {
|
|
781
711
|
const pointerKey = sourcePointerKey(orgId, contentHash);
|
|
@@ -802,19 +732,15 @@ var SourceCache = class SourceCache {
|
|
|
802
732
|
return (await this.getUrlAndDigest(orgId, contentHash))?.url ?? null;
|
|
803
733
|
}
|
|
804
734
|
/**
|
|
805
|
-
* A pre-signed upload URL
|
|
806
|
-
*
|
|
807
|
-
* Takes the tarball's OWN digest, not the workflow `contentHash`: the agent
|
|
808
|
-
* computes it before asking for a URL, and the object is named by it.
|
|
735
|
+
* A pre-signed agent-to-storage upload URL, keyed by the tarball's OWN digest
|
|
736
|
+
* (which the agent computes first), not the workflow `contentHash`.
|
|
809
737
|
*/
|
|
810
738
|
async getUploadUrl(orgId, sourceTarDigest) {
|
|
811
739
|
return this.storage.getUploadUrl(sourceTarballKey(orgId, sourceTarDigest));
|
|
812
740
|
}
|
|
813
741
|
/**
|
|
814
|
-
* Publish the pointer
|
|
815
|
-
*
|
|
816
|
-
* publishing before the bytes land would let a reader follow the pointer to a
|
|
817
|
-
* missing object.
|
|
742
|
+
* Publish the pointer to an uploaded tarball. Called only after the agent
|
|
743
|
+
* confirms the upload, or a reader could follow it to a missing object.
|
|
818
744
|
*/
|
|
819
745
|
async publishPointer(orgId, contentHash, sourceTarDigest) {
|
|
820
746
|
await this.storage.put(sourcePointerKey(orgId, contentHash), sourceTarDigest);
|
|
@@ -835,7 +761,6 @@ var SourceCache = class SourceCache {
|
|
|
835
761
|
static computeDigest(data) {
|
|
836
762
|
return sha256(data);
|
|
837
763
|
}
|
|
838
|
-
/** Remove a source tarball and its pointer. */
|
|
839
764
|
async remove(orgId, contentHash) {
|
|
840
765
|
const digest = await this.resolvePointer(orgId, contentHash);
|
|
841
766
|
await this.storage.delete(sourcePointerKey(orgId, contentHash));
|
|
@@ -847,12 +772,9 @@ var SourceCache = class SourceCache {
|
|
|
847
772
|
//#endregion
|
|
848
773
|
//#region src/cache/dep-cache.ts
|
|
849
774
|
/**
|
|
850
|
-
* Dependency
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
* resolving lockfileHash + platform + arch to that hash. Shared CacheStorage
|
|
854
|
-
* backend with SourceCache (same S3 bucket). Refreshes TTL on reads
|
|
855
|
-
* (touch-on-read).
|
|
775
|
+
* Dependency cache over CacheStorage (the same S3 bucket as SourceCache).
|
|
776
|
+
* Tarballs live under their own content hash, with a small pointer resolving
|
|
777
|
+
* lockfileHash + platform + arch to it. Reads refresh TTL (touch-on-read).
|
|
856
778
|
*
|
|
857
779
|
* Keys: `deps/{platform}-{arch}/{depsHash}.tar.gz` (immutable) and
|
|
858
780
|
* `deps/{platform}-{arch}/{lockfileHash}.hash` (the pointer).
|
|
@@ -864,17 +786,14 @@ const DEFAULT_MAX_TARBALL_BYTES = 524288e3;
|
|
|
864
786
|
const DEFAULT_CACHE_TTL_DAYS = 30;
|
|
865
787
|
const MS_PER_DAY = 864e5;
|
|
866
788
|
/**
|
|
867
|
-
* Cache key for a dependency tarball, addressed by
|
|
868
|
-
*
|
|
789
|
+
* Cache key for a dependency tarball, addressed by its OWN content hash:
|
|
790
|
+
* `deps/{platform}-{arch}/{depsHash}.tar.gz`.
|
|
869
791
|
*
|
|
870
|
-
* Content-addressing
|
|
871
|
-
*
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
* verification durably. Naming the object by its own hash means every pair a
|
|
876
|
-
* reader can observe is self-consistent, and the bytes at a given key can never
|
|
877
|
-
* change after a URL is signed for it.
|
|
792
|
+
* Content-addressing makes a mismatched pair unrepresentable. Under a
|
|
793
|
+
* lockfile-derived name, concurrent builders sharing lockfile + platform + arch
|
|
794
|
+
* could leave one's `.tar.gz` and the other's `.hash`, and the reader failed
|
|
795
|
+
* verification durably. Named by its own hash, every observable pair is
|
|
796
|
+
* self-consistent and a key's bytes never change after a URL is signed for it.
|
|
878
797
|
*/
|
|
879
798
|
function depTarballKey(depsHash, platform, arch) {
|
|
880
799
|
return `deps/${platform}-${arch}/${depsHash}.tar.gz`;
|
|
@@ -884,17 +803,14 @@ function depTarballKey(depsHash, platform, arch) {
|
|
|
884
803
|
* tarball built from it: `deps/{platform}-{arch}/{lockfileHash}.hash`, or
|
|
885
804
|
* `…/{lockfileHash}-{siblingsDigest}.hash` when the lock carries one.
|
|
886
805
|
*
|
|
887
|
-
* `siblingsDigest` is
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
* lock with no in-repo sibling carries no digest and keys exactly as before, so
|
|
892
|
-
* existing pointers stay live.
|
|
806
|
+
* `siblingsDigest` is keyed because the tarball carries in-repo `workspace:`
|
|
807
|
+
* siblings WITH their built output, and editing a sibling moves no lock file —
|
|
808
|
+
* a `lockfileHash`-only key restored a stale sibling build over the fresh clone.
|
|
809
|
+
* A lock with no in-repo sibling carries no digest, so existing pointers stay live.
|
|
893
810
|
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
* and both tarballs remain valid and fetchable.
|
|
811
|
+
* The one mutable object in the scheme. A write replaces the pointer wholesale
|
|
812
|
+
* (never half-written), so racing builders at worst leave whichever landed
|
|
813
|
+
* last, and both tarballs stay valid and fetchable.
|
|
898
814
|
*/
|
|
899
815
|
function depPointerKey(lockfileHash, platform, arch, siblingsDigest) {
|
|
900
816
|
return `deps/${platform}-${arch}/${siblingsDigest ? `${lockfileHash}-${siblingsDigest}` : lockfileHash}.hash`;
|
|
@@ -911,28 +827,21 @@ var DepCache = class DepCache {
|
|
|
911
827
|
this.clusterSettings = options.clusterSettings;
|
|
912
828
|
}
|
|
913
829
|
/**
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
* its own configured TTL. Passed to read/expiry operations so an operator's
|
|
918
|
-
* `cache_ttl_days` change takes effect on the next lookup.
|
|
830
|
+
* Per-operation TTL override (ms) from `cluster_settings.cache_ttl_days`, so
|
|
831
|
+
* an operator's change takes effect on the next lookup. `undefined` when no
|
|
832
|
+
* reader is wired, so the backend uses its own configured TTL.
|
|
919
833
|
*/
|
|
920
834
|
async resolveTtlMsOverride() {
|
|
921
835
|
if (!this.clusterSettings) return void 0;
|
|
922
836
|
return await this.clusterSettings.getNumber("cache_ttl_days", this.cacheTtlDaysFallback) * MS_PER_DAY;
|
|
923
837
|
}
|
|
924
|
-
/**
|
|
925
|
-
* Resolve a lockfile to the content hash of the tarball built from it, or
|
|
926
|
-
* null when no pointer exists. The single place the indirection is read.
|
|
927
|
-
*/
|
|
838
|
+
/** Lockfile → tarball content hash, or null when no pointer exists. */
|
|
928
839
|
async resolvePointer(lockfileHash, platform, arch, ttlMsOverride, siblingsDigest) {
|
|
929
840
|
return (await this.storage.get(depPointerKey(lockfileHash, platform, arch, siblingsDigest), ttlMsOverride))?.toString("utf-8").trim() || null;
|
|
930
841
|
}
|
|
931
842
|
/**
|
|
932
|
-
*
|
|
933
|
-
*
|
|
934
|
-
* Both halves must be present: a pointer whose tarball has aged out is a miss,
|
|
935
|
-
* not a hit, or the caller skips a rebuild and dispatches a URL that 404s.
|
|
843
|
+
* Both halves must be present: a pointer whose tarball aged out is a miss, or
|
|
844
|
+
* the caller skips a rebuild and dispatches a URL that 404s.
|
|
936
845
|
*/
|
|
937
846
|
async has(lockfileHash, platform, arch, siblingsDigest) {
|
|
938
847
|
const ttlMsOverride = await this.resolveTtlMsOverride();
|
|
@@ -951,18 +860,13 @@ var DepCache = class DepCache {
|
|
|
951
860
|
});
|
|
952
861
|
return exists;
|
|
953
862
|
}
|
|
954
|
-
/**
|
|
955
|
-
* Get a pre-signed download URL for the dep tarball (for agent delivery).
|
|
956
|
-
* Refreshes TTL on hit.
|
|
957
|
-
*/
|
|
863
|
+
/** Pre-signed download URL for agent delivery. Refreshes TTL on hit. */
|
|
958
864
|
async getUrl(lockfileHash, platform, arch, siblingsDigest) {
|
|
959
865
|
return (await this.getUrlAndHash(lockfileHash, platform, arch, siblingsDigest))?.url ?? null;
|
|
960
866
|
}
|
|
961
867
|
/**
|
|
962
|
-
*
|
|
963
|
-
*
|
|
964
|
-
* Returns null on cache miss — including an entry with no pointer, which is
|
|
965
|
-
* unverifiable and so is deliberately not served.
|
|
868
|
+
* Pre-signed download URL plus tarball content hash; null on a miss,
|
|
869
|
+
* including an entry with no pointer (unverifiable, so never served).
|
|
966
870
|
*/
|
|
967
871
|
async getUrlAndHash(lockfileHash, platform, arch, siblingsDigest) {
|
|
968
872
|
const ttlMsOverride = await this.resolveTtlMsOverride();
|
|
@@ -996,17 +900,13 @@ var DepCache = class DepCache {
|
|
|
996
900
|
hash
|
|
997
901
|
};
|
|
998
902
|
}
|
|
999
|
-
/**
|
|
1000
|
-
* Get a pre-signed upload URL for direct agent-to-S3 upload.
|
|
1001
|
-
*/
|
|
903
|
+
/** Pre-signed URL for direct agent-to-S3 upload. */
|
|
1002
904
|
async getUploadUrl(depsHash, platform, arch) {
|
|
1003
905
|
return this.storage.getUploadUrl(depTarballKey(depsHash, platform, arch));
|
|
1004
906
|
}
|
|
1005
907
|
/**
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
* Called only after the agent confirms its upload completed. Publishing before
|
|
1009
|
-
* the bytes land would let a reader follow the pointer to a missing object.
|
|
908
|
+
* Make an uploaded tarball discoverable by lockfile. Called only after the
|
|
909
|
+
* agent confirms its upload, or a reader could follow the pointer to nothing.
|
|
1010
910
|
*/
|
|
1011
911
|
async publishPointer(lockfileHash, platform, arch, depsHash, siblingsDigest) {
|
|
1012
912
|
await this.storage.put(depPointerKey(lockfileHash, platform, arch, siblingsDigest), depsHash);
|
|
@@ -1015,10 +915,7 @@ var DepCache = class DepCache {
|
|
|
1015
915
|
arch
|
|
1016
916
|
});
|
|
1017
917
|
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Store a dep tarball in cache.
|
|
1020
|
-
* Throws if tarball exceeds max size (per user decision).
|
|
1021
|
-
*/
|
|
918
|
+
/** Throws when the tarball exceeds the max size. */
|
|
1022
919
|
async store(lockfileHash, platform, arch, tarballData) {
|
|
1023
920
|
const maxTarballBytes = this.clusterSettings ? await this.clusterSettings.getNumber("cache_max_tarball_bytes", this.maxTarballBytes) : this.maxTarballBytes;
|
|
1024
921
|
if (tarballData.length > maxTarballBytes) throw new Error(`Dep tarball exceeds max size: ${tarballData.length} bytes > ${maxTarballBytes} bytes limit`);
|
|
@@ -1033,8 +930,8 @@ var DepCache = class DepCache {
|
|
|
1033
930
|
});
|
|
1034
931
|
}
|
|
1035
932
|
/**
|
|
1036
|
-
* Compute SHA-256 hash of a tarball buffer
|
|
1037
|
-
*
|
|
933
|
+
* Compute SHA-256 hash of a tarball buffer: the `depsHash` that `store()`
|
|
934
|
+
* keys and logs the tarball under.
|
|
1038
935
|
*/
|
|
1039
936
|
static computeHash(data) {
|
|
1040
937
|
return sha256(data);
|
|
@@ -1054,10 +951,9 @@ var DepCache = class DepCache {
|
|
|
1054
951
|
/**
|
|
1055
952
|
* A collision-resistant discriminator for a user-supplied object-key segment.
|
|
1056
953
|
*
|
|
1057
|
-
* Hashes the **exact** name
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1060
|
-
* the pre-sanitization name is what keeps each of those pairs two objects.
|
|
954
|
+
* Hashes the **exact** pre-sanitization name: `seg()` maps both `/` and `_` to
|
|
955
|
+
* `_` (`a/b` and `a_b` collapse), and a case-insensitive backend folds `build`
|
|
956
|
+
* onto `Build`, so only the hash keeps each pair two objects.
|
|
1061
957
|
*/
|
|
1062
958
|
function keyDiscriminator(exact) {
|
|
1063
959
|
return createHash("sha256").update(exact, "utf8").digest("hex").slice(0, 32);
|
|
@@ -1068,47 +964,40 @@ function keyDiscriminator(exact) {
|
|
|
1068
964
|
* User-facing cache layer wrapping CacheStorage.
|
|
1069
965
|
*
|
|
1070
966
|
* Namespacing: `cache/<orgId>/<repoId>/<refScope>/<key>-<discriminator>.tar.gz`.
|
|
1071
|
-
* The discriminator is a hash of the exact cache key, so
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
*
|
|
1077
|
-
* strictly per org so no tenant can read another tenant's cache.
|
|
967
|
+
* The discriminator is a hash of the exact cache key, so keys differing only by
|
|
968
|
+
* case stay two objects on a case-insensitive store. `refScope` is `shared` for
|
|
969
|
+
* trusted refs (org-shared, default-branch cache) or `iso/<runId>` for
|
|
970
|
+
* untrusted refs. An untrusted ref reads the shared scope as a fallback but can
|
|
971
|
+
* NEVER write it — the GitHub Actions cache-isolation model. Keyed strictly per
|
|
972
|
+
* org so no tenant can read another tenant's cache.
|
|
1078
973
|
*
|
|
1079
|
-
* Saves are immutable (first save under an exact key wins) and atomic
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
974
|
+
* Saves are immutable (first save under an exact key wins) and atomic (upload
|
|
975
|
+
* to `.tmp-<uuid>`, server-side copy to the final key, delete the temp), so a
|
|
976
|
+
* crashed save never leaves a corrupt final entry.
|
|
1082
977
|
*
|
|
1083
|
-
* Eviction: per-org byte quota plus the TTL the backing CacheStorage
|
|
1084
|
-
*
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
* hash and size accounting outside the tarball's own (presigned, metadata-less)
|
|
1089
|
-
* upload.
|
|
978
|
+
* Eviction: per-org byte quota plus the TTL the backing CacheStorage enforces
|
|
979
|
+
* lazily on access. A save that pushes the org over quota evicts LRU entries
|
|
980
|
+
* (oldest `lastAccessedAt` from backend metadata) until under quota, logging
|
|
981
|
+
* each. The `.hash` / `.size` sidecars carry the integrity hash and size
|
|
982
|
+
* accounting outside the presigned, metadata-less tarball upload.
|
|
1090
983
|
*/
|
|
1091
984
|
const logger$2 = createLogger({ prefix: "user-cache" });
|
|
1092
985
|
/**
|
|
1093
|
-
* Cluster-wide default quota: 5 GiB
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
986
|
+
* Cluster-wide default quota: 5 GiB, used when an org has no
|
|
987
|
+
* `org_settings.user_cache_quota_bytes` override. Operator-configurable via
|
|
988
|
+
* KICI_USER_CACHE_QUOTA_BYTES.
|
|
1096
989
|
*/
|
|
1097
990
|
const DEFAULT_USER_CACHE_QUOTA_BYTES = 5368709120;
|
|
1098
991
|
/**
|
|
1099
|
-
* Cluster-wide default entry TTL: 7 days
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
992
|
+
* Cluster-wide default entry TTL: 7 days, used when an org has no
|
|
993
|
+
* `org_settings.user_cache_ttl_ms` override. Operator-configurable via
|
|
994
|
+
* KICI_USER_CACHE_TTL_MS.
|
|
1102
995
|
*/
|
|
1103
996
|
const DEFAULT_USER_CACHE_TTL_MS = 6048e5;
|
|
1104
997
|
/** Tarball suffix for committed cache entries. */
|
|
1105
998
|
const TAR_SUFFIX = ".tar.gz";
|
|
1106
|
-
/**
|
|
1107
|
-
* Stem prefix of an in-flight upload, `.tmp-<uuid>.tar.gz`: the presigned PUT
|
|
1108
|
-
* target a save lands on before commit copies it to its final key.
|
|
1109
|
-
*/
|
|
999
|
+
/** Stem of an in-flight upload, `.tmp-<uuid>.tar.gz`, before commit copies it to its final key. */
|
|
1110
1000
|
const TEMP_UPLOAD_STEM = ".tmp-";
|
|
1111
|
-
/** Whether a listed object is an in-flight upload rather than a cache entry. */
|
|
1112
1001
|
function isTempUpload(key) {
|
|
1113
1002
|
return key.slice(key.lastIndexOf("/") + 1).startsWith(TEMP_UPLOAD_STEM);
|
|
1114
1003
|
}
|
|
@@ -1116,36 +1005,28 @@ function isTempUpload(key) {
|
|
|
1116
1005
|
const KEY_DISCRIMINATOR_TAIL = new RegExp(`^(.*)-([0-9a-f]{32})$`);
|
|
1117
1006
|
/**
|
|
1118
1007
|
* Whether a listed object is a committed entry, `<stem>-<discriminator>.tar.gz`.
|
|
1119
|
-
*
|
|
1120
|
-
*
|
|
1121
|
-
*
|
|
1122
|
-
* `.tmp-<uuid>.tar.gz` upload, which is a partial tarball until commit copies
|
|
1123
|
-
* it to its final key, and an object under the retired un-discriminated layout,
|
|
1124
|
-
* which nothing writes.
|
|
1008
|
+
* The `restoreKeys` prefix scan filters on it, which keeps out an in-flight
|
|
1009
|
+
* `.tmp-` upload (a partial tarball until commit) and an object under the
|
|
1010
|
+
* retired un-discriminated layout, which nothing writes.
|
|
1125
1011
|
*/
|
|
1126
1012
|
function isCommittedEntry(key) {
|
|
1127
1013
|
return key.endsWith(TAR_SUFFIX) && KEY_DISCRIMINATOR_TAIL.test(key.slice(0, -7));
|
|
1128
1014
|
}
|
|
1129
1015
|
/**
|
|
1130
|
-
* Recover the reported cache key from a committed entry's
|
|
1131
|
-
*
|
|
1132
|
-
*
|
|
1133
|
-
*
|
|
1134
|
-
* ends in a discriminator-shaped tail loses it — that affects only the string
|
|
1135
|
-
* reported back as `matchedKey`, never which object is served.
|
|
1016
|
+
* Recover the reported cache key from a committed entry's (already sanitized,
|
|
1017
|
+
* lossy) stem by undoing the appended discriminator. A stem that itself ends in
|
|
1018
|
+
* a discriminator-shaped tail loses it — that affects only the reported
|
|
1019
|
+
* `matchedKey`, never which object is served.
|
|
1136
1020
|
*/
|
|
1137
1021
|
function stripKeyDiscriminator(stem) {
|
|
1138
1022
|
return KEY_DISCRIMINATOR_TAIL.exec(stem)?.[1] ?? stem;
|
|
1139
1023
|
}
|
|
1140
1024
|
/**
|
|
1141
1025
|
* Sanitize a path segment so a key can never escape its org/repo/scope
|
|
1142
|
-
* namespace.
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1146
|
-
* signature on a pre-signed PUT/GET. Repo identifiers like `.` (the internal
|
|
1147
|
-
* provider's repo id) hit exactly this case, so the all-dots guard keeps the
|
|
1148
|
-
* object key canonical and the namespace boundary intact.
|
|
1026
|
+
* namespace. An all-dots segment (`.`, `..`, …) is also rewritten: HTTP/S3
|
|
1027
|
+
* path canonicalization collapses it (`a/./b` → `a/b`, `a/../b` → `b`), which
|
|
1028
|
+
* corrupts the namespace and breaks the SigV4 signature on a pre-signed
|
|
1029
|
+
* PUT/GET. The internal provider's repo id `.` hits exactly this case.
|
|
1149
1030
|
*/
|
|
1150
1031
|
function sanitizeSegment(s) {
|
|
1151
1032
|
const cleaned = s.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -1178,36 +1059,27 @@ function readPrefixes(ref) {
|
|
|
1178
1059
|
return [`${base}/shared/`];
|
|
1179
1060
|
}
|
|
1180
1061
|
/**
|
|
1181
|
-
* Object key for a committed cache entry.
|
|
1182
|
-
*
|
|
1183
|
-
*
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1186
|
-
*
|
|
1187
|
-
* tarball.
|
|
1188
|
-
*
|
|
1189
|
-
* It is deliberately a SUFFIX: `restoreByPrefix` matches `restoreKeys` by
|
|
1190
|
-
* string prefix, so appending leaves those semantics untouched. The only
|
|
1191
|
-
* parse that has to know about it is the `matchedKey` slice.
|
|
1062
|
+
* Object key for a committed cache entry. The trailing discriminator, a hash of
|
|
1063
|
+
* the EXACT cache key, keeps `build` and `Build` apart on a case-insensitive
|
|
1064
|
+
* namespace (the filesystem backend on macOS or Windows), where a restore could
|
|
1065
|
+
* otherwise return the other key's tarball. It is deliberately a SUFFIX:
|
|
1066
|
+
* `restoreByPrefix` matches `restoreKeys` by string prefix, so only the
|
|
1067
|
+
* `matchedKey` slice has to know about it.
|
|
1192
1068
|
*/
|
|
1193
1069
|
function finalKey(prefix, key) {
|
|
1194
1070
|
return `${prefix}${sanitizeSegment(key)}-${keyDiscriminator(key)}${TAR_SUFFIX}`;
|
|
1195
1071
|
}
|
|
1196
|
-
/**
|
|
1197
|
-
* The object key a committed save under `key` lands on for this ref. This is
|
|
1198
|
-
* the writer's own path: `beginSave` and `commitSave` address the entry
|
|
1199
|
-
* through it.
|
|
1200
|
-
*/
|
|
1072
|
+
/** The writer's object key for `key` under this ref, used by `beginSave` and `commitSave`. */
|
|
1201
1073
|
function userCacheEntryKey(ref, key) {
|
|
1202
1074
|
return finalKey(writePrefix(ref), key);
|
|
1203
1075
|
}
|
|
1204
1076
|
var UserCache = class {
|
|
1205
1077
|
storage;
|
|
1206
|
-
/**
|
|
1078
|
+
/** `KICI_USER_CACHE_QUOTA_BYTES`. */
|
|
1207
1079
|
defaultQuotaBytes;
|
|
1208
|
-
/**
|
|
1080
|
+
/** `KICI_USER_CACHE_TTL_MS`. */
|
|
1209
1081
|
defaultTtlMs;
|
|
1210
|
-
/**
|
|
1082
|
+
/** Absent = always use the cluster defaults. */
|
|
1211
1083
|
orgLimitsReader;
|
|
1212
1084
|
constructor(opts) {
|
|
1213
1085
|
this.storage = opts.storage;
|
|
@@ -1216,10 +1088,9 @@ var UserCache = class {
|
|
|
1216
1088
|
this.orgLimitsReader = opts.orgLimitsReader;
|
|
1217
1089
|
}
|
|
1218
1090
|
/**
|
|
1219
|
-
*
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1222
|
-
* restore/save because the settings lookup hiccupped.
|
|
1091
|
+
* Per-org override when present, else the cluster default. A reader failure
|
|
1092
|
+
* falls back to the defaults (logged): a settings-lookup hiccup must never
|
|
1093
|
+
* fail a restore/save.
|
|
1223
1094
|
*/
|
|
1224
1095
|
async resolveLimits(org) {
|
|
1225
1096
|
if (!this.orgLimitsReader) return {
|
|
@@ -1318,13 +1189,11 @@ var UserCache = class {
|
|
|
1318
1189
|
return (await this.storage.get(`${key}.hash`))?.toString("utf-8") || void 0;
|
|
1319
1190
|
}
|
|
1320
1191
|
/**
|
|
1321
|
-
* Evict
|
|
1322
|
-
*
|
|
1323
|
-
*
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1326
|
-
* fail on its copy to the final key. Retired un-discriminated entries stay
|
|
1327
|
-
* in the set: they hold accounted bytes and are evictable like any other.
|
|
1192
|
+
* Evict the org's LRU entries until total tarball size <= the per-org quota.
|
|
1193
|
+
* An in-flight `.tmp-` upload is never a candidate: with no metadata it would
|
|
1194
|
+
* rank oldest and be reclaimed first, freeing nothing (no `.size`) and failing
|
|
1195
|
+
* the concurrent commit's copy. Retired un-discriminated entries stay
|
|
1196
|
+
* candidates: they hold accounted bytes.
|
|
1328
1197
|
*/
|
|
1329
1198
|
async enforceQuota(ref) {
|
|
1330
1199
|
const { quotaBytes } = await this.resolveLimits(ref.org);
|
|
@@ -1367,34 +1236,25 @@ var UserCache = class {
|
|
|
1367
1236
|
}
|
|
1368
1237
|
};
|
|
1369
1238
|
/**
|
|
1370
|
-
* `metadata` key set when a coordinator retires
|
|
1371
|
-
*
|
|
1372
|
-
* retirement is told apart from a revoke.
|
|
1239
|
+
* `metadata` key set when a coordinator retires its previous run's self-issued
|
|
1240
|
+
* credential. An operator revoke never sets it, which tells the two apart.
|
|
1373
1241
|
*/
|
|
1374
1242
|
const RETIRED_BY_INSTANCE_KEY = "retiredByInstance";
|
|
1375
1243
|
/** Default credential expiry: 90 days. */
|
|
1376
1244
|
const DEFAULT_EXPIRY_DAYS = 90;
|
|
1377
|
-
/**
|
|
1378
|
-
* Database CRUD operations for peer credentials.
|
|
1379
|
-
* Used by the coordinator to manage credentials for all cluster members.
|
|
1380
|
-
*/
|
|
1245
|
+
/** The coordinator's DB store of every cluster member's credential. */
|
|
1381
1246
|
var PeerCredentialStore = class {
|
|
1382
1247
|
db;
|
|
1383
1248
|
constructor(db) {
|
|
1384
1249
|
this.db = db;
|
|
1385
1250
|
}
|
|
1386
1251
|
/**
|
|
1387
|
-
* Save a
|
|
1388
|
-
*
|
|
1389
|
-
* permits exactly one active credential per instanceId).
|
|
1252
|
+
* Save a credential, revoking the instanceId's prior active one
|
|
1253
|
+
* (`peer_credentials_active_uniq` permits one active credential per instanceId).
|
|
1390
1254
|
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1394
|
-
* peer-client of `instanceId` that was authenticating with the old
|
|
1395
|
-
* credential is now invalidated and will be rejected on its next proof. The
|
|
1396
|
-
* caller logs this so a revoke that cascades sibling rejections is visible in
|
|
1397
|
-
* the orchestrator logs.
|
|
1255
|
+
* `revokedCount > 0` means every sibling peer-client of `instanceId` still on the
|
|
1256
|
+
* old shared credential is rejected on its next proof; the caller logs it so a
|
|
1257
|
+
* cascade of sibling rejections is visible.
|
|
1398
1258
|
*/
|
|
1399
1259
|
async save(opts) {
|
|
1400
1260
|
const expiryDays = opts.expiryDays ?? DEFAULT_EXPIRY_DAYS;
|
|
@@ -1421,16 +1281,12 @@ var PeerCredentialStore = class {
|
|
|
1421
1281
|
throw err;
|
|
1422
1282
|
}
|
|
1423
1283
|
}
|
|
1424
|
-
/**
|
|
1425
|
-
* Find a non-revoked, non-expired credential by its hash.
|
|
1426
|
-
*/
|
|
1284
|
+
/** Non-revoked, non-expired credential by hash. */
|
|
1427
1285
|
async findByCredentialHash(hash) {
|
|
1428
1286
|
const row = await this.db.selectFrom("peer_credentials").selectAll().where("credential_hash", "=", hash).where("revoked_at", "is", null).where("expires_at", ">", /* @__PURE__ */ new Date()).executeTakeFirst();
|
|
1429
1287
|
return row ? mapRow(row) : null;
|
|
1430
1288
|
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Find a non-revoked, non-expired credential by instance ID.
|
|
1433
|
-
*/
|
|
1289
|
+
/** Newest non-revoked, non-expired credential for the instance. */
|
|
1434
1290
|
async findByInstanceId(instanceId) {
|
|
1435
1291
|
const row = await this.db.selectFrom("peer_credentials").selectAll().where("instance_id", "=", instanceId).where("revoked_at", "is", null).where("expires_at", ">", /* @__PURE__ */ new Date()).orderBy("created_at", "desc").executeTakeFirst();
|
|
1436
1292
|
return row ? mapRow(row) : null;
|
|
@@ -1449,10 +1305,9 @@ var PeerCredentialStore = class {
|
|
|
1449
1305
|
return row ? mapRow(row) : null;
|
|
1450
1306
|
}
|
|
1451
1307
|
/**
|
|
1452
|
-
* Revoke `instanceId`'s unrevoked
|
|
1453
|
-
*
|
|
1454
|
-
*
|
|
1455
|
-
* Returns whether a row was retired.
|
|
1308
|
+
* Revoke `instanceId`'s unrevoked self-issued row with `credentialHash`,
|
|
1309
|
+
* recording who retired it. Every condition is in the WHERE clause, so a row
|
|
1310
|
+
* that changed meanwhile is left alone. Returns whether a row was retired.
|
|
1456
1311
|
*/
|
|
1457
1312
|
async retireSelfIssued(opts) {
|
|
1458
1313
|
const marker = JSON.stringify({ [RETIRED_BY_INSTANCE_KEY]: opts.retiredByInstance });
|
|
@@ -1462,39 +1317,25 @@ var PeerCredentialStore = class {
|
|
|
1462
1317
|
}).where("instance_id", "=", opts.instanceId).where("credential_hash", "=", opts.credentialHash).where("revoked_at", "is", null).where(sql`metadata->>'issuance'`, "=", "self").executeTakeFirst();
|
|
1463
1318
|
return Number(result?.numUpdatedRows ?? 0n) > 0;
|
|
1464
1319
|
}
|
|
1465
|
-
/**
|
|
1466
|
-
* Update the last_seen_at timestamp for a credential.
|
|
1467
|
-
* Optionally records which coordinator validated it.
|
|
1468
|
-
*/
|
|
1320
|
+
/** Bump last_seen_at; optionally record the validating coordinator. */
|
|
1469
1321
|
async updateLastSeen(credentialHash, validatedBy) {
|
|
1470
1322
|
const updates = { last_seen_at: /* @__PURE__ */ new Date() };
|
|
1471
1323
|
if (validatedBy) updates.last_validated_by = validatedBy;
|
|
1472
1324
|
await this.db.updateTable("peer_credentials").set(updates).where("credential_hash", "=", credentialHash).execute();
|
|
1473
1325
|
}
|
|
1474
|
-
/**
|
|
1475
|
-
* Revoke a peer's credential by instance ID.
|
|
1476
|
-
*/
|
|
1477
1326
|
async revoke(instanceId) {
|
|
1478
1327
|
await this.db.updateTable("peer_credentials").set({ revoked_at: /* @__PURE__ */ new Date() }).where("instance_id", "=", instanceId).where("revoked_at", "is", null).execute();
|
|
1479
1328
|
}
|
|
1480
|
-
/**
|
|
1481
|
-
* Revoke all active credentials.
|
|
1482
|
-
* @returns The number of credentials revoked.
|
|
1483
|
-
*/
|
|
1329
|
+
/** @returns The number of credentials revoked. */
|
|
1484
1330
|
async revokeAll() {
|
|
1485
1331
|
const result = await this.db.updateTable("peer_credentials").set({ revoked_at: /* @__PURE__ */ new Date() }).where("revoked_at", "is", null).executeTakeFirst();
|
|
1486
1332
|
return Number(result?.numUpdatedRows ?? 0);
|
|
1487
1333
|
}
|
|
1488
|
-
/**
|
|
1489
|
-
* List all active (non-revoked, non-expired) credentials.
|
|
1490
|
-
*/
|
|
1334
|
+
/** Non-revoked, non-expired credentials, oldest first. */
|
|
1491
1335
|
async listActive() {
|
|
1492
1336
|
return (await this.db.selectFrom("peer_credentials").selectAll().where("revoked_at", "is", null).where("expires_at", ">", /* @__PURE__ */ new Date()).orderBy("created_at", "asc").execute()).map(mapRow);
|
|
1493
1337
|
}
|
|
1494
1338
|
};
|
|
1495
|
-
/**
|
|
1496
|
-
* Map a database row to a PeerCredential interface.
|
|
1497
|
-
*/
|
|
1498
1339
|
function mapRow(row) {
|
|
1499
1340
|
return {
|
|
1500
1341
|
id: row.id,
|
|
@@ -1511,15 +1352,7 @@ function mapRow(row) {
|
|
|
1511
1352
|
metadata: row.metadata ?? {}
|
|
1512
1353
|
};
|
|
1513
1354
|
}
|
|
1514
|
-
/**
|
|
1515
|
-
* Write credential data to a JSON file with restrictive permissions.
|
|
1516
|
-
*
|
|
1517
|
-
* Creates the parent directory (e.g., ~/.kici/) with 0700 if it doesn't exist.
|
|
1518
|
-
* The credential file is written with 0600 permissions (owner read/write only).
|
|
1519
|
-
*
|
|
1520
|
-
* @param filePath - Absolute path to the credential file
|
|
1521
|
-
* @param data - Credential data to persist
|
|
1522
|
-
*/
|
|
1355
|
+
/** Write the credential file 0600, creating its parent directory 0700 if missing. */
|
|
1523
1356
|
async function writeCredentialFile(filePath, data) {
|
|
1524
1357
|
const dir = dirname(filePath);
|
|
1525
1358
|
await mkdir(dir, {
|
|
@@ -1530,12 +1363,7 @@ async function writeCredentialFile(filePath, data) {
|
|
|
1530
1363
|
await writeFile(filePath, json, { mode: 384 });
|
|
1531
1364
|
await chmod(filePath, 384);
|
|
1532
1365
|
}
|
|
1533
|
-
/**
|
|
1534
|
-
* Read credential data from a JSON file.
|
|
1535
|
-
*
|
|
1536
|
-
* @param filePath - Absolute path to the credential file
|
|
1537
|
-
* @returns Parsed credential data, or null if the file doesn't exist
|
|
1538
|
-
*/
|
|
1366
|
+
/** @returns The parsed credential file, or null if it does not exist. */
|
|
1539
1367
|
async function readCredentialFile(filePath) {
|
|
1540
1368
|
try {
|
|
1541
1369
|
const content = await readFile(filePath, "utf-8");
|
|
@@ -1546,13 +1374,9 @@ async function readCredentialFile(filePath) {
|
|
|
1546
1374
|
}
|
|
1547
1375
|
}
|
|
1548
1376
|
/**
|
|
1549
|
-
*
|
|
1550
|
-
* store
|
|
1551
|
-
*
|
|
1552
|
-
*
|
|
1553
|
-
* Intended for tests of the store's own CRUD methods — production
|
|
1554
|
-
* code constructs the store with an already-allocated Kysely instance
|
|
1555
|
-
* shared with the orchestrator.
|
|
1377
|
+
* A store bound to a Postgres URL plus a pool disposer, so tests exercise the real
|
|
1378
|
+
* store without importing pg/kysely. Test-only: production passes the
|
|
1379
|
+
* orchestrator's shared Kysely instance.
|
|
1556
1380
|
*/
|
|
1557
1381
|
async function createPeerCredentialStoreFromUrl(databaseUrl, opts) {
|
|
1558
1382
|
const pool = new pg.Pool({
|
|
@@ -1609,22 +1433,19 @@ createLogger({ prefix: "coordinator-credential" });
|
|
|
1609
1433
|
/**
|
|
1610
1434
|
* Per-orchestrator coordinator that owns the shared peer credential file.
|
|
1611
1435
|
*
|
|
1612
|
-
*
|
|
1613
|
-
*
|
|
1614
|
-
*
|
|
1615
|
-
*
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1618
|
-
* per storm, and it never deletes a credential file a sibling has refreshed.
|
|
1436
|
+
* An orchestrator's peer-clients (one per peer) share one identity-scoped
|
|
1437
|
+
* credential file. Uncoordinated, a reconnect storm makes each sibling token-join
|
|
1438
|
+
* (each join revokes the prior credential, invalidating the others) and delete the
|
|
1439
|
+
* shared file on any rejection — a revocation cascade. One in-process mutex
|
|
1440
|
+
* serializes all file access, so one peer-client token-joins per storm, and a file
|
|
1441
|
+
* a sibling has refreshed is never deleted.
|
|
1619
1442
|
*
|
|
1620
|
-
* A coordinator without a join token
|
|
1621
|
-
*
|
|
1622
|
-
* coordinator its own credential, once per storm, under the same mutex.
|
|
1443
|
+
* A coordinator without a join token wires `selfIssue`: with no credential and no
|
|
1444
|
+
* token, it issues its own credential, once per storm, under the same mutex.
|
|
1623
1445
|
*
|
|
1624
|
-
* A coordinator also wires `corroborateRejection`. Any dialled endpoint can
|
|
1625
|
-
*
|
|
1626
|
-
*
|
|
1627
|
-
* cannot read, keeps the file.
|
|
1446
|
+
* A coordinator also wires `corroborateRejection`. Any dialled endpoint can send a
|
|
1447
|
+
* rejection, so before deleting the file it asks its own cluster database: a
|
|
1448
|
+
* credential the database holds as valid, or an unreadable database, keeps the file.
|
|
1628
1449
|
*/
|
|
1629
1450
|
const logger = createLogger({ prefix: "peer-auth-coordinator" });
|
|
1630
1451
|
/** How long a waiting peer-client awaits an in-flight sibling token-join. */
|
|
@@ -1860,10 +1681,7 @@ var JoinTokenManager = class {
|
|
|
1860
1681
|
constructor(deps) {
|
|
1861
1682
|
this.deps = deps;
|
|
1862
1683
|
}
|
|
1863
|
-
/**
|
|
1864
|
-
* Create a new join token.
|
|
1865
|
-
* Returns the full token string (only available at creation time).
|
|
1866
|
-
*/
|
|
1684
|
+
/** Returns the full token string, available only at creation time. */
|
|
1867
1685
|
async createToken(opts) {
|
|
1868
1686
|
const expiryMs = opts.expiryMs ?? DEFAULT_EXPIRY_MS;
|
|
1869
1687
|
const expiry = Date.now() + Math.floor(expiryMs);
|
|
@@ -1896,12 +1714,11 @@ var JoinTokenManager = class {
|
|
|
1896
1714
|
* Peer token mode: claim the row the presented token's secret hashes to. Role and
|
|
1897
1715
|
* routing come from the row (`claimByHash`), never from the token's routing part.
|
|
1898
1716
|
*
|
|
1899
|
-
* Self-healing reuse:
|
|
1900
|
-
*
|
|
1901
|
-
*
|
|
1902
|
-
*
|
|
1903
|
-
*
|
|
1904
|
-
* usefulness beyond the instance that first consumed it.
|
|
1717
|
+
* Self-healing reuse: the same joining peer (`peerInstanceId`) can re-consume
|
|
1718
|
+
* the token until `expires_at`, so a peer that lost its credential re-presents
|
|
1719
|
+
* the token in its env and gets a fresh credential without an operator action.
|
|
1720
|
+
* Bounding reuse by `expires_at` and the consuming instanceId keeps a leaked
|
|
1721
|
+
* token useless beyond the instance that first consumed it.
|
|
1905
1722
|
*/
|
|
1906
1723
|
async validateAndConsumeToken(token, consumedBy, peerInstanceId) {
|
|
1907
1724
|
return this.claimByHash(tokenHashOf(parseToken(token).secretHex), consumedBy, peerInstanceId);
|
|
@@ -1982,10 +1799,8 @@ var JoinTokenManager = class {
|
|
|
1982
1799
|
}
|
|
1983
1800
|
};
|
|
1984
1801
|
/**
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
1987
|
-
* used by tests that need to exercise token validation/reuse against a real
|
|
1988
|
-
* cluster DB.
|
|
1802
|
+
* A JoinTokenManager with its own connection pool, for tests against a real
|
|
1803
|
+
* cluster DB. Mirrors `createPeerCredentialStoreFromUrl`.
|
|
1989
1804
|
*/
|
|
1990
1805
|
function createJoinTokenManagerFromUrl(databaseUrl, opts) {
|
|
1991
1806
|
const pool = new pg.Pool({
|
|
@@ -2000,9 +1815,6 @@ function createJoinTokenManagerFromUrl(databaseUrl, opts) {
|
|
|
2000
1815
|
}
|
|
2001
1816
|
};
|
|
2002
1817
|
}
|
|
2003
|
-
/**
|
|
2004
|
-
* Parse a join token string into routing info and secret hex.
|
|
2005
|
-
*/
|
|
2006
1818
|
function parseToken(token) {
|
|
2007
1819
|
const parts = token.split(".");
|
|
2008
1820
|
if (parts.length !== 3 || parts[0] !== TOKEN_PREFIX) throw new Error(`Invalid join token format. Expected: ${TOKEN_PREFIX}.<routing>.<secret>`);
|
|
@@ -2529,32 +2341,18 @@ function createTrustPolicyStoreFromUrl(databaseUrl, opts) {
|
|
|
2529
2341
|
}
|
|
2530
2342
|
//#endregion
|
|
2531
2343
|
//#region src/cli/api-client.ts
|
|
2344
|
+
/** kici-admin's HTTP client for the orchestrator admin API (Bearer token auth). */
|
|
2532
2345
|
/**
|
|
2533
|
-
*
|
|
2534
|
-
*
|
|
2535
|
-
*
|
|
2536
|
-
*
|
|
2537
|
-
*
|
|
2538
|
-
|
|
2539
|
-
/**
|
|
2540
|
-
* `fetch` rejects with a bare `TypeError: fetch failed` when it cannot reach the
|
|
2541
|
-
* host — it names neither the address dialled nor the knob that sets it. Printed
|
|
2542
|
-
* through the CLI's `Error: ${message}` handler that becomes `Error: fetch
|
|
2543
|
-
* failed`, which reads like a fault in the subcommand rather than a misaddressed
|
|
2544
|
-
* client, and sends the operator debugging the wrong thing.
|
|
2545
|
-
*
|
|
2546
|
-
* The trap is sharpened by two details of this CLI. The base URL comes from
|
|
2547
|
-
* `KICI_ADMIN_URL`, not the `KICI_ORCHESTRATOR_URL` an operator is likelier to
|
|
2548
|
-
* have exported; and subcommands accepting `--database-url` fall back to direct
|
|
2549
|
-
* DB access, so on a host with `KICI_DATABASE_URL` set they keep working while
|
|
2550
|
-
* the HTTP-only ones fail — making it look like specific subcommands are broken.
|
|
2551
|
-
*
|
|
2552
|
-
* So: name the address, name the variable, and name the near-miss.
|
|
2346
|
+
* A bare `TypeError: fetch failed` names neither the address nor the knob that
|
|
2347
|
+
* sets it, so it reads as a broken subcommand rather than a misaddressed client.
|
|
2348
|
+
* The base URL comes from `KICI_ADMIN_URL`, not `KICI_ORCHESTRATOR_URL`, and
|
|
2349
|
+
* subcommands with `--database-url` fall back to direct DB access, so with
|
|
2350
|
+
* `KICI_DATABASE_URL` set only the HTTP-only ones fail. The error therefore names
|
|
2351
|
+
* the address, the variable and the near-miss.
|
|
2553
2352
|
*/
|
|
2554
2353
|
/**
|
|
2555
|
-
* Best-effort one-line detail from a rejected `fetch
|
|
2556
|
-
*
|
|
2557
|
-
* print an empty one.
|
|
2354
|
+
* Best-effort one-line detail from a rejected `fetch`; '' when none, so the
|
|
2355
|
+
* caller omits the parenthetical.
|
|
2558
2356
|
*/
|
|
2559
2357
|
function firstCauseMessage(err) {
|
|
2560
2358
|
if (!(err instanceof Error)) return "";
|
|
@@ -2568,21 +2366,16 @@ function firstCauseMessage(err) {
|
|
|
2568
2366
|
return "";
|
|
2569
2367
|
}
|
|
2570
2368
|
/**
|
|
2571
|
-
* True when a rejected `fetch` failed at the transport layer,
|
|
2572
|
-
*
|
|
2573
|
-
*
|
|
2574
|
-
* `fetch` uses a `TypeError` for both, so they are easy to conflate — but only
|
|
2575
|
-
* a transport failure means the orchestrator was actually dialled. Measured
|
|
2576
|
-
* against Node 24's undici:
|
|
2369
|
+
* True when a rejected `fetch` failed at the transport layer, not while the
|
|
2370
|
+
* request was being built. Both are a `TypeError`, but only a transport failure
|
|
2371
|
+
* means the orchestrator was dialled. Measured against Node 24's undici:
|
|
2577
2372
|
*
|
|
2578
2373
|
* closed port → TypeError('fetch failed') cause: Error(ECONNREFUSED)
|
|
2579
2374
|
* invalid header → TypeError('Headers.append: … invalid header …') cause: undefined
|
|
2580
2375
|
* invalid URL → TypeError('Failed to parse URL from …') cause: Error(ERR_INVALID_URL)
|
|
2581
2376
|
*
|
|
2582
|
-
*
|
|
2583
|
-
*
|
|
2584
|
-
* transport failure and a descriptive message for everything it rejects before
|
|
2585
|
-
* opening a connection.
|
|
2377
|
+
* So the discriminator is the message, NOT a `cause` (the invalid-URL case has
|
|
2378
|
+
* one): undici says exactly `fetch failed` only for a transport failure.
|
|
2586
2379
|
*/
|
|
2587
2380
|
function isTransportFailure(err) {
|
|
2588
2381
|
return err instanceof TypeError && err.message === "fetch failed";
|
|
@@ -2620,9 +2413,6 @@ var AdminApiClient = class {
|
|
|
2620
2413
|
this.baseUrl = baseUrl;
|
|
2621
2414
|
this.token = token;
|
|
2622
2415
|
}
|
|
2623
|
-
/**
|
|
2624
|
-
* Make an authenticated HTTP request to the admin API.
|
|
2625
|
-
*/
|
|
2626
2416
|
async request(method, path, body) {
|
|
2627
2417
|
const res = await fetchAdminApi(`${this.baseUrl}${path}`, {
|
|
2628
2418
|
method,
|
|
@@ -2645,39 +2435,21 @@ var AdminApiClient = class {
|
|
|
2645
2435
|
if (res.status === 204) return;
|
|
2646
2436
|
return await res.json();
|
|
2647
2437
|
}
|
|
2648
|
-
/**
|
|
2649
|
-
* Public GET request returning parsed JSON.
|
|
2650
|
-
*/
|
|
2651
2438
|
async get(path) {
|
|
2652
2439
|
return this.request("GET", path);
|
|
2653
2440
|
}
|
|
2654
|
-
/**
|
|
2655
|
-
* Public POST request returning parsed JSON.
|
|
2656
|
-
*/
|
|
2657
2441
|
async post(path, body) {
|
|
2658
2442
|
return this.request("POST", path, body);
|
|
2659
2443
|
}
|
|
2660
|
-
/**
|
|
2661
|
-
* Public PATCH request returning parsed JSON.
|
|
2662
|
-
*/
|
|
2663
2444
|
async patch(path, body) {
|
|
2664
2445
|
return this.request("PATCH", path, body);
|
|
2665
2446
|
}
|
|
2666
|
-
/**
|
|
2667
|
-
* Public PUT request returning parsed JSON.
|
|
2668
|
-
*/
|
|
2669
2447
|
async put(path, body) {
|
|
2670
2448
|
return this.request("PUT", path, body);
|
|
2671
2449
|
}
|
|
2672
|
-
/**
|
|
2673
|
-
* Public DELETE request returning parsed JSON.
|
|
2674
|
-
*/
|
|
2675
2450
|
async delete(path) {
|
|
2676
2451
|
return this.request("DELETE", path);
|
|
2677
2452
|
}
|
|
2678
|
-
/**
|
|
2679
|
-
* Public GET request returning raw response text.
|
|
2680
|
-
*/
|
|
2681
2453
|
async getText(path) {
|
|
2682
2454
|
const res = await fetchAdminApi(`${this.baseUrl}${path}`, {
|
|
2683
2455
|
method: "GET",
|
|
@@ -2841,13 +2613,9 @@ var AdminApiClient = class {
|
|
|
2841
2613
|
return this.request("GET", `/api/v1/agent-tokens${qs}`);
|
|
2842
2614
|
}
|
|
2843
2615
|
/**
|
|
2844
|
-
* Revoke an agent token.
|
|
2845
|
-
*
|
|
2846
|
-
*
|
|
2847
|
-
* synchronously closes every in-flight agent WS authenticated by
|
|
2848
|
-
* this token. The returned `kicked` count is the number
|
|
2849
|
-
* of WS connections that were closed on the wire — surface it to
|
|
2850
|
-
* the operator so they know the revocation actually propagated.
|
|
2616
|
+
* Revoke an agent token: the orchestrator sets `agent_tokens.revoked_at` AND
|
|
2617
|
+
* synchronously closes every agent WS authenticated by it. `kicked` counts
|
|
2618
|
+
* the closed connections; show it so the operator sees the revocation propagated.
|
|
2851
2619
|
*/
|
|
2852
2620
|
async revokeAgentToken(id) {
|
|
2853
2621
|
return this.request("DELETE", `/api/v1/agent-tokens/${encodeURIComponent(id)}`);
|
|
@@ -2905,7 +2673,6 @@ var AdminApiClient = class {
|
|
|
2905
2673
|
async drainStatus() {
|
|
2906
2674
|
return this.request("GET", "/api/v1/admin/orchestrator/drain");
|
|
2907
2675
|
}
|
|
2908
|
-
/** Forget a departed peer on this coordinator and every connected sibling. */
|
|
2909
2676
|
/**
|
|
2910
2677
|
* Forget a departed peer on this coordinator and every connected sibling. A
|
|
2911
2678
|
* refusal that only needs the backstop acknowledgement comes back as a body
|
|
@@ -3016,10 +2783,9 @@ var AdminApiClient = class {
|
|
|
3016
2783
|
return this.request("POST", "/api/v1/admin/backends/sync");
|
|
3017
2784
|
}
|
|
3018
2785
|
/**
|
|
3019
|
-
* List
|
|
3020
|
-
*
|
|
3021
|
-
*
|
|
3022
|
-
* are returned.
|
|
2786
|
+
* List runs. `status` takes one status or a comma-separated list (e.g.
|
|
2787
|
+
* `success,failed`). `since` is an ISO-8601 timestamp; only runs with
|
|
2788
|
+
* `created_at` strictly later are returned.
|
|
3023
2789
|
*/
|
|
3024
2790
|
async listRuns(opts) {
|
|
3025
2791
|
const qs = toQuery({
|
|
@@ -3425,14 +3191,10 @@ function startManifestLoopback(opts) {
|
|
|
3425
3191
|
//#endregion
|
|
3426
3192
|
//#region src/cli/open-browser.ts
|
|
3427
3193
|
/**
|
|
3428
|
-
*
|
|
3429
|
-
*
|
|
3430
|
-
*
|
|
3431
|
-
*
|
|
3432
|
-
*
|
|
3433
|
-
* `KICI_BROWSER_CMD` overrides the launcher: `none` suppresses it entirely
|
|
3434
|
-
* (automated tests / headless capture), any other value is run with `{url}` substituted —
|
|
3435
|
-
* mirroring the `kici login` convention.
|
|
3194
|
+
* Best-effort: the caller always prints the URL too, so a headless host falls
|
|
3195
|
+
* back to copy-paste. `KICI_BROWSER_CMD` overrides the launcher as in
|
|
3196
|
+
* `kici login`: `none` suppresses it (tests, headless capture), any other
|
|
3197
|
+
* value runs with `{url}` substituted.
|
|
3436
3198
|
*/
|
|
3437
3199
|
function openBrowserBestEffort(url) {
|
|
3438
3200
|
const override = process.env.KICI_BROWSER_CMD;
|
|
@@ -3466,15 +3228,13 @@ const WebhookUrlNote = z.enum([
|
|
|
3466
3228
|
//#endregion
|
|
3467
3229
|
//#region src/cli/webhook-url-notes.ts
|
|
3468
3230
|
/**
|
|
3469
|
-
*
|
|
3470
|
-
*
|
|
3471
|
-
* `WebhookUrlNote` reads the same in both.
|
|
3231
|
+
* Text for a missing webhook URL, shared by `kici-admin source add` and the
|
|
3232
|
+
* manifest setup flow.
|
|
3472
3233
|
*/
|
|
3473
3234
|
const DASHBOARD_HINT = "Find the webhook URL in the dashboard under Sources.";
|
|
3474
3235
|
/**
|
|
3475
|
-
*
|
|
3476
|
-
*
|
|
3477
|
-
* version can send a note this CLI does not know.
|
|
3236
|
+
* The reason in `(unavailable — <reason>)`. Takes a plain string: another
|
|
3237
|
+
* orchestrator version can send a note this CLI does not know.
|
|
3478
3238
|
*/
|
|
3479
3239
|
function webhookNoteReason(note) {
|
|
3480
3240
|
switch (note) {
|
|
@@ -3506,16 +3266,14 @@ function webhookNoteHint(note) {
|
|
|
3506
3266
|
//#endregion
|
|
3507
3267
|
//#region src/cli/commands/source-manifest.ts
|
|
3508
3268
|
/**
|
|
3509
|
-
* `kici-admin source add github --manifest
|
|
3510
|
-
*
|
|
3511
|
-
*
|
|
3512
|
-
*
|
|
3513
|
-
*
|
|
3514
|
-
* App's id + private key + webhook secret, then reuses the existing source
|
|
3515
|
-
* storage + Platform-registration path.
|
|
3269
|
+
* `kici-admin source add github --manifest`: GitHub's App Manifest flow end to
|
|
3270
|
+
* end. The pre-configured manifest goes to GitHub through an auto-submitting
|
|
3271
|
+
* form; the short-lived setup code comes back over a localhost loopback (or is
|
|
3272
|
+
* pasted with --no-browser) and is exchanged for the App's id, private key and
|
|
3273
|
+
* webhook secret, which take the normal source storage and registration path.
|
|
3516
3274
|
*
|
|
3517
|
-
* The private-key-bearing conversion
|
|
3518
|
-
*
|
|
3275
|
+
* The private-key-bearing conversion runs only on the orchestrator host and
|
|
3276
|
+
* never transits the Platform (sovereignty invariant).
|
|
3519
3277
|
*/
|
|
3520
3278
|
/** Static marketing-site page the CLI points at in headless paste-code mode. */
|
|
3521
3279
|
const STATIC_CALLBACK_URL = "https://kici.dev/gh-manifest-callback";
|