Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Changelog tracking starts with 0.2.0. Prior versions were not tracked.

### Added

- **`astrid:process@1.0.0` persistent-process tier — implemented.** A capsule can now spawn a background child that **outlives the pooled, stateless WASM instance** that started it. Previously an ephemeral `process-handle` is reaped when its instance resets on return to the dynamic pool, so a process started in one tool invocation could not survive to the next — the split `spawn → read → stop` pattern was impossible. A new host-owned `PersistentProcessRegistry` — cloned into every pooled `HostState` exactly like the cancellation `ProcessTracker`, so a `process-id` survives instance churn — owns the child (spawned on the daemon runtime under the same `bwrap`/Seatbelt sandbox as the ephemeral tier), its per-stream log rings, and its stdin pipe. **Implemented:** `spawn-persistent` (returns a 256-bit host-minted CSPRNG `process-id`, lowercase base32 so it doubles as an IPC topic suffix; the registry stores only a keyed BLAKE3 hash, never the raw token), `status` / `status-many` / `list-processes`, `read-logs` (drain) + `read-since` (non-draining, cursor-addressed, byte-faithful `list<u8>`), `signal` (incl. `stop`/`cont`), bounded `wait`, `stop` (SIGTERM→grace→SIGKILL, frees the slot), `release-process`, and `write-stdin` / `close-stdin` (via `keep-stdin-open` capture). Every id-keyed call re-resolves the live `(principal, capsule)` and checks it against the recorded creator, so a leaked id is inert across the principal/capsule boundary — unknown / wrong-owner / wrong-capsule / reaped all collapse to `no-such-process` with no oracle; `spawn-persistent` refuses the owner-fallback principal (`persist-unsupported`) so tenants never share a `default` namespace. Lifecycle is enforced by a per-capsule reaper task: per-principal concurrent + retained-id caps, idle / max-lifetime / exit-retention TTLs (guest values clamped DOWN to host ceilings), and a kill-all on capsule unload / daemon graceful shutdown. Works on Linux and macOS (the macOS caveat — a daemon *hard crash*, not a graceful shutdown, can orphan a still-sandboxed child because Seatbelt has no `die-with-parent` — is a weaker cleanup guarantee, not a containment gap). **Still deferred, honestly:** `attach` (the resource-handle composition sugar; the id-keyed ops are its documented `attach(id)?.method()` equivalent), `watch` / `unwatch` (host-published lifecycle events — an OPEN publish-authority question in RFC host_abi, with `status` + bounded `wait` polling as the working alternative), and the WIT's own `(NOT YET …)` items (resource-limit enforcement, `cpu-ms` / `mem-bytes-peak`, instance-local pollables). Contract: unicity-astrid/wit#12. Design: unicity-astrid/rfcs#22. Closes #866.
- **Per-principal peak memory in usage reporting.** `ResourceUsage` gains `memory_bytes_peak_total: Option<u64>` — the cross-capsule high-water linear memory a principal has driven (max across every capsule it invokes), read from the shared memory ledger and surfaced by the admin `UsageGet`, `GET /api/sys/principals/{id}/usage` (a new field on the OpenAPI `ResourceUsageView`), and `astrid quota show` (a new "memory peak" row). This fills the memory side of per-principal usage, which previously reported only the per-instance ceiling. Under pooled, shared Stores a live "current" total is not cleanly attributable, so the **peak** is the reported signal — the principal that grows a Store owns the peak; `memory_bytes_current_total` stays `None`. Refs #816.

- **Operator overrides for capsule runtime sizing (config + env + CLI).** New `[capsule]` config section with `host_blocking_concurrency`, `host_io_concurrency`, and `instance_pool_size` (all optional; unset → the host-derived default), the matching `ASTRID_CAPSULE_HOST_BLOCKING_CONCURRENCY` / `ASTRID_CAPSULE_HOST_IO_CONCURRENCY` / `ASTRID_CAPSULE_INSTANCE_POOL_SIZE` env vars, and `astrid-daemon --host-blocking-concurrency` / `--host-io-concurrency` / `--instance-pool-size` flags. Precedence is CLI flag > config file > env > host-derived default; the daemon resolves the values once at boot and the kernel forwards them, unmodified, to every capsule's `WasmEngine` (the same handle-plumbing shape as `FuelLedger`). A zero override is rejected at config-validation time (it would wedge a host-call class or leave a capsule with no instance to lease, rather than throttle). Refs #816.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ impl HostProcessHandle for HostState {
ProcessSignal::Usr1 => nix::sys::signal::Signal::SIGUSR1,
ProcessSignal::Usr2 => nix::sys::signal::Signal::SIGUSR2,
ProcessSignal::Int => nix::sys::signal::Signal::SIGINT,
ProcessSignal::Stop => nix::sys::signal::Signal::SIGSTOP,
ProcessSignal::Cont => nix::sys::signal::Signal::SIGCONT,
};
let raw = i32::try_from(pid).map_err(|_| ErrorCode::InvalidInput)?;
nix::sys::signal::kill(nix::unistd::Pid::from_raw(raw), nix_sig)
Expand Down
Loading
Loading