Skip to content

Latest commit

 

History

History
118 lines (103 loc) · 8.22 KB

File metadata and controls

118 lines (103 loc) · 8.22 KB

Bobsled security model

Boundary

Bobsled's trusted boundary is the local control process, filesystem ledger, and registered Git repositories. The local browser is an operator interface. Agent output, prompts, repository instructions, and vendor-native hooks are untrusted inputs and have no direct workflow authority.

The service binds to loopback, accepts only typed endpoints, limits request and output sizes, validates registered Git paths, and uses structured child-process arguments with shell: false. There is intentionally no arbitrary shell API.

Current controls

Control Status Meaning
Hash-chained event ledger Enforced Startup fails on sequence, link, digest, payload, domain, historical-reference, or ownership corruption before publishing state
Single local ledger writer Enforced A process lock prevents concurrent control planes
Guarded state transitions Enforced Only the server can advance a change
Artifact approval freshness Enforced Current bytes are re-digested before protected transitions
Repository admission Enforced Invocation working directories must be registered Git repositories
Invocation time/output bounds Enforced Fixed timeout and five-megabyte retained output ceiling
Runner permission flags Advisory Vendor CLI restrictions are not an independent OS sandbox
Human role authorization Advisory Local operator identity is asserted, not authenticated
Mutating runner admission Enforced Only adapters implementing isolated-worktree may build; currently Codex only
Diff path and commit admission Enforced Bobsled rejects protected/sensitive paths and creates the governed commit
Verification filesystem isolation Enforced on macOS Configured command vectors run through Seatbelt with writes limited to the worktree and isolated home
Author/reviewer independence Enforced Review denies same-runner reuse and higher-risk same-family reuse
Blocking review disposition Enforced Schema-valid review results return blocking work to rework; prose alone cannot open PR approval
Merge-candidate freshness Enforced Final configured checks bind target, branch, and synthetic merge commits; target movement invalidates PR progression
Pull-request target External Explicit operator action may push only to an existing GitHub origin
Brokered GitHub merge reads Enforced One fixed gh pr view action is stage-scoped, parameter-validated, bounded, timed, and unable to express a mutation
Broker credential environment Enforced BOBSLED_GITHUB_TOKEN is mapped only into the broker child and inherited GitHub token variables are excluded
Runner credential filesystem isolation Advisory Stored GitHub CLI credentials in the user's home remain visible without an independent OS boundary
Local deployment identity Enforced A receipt binds process boot commit, clean default-branch HEAD, and ancestry of GitHub's reported merge commit
Governed change-detail scope Enforced Read routes accept recorded change and record IDs only; caller-selected paths, directory discovery, and cross-change records are unavailable
Record containment and integrity Enforced The server uses canonical parent containment, no-follow opens, opened-file identity checks, regular-file and size gates, and raw artifact digest verification before serving
Served-content redaction Enforced Complete UTF-8 text is redacted for configured secrets, credential patterns, assignments, and host paths before line truncation; unredactable bytes are refused
Inert governed rendering Enforced Markdown, diffs, and evidence become a closed React element set without governed URLs, resources, handlers, styles, or raw HTML
Detail-page operator identity Advisory Loopback origin checks and record scope are enforced, but the local operator role is not authenticated

Child-process environment

Agent adapters receive a small shared environment plus only the selected provider's credential namespace. The control plane never returns credentials to the UI or stores them in the ledger. CLIs may still use their normal credential files in the user's home directory. The merge broker accepts a dedicated BOBSLED_GITHUB_TOKEN, maps it to GH_TOKEN only inside its fixed child process, and filters inherited GH_TOKEN, GITHUB_TOKEN, GH_ENTERPRISE_TOKEN, and GITHUB_ENTERPRISE_TOKEN. Before any broker output is retained, returned, or digested, the resolved credential and recognized GitHub token patterns are replaced with [redacted].

Configured verification receives a synthetic home, no provider credential namespace, loopback-only worker networking, and worktree-only writes on macOS. Sandbox policy self-tests run in the primary suite; macOS does not permit applying a second Seatbelt profile from inside the already-sandboxed configured-verification process, so those tests are marked as nested skips there rather than treating exit 71 as a security assertion.

Raw model output may itself contain sensitive repository data. Runtime logs are created mode 0600, ignored by Git, and must be handled as confidential. The change-detail surface redacts eligible text at its serving boundary; it does not rewrite or normalize retained logs. Direct filesystem access to those mode-0600 records remains inside the local trust boundary.

The application CSP is defense in depth around inert rendering. The delivered policy intentionally permits 'unsafe-inline' for Next.js bootstrap scripts and generated application styles; the production build currently depends on those exceptions. It still confines scripts, styles, images, and connections to the local application, disables objects and form actions, and denies framing and base-URL changes. Governed content does not rely on those exceptions: it is rendered as a closed set of React text elements with no content-derived URL, style, script, or event-handler properties.

Threats still open

  • A malicious local process can call the loopback API or impersonate an actor.
  • A compromised runner executable can ignore its CLI permission flags; worktree diff admission limits what Bobsled accepts but is not a general process jail.
  • Read-only agents may observe more local data than intended without an OS sandbox.
  • Invalid ledger history causes a fail-closed startup outage until an operator diagnoses and resolves it outside Bobsled; there is intentionally no automatic repair or valid-prefix recovery path.
  • The approval inbox does not authenticate the asserted local role. Operators must inspect the linked read-only change detail before deciding; opening that page has no judgment or transition authority.
  • Verification fails closed on platforms where Bobsled has no supported OS sandbox profile.
  • Git commit checks do not prove installed dependency state for a local deployment.
  • Squash/rebase content equivalence is not independently reconstructed beyond the approved pull-request head and GitHub-reported merge commit binding.
  • Artifact recording still performs lexical containment rather than write-side realpath enforcement. Change-detail independently applies canonical containment and no-follow reads, but does not repair that existing admission boundary.
  • Evidence outputDigest is receipt metadata, not necessarily the retained log file digest. Change detail labels a log's separately observed digest as a read observation rather than approval-grade verification.
  • Binary, invalid-UTF-8, oversized, and digest-mismatched governed bytes are deliberately unavailable through the browser. The UI exposes the refusal state and identity without serving a plausible substitute.
  • npm run dev:control uses hot reload and narrows the boot-commit claim; governed deployment verification requires a process started with npm run control after the checkout is upgraded.

Use mutating execution only with providers you trust, inspect retained diffs and evidence before approval, and do not expose port 4242 to a network interface.

Reporting

Record security findings as a governed Bobsled change. Include the affected control, evidence, expected invariant, and whether the issue can cross the loopback or repository boundary. Do not place credentials or sensitive raw runner logs in Git.