feat(sys): implement enumerate-capabilities, de-stub capability check#869
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new capability introspection mechanism for capsules, enabling them to query their own manifest-declared capabilities at runtime. By centralizing capability logic into Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Capabilities defined at the start, Each capsule knows its own heart. With a list now in hand, It obeys the command, And keeps all the logic apart. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces capability introspection for capsules by adding the enumerate-capabilities function to the host interface. It precomputes and stores the capsule's held capability names at load time within HostState and implements held_names and has helper methods on CapabilitiesDef to query these capabilities. Feedback suggests optimizing held_names to return Vec<&str> instead of Vec<String> to avoid unnecessary allocations of static string literals.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
0ec6b0b to
51f49fc
Compare
There was a problem hiding this comment.
Pull request overview
This PR implements capability introspection in the astrid:sys/[email protected] ABI by adding an infallible enumerate-capabilities() -> list<string> host function and completing check-capsule-capability to consult the full manifest capability namespace (instead of a stubbed single-capability check). It fits into the kernel/wasm host layer by exposing read-only, fail-closed capability posture as structural metadata derived from the capsule’s manifest.
Changes:
- Add
CapabilitiesDef::held_names()andCapabilitiesDef::has(name)as the single source of truth for capability introspection. - Plumb a load-time snapshot of capability names into
HostStateand implementenumerate_capabilities()using that snapshot. - Update the staged
astrid:sys/[email protected]WIT to document and includeenumerate-capabilities, and add unit tests covering the new behavior.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| crates/astrid-hooks/src/handler/wasm.rs | Initializes hooks’ HostState with an empty capability_names set (fail-closed) since hooks run outside the manifest lifecycle. |
| crates/astrid-capsule/wit-staging/deps/astrid-sys/[email protected] | Adds/updates WIT surface + documentation for enumerate-capabilities and aligns check-capsule-capability docs with the intended namespace. |
| crates/astrid-capsule/src/manifest/capabilities.rs | Implements held_names() and has() on CapabilitiesDef and adds unit tests ensuring consistency + fail-closed semantics. |
| crates/astrid-capsule/src/engine/wasm/test_fixtures.rs | Updates minimal_host_state fixture to include capability_names. |
| crates/astrid-capsule/src/engine/wasm/mod.rs | Captures capability name snapshot at load and stores it in each pooled HostState; sets empty snapshot for lifecycle hooks. |
| crates/astrid-capsule/src/engine/wasm/host/sys.rs | Completes check_capsule_capability using CapabilitiesDef::has and implements enumerate_capabilities; adds a host-fn unit test. |
| crates/astrid-capsule/src/engine/wasm/host_state.rs | Adds capability_names to HostState with documentation explaining why it’s an owned load-time snapshot. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…heck Bumps the wit submodule to pick up astrid:[email protected]'s new infallible enumerate-capabilities() -> list<string>, and implements it on the kernel side. The host fn returns the CALLING capsule's own held capability NAMES — the categories declared in its [capabilities] manifest block (host_process, net_connect, fs_read, …), not the scoped arguments within them. Because the WIT is infallible (a bare list<string>, no result), it reads an owned, lock-free snapshot taken once at load and stored on HostState rather than the capsule_registry — there is no registry-unavailable failure mode to surface, and an empty list is the valid no-capabilities answer. Capsule capabilities are fixed at load (the grant/revoke model is principal-scoped, a separate axis), so the snapshot is correct for the capsule's whole lifetime and across the pooled instances. check-capsule-capability is de-stubbed off the same namespace: it previously answered only allow_prompt_injection and returned false for every other capability. Both host fns now consult CapabilitiesDef, kept in lockstep by a single source of truth — held_names() (the list) and has(name) (the per-name dual), where n appears in held_names() iff has(n) is true. Unknown names fail closed. Both are ungated, read-only, and audit-not-recorded per the WIT contract: capability posture is structural metadata, not a secret (enforce-don't- conceal) — knowing a capability conveys no ability to use it.
51f49fc to
0f8d27e
Compare
## Linked Issue Closes #853 ## Summary `chore: release` — bumps all workspace crates **0.7.0 → 0.8.0** and rolls `CHANGELOG [Unreleased]` into `[0.8.0] - 2026-06-10`. Consolidates the ~50 PRs landed since v0.7.0: - **HTTP admin gateway** (#756 + follow-ups) — `astrid-gateway`: principals/caps/quotas/groups/invites/env over HTTP, audit SSE + historical queries, agent-prompt SSE, OpenAPI emission, bus-direct admin path (285× throughput), rustls TLS, CORS, metrics, invite + keypair CLI verbs. - **Runtime concurrency overhaul** (#813/#816/#819/#820) — routed per-(capsule, topic, principal) IPC with DRR fairness, async Wasmtime, dynamic instance pools, split blocking/IO semaphores, per-principal fuel + memory ledgers with enforcement and usage reporting. - **Host process + introspection surface** — `astrid:process` persistent tier (#866/#867/#871/#873), capability introspection (#868/#869), `astrid mcp serve` (#879/#880), `astrid-emit` (#814/#842). - **Security** — macOS 15+ sandbox no longer silently disabled (#855/#857), audit-feed principal scoping (#850/#851), failed-redeem audit rows (#846), `self:agent:list` roster leak (#848), pair-device redeem rate-limit (#844), bearer revocation + wire format v2 (#772). Breaking: `Capsule.toml` `[publish]`/`[subscribe]`-only IPC surface (#858/#864/#865, `[[interceptor]]` / `ipc_*` arrays / `[[topic]]` removed), bearer v2, MSRV 1.95, `astrid-openclaw` removed (#833). ## Changes - `[workspace.package].version` `0.7.0` → `0.8.0` + the 23 in-workspace path-dependency pins (now including `astrid-emit`; `axum`/`axum-server` stay on their external 0.7). - CHANGELOG: `[Unreleased]` → `[0.8.0] - 2026-06-10` with a release **synopsis** (extracted into the GitHub release body by release.yml on tag). Keeps the earlier roll's curation (canonical section order, bullets verbatim) and merges the 13 entries added to `[Unreleased]` by the 12 PRs that landed after the branch was first cut (#851–#885) — verified both directions that no bullet was lost. Branch history note: rebuilt on current `main` (was `CONFLICTING` and 12 commits behind); previous tip was `54c7294`. ## Release steps (maintainer) 1. Merge this PR. 2. Tag `v0.8.0` on the merge commit → release.yml creates the GitHub release from the CHANGELOG block. 3. Publish/distribute the new `astrid` binaries as usual. ## Test Plan Release PR — no code changes. `cargo check --workspace` passes on the rebuilt branch; full test/clippy/MSRV matrix runs in CI. The changelog merge was verified bullet-by-bullet in both directions: every `[Unreleased]` bullet on `main` appears in the rolled `[0.8.0]` section, and every bullet from the earlier curated roll is preserved verbatim.
Linked Issue
Closes #868
Summary
Implements
astrid:sys/host.enumerate-capabilities(the infallible() -> list<string>added by astrid-runtime/wit#13, merged) and completes the previously-stubbedcheck-capsule-capabilityonto the same capability namespace, so a capsule can introspect its own — or any capsule's — manifest capability posture. Motivation is Claude-on-Astrid: a reusable supervisor binary deployed under different manifests can ground its behaviour in what it can actually do instead of hard-coding it.Changes
enumerate_capabilities()returns the calling capsule's held capability NAMES — the categories declared in its[capabilities]manifest block (host_process,net_connect,fs_read, …), not the scoped arguments within them. Infallible per the WIT (a barelist<string>, noresult): it reads an owned, lock-free snapshot taken once at load (CapabilitiesDef::held_names) stored onHostState, never thecapsule_registry— so there is noregistry-unavailablefailure mode, and an empty list is the valid "no capabilities" answer. Capsule capabilities are fixed at load (grant/revoke is principal-scoped, a separate axis), so the snapshot is correct for the capsule's lifetime and across the pool.check_capsule_capabilityde-stubbed: it answered onlyallow_prompt_injectionand returnedfalsefor everything else. Both host fns are now derived fromCapabilitiesDef's serialized fields rather than a hand-maintained list —held_names()(the list) andhas(name)(the per-name dual) read the same struct, so a capability added to it flows through both automatically and the two cannot drift (n ∈ held_names()iffhas(n)); unknown names fail closed.witsubmodule to the mergedastrid:[email protected]enumerate commit (9742f80) and restageswit-staging.astrid-hookshost states (which run outside the capsule manifest/security-gate lifecycle) report an empty set, fail-closed.Both host fns are ungated, read-only, and audit-not-recorded per the WIT: capability posture is structural metadata, not a secret (enforce-don't-conceal).
Test Plan
Automated
cargo test --workspacepasses (365 inastrid-capsuleincl. the new tests; full workspace green — thegateway_tlsintegration tests only fail under the dev sandbox, which blocks the ephemeral TCP bind)cargo clippy -p astrid-capsule -p astrid-hooks --all-targetsclean)New coverage:
CapabilitiesDefunit tests assertheld_names/hasagree across the full namespace, thatheld_namescovers every serialized field (derivation guard against a future hand-picked subset), the default holds nothing, and unknown names fail closed; a host-fn test assertsenumerate-capabilitiesreturns the load-time snapshot and the empty default.Checklist
[Unreleased]