Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
ac64b5c
feat(sdk): isolate hosted Babysitter capability
khaliqgant Sep 22, 2026
532de19
fix(sdk): bind hosted Babysitter isolation to trusted loaders
khaliqgant Sep 22, 2026
670f59d
fix(sdk): await hosted capability outcomes
khaliqgant Sep 22, 2026
eda1ce8
fix(sdk): bind hosted authorities to one flow
khaliqgant Sep 22, 2026
30c2276
fix(sdk): preserve hosted adapter rejection
khaliqgant Sep 22, 2026
72fc056
fix(sdk): mount verified hosted artifact snapshot
khaliqgant Sep 22, 2026
238af2b
fix(sdk): bind hosted runtime generation
khaliqgant Sep 22, 2026
5a645f1
fix(sdk): snapshot hosted base generation
khaliqgant Sep 22, 2026
f7d2545
fix(sdk): attest hosted base dependencies
khaliqgant Sep 22, 2026
0f69d1b
test(sdk): refuse hosted base import escape
khaliqgant Sep 22, 2026
3bc3626
fix(sdk): pin hosted runtime identities
khaliqgant Sep 22, 2026
040a6ff
fix(sdk): hard-limit hosted extension memory
khaliqgant Sep 22, 2026
84a97b7
fix(sdk): bound hosted source reads
khaliqgant Sep 22, 2026
9d29580
refactor(sdk): remove obsolete hosted base brand
khaliqgant Sep 22, 2026
80de6b3
fix(sdk): bound sandbox memory across Node releases
khaliqgant Sep 22, 2026
672365c
fix(sdk): bound hosted source traversal
khaliqgant Sep 22, 2026
d5b561d
docs(evidence): record complete Node matrix output
khaliqgant Sep 22, 2026
5ae3ef2
fix(sdk): bound plugin store verification reads
khaliqgant Sep 22, 2026
7e3781a
fix(sdk): validate snapshotted delivery data
khaliqgant Sep 22, 2026
b975710
fix(sdk): bound hostile delivery traversal
khaliqgant Sep 22, 2026
7f7230e
fix(sdk): bound hosted declaration parsing
khaliqgant Sep 22, 2026
6a57998
fix(sdk): stream hosted JSON properties
khaliqgant Sep 22, 2026
6928730
fix(sdk): reject hostile array prototypes
khaliqgant Sep 22, 2026
080b875
fix(sdk): neutralize array serialization hooks
khaliqgant Sep 22, 2026
d2abc3f
fix(sdk): capture hosted byte intrinsics
khaliqgant Sep 22, 2026
1cf1b7c
fix(sdk): capture hosted authority intrinsics
khaliqgant Sep 22, 2026
81bf2a6
fix(sdk): capture hosted hash methods
khaliqgant Sep 22, 2026
4131f14
fix(sdk): bind hosted authority intrinsics
khaliqgant Sep 22, 2026
994c856
test(sdk): synchronize premature hosted completion
khaliqgant Sep 22, 2026
e0348e0
fix(sdk): capture hosted sandbox intrinsics
khaliqgant Sep 22, 2026
40eb094
fix(sdk): capture hosted adapter continuation
khaliqgant Sep 22, 2026
6c865a6
fix(sdk): capture hosted protocol promise
khaliqgant Sep 22, 2026
070e097
fix(sdk): capture hosted bound intrinsics
khaliqgant Sep 22, 2026
7bcea42
fix(sdk): bind hostile protocol operations
khaliqgant Sep 22, 2026
8b38362
fix(sdk): bind hosted descriptor reads
khaliqgant Sep 22, 2026
116f10f
fix(sdk): bind hosted filesystem authority
khaliqgant Sep 22, 2026
9c3eedd
test(sdk): track current surface fixture
khaliqgant Sep 22, 2026
723bf71
fix(sdk): close remaining hosted intrinsic seams
khaliqgant Sep 22, 2026
1edcc62
docs(evidence): record current-main isolation gate
khaliqgant Sep 22, 2026
b44df3c
fix(sdk): close hosted launch and traversal gaps
khaliqgant Sep 22, 2026
2639a4c
fix(sdk): harden hosted serialization envelopes
khaliqgant Sep 22, 2026
6f51118
fix(sdk): reject inherited hosted boundary behavior
khaliqgant Sep 22, 2026
40a6444
fix(sdk): stream immutable hosted sandbox bytes
khaliqgant Sep 22, 2026
25aa130
fix(sdk): snapshot hosted runtime declarations
khaliqgant Sep 22, 2026
fc8b34b
docs(evidence): record rebased isolation gate
khaliqgant Sep 22, 2026
4abe4e0
fix(sdk): bind hosted payload byte counts
khaliqgant Sep 22, 2026
a234e41
docs(evidence): record payload-boundary gate
khaliqgant Sep 22, 2026
bc36954
fix(sdk): pin hosted runtime versions at compile time
khaliqgant Sep 22, 2026
611df0b
docs(evidence): record standalone runtime fix
khaliqgant Sep 22, 2026
ae6ad50
fix(sdk): synchronize hosted runtime release pins
khaliqgant Sep 22, 2026
f48e4ca
docs(evidence): record v2.0.28 isolation gate
khaliqgant Sep 22, 2026
06a83ca
fix(sdk): embed hosted runtime versions
miyaontherelay Sep 22, 2026
d678570
docs(evidence): record embedded-version gate
khaliqgant Sep 22, 2026
9588733
fix(sdk): isolate hosted metadata parsing
khaliqgant Sep 22, 2026
e0bc480
docs(evidence): record hosted parser gate
khaliqgant Sep 22, 2026
b94f913
fix(sdk): capture hosted hash factory
khaliqgant Sep 22, 2026
50b88c6
docs(evidence): record hash factory gate
khaliqgant Sep 22, 2026
8a7cfbf
fix(sdk): block inherited hosted thenables
khaliqgant Sep 22, 2026
17ce74b
docs(evidence): record promise safety gate
khaliqgant Sep 22, 2026
61ac374
fix(sdk): preserve hosted intrinsic invariants
khaliqgant Sep 22, 2026
0c368af
docs(evidence): record hosted invariant gate
khaliqgant Sep 22, 2026
9b25039
fix(sdk): shield native fs promise values
miyaontherelay Sep 22, 2026
11b8cde
fix(sdk): seal hosted promise result prototypes
khaliqgant Sep 22, 2026
7dfe3e8
docs(evidence): record prototype safety gate
khaliqgant Sep 22, 2026
3c0c375
fix(sdk): retain narrower promise shielding
khaliqgant Sep 22, 2026
5b4cb44
docs(evidence): record native promise gate
khaliqgant Sep 22, 2026
0e45992
fix(sdk): bypass inherited array setters
khaliqgant Sep 23, 2026
fcb886d
docs(evidence): record array-setter isolation gate
khaliqgant Sep 23, 2026
279c3ea
fix(sdk): snapshot stored plugin manifests
khaliqgant Sep 23, 2026
e20742e
docs(evidence): record manifest snapshot gate
khaliqgant Sep 23, 2026
eb13c12
docs(evidence): record rebased manifest gate
khaliqgant Sep 23, 2026
1756c95
fix(sdk): preserve hosted dispatch matches
miyaontherelay Sep 23, 2026
0cba351
docs(evidence): record dispatch setter gate
miyaontherelay Sep 23, 2026
21dc984
fix(sdk): bound hosted surface reads
khaliqgant Sep 23, 2026
56955f4
docs(evidence): record bounded surface gate
khaliqgant Sep 23, 2026
4ced3d5
fix(sdk): deduplicate hardened dispatch import
khaliqgant Sep 23, 2026
1c595c0
docs(evidence): record release-rebased surface gate
khaliqgant Sep 23, 2026
6987e2e
fix(sdk): shield verified surface files
khaliqgant Sep 23, 2026
6a6299c
docs(evidence): record surface promise gate
khaliqgant Sep 23, 2026
d401cb8
fix(sdk): pin hosted Node executable bytes
khaliqgant Sep 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 69 additions & 5 deletions docs/BABYSITTER-CATALOG-HANDOFF.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,75 @@ exact live `babysit` label and the bound session/head. Permission declarations
are not enforcement. Export success is byte verification, not execution approval.

The native package source is `extensions/babysitter` (see its README for the
turn contract). It is unreleased and cannot execute: #549 still refuses it,
the SDK context has no `capabilities.cloud.babysitterTurn`, and its
`compat` requires a surface release after 2.0.25 that routes `labeled`,
`unlabeled`, and `ready_for_review`. Export it only from a reviewed, merged
commit.
turn contract). Flows 2.0.26 is published, but the package cannot execute
through the generic executor: #549 still refuses it. The SDK now has a separate Linux-only
capability sandbox that injects exactly
`capabilities.cloud.babysitterTurn.queue` without exposing the base context,
workspace, environment credentials, network, helpers, MCP, or harnesses. It is
not wired to hosted dispatch and must not be treated as enablement. The package's
`compat` requires the published 2.0.26 Surface/SDK release that routes
`labeled`, `unlabeled`, and `ready_for_review`. Export it only from the reviewed
release commit pinned below. The Software Factory flow's own independently
versioned header remains `2.0.22`; a regression requires the hosted identity to
equal the identity obtained from that exact reviewed source.

The sandbox contract is deliberately narrower than #442. It re-verifies the
complete lock-backed installation and every manifest, binds it to the exact
reviewed Software Factory source in the same `loadHostedExtensionRuntime` generation, detects
cross-extension route ambiguity, accepts only the exact published Babysitter
ref/digest/manifest and native permission profile, imports the entry only
inside Linux bubblewrap plus Node's
permission model under inherited 16 GiB address-space and 3 GiB
data/anonymous-memory hard limits, mounts a
minimal trusted Surface facade (`flow`, `github`,
and `getFlowDefinition`) from six integrity-pinned private runtime files instead
of the general helper runtime, checks
normalized input against non-serializable verified dispatch authority, and
permits one queue call. The parent capability adapter
receives that original authority plus immutable extension provenance; the
capability request never carries workspace, activation, listener, session,
lineage, label, head, prompt, merge, route, or config authority. Cloud PR #3942 owns the
lineage/authority core and must inject workspace, activation, and listener from
persisted dispatch context, re-read live PR/label/head state, and return only
`{ receiptId, status: 'queued' | 'duplicate' }`. Refusal or in-doubt transport
rejects once with no fallback.

Only the parent validator is a security boundary. The isolated entry can write
its inherited protocol descriptor directly and bypass child-side routing,
context, call-count, and completion checks. Parent validation therefore treats
every frame as hostile, permits at most the one exact delivery already bound to
the branded dispatch, and waits for the adapter's authoritative outcome before
settling any premature child terminal frame. An authoritative adapter rejection
settles immediately with its original typed error even if the child hangs.

Before replacing #549's refusal, the hosted caller must obtain an opaque base
and installation as one generation with `loadHostedExtensionRuntime`, then call
`runHostedCapabilityExtension` with both values. Every dispatch rechecks the
current extension declarations and complete project source tree against that
generation. Directory entries are streamed beneath a shared entry bound;
nonblocking no-follow descriptors and explicitly bounded reads enforce the
cumulative-byte limit before source contents are buffered. The loader never imports
tenant base code to derive authority. It
requires the exact reviewed Software Factory flow-file SHA-256 and assigns its
pinned name/version in the parent; project `node_modules`, relative imports,
stdout, process termination, globals, and module caches therefore cannot forge
that identity. The sandbox separately requires exact SHA-256 pins for every
Surface runtime file it needs, copies those bytes into its private runtime, and
mounts only the copies. A final generation check runs after both private
snapshots exist and immediately before launch;
using the ordinary compose loader would import extension top-level JavaScript
in the host before the sandbox exists. Cross-project, cross-redeploy, stale,
and structural pairings fail before import. The selected store bytes are copied into a
private snapshot whose digest is recomputed before bubblewrap mounts it, so a
later live-store replacement cannot alter imported code. Independent review
must prove this path at the exact release head. Broader per-agent-step
file/network/access-preset enforcement remains open in #442 and is not claimed
by this slice.

The reviewed native source ref is
`github:AgentWorkforce/flows@8b33ebab8347514f80d9da5a81206a087f641714#extensions/babysitter`,
the commit included in the published 2.0.26 install. The earlier byte-identical
pre-release commit is not accepted as authority.

## Export reviewed bytes

Expand Down
22 changes: 20 additions & 2 deletions docs/CLOUD.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,26 @@ handler keep the base flow's default body; overlapping or malformed matches
fail closed. This release also refuses a matching handler with
`plugin_unsupported` before either body starts because schema-2 entries are
ordinary JavaScript and their manifest permissions are not yet isolated by the
runtime (gate 8 / #442). A handler's trigger is therefore a requirement, but
the handler cannot execute until that boundary exists. The deploy body carries
runtime (gate 8 / #442). The SDK now contains a Linux-only, capability-only
isolation primitive for the native Babysitter profile. Hosted callers must load
the base and complete installation together with
`loadHostedExtensionRuntime` (which internally sets `extensions: 'none'` and
does not import extension JavaScript), then pass its two opaque,
same-generation results to `runHostedCapabilityExtension`. Every dispatch
rechecks the current declarations and complete project source against that
generation. The hosted loader never executes tenant base code to establish
authority: it hashes a private snapshot and accepts only the exact reviewed,
host-pinned Software Factory source and identity. The runner then verifies the
complete lock-backed set, actual base compatibility, route uniqueness, and
the exact reviewed Babysitter ref/digest/manifest before the artifact is imported inside a
bubblewrap mount/PID/network/user namespace with an empty credential
environment and a context exposing only
`capabilities.cloud.babysitterTurn.queue` plus `done`. This is a prerequisite,
not enablement: the generic executor refusal remains until the Cloud adapter,
Relay route, end-to-end canary, and independent security review are complete.
A handler's
trigger is therefore a requirement, but the handler cannot execute through the
generic executor yet. The deploy body carries
the same list as `requirements` for Cloud to cross-check, and a declared harness Cloud cannot run yet (`gemini`)
refuses the deploy unless `--agents` overrides it.

Expand Down
74 changes: 74 additions & 0 deletions evidence/babysitter-gate8-isolation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Native Babysitter gate-8 isolation evidence

This slice adds a Linux-only, capability-only execution primitive for the
immutable native Babysitter extension. It does not wire hosted dispatch, change
the generic executor refusal introduced by #549, publish a package, or enable a
deployment.

The enforced boundary is:

- require the exact reviewed Software Factory base source hash without
importing tenant base code, and resolve the complete
installation as one opaque generation without importing extension
JavaScript; cross-project and same-path cross-redeploy pairing refuse, and
every dispatch rechecks the current declarations plus complete project
source; base stdout, globals, process termination, relative/package imports,
and an earlier module cache cannot forge the parent-pinned identity; source
directory entries are streamed beneath a shared entry bound; `flows.json`
and `flows.lock.json` plus source files use nonblocking no-follow descriptors
and explicitly bounded reads before contents are buffered or parsed;
- reverify every content-addressed artifact, lock metadata, manifest hash,
base/runtime compatibility, and route uniqueness using bounded descriptor
reads that enforce the fetched plugin's 500-file, 256-KB-per-file, and 2-MB
total limits before allocation, then require the exact
immutable native ref/digest/manifest and narrow permission profile; copy the
selected bytes into a private snapshot, recompute its digest, and mount only
that snapshot so later store replacement cannot change executed code;
- clone the delivery descriptor into behavior-free, frozen JSON with captured
parent intrinsics before validation or serialization, rejecting proxies,
accessors, inherited `toJSON`, cycles, holes, and extra JSON-visible fields
while stripping symbol and non-enumerable metadata; enforce depth,
property/node-count, and encoded-byte limits incrementally while traversing
(including repeatedly shared subtrees), count string escaping without
materializing an unbounded encoded copy, and enumerate JSON-visible keys
without first allocating an attacker-sized key array or using ambient array
methods; then validate that snapshot against the
symbol-branded verified dispatch before import;
- import and execute the matching handler only inside a bubblewrap namespace
plus Node's permission model, with no network, writable filesystem, inherited
environment, child process, workspace mount, MCP, helpers, harnesses, or base
flow context; a generated facade mounts only `flow`, `github`,
`getFlowDefinition`, and private copies of six SHA-256-pinned Surface runtime
files, followed by one final runtime-generation check before launch; an
inherited hard limits of 16 GiB address space and 3 GiB data/anonymous
memory cover heap, Buffer/native memory, mappings, and descendants in
addition to the 64 MiB V8 old-space setting; the wider address-space ceiling
admits Node's virtual V8/Wasm reservations while the tighter data limit
refuses two hostile 2 GiB Buffers;
- expose one `capabilities.cloud.babysitterTurn.queue({ delivery })` call and
`done`, validate the exact request and `{ receiptId, status }` response in the
parent, and pass the original non-serializable authority to the host adapter;
- treat descriptor 3 as hostile transport: clone and validate every frame and
both boundary payloads with captured parent intrinsics, so direct writes can
consume only the same exact one-shot delivery capability already granted and
cannot claim premature success;
- fail closed on unknown frames, repeated/omitted calls, premature completion,
timeouts, adapter rejection, incompatible bytes, route ambiguity, forged
loader results, an unverified/composed base, and broader permissions.

The parent validations are the security boundary. Child-side handler routing,
the context proxy, its once-only counter, and `done('success')` are correctness
checks only: hostile code may bypass all of them by writing descriptor 3. The
parent therefore enforces the exact delivery, one-call limit, terminal ordering,
and authoritative adapter outcome independently, including when a child emits
an error immediately after its request or hangs after a typed adapter rejection.

Cloud PR #3942 remains responsible for persisted dispatch context, live PR and
label/head revalidation, authorized existing-session resolution, lineage-based
deduplication, and Relay native-turn delivery. The broader per-step permission
enforcement requested by #442 is not implemented here and remains a separate
gate. The committed extension declares SDK/Surface `^2.0.26`; an injected
2.0.25 runtime is refused before import, while the exact published 2.0.26 bytes
are exercised inside the sandbox.

See `verification.txt` for the final local verification record.
Loading
Loading