Skip to content

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 24 Mar 20:24
· 20 commits to main since this release
112bec5

Changed

  • workspace:// VFS scheme renamed to cwd:// — the scheme maps to the daemon's CWD at boot; the old name implied a structured project workspace concept that was never implemented.

  • Tools are now a pure IPC convention. Removed kernel-side tool dispatch (WasmCapsuleTool, CapsuleTool trait, inject_tool_schemas, CapsuleToolContext), ToolDef and [[tool]] from manifest, inject_tool_schemas from astrid-build. The kernel no longer parses or manages tool schemas. Tool capsules use IPC interceptors on tool.v1.execute.<name> and tool.v1.request.describe. The router capsule handles discovery and dispatch.

  • LLM providers are now a pure IPC convention. Removed LlmProviderDef and [[llm_provider]] from manifest, LlmProviderInfo and llm_providers from CapsuleMetadataEntry. The kernel no longer parses or manages provider metadata. LLM capsules self-describe via llm.v1.request.describe interceptors; the registry capsule discovers them via hooks::trigger.

  • Removed dead cron host functions. astrid_cron_schedule and astrid_cron_cancel were never implemented (stubs only). CronDef and [[cron]] removed from manifest. WIT spec updated: 49 host functions across 10 domain interfaces.

  • Append-only artifact store — bin/ and wit/ are never deleted on capsule remove. Content-addressed artifacts are the audit trail; deleting them breaks provability. Future astrid gc for explicit cleanup.

  • Replace [dependencies] provides/requires string arrays with [imports]/[exports] namespaced TOML tables — semver version requirements on imports (^1.0), exact versions on exports (1.0.0), optional imports, namespace/interface name validation

  • WIT spec: Rewrite wit/astrid-capsule.wit to document all 51 host ABI functions (was 7). Split monolithic host interface into 11 domain-specific interfaces (fs, ipc, uplink, kv, net, http, sys, cron, process, elicit, approval, identity). Updated guest exports to reflect actual entry points (astrid_hook_trigger, astrid_tool_call, run, astrid_install, astrid_upgrade). Bumped package version to 0.2.0.

Added

  • cargo install astrid installs both astrid (CLI) and astrid-daemon binaries from a single crate. The CLI crate now includes the daemon as a second [[bin]] entry point.
  • astrid self-update command — checks GitHub releases for newer versions, downloads platform-specific binary to ~/.astrid/bin/, no sudo required. Startup update banner (cached 24h) notifies on interactive commands.
  • astrid init PATH setup — detects shell (zsh/bash/fish), offers to append ~/.astrid/bin to the appropriate RC file
  • Standard WIT interface installation during astrid init — fetches 9 WIT files (llm, session, spark, context, prompt, tool, hook, registry, types) from the canonical WIT repo and installs to ~/.astrid/home/{principal}/wit/ for capsule and LLM access via home://wit/
  • Short-circuit interceptor chain — interceptors return Continue, Final, or Deny to control the middleware chain. A guard at priority 10 can veto an event before the core handler at priority 100 ever sees it. Wire format: discriminant byte (0x00/0x01/0x02) + payload, backward compatible with existing capsules.
  • Export conflict detection on capsule install — detects when a new capsule exports interfaces already provided by an installed capsule, prompts user to replace. Nix-aligned approach: conflicts derived from exports data, no name-based supersedes field needed.
  • Interceptor priority — priority field on [[interceptor]] in Capsule.toml (lower fires first, default 100). Enables layered interception (e.g. input guard before react loop).
  • Distro.lock regeneration on astrid capsule update — keeps the lockfile in sync after capsule updates
  • Content-addressed WIT storage — capsule install hashes .wit files into ~/.astrid/wit/, capsule remove cleans up unreferenced WIT files, wit_files field in meta.json
  • astrid capsule tree command — renders the imports/exports dependency graph of all installed capsules, showing which capsule exports satisfy each import, with unsatisfied imports highlighted in red (astrid capsule deps retained as hidden alias)
  • astrid init with distro-based capsule installation — fetches Distro.toml, multi-select provider groups, shared variable prompts with {{ var }} template resolution, progress bars, writes Distro.lock for reproducibility. Supports --distro flag for custom distros.
  • Distro.toml parser and Distro.lock generator — parse distro manifests with full os-release style metadata, shared variables with {{ var }} templates, provider groups, uplink roles, and semver validation. Atomic lockfile writes with BLAKE3 hashes for reproducible installs.
  • Kernel boot validation — validates every capsule's required [imports] has a matching [exports] from another loaded capsule, logs errors for unsatisfied required imports and info for optional ones
  • astrid capsule remove command with dependency safety checks — blocks removal if the capsule is the sole exporter of an interface that another capsule imports (--force to override), cleans up content-addressed WASM binaries from bin/ when no other capsule references the same hash
  • Install capsules from GitHub release WASM assets — astrid capsule install @org/repo now downloads pre-built .wasm binaries from release assets before falling back to clone + build from source
  • Per-principal audit chain splitting — each principal maintains its own independent chain per session, independently verifiable via verify_principal_chain() and get_principal_entries()
  • AuditLog::append_with_principal() for principal-tagged audit entries
  • Auto-provisioning gated on identity store — only "default" principal is auto-provisioned when identity store is configured
  • Linux FHS-aligned directory layout (etc/, var/, run/, log/, keys/, bin/, home/) replacing the flat ~/.astrid/ structure
  • PrincipalId type for multi-principal (multi-user) deployments — each principal gets isolated capsules, KV, audit, tokens, and config under home/{principal}/
  • Content-addressed WASM binaries in bin/ using BLAKE3 hashing — integrity verified on every capsule load (no hash = no load, wrong hash = no load)
  • Per-capsule daily log rotation at home/{principal}/.local/log/{capsule}/{YYYY-MM-DD}.log with 7-day retention
  • /tmp VFS mount backed by home/{principal}/.local/tmp/ for per-principal temp isolation
  • Multi-source capsule discovery with precedence: principal > workspace (dedup by name)
  • PrincipalHome struct with .local/ and .config/ following XDG conventions
  • Per-invocation principal resolution — KV, audit, logging, and capability checks scope to the calling user per IPC message, not per capsule load
  • IpcMessage.principal field for carrying the acting principal through event chains (transparent to capsules)
  • AstridUserId.principal field mapping platform identities to PrincipalId with auto-derivation from display name
  • Dynamic KV scoping via invocation_kv on HostState — capsules call kv::get("key") and the kernel returns the right value for the current principal
  • Principal auto-propagation on ipc_publish — capsules never touch the principal, it flows through event chains automatically
  • Auto-provisioning of principal home directories on first encounter
  • astrid_get_caller host function now returns { principal, source_id, timestamp } instead of empty object
  • Dynamic per-principal log routing — cross-principal invocations write to the target principal's log directory
  • AuditEntry.principal field with length-delimited signing data encoding
  • ScopedKvStore::with_namespace() for creating scoped views sharing the same underlying store
  • AuditEntry::create_with_principal() builder for principal-tagged audit entries
  • layout-version sentinel in etc/ for future migration support
  • lib/ directory reserved for future WIT shared WASM component libraries
  • End-to-end Tier 2 OpenClaw plugin support: TypeScript plugins with npm dependencies install, transpile, sandbox, and run as MCP capsules with full tool integration
  • OXC strip_types() transpiler for Tier 2 TS→JS (preserves ESM, unlike Tier 1's CJS conversion)
  • Node.js binary resolution at build time: prefers versioned Homebrew installs (node@22+), validates each candidate
  • MCP-discovered tools are now merged into the LLM tool schema injection alongside WASM capsule tools
  • astrid_net_read now uses a self-describing NetReadStatus wire format: every response is prefixed with a discriminant byte (0x00 = data, 0x01 = closed, 0x02 = pending), replacing the previous single-byte sentinel hack
  • Headless mode: astrid -p "prompt" for non-interactive single-prompt execution with stdin piping support
  • Post-install onboarding: astrid capsule install now prompts for [env] fields immediately after install
  • Shared astrid_telemetry::log_config_from() behind config feature flag — replaces duplicate config bridge code
  • --snapshot-tui mode — renders the full TUI to stdout as ANSI-colored text frames using ratatui's TestBackend. Each significant event (ready, input, tool call, approval, response) produces a frame dump. Configurable with --tui-width and --tui-height. Enables automated smoke testing without an interactive terminal.

Fixed

  • cwd:// VFS scheme was handled in the security gate (capability checks) but not in the runtime path resolver — capsules using cwd:// paths at runtime received a security denial because the path resolved to <cwd>/cwd:/path instead of <cwd>/path
  • sandbox-exec (Seatbelt) crashes with SIGABRT on macOS 15+ (Darwin >= 24) — skip sandboxing on affected versions
  • Headless approval response published to wrong IPC topic (astrid.v1.approval.response instead of astrid.v1.approval.response.{request_id}) and used wrong decision string (allow instead of approve)
  • [[component]].capabilities (fs_read, fs_write, host_process) not merged into root capabilities — security gate couldn't see them
  • Lifecycle hooks (on_install) couldn't access home:// VFS — added home_root to LifecycleConfig
  • astrid init standard WIT files were installed to ~/.astrid/wit/astrid/ (root-level, no VFS scheme). Capsules access the VFS via home:// which maps to ~/.astrid/home/{principal}/ — the files were unreachable. Now installed to ~/.astrid/home/{principal}/wit/, accessible as home://wit/ (fixes #598)
  • Dispatcher known_principals HashSet capped at 10K entries to prevent unbounded memory growth
  • Dispatcher only caches principal after successful home provisioning — transient failures allow retry on next event
  • AstridUserId.principal now has #[serde(default)] — existing identity records without the field deserialize with "default" instead of failing
  • transpile_and_install now correctly unpacks .capsule archives from astrid-build output
  • copy_capsule_dir only skips dist/ at the top level; npm packages inside node_modules retain their dist/ directories
  • MCP host engine: absolute system binaries (e.g. /opt/homebrew/opt/node@22/bin/node) skip path traversal check when declared in host_process capability
  • MCP host engine: allow_network derived from capsule capabilities (uplink/net) instead of defaulting to false
  • Capsule env resolution no longer blocks loading on missing optional fields; fills with empty defaults so uplink capsules can boot before clients connect
  • macOS Seatbelt sandbox: added mach* permission and unrestricted file-read* for Node.js compatibility
  • macOS Seatbelt sandbox: hidden path deny rules skip paths that are ancestors of the writable root
  • MCP tool schemas now include properties field for LLM API compatibility
  • net_write no longer causes a WASM trap on broken pipe / connection reset when a headless client disconnects; write errors are logged at debug level and the dead stream is cleaned up on the next read
  • net_read returns a NET_STREAM_CLOSED sentinel byte instead of trapping on peer EOF/disconnect, allowing the CLI capsule run loop to remove dead streams gracefully
  • Also fixes a variable name mismatch (capsule vs plugin) in approval.rs that caused a compile error
  • ~/.astrid/shared/ directory now created on boot, eliminating global:// VFS not mounted warning on fresh installs
  • Capsule reinstall now preserves existing .env.json rather than overwriting it with an empty file
  • WASM execution timeout bumped from 30s to 5 minutes to prevent premature cancellation on slow operations
  • IPC event dispatcher now delivers events to each capsule in publish order via per-capsule mpsc queues, fixing out-of-order stream text assembly in the ReAct capsule
  • IpcMessage gains a monotonic seq field assigned at publish time for ordering and diagnostics
  • KV host function double-encoding: kv_get_impl returned serde_json::to_vec of raw bytes instead of raw bytes directly
  • Config host function double-encoding: get_config_impl wrapped string values in JSON quotes, breaking URLs and other string config
  • React capsule LLM topic validation: active_llm_topic() could produce topics with empty segments causing IPC publish failures
  • astrid_read_file host function trapped (WASM abort) on recoverable errors (file-not-found, permission denied) — now returns status-prefix wire format (0x00+content / 0x01+error), paired with SDK-side decoding. Eliminates crashes in memory, agents, identity, and fs capsules when reading optional files.

Changed

  • global:// VFS scheme renamed to home://
  • Capsule::invoke_interceptor now accepts Option<&IpcMessage> for per-invocation principal context
  • CapsuleContext.global_root renamed to home_root; HostState.global_vfs renamed to home_vfs
  • AstridUserId now requires principal: PrincipalId field (existing KV records incompatible — nuke ~/.astrid/)
  • Capsule install target moved from ~/.astrid/capsules/ to home/{principal}/.local/capsules/ (capsule dir now holds only manifest + meta.json)
  • KV namespace format changed from capsule:{name} to {principal}:capsule:{name}
  • Socket/token/ready paths moved from sessions/ to run/
  • Env config moved from capsule dir .env.json to home/{principal}/.config/env/{capsule}.env.json
  • System logs now use .log extension, no ANSI escape codes in file output, 7-day retention
  • user_key_path() renamed to runtime_key_path() (now at keys/runtime.key), logs_dir() renamed to log_dir()
  • Ephemeral daemon now shuts down immediately when the last client disconnects (idle timeout 0, 1s check interval) instead of waiting 5 minutes
  • Renamed plugincapsule in the WASM host layer and audit log fields for consistency with project terminology
  • Split astrid-build 1166-line build.rs into focused modules: rust.rs, openclaw.rs, mcp.rs

Removed

  • ~/.astrid/capsules/ system capsules directory (user installs go to principal home)
  • sessions/, shared/, audit.db, capabilities.db, state/, spark.toml, cache/capsules/ — replaced by FHS equivalents or moved to principal home

Breaking

  • Existing ~/.astrid/ must be deleted — no migration path. Reinstall all capsules after upgrading.

Install

From source (requires Rust 1.94+):

cargo install astrid

Pre-built binaries:
Download the archive for your platform, extract, and add to PATH:

tar xzf astrid-*-$(uname -m)-*.tar.gz
sudo mv astrid-*/astrid astrid-*/astrid-daemon /usr/local/bin/

Then run astrid init to set up capsules.


With many thanks from the following Astrinauts 🚀

  • Joshua J. Bouw