Skip to content

Commit 278dbca

Browse files
authored
fix!: restore frozen [email protected]; move file-injection to new [email protected] (#20)
Closes #19 #15 mutated the frozen `astrid:[email protected]` in place (`spawn-request` gained `file-injections`). The component-model linker matches package versions structurally, so every capsule built against the published contract fails to instantiate on a host serving the mutated shape — confirmed live on astrid 0.9.0: the sage supervisor fails `astrid init`, and capsule-shell v0.2.0 fails to load, both with `component imports instance astrid:process/[email protected], but a matching implementation was not found in the linker`. `astrid-sys 0.7.1` (crates.io — what every shipped capsule embeds) carries the pre-#15 shape, verified by extracting the artifact. - `host/[email protected]`: restored **byte-identical** to 83ebc6c (the published shape; verified by diff in CI-adjacent check). - `host/[email protected]`: the injection extension as an additive successor — same dual-version pattern as `[email protected]`/`1.1.0`. Header documents the provenance. - README: package-table row for the new version. - `scripts/validate-wit.sh` passes for both (it already stages same-package multi-version since the http split). Core-side dual-version serving is implemented and green (570 tests) on `unicity-astrid/astrid` branch `fix/process-dual-version-host`; its submodule pin bumps to this once merged. Tracked: astrid-runtime/astrid#1107. https://claude.ai/code/session_01NvX2tE7tgXuCRevqqiXTGU
1 parent 812c833 commit 278dbca

3 files changed

Lines changed: 639 additions & 67 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Each domain is its own package, frozen at a per-file version. A capsule imports
2626
| `host/[email protected]` | `astrid:[email protected]` | Additive successor to `@1.0.0` — caller-set per-request timeouts, redirect policy, response/decompression size caps, scheme restriction, subresource integrity, response metadata, streaming request bodies (uploads), and response trailers. An empty `request-options` reproduces `@1.0.0` behaviour. |
2727
| `host/[email protected]` | `astrid:[email protected]` | Logging, config, time, caller context, entropy, sleep, capability introspection. |
2828
| `host/[email protected]` | `astrid:[email protected]` | OS-sandboxed host process spawn (with stdin/env/cwd), wait, signal, kill, read-logs, stdin streaming. |
29+
| `host/[email protected]` | `astrid:[email protected]` | Additive successor to `@1.0.0` — host-verified, read-only per-spawn file injection (`file-injections` on `spawn-request`). Empty `file-injections` reproduces `@1.0.0` behaviour. |
2930
| `host/[email protected]` | `astrid:[email protected]` | Interactive user input during install/upgrade lifecycle. |
3031
| `host/[email protected]` | `astrid:[email protected]` | Human-in-the-loop approval gate for sensitive actions. |
3132
| `host/[email protected]` | `astrid:[email protected]` | Multi-platform identity resolve and link. |

host/[email protected]

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -157,66 +157,6 @@ interface host {
157157
max-open-files: option<u32>,
158158
}
159159

160-
/// Host-verified, read-only bytes exposed to a spawned child's OS sandbox.
161-
/// The motivating consumer is un-overridable per-spawn governance (a
162-
/// supervised agent reads a policy file a prompt-injected session cannot
163-
/// rewrite), but the primitive is AGENT-NEUTRAL: the capsule hands over the
164-
/// bytes plus how the child should find them (see `injection-placement`), and
165-
/// the host owns placement, integrity, and exposure. `content` is OPAQUE to
166-
/// the host — it never parses, validates, or filters the bytes.
167-
///
168-
/// Write-protection invariant: the bytes the child reads MUST NOT be writable
169-
/// by (a) the child or any subprocess it spawns, NOR (b) the spawning
170-
/// principal's capsule `fs` surface (`fs_*` runs in capsule space, OUTSIDE the
171-
/// child's sandbox; a `home://`-spanning `fs` capability could otherwise
172-
/// rewrite a home-staged file between authoring and read). The host therefore
173-
/// SNAPSHOTS `content` into a host-owned path outside every VFS mount,
174-
/// BLAKE3-hashes the snapshot, VERIFIES the exposed bytes against the pin
175-
/// (closing the copy->expose TOCTOU), records the hash in the spawn audit, and
176-
/// exposes ONLY that host-owned snapshot — never a live bind of bytes the
177-
/// capsule can still reach.
178-
///
179-
/// No new capability: injection rides `host_process` and is permitted only
180-
/// into the caller's OWN child. It is strictly a RESTRICTION surface — a
181-
/// capsule that already dictates the child's `args` / `env` / `cwd` / `stdin`
182-
/// gains nothing from also handing it an UNMODIFIABLE file. The host owns the
183-
/// materialized path (`env-pointer`) or only remaps within the child's own
184-
/// namespace (`fixed-path`), so it never writes to a caller-named host path.
185-
record file-injection {
186-
/// The bytes to expose. The capsule already holds them (it authored the
187-
/// policy), so there is no host-side file read, no read gate, and no
188-
/// home-staged intermediate file the `fs` surface could race.
189-
content: list<u8>,
190-
/// How the child is pointed at the bytes.
191-
placement: injection-placement,
192-
}
193-
194-
/// How an injected file is exposed to the child. Both modes expose the SAME
195-
/// verified bytes read-only; they differ only in how the agent finds them,
196-
/// chosen to match the target agent's config mechanism.
197-
variant injection-placement {
198-
/// The host materializes the verified snapshot at a HOST-OWNED path
199-
/// (outside every VFS mount), exposes it read-only (Linux `--ro-bind P P`
200-
/// in the `bwrap` namespace; macOS Seatbelt `allow file-read*` plus a
201-
/// trailing `deny file-write*` on that literal path), and sets the named
202-
/// environment variable on the child to that path. The host owns the path,
203-
/// so there is no caller-chosen target and no host write to a caller-named
204-
/// path. Works on Linux AND macOS — the OS-agnostic mode. For agents whose
205-
/// un-overridable config tier is reachable via an env-redirected file
206-
/// (Claude `CLAUDE_CODE_MANAGED_SETTINGS_PATH`, Gemini
207-
/// `GEMINI_CLI_SYSTEM_SETTINGS_PATH`). The string is the env-var name; the
208-
/// host supplies its value.
209-
env-pointer(string),
210-
/// The host ro-binds the verified snapshot at this absolute in-sandbox
211-
/// path (`--ro-bind <snapshot> <path>` in the `bwrap` namespace, which
212-
/// creates the mount point, so `path` need not exist on the host). LINUX
213-
/// ONLY: rejected on macOS with `invalid-input`, since Seatbelt has no
214-
/// mount namespace and materializing at a caller-named host path would be
215-
/// an arbitrary host write (escalation). For agents whose enforced tier is
216-
/// a FIXED path with no env redirect (Codex `/etc/codex/requirements.toml`).
217-
fixed-path(string),
218-
}
219-
220160
/// Request to spawn a host process.
221161
record spawn-request {
222162
/// Command to execute.
@@ -237,13 +177,6 @@ interface host {
237177
/// Per-child OS resource ceilings. Applies to EVERY tier.
238178
/// (NOT YET ENFORCED — see `resource-limits`.)
239179
limits: option<resource-limits>,
240-
/// Read-only files the host exposes inside the child's sandbox. Applies
241-
/// to EVERY tier. Each entry hands the host verified, unmodifiable bytes
242-
/// plus how the child should find them (see `file-injection` /
243-
/// `injection-placement`); empty => no injection. The host never parses
244-
/// the bytes; the BLAKE3 hash of each snapshot is recorded in the spawn
245-
/// audit.
246-
file-injections: list<file-injection>,
247180

248181
// ---- the fields below are honored ONLY by `spawn-persistent`
249182
// and ignored by `spawn` / `spawn-background`. ----

0 commit comments

Comments
 (0)