Skip to content

Security: jiggy/jig

Security

SECURITY.md

Security

Jig's direct-run alpha treats admitted project and FLOW package code as untrusted. Its Linux host uses rootless cgroup-v2 and Bubblewrap containment; qualified native Macs use an unprivileged sandbox, owned process coalition, and private bounded storage. Neither path requires an installed privileged helper or weakens containment when its mechanism is unavailable.

What the boundary protects

An admitted Flow receives only its exact execution package, fixed runtime support, explicitly supplied root attachments, private scratch space, and the Run/0 channel. Linux uses private process and network namespaces; Mac uses a restrictive native sandbox and selected network policy. The Flow does not receive the project tree, host environment, ambient PATH, unrestricted host process or network access, general host devices, inherited descriptors, or Jig's control channel. Trusted launchers exclude unselected descriptors before entering the sandbox and before launching package code, including access through visible parent processes. Missing enforcement refuses execution.

Linux applies aggregate CPU, memory, and process limits before package code can execute. Mac establishes an exclusive process coalition before admission and supervises its resource use; samples and termination are not hard kernel quotas and can overshoot. Both paths fence owned descendants and confirm cleanup before reporting completion.

The independent supervisor observes kernel core-dump state within its owned cgroup and fences a detected crash without waiting for the Run deadline. It preserves the actual termination signal rather than inferring the original fault from stderr. This does not disable the host's core collector or promise that no core data reaches it. Unconfirmed fencing or cleanup remains a failure.

Project evaluation, the fixed dependency installer, HTTP request worker, and Agent provider worker use the same containment mechanism in separate scopes. The preparation worker inherits networking only during jig review; supplied locks are validated before the fixed installer's first fetch. Without an authored lock, --allow-resolution-network explicitly permits dependency- selected requests before graph validation, including private-network and loopback destinations reachable from the host. This is not egress filtering. Failed or declined review cannot undo requests already made. The permission lasts only for that review; --yes is separate Run-admission approval. Known unsupported root declarations are rejected first; the resolved graph must pass the usual source/integrity policy before frozen installation and admission. Bun sees only captured manifests, the lock, and bounded declared workspace-root patch files during installation, not other authored configuration or foreign lockfiles. Patch declarations and bytes are captured, checked against the lock, and retained with the prepared installation; Bun owns their application inside the same script-disabled scope. An ordinary API-backed Agent prepares its request as data and uses an exact reviewed HTTP grant; the separate HTTP owner holds the credential and network authority. Native Codex, Claude Code, or Pi clients inherit networking only inside their admitted ACP execution scope. Their executable, authentication and launch policy remain operator configuration. Jig supplies no default model.

The selected native-client scope receives its bounded credential projection through a transient private channel. Instructions and selected Skill text arrive through the ordinary Agent Flow's admitted resource call. A native ACP client starts with an empty work directory. Jig provides it no filesystem, terminal, or MCP client capability, no MCP servers, and no persistent permission. Claude and Pi profiles disable their tools; Codex uses its pinned constrained workspace mode with permission requests refused. Linux additionally supplies managed restrictions; subscription authentication stays in memory with ephemeral storage, rather than in a tool-readable file. On qualified Intel build 23E224, Codex alone may read system preference change counters and contact exact preference services, without permission to read or write preference values. A fresh trusted observation refuses configured managed policy or unavailable synchronization before selection and each native launch. It does not continuously monitor later policy changes; other OS profiles require separate qualification. This is not a universal claim that native tools are disabled. Rosetta and Apple Silicon are refused for this Codex path. The trusted observer refuses inherited sandbox uncertainty and retains its deadline after parent loss. Profiles also restrict extensions, plugins and native Skills as specified in Finite ACP. Secrets never enter Flow environments, launch arguments, Plans, locks, or retained Run state. Instructions and selected Skills are explicit caller data, not secret authority or host-attested provenance. Non-secret native client, model, and exact support identities are reviewed as provider identity. Jig exposes no public provider SPI or registry.

The HTTP Request worker receives one reviewed exact URL/method grant and its optional bearer via private stdin. It alone has network access for that request; Flow code gets neither the credential nor sockets. No authored imports, redirects, proxies, arbitrary headers or automatic retries run in that worker. Request and response sizes and deadlines are bounded, and optional operator-owned Schema/0 validates the JSON request body. Public grant policy is review-pinned; secrets are excluded from identities and retained records. Revocation stops new commands; cancel existing commands to stop their snapshotted authority. Remote effects already accepted cannot be recalled. HTTP status is evidence, not domain success. Exact HTTPS URLs use normal TLS verification and host DNS; this is not an IP firewall, and selected private services are permitted. Numeric-loopback HTTP is also supported explicitly. The selected service must be trusted with its token: literal echoes reject, but encoded disclosure is not generally detectable. See HTTP Request for the complete boundary.

Authored package code and lifecycle scripts never execute during preparation, and every Flow Run remains offline.

The bundled Markdown interpreter runs inside that same keyless Flow envelope. It selects only captured original recipes and calls an admitted ordinary Agent through the reserved markdown-agent route. Model decisions, resource content and allowed-tools text cannot create routes, credentials or host permissions. Raw resource reads are limited to captured package bytes. Reasoning requests carry complete bounded context; overflow fails instead of silently dropping instructions. Markdown resources do not trigger dependency installation.

Project Command effects execute supplied immutable text using installed Bun and reviewed Binding invocations in their own keyless, offline scopes. Their collector stays outside candidate execution. Candidates see no Flow package, host repository, credentials, or writable input controls. Collected output and termination do not prove that repository tests ran honestly: candidate code can interfere with their runner. Independent application assertions must inspect captured behavior without importing candidate code.

Supported trust boundary

Local browser inspection

Operator-selected --display web runs a command-owned read-only HTTP display on numeric IPv4 loopback, after execution admission or valid saved-packet capture. It is separate from the outbound HTTP Request grant and gives Flows no network or browser authority. Fixed bundled assets render validated semantic declarations and host observations as inert text. They do not execute artifact HTML, SVG, scripts or Markdown HTML. Browser controls can close presentation, not approve, retry, cancel or apply work.

A random session capability in the initial URL fragment authorizes observation, artifact and close requests. The browser removes the fragment and keeps the capability in memory, without cookies or storage; reloading requires the original terminal link. Possession delegates access to this command's observations and presentation closure. Never include that link in result packets, diagnostics, model journals or request logs. Exact parser-visible Host/Origin checks, present fetch-site checks, no permissive CORS and restrictive CSP supplement authorization; loopback binding alone is not access control. Native request parsing can normalize the original request target before the handler observes it.

Artifact endpoints resolve only opaque identities in verified immutable captures, never arbitrary paths, project roots or mutable output destinations. Live inspection transfers bounded excerpts and retires delivery/checkpoint/native authority under one finite completion allowance before waiting indefinitely. Lost retirement acknowledgment or uncertain cleanup closes inspection and retains known execution/delivery facts with a separate command-cleanup failure. Saved inspection checks file consistency without authenticating recorded report claims.

Application limits bound snapshots, client counts, stream queues and preview suppliers after native parsing. They do not enforce a hard process memory/socket quota on headers, ingress buffers or native copies before the handler. Local traffic can exhaust availability or lose the CLI coordinator; normal independent execution fencing and cleanup remain necessary. This boundary does not defend against a compromised operator account, browser or trusted runtime. See the complete local display contract.

Installation verification

The operator controls installation change detection with --verification cached|strict|fast on review, Run and inspection. The argument overrides the JIG_VERIFICATION environment preference. The default, cached, reuses SHA-256 digests while the selected canonical path and filesystem identity/metadata match, including inode, permissions, size, and nanosecond modification/change times. strict hashes current bytes at every verification boundary and bypasses the cache. fast reuses stored digests without checking freshness; changed tool bytes at the same path can retain an old reviewed identity. Cache misses require hashing in every mode that uses the cache. These modes are an explicit performance/integrity tradeoff, not equivalent byte-integrity guarantees.

The disposable cache is operator-private, outside project source, and stores only installation paths, metadata and digests. Unsafe or invalid caches fall back to fresh hashes. The policy never caches approval authority or changes verification of captured packages, retained artifacts or invocation files. All modes retain executable eligibility, sandbox enforcement, credential and permission checks, limits, recovery, cancellation and cleanup. See installation verification for the exact contract.

The alpha does not defend against:

  • compromise of the host kernel, Linux systemd/Bubblewrap/cgroup v2, Mac sandbox/launchd/disk-image facilities, or Jig's fixed Bun runtime and trusted support files;
  • the host administrator or another malicious process running as the same operating-system user;
  • physical access or compromise outside the supported host; or
  • denial of service within the documented resource ceilings or through bounded content-addressed storage retained while a project is reviewed.

An unsupported host or missing containment capability fails closed. Linux does not replace cgroup-v2 ownership with ulimit, per-process accounting, process-group killing, or /proc polling. Mac's coalition ownership and supervised limits have a distinct, explicit overshoot tradeoff.

Alpha ceilings

The table gives hard aggregate CPU, memory, and process ceilings on Linux. Mac applies the same requested values through supervised sampling and termination; short overshoot is possible. Wall-clock deadlines and confirmed descendant fencing still apply on both hosts.

Operation Wall clock Aggregate memory Aggregate PIDs CPU quota
Each Flow execution scope root deadline, at most 24 hours 256 MiB 64 50% of one CPU
Each Agent provider scope parent's remaining root deadline 256 MiB 128 50% of one CPU
Each project-command scope 10 seconds, within its parent deadline 256 MiB 64 50% of one CPU
Project evaluation Linux: 3 seconds; Mac: 10 seconds 256 MiB 64 50% of one CPU
One dependency resolution and preparation 60 seconds 512 MiB 64 one CPU

Root Runs default to 30 seconds. The trusted CLI caller may select a positive integer duration with --timeout, using ms, s, m, or h, up to 24 hours. This is invocation policy, not FLOW metadata, project configuration, or Flow-controlled authority. The deadline starts when the root Run is accepted; project acquisition happens before it, and mandatory fencing and cleanup may finish afterward. The memory, PID, and CPU ceilings in the table are fixed.

A Binding child Flow or effect runs in a separate scope while its parent remains live. The root permits two sibling Flows or one exclusive effect; each child permits one Flow or effect. The longest admitted branch is at most five child Flow levels, reduced when another branch reserves capacity. Before dispatch, durable root ownership reserves the longest admitted Flow path in each branch, including its largest possible effect. Two simultaneous two-level branches fit, each with its own effect. Reservations last through confirmed fencing and cleanup, even after execution fails.

At most seven payload/provider envelopes fit the fixed root aggregate ceiling: for example, the root, four child Flows and two effects, or the root, five child Flows and one effect. The budget is 1,792 MiB memory, 576 tasks, and 3.5 CPU cores (100 ms quota period). The table's individual Linux kernel ceilings stay unchanged and their reserved sum cannot exceed that budget. On Mac, the same reservations bound admission but actual use can overshoot before termination. Unused reservations are not borrowed. Trusted coordinators and supervisors are outside this budget; this is not combined utilization accounting or fair-share scheduling. Every child's deadline is capped by the root deadline. The fixed reservation policy is part of the admitted launch identity; changing it requires review, not silent reuse of an earlier admission.

After bounded project capture, one jig review dependency-planning phase uses one 180-second cancellation deadline, performs at most 16 distinct dependency preparations, and accepts at most 256 MiB of prepared file content across them. Each contained preparation has the earlier 60-second hard deadline. Each accepts at most 4,096 source files and 16 MiB of source content, and produces at most 4,096 files and 32 MiB of prepared content.

The protected Package/0 store accepts at most 64 MiB per canonical artifact and 1 GiB per project. Review may retain content-addressed source and prepared artifacts before the user approves a Plan. Declined and superseded artifacts therefore continue to consume the same fixed cap; Jig does not silently garbage-collect review evidence in this alpha. Exact retained bytes remain reusable at the cap, while a review requiring any new artifact fails closed. This alpha has no selective reclamation command. Reclaiming space requires closing Jig and intentionally removing the project's protected .jig state, which also discards its local admission and Run history.

File capture and delivery

Root read attachments contain immutable captured bytes, not live host mounts. Capture accepts at most 8 MiB across 64 files; selectors avoid enumerating unselected subtrees. Linux descriptor-relative operations reject symlinks, multiply linked files, nested mounts and protected host-state aliases. Source and destination-parent filesystems must be ext4, XFS, Btrfs or tmpfs. This does not detect secrets in explicitly selected files or defeat a malicious host user.

Bindings may select read-only project-relative resource trees for capture during review. Their bytes and manifests are retained under the existing Package/0 store cap, including after declined review. They are not secret storage or live host mounts. Each Run revalidates and projects only its selected Binding's retained bytes, combined with per-run input under the same 64-file/8-MiB limit. Children borrow complete immutable read views only through reviewed maps from their immediate caller. Native provider scopes receive no attachment authority. Tool code receives no additional execution privilege: all of its behavior is available within the existing sandbox, without subcommand restrictions, network or credentials.

Each Flow has its own initially empty writable output. Linux uses a 16 MiB anonymous tmpfs; original held descriptors retain it beyond writer fencing. With writable child maps, the trusted launcher projects only its public subtree and closes privileged/control references before package execution. Hidden import stages share that original bounded tmpfs, outside the payload view. Payload writes are charged to its 256 MiB cgroup ceiling. Trusted import writes can be charged to the coordinator instead; the per-volume 16 MiB ceiling still applies. Removing a cgroup does not release retained pages before the last holder closes them. Mac uses the existing bounded 512 MiB per-Flow filesystem image, with output and denied import siblings on the original volume, under its supervised process limits. This is a finite storage allocation, not a Linux memory-accounting claim.

Final validation accepts at most 16 MiB of logical bytes, 64 singly linked regular files and 256 entries, including imported files and folder prefixes. It rejects oversized sparse files, links, special files and excess metadata. After accepted child results and writer fencing, bounded streaming copies go into inaccessible caller stages. Child/source/native/package ownership settles before the existing native caller owner arbitrates original deadlines, accepted cancellation and one no-replace rename. At most two stages belong to one caller; existing branch reservations still apply. Unconfirmed copy, commit or descriptor cleanup ends the caller and never releases its destination for retry. Recovery fences the caller without resuming its program.

Finite caps bound hostile allocation and trusted capture/export work. The 8 MiB input and 16 MiB output values are conservative host policy for small file jobs, not portable FLOW limits or benchmark-derived optimums. Changing them requires reviewing the complete retained-storage budget and runtime policy together; removing them is not a usability fallback.

At most six Flow envelopes coexist under the unchanged root reservation: one root and five children, with one effect allowance. A two-branch shape reserves an effect per branch and therefore admits fewer Flows. Whole immutable read aliases share the root's original <=64 captures/8 MiB; equal content alone does not authenticate a shared view. Each Flow materializes at most 8 MiB of unique read bytes (Linux input tmpfs ceiling 16 MiB). Linux's six output backings sum to at most 96 MiB, including private stages. Mac's six 512 MiB images sum to at most 3 GiB allocated storage, including scratch, input, public output and stages. There can be at most five live child import stages, since each owns one child edge; they do not allocate another volume or independent output allowance.

Existing Mac output collection may retain one <=16 MiB anonymous snapshot per Flow while its source volume retires, conservatively <=96 MiB across six Flows. Streaming capture and import borrow <=64 KiB per active stream rather than retaining whole-tree buffers. A root final packet may add <=16 MiB of file staging; accepted and replacing checkpoint aggregates can overlap, each with <=1 MiB of files and <=2 MiB canonical JSON under the existing checkpoint policy. The original <=8 MiB input captures, bounded capture buffers, protocol records, checkpoint JSON and existing command/provider evidence have their separate caps. These trusted coordinator/collector allocations are outside payload cgroups; the root payload ceiling is not combined host utilization accounting. No per-invocation source tree enters the retained Package/0 store. Only bounded request and terminal evidence survives command cleanup; the requested final packet is intentionally retained. An accepted root terminal, checkpoint or already published packet does not depend on a new child storage journal.

A project command accepts at most 256 KiB of text across 64 files, 16 KiB of stdin, and 32 arguments. Its collector retains the first 64 KiB of each pipe and drains the remainder; decoded invalid UTF-8 can use more bytes than the retained raw prefix. The command's named input tree uses the same bounded, read-only projection. These invocation bytes do not become installed code or retained package artifacts.

An explicit Codex retainSessions: true grant permits a separate private history store: at most sixteen available 8 MiB snapshots per project, with 24-hour logical expiry pruned on access. Only the owned, complete rollout for the qualified native version is collected from bounded anonymous output, after clean process exit and fencing. Credentials, native home directories, SQLite and logs are not copied. The collector rejects known credential bytes; this is not a detector for secrets explicitly supplied as conversation content. Retained history is sensitive local data protected by the admission store's filesystem/ownership checks, not encrypted storage or a secure-erasure promise. A current authorized caller atomically consumes an exact-scope reference before restoration; uncertain or interrupted work is never replayed. New references follow cleanup and store commit. See Finite ACP for the exact qualification ceiling.

The independent outer command owns output staging and removes it if its execution coordinator dies during copying. Flow code sees neither the host destination nor the delivery socket. On cancellation, the owner sends SIGTERM to its exact trusted child and allows at most 60 seconds for cooperative Run settlement and delivery before SIGKILL and reaping. The existing absolute command expiry can shorten that wait and retains a 250 ms escalation grace; later signals cannot extend either bound. Eligible interactive dashboards omit the default outer command expiry so settled read-only inspection can wait for the operator; explicit enclosing limits and bounded setup, Run execution and cleanup remain enforced. This changes no payload deadline or cancellation authority. The independent cgroup owner still fences the complete payload tree; the child signal is not a substitute for that boundary. Atomic no-replace publication creates private files and directories; it promises complete visibility, not power-loss durability. A published packet survives later cancellation or acknowledgement loss. Killing the whole host or both trusted owners is outside the coordinator- loss cleanup guarantee. After confirmed settlement, an interrupted command can publish its terminal record and previously accepted checkpoint, never unfinished final output files. No automatic replay or general artifact-recovery service is provided. Cleanup failures are surfaced rather than reported as zero residue.

Run Checkpoint is an optional, reviewed root-only effect with a writable output mapping. Its independent command owner retains one accepted aggregate (up to 2 MiB canonical JSON, including 64 UTF-8 files totaling 1 MiB) plus a bounded replacement. Protocol decoding, immutable snapshots, serialized replies, file buffers, and final staging can coexist; these trusted-owner allocations are separate from the Run's cgroup memory ceiling and remain bounded by the request and publication limits. At most 16 saves and one in-flight save are accepted; worker reservations are unchanged. No control socket, project descriptor or host output path enters package code. A retained record's identity is supplied by the host; application evidence is not certified by retention.

On coordinator loss, that owner opens one recovery-only coordinator for the bound project inode, epoch and Run. It must fence and release the complete tree before publishing saved bytes. Recovery never submits work, extends execution budgets or retries effects. Unconfirmed recovery delivers no saved files. Previously accepted bytes survive cancellation, not death of both owners or machine failure. Normal completion and interrupted progress remain distinct in the output packet.

Reporting a vulnerability

Do not open a public issue for a suspected vulnerability. Report it through GitHub's private vulnerability form. Include the affected version, supported-host details, reproduction steps, and the observed security impact. Never include secrets or third-party data.

There aren't any published security advisories