@loucompanion/forge-bridge 0.1.3-dev.62c25087ca89 → 0.1.3-dev.6bad40c431d6

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.
Files changed (65) hide show
  1. package/README.md +44 -0
  2. package/dist/bridge/bridge.base.d.ts +5 -1
  3. package/dist/bridge/bridge.service.d.ts +36 -2
  4. package/dist/bridge/bridge.service.js +580 -16
  5. package/dist/bridge/bridge.types.d.ts +114 -1
  6. package/dist/cli/cli.help.js +12 -0
  7. package/dist/cli/cli.service.d.ts +12 -0
  8. package/dist/cli/cli.service.js +171 -1
  9. package/dist/cli/local-config.d.ts +2 -0
  10. package/dist/cli/local-config.js +6 -2
  11. package/dist/sandbox/command-runner.d.ts +4 -0
  12. package/dist/sandbox/command-runner.js +47 -0
  13. package/dist/sandbox/contract.d.ts +5 -0
  14. package/dist/sandbox/contract.js +276 -0
  15. package/dist/sandbox/event-budget.d.ts +11 -0
  16. package/dist/sandbox/event-budget.js +27 -0
  17. package/dist/sandbox/image-policy.d.ts +33 -0
  18. package/dist/sandbox/image-policy.js +245 -0
  19. package/dist/sandbox/index.d.ts +5 -0
  20. package/dist/sandbox/index.js +5 -0
  21. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  22. package/dist/sandbox/lease-watchdog.js +67 -0
  23. package/dist/sandbox/preflight.d.ts +36 -0
  24. package/dist/sandbox/preflight.js +239 -0
  25. package/dist/sandbox/process.d.ts +20 -0
  26. package/dist/sandbox/process.js +124 -0
  27. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  28. package/dist/sandbox/provider-auth/codex.js +212 -0
  29. package/dist/sandbox/repository-lock.d.ts +5 -0
  30. package/dist/sandbox/repository-lock.js +83 -0
  31. package/dist/sandbox/runtime.d.ts +218 -0
  32. package/dist/sandbox/runtime.js +1672 -0
  33. package/dist/sandbox/sandbox.types.d.ts +83 -0
  34. package/dist/sandbox/sandbox.types.js +1 -0
  35. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  36. package/dist/sandbox/worker-supervisor.js +36 -0
  37. package/dist/sandbox/worker.d.ts +1 -0
  38. package/dist/sandbox/worker.js +228 -0
  39. package/dist/sandbox/workspace.service.d.ts +12 -0
  40. package/dist/sandbox/workspace.service.js +49 -0
  41. package/dist/session/phase-completion.artifacts.js +23 -2
  42. package/dist/session/phase-completion.schemas.js +24 -2
  43. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  44. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +2 -0
  45. package/dist/session/provider-cli/provider-cli.deterministic.js +20 -9
  46. package/dist/session/provider-cli/provider-cli.process.d.ts +1 -0
  47. package/dist/session/provider-cli/provider-cli.process.js +23 -1
  48. package/dist/session/provider-cli/provider-cli.prompts.d.ts +13 -1
  49. package/dist/session/provider-cli/provider-cli.prompts.js +143 -2
  50. package/dist/session/provider-cli/provider-cli.service.d.ts +4 -1
  51. package/dist/session/provider-cli/provider-cli.service.js +161 -43
  52. package/dist/session/provider-cli/provider-cli.types.d.ts +1 -0
  53. package/dist/session/session.service.js +34 -24
  54. package/dist/session/session.types.d.ts +89 -1
  55. package/dist/session/session.types.js +209 -3
  56. package/dist/shared/process/windows-launch.d.ts +1 -1
  57. package/dist/shared/process/windows-launch.js +6 -3
  58. package/dist/shared/provider-capabilities.d.ts +1 -0
  59. package/dist/shared/provider-capabilities.js +3 -0
  60. package/dist/shared/redaction/redaction.js +4 -2
  61. package/dist/version.d.ts +1 -1
  62. package/dist/version.js +1 -1
  63. package/dist/worktree/dependency-warmer.d.ts +11 -0
  64. package/dist/worktree/dependency-warmer.js +45 -1
  65. package/package.json +4 -2
package/README.md CHANGED
@@ -53,6 +53,50 @@ forge-bridge add-repo Forge /path/to/Forge
53
53
  forge-bridge remove-repo Forge
54
54
  ```
55
55
 
56
+ ## Docker sandbox runners
57
+
58
+ Protocol-v2 bridges preflight Docker before advertising the `docker` sandbox runtime. Validate a repository contract and the local runner before enabling a project's `sandbox_required` policy:
59
+
60
+ ```bash
61
+ forge sandbox init
62
+ forge sandbox doctor
63
+ forge-bridge sandbox reap
64
+ ```
65
+
66
+ Production shared runners require a Linux rootless Docker daemon. Docker Desktop is developer-only and must be opted in with `FORGE_ALLOW_DOCKER_DESKTOP_SANDBOX=true`. Useful runner settings are:
67
+
68
+ - `FORGE_TRUST_DOMAIN_ID` — must match the project trust domain.
69
+ - `FORGE_MAX_CONCURRENT_SANDBOXES` — hard slot ceiling; start at `1`.
70
+ - `FORGE_MAX_CONCURRENT_SANDBOX_BUILDS` / `FORGE_SANDBOX_PID_CAPACITY` — weighted build and PID admission ceilings (defaults: `1` and `2048`).
71
+ - `FORGE_DOCKER_CONTEXT` / `FORGE_DOCKER_EXECUTABLE` — dedicated daemon selection.
72
+ - `FORGE_SANDBOX_STATE_ROOT` — fsync'd attempt ledgers, restricted diagnostics, and build state.
73
+ - `FORGE_DISK_HIGH_WATERMARK_PERCENT` / `FORGE_DISK_LOW_WATERMARK_PERCENT` / `FORGE_MINIMUM_WORKSPACE_BYTES` — admission watermarks with high/low hysteresis.
74
+ - `FORGE_SANDBOX_DEDICATED_DISK=true` — operator assertion that sandbox storage is on a dedicated, conservatively watermarked disk when the Docker storage driver cannot enforce per-volume quotas. `sandbox_required` fails placement unless either quota support or this assertion is advertised.
75
+ - `FORGE_SANDBOX_IMAGE_RETENTION_COUNT` / `FORGE_SANDBOX_IMAGE_RETENTION_TTL_MS` — per-project image-cache retention (defaults: 3 images and 7 days).
76
+ - `FORGE_SANDBOX_AUDIT_RETENTION_COUNT` / `FORGE_SANDBOX_AUDIT_RETENTION_TTL_MS` — restricted failed-build/attestation retention (defaults: 200 keys and 30 days); evidence for active image digests is protected.
77
+ - `FORGE_AGENT_TOOLING_IMAGE` — digest-pinned Node tooling base required by `sandbox_required`.
78
+ - `FORGE_SANDBOX_IMAGE_POLICY_ENFORCED`, `FORGE_SANDBOX_SBOM_EXECUTABLE`, `FORGE_SANDBOX_VULNERABILITY_EXECUTABLE`, and `FORGE_SANDBOX_PROVENANCE_SIGNING_KEY` — required generated-image promotion gates. The SBOM command must accept Syft-compatible `<image> -o spdx-json`; the vulnerability command must accept Trivy-compatible image arguments.
79
+ - `FORGE_SANDBOX_SIGNATURE_EXECUTABLE` / `FORGE_SANDBOX_SIGNATURE_PUBLIC_KEY` — Cosign-compatible verifier and bounded public-key file. `sandbox_required` verifies every digest-pinned external Dockerfile base/`COPY --from` source and Forge tooling base before generated-image promotion, plus every service-catalog and egress-proxy image before runtime use; adoption modes emit a warning when verification is not configured.
80
+ - `FORGE_SANDBOX_BUILD_EGRESS_ENFORCED` — confirms the dedicated BuildKit builder is behind the approved build-egress control; required for `sandbox_required`.
81
+ - `FORGE_SANDBOX_BUILD_MEMORY` / `FORGE_SANDBOX_BUILD_CPU_QUOTA` / `FORGE_SANDBOX_BUILD_NOFILE` — bounded per-build-step memory/swap, CPU CFS quota, and file-descriptor caps (defaults: `4g`, `200000` per `100000` period, and `4096`). `FORGE_SANDBOX_BUILDER_PIDS_LIMIT` caps and verifies aggregate processes on the dedicated BuildKit container (default `512`).
82
+ - `FORGE_SANDBOX_BUILDKIT_CACHE_MAX` / `FORGE_SANDBOX_BUILDKIT_CACHE_RESERVED` — pressure-triggered dedicated-builder cache bounds (defaults: 20 GiB maximum and 2 GiB reserved; minimum free space follows `FORGE_MINIMUM_WORKSPACE_BYTES`).
83
+ - `FORGE_SANDBOX_SECCOMP_PROFILE` / `FORGE_SANDBOX_LSM_SECURITY_OPT` — absolute hardened seccomp profile and AppArmor/SELinux Docker security option applied to every agent and service; required for `sandbox_required`.
84
+ - `FORGE_SANDBOX_HARD_WALL_SECONDS` / `FORGE_SANDBOX_IDLE_SECONDS` — monotonic worker deadlines (defaults: 8 hours and 30 minutes; both are capped by the 8-hour hard maximum).
85
+ - `FORGE_SANDBOX_PUBLICATION_MODE=bridge_push` — remote-safe exact-SHA result publication. This is required for `sandbox_required`; the default `shared_filesystem` mode is intended only for deployments where the bridge and API intentionally share the repository filesystem.
86
+ - `FORGE_SERVICE_POSTGRES_IMAGE`, `FORGE_SERVICE_REDIS_IMAGE`, and `FORGE_SERVICE_BROWSER_IMAGE` — digest-pinned service-catalog images when declared by a contract.
87
+
88
+ `public_allowlist` uses an internal agent network plus a separate external network joined only by a restricted Squid-compatible proxy. Configure a digest-pinned `FORGE_SANDBOX_EGRESS_PROXY_IMAGE` and `FORGE_SANDBOX_EGRESS_ENFORCED=true`; Forge generates the domain allowlist with private, loopback, link-local, metadata, unsafe-port, and default-deny rules and injects proxy variables into the worker. `open_public` is developer-only and additionally requires `FORGE_ALLOW_OPEN_PUBLIC_SANDBOX=true`. Neither setting weakens `sandbox_required`.
89
+
90
+ Before selecting `sandbox_required`, save the full approved 40- or 64-character Git commit SHA in the project's Sandbox policy dialog. The server persists that SHA on the immutable attempt and the bridge reads `.forge/forge.sandbox.yml`, `.forge/Dockerfile`, and the tracked build context only from that commit. API-key dispatches carry an opaque one-time grant handle; the authenticated current runner instance redeems it after reservation, and plaintext stays out of offers, database payloads, Docker metadata, and host-persisted control files.
91
+
92
+ Temporary host fallback for `sandbox_preferred` requires both sides to opt in: a project owner must save a reason and future expiry (maximum 30 days), and an operator must enable **Host fallback** for the runner in Profile Settings. Either missing or expired authorization fails closed. `sandbox_required` never falls back.
93
+
94
+ The bridge reaps exact-labeled orphan resources before connecting and every minute afterward. Connected passes reconcile current-instance ledgers against an authoritative, instance-scoped server attempt snapshot; startup does not adopt old-instance resources and terminates them before advertising capacity. Per-resource removal outcomes are written mode-0600 under `FORGE_SANDBOX_STATE_ROOT/cleanup-reports` before transmission, replayed after reconnect, acknowledged by report ID, and persisted by the API only when attempt, fence, resource runner instance, and current reporting runner match. Fenced dispatch events also persist image/cache status, build/agent-start/import/teardown timestamps, and a validated inventory of created agent/sidecar containers, networks, volumes, and published ports. Selected-session diagnostics expose that lifecycle data and recent cleanup actions while retaining cleanup-pending state after the agent outcome is terminal. Install `forge-bridge sandbox reap` as an independently supervised OS timer with the same `FORGE_RUNNER_ID`, Docker context, and state root so resources are reconciled while the bridge is down; the Docker-context ownership lock makes an overlapping pass fail safely. A non-zero `degradedReservationCount` means teardown has not completed; the affected capacity remains unavailable until a successful reaper pass. Drain a runner from Profile Settings before upgrades or Docker maintenance, wait for active/starting counts to reach zero, then restart the bridge. Profile Settings also exposes a scoped **Reap** action; the API delivers it only to the currently established protocol-v2 runner instance. Image GC runs only while disk pressure is latched, shares the build lock, preserves active image digests, and prunes only expired images beyond the per-project retention count. Lease renewals are HMAC-bound to the attempt and fence and advance an in-container monotonic deadline; lease, hard-wall, and idle expiry produce distinct failure codes.
95
+
96
+ In `bridge_push` mode, the start packet carries only an attempt/fence/branch-bound publication handle. After validating and importing the output bundle, the authenticated current bridge redeems that handle for an in-memory, exact-repository credential; the API rechecks the live lease/fence/grant/branch/result SHA and records redemption before returning it. Git receives the credential through a process environment configuration, never command arguments, files, Git config, or the container. The bridge compares the remote branch with the immutable input commit and pushes only the exact result commit using `--force-with-lease`; cancellation aborts Git, an already-published matching result is an idempotent success, and any other remote value is rejected as stale. The API records the fenced publication acknowledgment before sign-off and does not push the branch a second time while opening the pull request.
97
+
98
+ Failed project, tooling, and agent image stages preserve only a redacted, capped, mode-0600 diagnostic at `FORGE_SANDBOX_STATE_ROOT/build-logs/<image-key>/<stage>.log`. The event stream carries its digest, byte count, and truncation flag, never the log contents. Treat this directory as restricted source-bearing audit data and apply local retention controls.
99
+
56
100
  ## Package smoke check
57
101
 
58
102
  From this package directory:
@@ -1,4 +1,4 @@
1
- import type { BridgeCancelHandler, BridgeDispatchHandler, BridgeServiceConfig } from "./bridge.types.js";
1
+ import type { BridgeCancelHandler, BridgeDispatchHandler, BridgeLeaseUpdateHandler, BridgeOfferHandler, BridgeReapHandler, BridgeServiceConfig, BridgeStartHandler } from "./bridge.types.js";
2
2
  export interface BridgeService {
3
3
  connectOnce(): Promise<void>;
4
4
  run(signal?: AbortSignal): Promise<void>;
@@ -9,7 +9,11 @@ export interface BridgeConnection {
9
9
  stop(): Promise<void>;
10
10
  onClose(handler: (error?: Error) => void): void;
11
11
  onDispatch(handler: BridgeDispatchHandler): void;
12
+ onOffer?(handler: BridgeOfferHandler): void;
13
+ onStart?(handler: BridgeStartHandler): void;
14
+ onLeaseUpdate?(handler: BridgeLeaseUpdateHandler): void;
12
15
  onCancel(handler: BridgeCancelHandler): void;
16
+ onReap?(handler: BridgeReapHandler): void;
13
17
  invoke<TResponse>(methodName: string, payload: unknown): Promise<TResponse>;
14
18
  }
15
19
  export interface BridgeConnectionFactory {
@@ -1,29 +1,57 @@
1
1
  import type { SessionService } from "../session/session.base.js";
2
+ import { type DispatchSandboxResourceRequest } from "../session/session.types.js";
3
+ import { DockerPreflight } from "../sandbox/preflight.js";
4
+ import { type SandboxRuntime } from "../sandbox/runtime.js";
2
5
  import type { BridgeConnection, BridgeConnectionFactory, BridgeService } from "./bridge.base.js";
3
- import type { BridgeCancelHandler, BridgeDispatchHandler, BridgeServiceConfig } from "./bridge.types.js";
6
+ import type { BridgeCancelHandler, BridgeDispatchHandler, BridgeLeaseUpdateRequest, BridgeReapRequest, BridgeServiceConfig } from "./bridge.types.js";
4
7
  export declare class ForgeBridgeService implements BridgeService {
5
8
  private readonly config;
6
9
  private readonly sessions;
7
10
  private readonly connections;
8
11
  private connection;
9
12
  private heartbeatTimer;
13
+ private leaseWatchdogTimer;
14
+ private sandboxMaintenanceTimer;
15
+ private sandboxMaintenanceRunning;
10
16
  private closed;
11
17
  private stopped;
12
18
  private readonly activeSessions;
13
- constructor(config: BridgeServiceConfig, sessions?: SessionService, connections?: BridgeConnectionFactory);
19
+ private readonly reservations;
20
+ private readonly preflight;
21
+ private readonly sandboxRuntime;
22
+ constructor(config: BridgeServiceConfig, sessions?: SessionService, connections?: BridgeConnectionFactory, preflight?: DockerPreflight, sandboxRuntime?: SandboxRuntime);
14
23
  connectOnce(): Promise<void>;
15
24
  run(signal?: AbortSignal): Promise<void>;
16
25
  stop(): Promise<void>;
17
26
  private handleDispatch;
27
+ private handleOffer;
28
+ private handleStart;
29
+ private redeemSecretGrant;
30
+ private redeemPublishGrant;
31
+ private handleLeaseUpdate;
32
+ private sendOfferDecision;
18
33
  private parseDispatchOrReport;
19
34
  private handleCancel;
35
+ private handleReap;
20
36
  private abortActiveSessions;
21
37
  private normalizeDispatch;
22
38
  private sendResult;
39
+ private sendLegacyResult;
40
+ private sendResultPayload;
23
41
  private startHeartbeat;
42
+ private startLeaseWatchdog;
43
+ private stopLeaseWatchdog;
44
+ private expireLeases;
24
45
  private stopHeartbeat;
46
+ private startSandboxMaintenance;
47
+ private stopSandboxMaintenance;
48
+ private runSandboxMaintenance;
49
+ private readAttemptSnapshot;
50
+ private flushCleanupReports;
25
51
  private sendHeartbeat;
26
52
  private capabilities;
53
+ private usedSandboxResources;
54
+ private sandboxResourceCapacity;
27
55
  private invokeAck;
28
56
  private waitForClose;
29
57
  private stopConnection;
@@ -39,8 +67,14 @@ export declare class SignalRBridgeConnection implements BridgeConnection {
39
67
  stop(): Promise<void>;
40
68
  onClose(handler: (error?: Error) => void): void;
41
69
  onDispatch(handler: BridgeDispatchHandler): void;
70
+ onOffer(handler: BridgeDispatchHandler): void;
71
+ onStart(handler: BridgeDispatchHandler): void;
72
+ onLeaseUpdate(handler: (request: BridgeLeaseUpdateRequest) => Promise<void>): void;
42
73
  onCancel(handler: BridgeCancelHandler): void;
74
+ onReap(handler: (request: BridgeReapRequest) => Promise<void>): void;
43
75
  invoke<TResponse>(methodName: string, payload: unknown): Promise<TResponse>;
44
76
  }
45
77
  export declare function defaultBridgeServiceConfig(overrides?: Partial<BridgeServiceConfig>): BridgeServiceConfig;
46
78
  export declare function resolveBridgeRunnerHubUrl(serverUrl: string): URL;
79
+ export type SandboxResourceVector = DispatchSandboxResourceRequest;
80
+ export declare function canReserveSandboxResources(used: SandboxResourceVector, requested: SandboxResourceVector, capacity: SandboxResourceVector): boolean;