Skip to content

Security: All-The-Vibes/ecorp

Security

docs/SECURITY.md

Security

Current status

The current implementation has production authentication, workload identity, and scoped secret boundaries plus durable artifact storage, but it is not yet suitable for fully untrusted child processes until stronger OS/container isolation lands.

Known development-only shortcuts:

  • fixed demo identities
  • permissive CORS
  • fake process runs with the local user's permissions
  • no network sandbox

These gaps are tracked in ECorp Build GitHub Project #3 and linked issues, not in docs/BACKLOG.md, which is historical seed material only. They are not production claims.

Claude Code is launched in safe mode without user plugins, hooks, MCP servers, browser integration, slash commands, or auto-memory. OpenCode uses its plugin-free --pure mode. These controls prevent the customization leakage observed during the September 1, 2026 enterprise dogfood pass, but they are only a partial boundary. Claude's supported stdio permission control protocol, manual permission mode, and required initialize handshake now translate tool requests to durable ECorp approvals without permission bypass, terminal scraping, or custom stdin messages. GitHub issue #51 still tracks isolated provider homes, inherited-environment allowlisting, stronger container isolation, and remaining real-provider recovery drills.

Production mode validates OIDC bearer tokens against the configured issuer's UserInfo endpoint and maps (issuer, subject) to a Corp-local human actor. Claimed actor IDs cannot override that mapping. Development mode still enforces room membership in persistence, snapshots, writes, WebSocket replay, and live delivery. Eve is a deliberate non-member fixture used to prove that room-scoped missions, tasks, runs, messages, and events are not returned.

Runner nodes require one-time enrollment followed by rotating, expiring workload credentials. Only credential hashes are stored. Replayed, expired, unknown, and revoked credentials are denied.

Secrets are encrypted with ChaCha20-Poly1305 and authenticated associated data. The broker checks actor, task, run, runner, tool, resource, and expiry scope before dispatch. Events and snapshots contain grant metadata only. Environment injection is labeled reduced assurance. Production requires a private 32-byte master key and rejects the public built-in development key, including equivalent case and surrounding-whitespace representations, before database or storage initialization. Development retains its fixture key for local compatibility; it is not a deployment credential. The example environment leaves this setting empty. Expired grants are rejected before provider start, and the provider is stopped when the earliest active grant expires.

Risky action approvals are Corp-scoped, role-gated, expiring, and idempotent. Approval decisions transactionally enqueue durable runner commands. Commands remain pending until the runner acknowledges application, command IDs fence duplicate delivery, and expiry automatically rejects the action and repairs run/task/mission/agent state.

An ordinary provider's finished outcome-review wait is not a live process claim. Runner-loss handling preserves only a matching Corp/run/task/mission review and current gate with a positive, same-room/mission termination receipt and no later teardown uncertainty. A pending tool action, stop/suspend, quarantine or provider-free mode cannot use that exception. Preservation changes no policy, spending, source or review decision and grants no checkpoint/recovery capability. The existing human role, membership, requester-exclusion and decision checks remain authoritative.

Dark-factory claims use a separate opaque fencing token plus a monotonic work-item version. Claim tokens are returned only to the authorized operator and are omitted from shared snapshots, events, prompts, logs, and artifacts. A GitHub label or Project status is never treated as an execution lock. Mission materialization requires an active lease and atomically persists the issue-to-mission link. The trusted CLI invokes GitHub CLI without putting its credential in an argument, log, mission contract, or agent environment. Expired reclaims must exactly match the original source and policy snapshots and cannot widen permissions or replace the claimed revision before materialization. GitHub owner and repository names are canonicalized before advisory locking and database lookup so case variants cannot bypass the one-work-item-per-Project-item boundary.

The persisted factory policy is enforced again during mission materialization. A later request cannot widen its repository, adapter, strategy, model, reasoning effort, tools, secrets, required prohibitions, write scope, token budget, or cost budget. Factory verified state requires authoritative completed mission and passing task-verification records. New claim cost admission uses the same strategy allocation as planning and rejects incompatible per-task costs before durable intake. Legacy cost-invalid claimed/blocked records with no mission can only reconcile to terminal failure through an exact source/policy reclaim by the current owner or, after expiry, another authorized operator. Connection authorization still applies. The CLI also checks the requested connection binding and source ref against the persisted policy before a legacy preview or reconciliation, repeating the checks after refresh without reading a checkout or pinning a source commit. Substituting or omitting a bound connection cannot authorize terminal reconciliation under the original connection. The transaction records the existing state-change audit and operation, releases the lease, and returns no execution token; historical policies, materialized graphs, spend, attempts and source pins are not rewritten. This is request-driven reconciliation, not a new sweeper or execution authority. An explicit max_task_attempts is also prospective, immutable Factory policy. The shared ceiling remains three; omitted legacy policy cannot newly authorize a third planned attempt. Preflight/materialization requests must preserve the recorded choice, including omission. Existing launch, resume, recovery and revision endpoints reject unsupported attempt settings. Contract revision decoding specifically rejects nested contract.max_task_attempts, including null, while preserving unrelated TaskContract decoding compatibility. No request resets attempts, spend, historical outcomes, or protected stops. Non-null policy model and reasoning settings are mandatory on every materialized task; omission is rejected rather than interpreted as permission to use a provider default. An optional saved execution-connection ID is part of the immutable Factory policy. Malformed/nil IDs and task-policy mismatches fail closed; an omitted legacy binding does not grant access to a bound native account. The exact connection lookup uses current Corp/room/operator authorization and emits only the shared DTO. Preflight and materialization reuse connection/source/readiness admission, and the native run/resume boundary keeps the persisted connection. Changing controller options cannot rebind an existing Factory lineage. Provider-backed factory outcomes require a manual verification gate; an artifact-only final result cannot become accepted completion without an authorized evidence decision. Internal handoffs retain their automated file, integrity, and content checks without an additional implicit human gate. Explicitly declared internal review gates are preserved. A deterministic terminal task still requires outcome review when it depends on provider-backed work. These rules apply when planning new work and never rewrite an existing run's persisted policy. Verifier rejection is persisted as verification_failed, not collapsed into an execution failure. Publication states cannot be asserted through the generic factory transition endpoint.

Guests and spectators do not receive factory work items in snapshots. Pre-materialization factory events omit GitHub source metadata; once a mission exists, factory events inherit its room visibility. The exact mission-context read retains Operate authorization and repeats human operator-role, Corp and mission-room membership checks in the same SQL statement as linkage lookup. It does not reveal hidden Factory context to guests or infer Direct from a denied/filtered query. Its minimal projection excludes claim tokens, policies, native sign-in details and credentials. This is an inspection surface, not publication or execution authority.

The controller's selected-Project-item lookup requires Corp Operate authorization and repeats the store-side human operator-role check before returning source or policy metadata. Requests are bounded to 1,000 Project item IDs of at most 160 characters and are deduplicated before the query. The normalized Project owner, positive Project number, source kind, Corp, and item IDs all scope the lookup, preventing an equal item ID in another Project from substituting its state or policy. Guests and spectators receive no lookup results, and claim tokens remain excluded from lookup responses. Claim and reclaim idempotency keys include the validated lease duration so a recovery request with a changed lease cannot collide with a persisted request containing another duration.

Controllers renew their fenced lease immediately before a GitHub mutation and again before launch. After each renewal they re-fetch and compare the Project item, issue revision, state, required label, and dependency eligibility. A changed or newly blocked source is durably blocked before the next effect. A final renewal follows each revalidation, and GitHub CLI subprocesses are killed on a bounded timeout below the effect lease. The controller resolves the source ref in a checkout whose GitHub remote matches the claimed repository, then persists the full 40- or 64-hex commit in policy. Factory tasks and run launch records carry repository, ref, and commit as one all-or-nothing source identity. The scheduler accepts only a runner advertising the same structured tuple, and the runner independently rejects both start and resume commands before worktree access if any element differs. Symbolic refs alone are never sufficient authority for factory write-capable routing. The browser also requires an operator to select and confirm one connected runner's structured repository, ref, and immutable commit before creating an ordinary mission. The server canonicalizes the selected tuple from live runner capabilities, applies it to every planned task, and rejects adapter, model, reasoning, repository, ref, or commit mismatch before mission persistence. Local repositories use a stable opaque local/<name>-<digest> identity rather than an absolute host path. Legacy API clients may omit source selection, but resume is always fenced to the source run's persisted workspace base commit.

Pre-commit factory records are migrated only from unambiguous persisted workspace evidence. Underivable legacy claims are not guessed or silently widened: migration marks them as requiring an upgrade, and only the active fenced operator may resolve and persist a new commit before any run exists. That dedicated idempotent operation is stored in factory_operations and emits factory.source_commit_pinned; it rejects stale tokens, stale versions, already-pinned policies, incompatible task contracts, and any mission that has already produced a run. New claim intake requires a valid immutable commit and cannot self-declare the migration-only upgrade marker. Recovery of a migration-marked record requires the controller's requested symbolic ref to exactly match the persisted policy ref before resolving or storing a commit.

Factory-wide pause, resume, and reconciliation controls require an owner, admin, or manager and an expected controller version plus idempotency key. Members may inspect controller health but cannot change Corp-wide intake. Controller heartbeats are fenced by a rotating connection epoch, bounded lease, service actor, and Corp. A stale process cannot extend the active lease or complete another process's reconciliation generation. Controller status never exposes GitHub credentials or work-item claim tokens.

External throttling does not grant authority. The trusted controller persists typed polling/backoff metadata under its current connection epoch; stale workers and other Corp/Project/repository work items cannot substitute state. Reconnect and operator control retain outstanding retry deadlines instead of bypassing upstream limits. Raw queries, headers, credentials and upstream bodies are not stored as quota telemetry. GraphQL errors or incomplete/drifting pagination are rejected before claims/effects; a discovery cache contains an identity hint, never source authorization.

Catch-up from a blocked factory item to verified repeats the existing server-side requirement for its original completed mission and passed task verification. A throttling response is not completion evidence. Local verification can finish independently, while remote effects still require current revalidation. No action permits merge/deployment implicitly.

Inline cockpit decisions do not weaken the underlying authorization boundary. Action approvals and verification reviews retain their existing role, expiry, requester-exclusion, and idempotency rules. Contextual comments remain ordinary durable room messages linked to the mission; comment text cannot approve an effect, create a task, or steer a provider session implicitly. Comment operation UUIDs are scoped to the Corp and exact normalized request; replay returns the existing message, while changed content under the same key is rejected.

Cockpit steering uses the existing single-holder agent lease and lease token. The token is checked before persistence but is never stored in the durable runner command. A separate client operation UUID prevents retry from creating another control message, and the runner command ID fences duplicate delivery across server reconnect. A monotonic lease version is persisted with the command and rechecked before dispatch, so renewal, release, transfer, or expiry cancels a pending stale steer. Negative runner acknowledgments also terminalize the command rather than leaving it pending indefinitely. A browser that lost its private token must reclaim the same actor's lease, which rotates the token and lease version before another steer. This does not add a second input path or convert a comment into provider control. External CLI failure details are collapsed to bounded single-line text before persistence so multi-line stderr cannot bypass the durable blocked transition.

Managed local Copilot processes receive --no-auto-update during catalog discovery, create, and resume. A selected SDK/runtime pair must not silently forward to a downloaded replacement. The current verified pair is Rust SDK 1.0.11 with CLI 1.0.79; the adapter checks the connected runtime version before exposing models or starting a session. An incompatible runtime is rejected before provider-backed work, including an explicit CLI or remote-runtime override. This version check is a compatibility gate, not proof of operating-system isolation or a substitute for the filesystem, approval, environment, and verifier boundaries.

Windows ordinary and extended drive/UNC spellings compare by their drive or server/share identity before the existing component-by-component boundary check. This does not canonicalize an untrusted path with ambient filesystem access or accept another drive, share, sibling prefix, device namespace, traversal, Git control path, or alternate data stream. Actual I/O still uses the retained directory capabilities and the existing link/opened-file checks.

GitHub Copilot receives a native SDK managed-settings layer on every create and resume. The runtime is told to disable bypass-permissions mode. Native read and write tools may proceed without a second ECorp prompt only because built-in filesystem operations are routed through an ECorp SessionFsProvider that rejects paths outside the worktree and isolated Copilot state directory. The provider retains capability-directory handles for both trusted roots and performs every filesystem operation relative to those handles. It rejects dangling and existing symbolic links, multiply-linked regular files, Git control paths, and Windows device/alternate-stream aliases. It checks an opened file before truncating or appending, so a hard link cannot modify a file in the source checkout. Native mutations also enforce the run's persisted task write scope; a directory-create may create only a granted path or an ancestor needed to reach it. This authority is rebuilt from the current task on resume. Ordinary Unix executable permission bits are honored; privilege bits and invalid modes are rejected before creating a file. The SDK-registered ecorp_mkdir tool accepts a worktree-relative path or an absolute path contained in the assigned worktree and invokes that same scoped provider. Unlike native SDK filesystem requests, this tool has no authority over the isolated state directory. It rejects root, traversal, Git-internal, link, and out-of-scope targets. It is idempotent, cannot delete or chmod, and never starts a command interpreter. Its finite directory-creation authority comes from the persisted run contract; the SDK tool declaration suppresses a redundant permission prompt for this one guarded primitive, not for shell or other tools. Every shell command remains an explicit ask. Command-family allowlists are not a filesystem boundary: interpreters, build tools, and PowerShell commands can write arbitrary host paths when the native sandbox is unavailable. Persisted runner verifier policies execute required build and test commands outside the model session. Common ambient credential environment variables are removed from the Copilot child process; explicit scoped SDK authentication remains available.

September 18, 2026 correction: the pinned Rust SDK 1.0.11 / CLI 1.0.79 path does not establish a native operating-system command, filesystem, or network sandbox. A zero-inference probe using ECorp's production ContainedSessionFs, session configuration, managed permissions, directory tool, and launch flags reported command sandboxing disabled and no native policy in force, even with restrictive account settings. This is configuration evidence; it does not replace the filesystem and permission-boundary tests. The launch flags remain requests to the runtime, not proof that credential stores, the user profile, network, or temporary directories are inaccessible to an approved shell command.

The adapter no longer writes per-worktree policy through account-wide user.settings.set. Those writes affected other Copilot clients sharing the native home without establishing the claimed session sandbox. Native home, authentication, and session history remain in place; the adapter does not reset existing account settings. The capability-scoped filesystem, managed permissions, durable approvals, environment filtering, and verifier policy remain the execution boundaries. Earlier evidence is retained with its original scope and limitations.

The ECorp handler remains a fail-closed backstop for unresolved requests. It automatically approves only native writes inside the assigned worktree, native reads it can prove are worktree-scoped, and reads from the SDK state directory isolated to that worktree. It canonicalizes existing ancestors without treating a dangling link or inaccessible path as absent. The native filesystem provider independently enforces the narrower task write scope. No shell spelling or provider readOnly flag is interpreted as an ECorp automatic grant. External paths, network URLs, sandbox bypass, managed-policy approvals, shell effects, and ambiguous requests suspend durably. Shell approval cards include the bounded command text rather than only a generic action label. Arbitrary shell can become approval-free only behind a separately verified operating-system isolation boundary such as a restricted identity with ACLs, AppContainer, container, or VM.

Claude permission requests use the same fail-closed worktree containment helper. Only recognized read/write tools with an explicit contained path can be allowed locally. Existing ancestors are canonicalized, so symlink escapes, traversal, unavailable worktree roots, blocked paths, Bash, network tools, unknown tools, and malformed or oversized control frames cannot be auto-approved. Suspended approval context preserves the request ID, tool-use ID, tool name, blocked path, decision reason, title, display name, and description within durable event bounds. Raw structured input, including Write/Edit content and secret-shaped values, remains only in process memory; durable approval text records bounded field metadata and SHA-256 hashes. Approval returns the exact original input only to the correlated provider request. Rejection and expiry return a bounded denial; duplicate, unknown, cancelled, or mismatched requests fail closed, and a failed control-response write fails the provider run.

External CLI provider roots are owned as complete process scopes rather than supervised as single PIDs. Windows uses a private kill-on-close Job Object assigned while the provider is still suspended. If assignment or resume setup fails, the suspended child remains under cleanup supervision until its death is verified. Every terminal path invokes the same bounded terminate-and-verify attempt and retries without dropping ownership when verification is uncertain. The adapter does not return until the provider root is reaped and the owned scope reports no active descendants. This prevents provider tools, plugin processes, or grandchildren from surviving an interrupt while ECorp reports provider_process_alive=false. Unix external CLI adapters are disabled because setsid or setpgid alone cannot prevent descendants from escaping the owned scope. Timed-out availability probes retain one shared cleanup guardian per adapter and reject duplicate probes until that guardian finishes, preventing unbounded detached cleanup tasks. This is host-process containment, not a network or filesystem sandbox.

Mission descriptions, task contracts, and verifier policies are authority-bearing records. Creation validates their bounds before persistence, and every revision stores both prior and replacement values rather than rewriting history invisibly. Revision idempotency is scoped by Corp and exact normalized request. New operations and replay require current mission-room membership; the mission and task are locked while membership, actor role, active runs, expected version, and mission budget are rechecked.

Pre-dispatch redispatch revisions are rejected after any run exists. resume revisions require the latest terminal preserved provider/worktree checkpoint and reject any lineage that reached a hard stop. Resume cannot change source identity, secret references, model, reasoning, budget, or deliverable authority; it cannot widen tools or write paths or remove a prohibition. The revision does not itself dispatch, preventing a contract mutation from implicitly authorizing execution.

A saved ready mission is held until an explicit authenticated launch. Automatic scheduling cannot create its first run, including from a stale candidate list: the run-creation transaction rechecks admission while locking the mission, task and agent. Explicit admission and replay also retain the operator's current Corp role and room membership through their transactions. The first actor-attributed run.requested event identifies the admission transition. Replay requires positive persisted runner-start evidence and cannot turn a dispatch_not_started failure or a terminal failed/cancelled mission into a successful launch response. Normal dependency and retry scheduling remains confined to already-running missions; this adds no tool approval or wider provider authority.

Budget policies constrain run, mission, requester, and Corp usage. Repeated tools and explicit no-progress events feed an auditable circuit breaker; ordinary human conversation does not.

Mission budget recovery is a dedicated owner/admin operation, not a resume parameter. Original limits and consumed usage are never reset. Proposal and decision requests are Corp-scoped, role-gated, versioned, and exactly idempotent; approval rechecks the current mission limits and usage under lock before changing authority. Current mission-room membership is required for new operations and idempotent replay, with the membership row protected against concurrent removal.

Finish-scope recovery cannot widen the task budget, write boundary, or verifier policy. Resume scope paths reject absolute paths, traversal components, drive prefixes, backslashes, and unsupported wildcard shapes before containment. The finish task must be the latest suspended task, and approval fails if its contract or verifier policy changed after proposal.

Resume rejects exhausted mission, requester rolling, or Corp rolling authority before provider dispatch and clamps the new run to all remaining limits. It accepts only the latest run in a serialized provider-workspace lineage; any stop in that lineage permanently fences its ancestors. Shared snapshots expose revision rationale and decisions for audit but no runner assignment or fencing token. Browser-generated proposal and decision keys remain stable across a lost response so retry replays one committed operation.

Budget-stopped source may instead receive an explicit checkpoint_verification operation in the existing Factory recovery aggregate. This is not provider resume. Admission validates the native termination and checkpoint journal records against the exact assignment, source, current evidence policy, and latest preserved lineage. The replacement is provider-free and has zero model-token and model-cost allocation; reporting model usage from any verifier-only run is rejected. Exhausted model-usage counters do not veto that exact authorized operation, but they are never reset or reallocated. A bare zero limit or changed execution-mode flag grants no exemption. Explicit stop, loop and quarantine checks, current room/role authority, independent review, and the separate publication/merge/deployment boundaries remain. Missing artifact evidence cannot be manufactured or dropped from the persisted verifier policy.

Checkpoint-cancellation reconciliation is not a generic terminal-state override. It requires current owner/admin/manager authority and mission-room membership, holds the actor row through commit, and reuses the native source, lineage, budget and Factory/publication locks. The exact current cancellation event must match its original controller transition operation, claim, actor, mission and version; later events, explicit stops, policy cancellations and quarantined lineage remain ineligible. A controller operation-key pattern identifies provenance, not authentication. Reconciliation grants no claim token, provider execution or publication right, and replay repeats current authorization. It does not rewrite original runs, task contracts, source evidence, attempts or accounting.

Provider source-correction after failed checkpoint verification has separate, versioned origin provenance. It never receives a checkpoint-verification model-budget exemption. Admission requires the exact native failed verifier and a current explicit resume contract revision, and can recognize only its original measured suspend—never stop, an explicit stop request, another suspension, a loop failure or quarantined lineage. Original proof is checked against immutable historical policy, separately from the authorized replacement. Original usage and attempts remain; a correction consumes a normal provider attempt and positive remaining allocation.

The pending correction command repeats current actor/room, Factory, assignment, source, policy, saved connection and remaining-budget checks before and after secret resolution. Native mutation gates are acquired before row waits, and command state is re-read before commit. Unavailable database reads do not turn into a failed provider attempt. Missing or changed correction-origin provenance denies replay and publication through ordinary errors; it does not authorize a fresh provider, artifact, merge or deployment.

Historical native verifier seals may omit a redundant head_commit only when the source is verification-only and its fingerprint exactly matches the valid authorized verifier guard. The retained authorized HEAD and independently reconstructed checkpoint authority remain mandatory. When a HEAD is required, explicit null, malformed or contradictory values still fail; provider sources cannot use this compatibility exception. No historical event or grant is rewritten.

Checkpoint retention distinguishes the original authorization HEAD from an exact runner-owned verification commit. Ready-artifact joins bind the new head to its run, producer, source, policy and verification digest; these relational checks are not fresh signature/byte validation. Same-run checkpoint re-attestation rechecks current role/room and source authority and cannot start a provider, alter budgets, or overwrite another assignment. Definite authorization denial retires only its command; database failures remain retryable. Outcome review and publication retain their separate authorization and integrity checks.

Artifact uploads are server-mediated and size-bounded. The server verifies byte count, digest, and declared media type in memory, then performs authoritative assignment, task, budget, and breaker checks while reserving the event-specific staging key in Postgres. No object bytes are written before that reservation commits. Finalization is idempotent, transient failures remain recoverable, old missing-object reservations are released for retry, and rejected or orphaned staging cleanup rechecks ownership before deletion and never deletes shared content-addressed bytes. The server signs provenance with a deployment key and never exposes backend bucket URLs. Downloads require ready metadata, Corp authorization, and room membership, then revalidate signature, retention, digest, length, and media type before returning an attachment with content sniffing disabled.

Verifier-only artifact transfer repeats authorization against the exact pending command, assigned runner, Corp, task, source run, current human author role, and mission-room membership. Only the ready provider_evidence artifact already linked to both runs can be transferred. Its signed object is validated before delivery, with a second authority/binding check after storage I/O. Transferred bytes are capped at 16 MiB, excluded from persisted commands, and omitted from typed debug output. The receiver enforces encoded and decoded bounds, length and SHA-256, and uses a fresh private staging name rather than the supplied path. Evidence staging is separate from the preserved source, sealed baseline, and per-command snapshots, so it cannot satisfy a missing source-file check. All snapshot cleanup must finish before accepted verification.

Prepared-workspace failures preserve or quarantine the workspace instead of leaving a false active disposition. A rejected fingerprint is not replaced by the newly observed bytes. Consumed recovery commands are not replayed, and terminal-target commands are retired without execution. Capability negotiation describes this transfer protocol; it is not OS-isolation attestation and does not relax source-path, budget, or hard-stop authority.

Portable source exports use a temporary Git index rooted in the assigned worktree. They include tracked changes and non-ignored untracked files, exclude provider evidence, and reject symbolic links, Git links, path escapes, runner-internal directories, ignored files, and secret-like names. The server signs the artifact role, filename, and exact deliverable metadata in addition to the content digest. A run cannot pass verification until the ready source object links the exact normalized verification digest to the exact exported-byte digest. The runner waits for durable storage acknowledgment before worktree cleanup.

Post-verification commit creation is fixed runner behavior on the isolated task branch. It does not authorize credential use, branch publication, pull-request creation, auto-merge, merge, or deploy. Commit/branch bundles use a unique temporary ref created only after the workspace branch is verified against the generated head, then delete it after bundle creation. A detached worktree HEAD cannot redirect or empty the bundle.

Pull-request publication runs only in the trusted publisher CLI after a dedicated server authorization check. GitHub credentials stay in the publisher's keyring or process environment; they are never returned by the server, passed to the runner or producing agent, written into the portable bundle, persisted in authorization/provenance records, or included in command arguments. Publisher fencing tokens are opaque, expiring capabilities omitted from shared state and events. They are insufficient by themselves: each publication mutation also requires a separate enrolled publisher workload credential. Enrollment and revocation require owner/admin Manage authority; only credential hashes are stored. Corp, publisher ID, hash, expiry, and revocation are rechecked and row-locked inside the same transaction that starts, renews, fails, or checkpoints publication. The authenticated credential determines the publisher ID; a caller cannot choose another workload identity while presenting a valid credential.

Publication revalidates persisted verifier, deliverable, policy, budget, breaker, Corp, role, target, base, branch, and source-issue authority before acquiring an attempt. The publisher accepts only an exact signed Git bundle, never force-pushes a conflicting branch, refuses closed or auto-merge pull requests, and records the pull-request identity before changing Project status. Duplicate, restart, timeout, and external-success/local-failure recovery adopt only matching remote effects. The database constrains auto-merge, merge authorization, and deployment authorization to false. The publisher reads its requested work item, publication, and mission deliverables through an exact Corp-authorized context endpoint rather than trusting bounded shared snapshots. Exact publication status reads apply the same mission-room join; knowing a work-item UUID does not expose pull-request body, authorization reason/snapshot, publisher identity, or failure detail to another-room members. The context's initial work-item lookup applies that join too, so source issue metadata, factory policy, claim ownership, and work-item failure details are not disclosed.

Before every branch push, pull-request creation, and Project mutation, lease renewal transactionally rechecks the current attempt actor against its persisted role snapshot and reruns current run, mission, requester, Corp-budget, and hard-breaker authority while also reauthenticating the enrolled publisher workload credential. Pull-request adoption requires isCrossRepository = false, the target repository owner, and the exact verified head SHA. Remote HEAD is accepted only when its symbolic branch target exists and advertises the same object ID. GitHub PR commands receive that verified branch name rather than the literal HEAD, and the resolved PR base is retained separately from the authorized symbolic base. The publisher's mission-room membership is rechecked in the same transaction on new start, idempotent replay, expired-lease recovery, and every renewal. Removing a still-manager actor from the room therefore blocks branch, pull-request, and Project effects.

Failure-only checkpoints do not renew or advance publication authority. Their HTTP route still authenticates the human Corp identity, then requires the independently authenticated publisher credential and the exact attempt actor, token, version and unexpired lease in the store. This allows an owned attempt to record its failure after role, room or artifact authority changes. It does not grant publication permission, relax ordinary room-scoped reads, or accept a revoked publisher credential. All effect-advancing checkpoint variants still require Publish.

Checkpoint publication provenance uses schema 3. Previously absent schema-1/2 checkpoint proof can be reconstructed only after current native source, review and authority validation. Altered non-null legacy proof and missing or altered schema-3 proof reject; reconstruction is not a general missing-provenance bypass.

PR adoption also requires the exact authorized title and body. A collaborator-created PR with the right branch and SHA but altered content is ignored and cannot advance Project state. Publication retries remain pinned to the persisted deliverable ID even when the mission contains other merge-ready outputs. Pull-request URL validation requires canonical GitHub scheme, host, path shape, and number, while owner/repository path components compare case-insensitively to GitHub's canonical casing.

Publication branches pass git check-ref-format --branch before the durable start request; the server independently rejects invalid path components such as doubled separators and .lock suffixes. Default start keys hash the complete normalized request, including publisher identity, authorization reason, and lease duration, so changed recovery authority cannot reuse a request key whose persisted operation has different fields. CLI-provided titles and bodies are normalized with the same bounds and control-character rules as the server before plan comparison and idempotency. Repository owner/name inputs are trimmed, validated, and lowercased at the same boundary.

GitHub Project status is read from the exact stored Project item node ID. The publisher verifies the returned Project ID, owner, number, item ID, Status field ID, and field type before any mutation, so truncated item listings cannot turn a present item into an apparent disappearance. The Status field definition and option IDs come from an exact GraphQL lookup on the known Project node rather than the first page of field-list. Publication base policy is branch-only: the controller and store accept HEAD, short branch names, or refs/heads/*, while rejecting tags, remote-tracking refs, invalid branch shapes, and control characters before durable claim. When omitted, it derives from the selected source base ref rather than independently assuming HEAD or main.

The resolved PR base can never also be the publication head branch. A read-only remote preflight rejects that configuration before the server persists a publication, and the guard repeats before push, so publication cannot mutate the default branch as a substitute for merge authorization. Generated authorization IDs remain stable across restart and duplicate invocation. A different recovery actor receives a new actor-bound ID rather than inheriting the first actor's grant. Local body-file normalization matches server canonicalization so idempotency cannot fail on CRLF or a trailing newline. The CLI reads the publisher credential from a file before its first publication API mutation and never places it in arguments, plans, output, events, snapshots, or durable publication records.

Publication of an explicitly recovered checkpoint starts no provider. Its model-budget exception is derived from exact native origin events, immutable checkpoint authority, completed zero-provider recovery, retained fingerprint and the ready verified export. The authorization never resets spending or permits another model session; unrelated enforced stops, explicit stops, loops and current scoped authority remain guards. New publication checkpoints repeat current authority/provenance validation instead of relying on a live lease alone. Metadata bindings do not replace the publisher's signed-byte and Git-bundle validation.

A retry of a durable published result performs no remote preflight or external effect. It returns the persisted PR identity even if the PR was later merged or the base branch advanced. For an unfinished publication, the publisher renews authority before remote reads, refreshes the exact Project item, and re-fetches the checkpointed PR. It then renews authority a second time immediately before Project mutation. Another PR check and authority renewal precede completion, preventing a stale publisher or a closed, retargeted, edited, or auto-merge-enabled PR from advancing or being recorded as reviewed.

Required production boundaries

Saved execution connections add a room-scoped resource boundary, not a remote shell API. Setup requests are typed and actor-authorized; local checkout imports also remain inside operator-configured roots. Private native state is outside source repositories and agent worktrees. Provider sign-in instructions and repository catalogues are visible only to the requesting actor, while shared connection status uses fixed non-sensitive copy.

Personal native GitHub configuration does not constitute a separate OS identity. Native keychain/file-storage behavior and environment delivery retain their reported assurance; setup does not claim stronger containment than the selected harness and operating system provide. One-time Claude authorization-code input is transient, prompt-ID bound, and may reach only its matching native login process—not an agent session or a general stdin/shell endpoint.

  • Every persistent object is scoped to a Corp.
  • Authorization runs before reads, writes, and subscriptions.
  • Human identity uses OIDC and passkeys.
  • Agent and runner identities are independent and revocable.
  • The server does not execute untrusted shell commands.
  • Runners connect outbound and receive scoped assignments.
  • Long-lived secrets never enter prompts, logs, command arguments, or agent-readable files.
  • Irreversible effects require authorization and idempotency.
  • GitHub Project status, pull-request publication, merge, and deployment remain separate effects; no factory claim implicitly authorizes a later effect.
  • Artifacts are content-hashed.
  • Agent control uses rotating fencing tokens; stale tokens are rejected.
  • Lease tokens are returned only when the current controller explicitly claims or renews control; they are omitted from shared snapshots, transfers, and event payloads.
  • Emergency stop is role-gated and audited.
  • Runner connections use epochs, and run assignments use independent private fencing tokens.
  • Assignment tokens are omitted from shared snapshots and event payloads.
  • Every runner event must match both the current connection epoch and the stored assignment token.
  • A stale runner cannot turn a lost run back into an active or cancelled run.

Reporting

Until private vulnerability reporting is enabled on the GitHub repository, report security issues directly to the repository owner rather than opening a public issue with exploit details.

There aren't any published security advisories