feat(ipc): publish/subscribe ACL trailing * is subtree, matching delivery - #883
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 addresses a divergence between ACL authorization and event delivery topic matching. By standardizing on the route-layer 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. The topics flow in deep array, With wildcards leading on the way. No more to list each depth in line, One star now makes the subtree shine. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request unifies IPC subscription and publishing ACL checks by refactoring them to use the route-layer astrid_events::TopicMatcher::matches_topic method, ensuring consistent subtree matching semantics. Feedback focuses on performance optimizations for hot paths, specifically recommending pre-compiling TopicMatcher patterns at capsule load time to avoid heap allocations during runtime checks, and refactoring matches_topic to use an iterator-based approach instead of collecting segments into a Vec.
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.
| if !state | ||
| .ipc_subscribe_patterns | ||
| .iter() | ||
| .any(|acl| crate::topic::topic_matches(topic_pattern, acl)) | ||
| .any(|acl| astrid_events::TopicMatcher::new(acl).matches_topic(topic_pattern)) |
There was a problem hiding this comment.
Performance Anti-Pattern: Heap Allocations on Hot Path
Instantiating a new TopicMatcher for every ACL pattern on every subscription check causes a heap allocation (String clone) because TopicMatcher::new takes ownership of the pattern string. Since IPC subscription and publishing are hot paths, this can lead to significant memory churn and latency.
Recommended Solution
To achieve zero-allocation ACL checks at runtime, we should pre-compile these patterns into TopicMatchers at capsule load time:
- In
HostState(defined incrates/astrid-capsule/src/engine/wasm/host_state.rs), change the types ofipc_publish_patternsandipc_subscribe_patternsfromVec<String>toVec<astrid_events::TopicMatcher>. - Populate them by mapping the manifest strings to
TopicMatcher::new(pat)during capsule initialization. - Update these check functions to directly call
.matches_topic(...)on the pre-compiled matchers without any new allocations.
References
- Flag excessive or unnecessary allocations (e.g. .clone(), .to_string(), or Box where references/lifetimes or impl Trait would suffice). (link)
db51075 to
532ffeb
Compare
There was a problem hiding this comment.
Pull request overview
This PR aligns IPC publish/subscribe ACL authorization wildcard semantics with the event delivery matcher by routing ACL checks through astrid_events::TopicMatcher (so a trailing * behaves as a subtree wildcard consistently across both paths), and adds regression tests + a changelog entry documenting the behavior change.
Changes:
astrid-events: factors outTopicMatcher::matches_topic(&str)and adds targeted tests for subtree semantics and compatibility with depth-enumerated patterns.astrid-capsule: updates the publish + subscribe ACL checks to authorize viaTopicMatcher::matches_topicinstead of the stricttopic_matches.- Updates
CHANGELOG.mdwith a detailed note about the semantics change and its scope.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| crates/astrid-events/src/route/matcher.rs | Extracts matches_topic as the shared matcher entrypoint and adds subtree/compat tests. |
| crates/astrid-capsule/src/engine/wasm/host/ipc.rs | Switches ACL checks to TopicMatcher semantics and adds a regression test around non-terminal wildcard rejection. |
| CHANGELOG.md | Documents the ACL semantics change and clarifies what is and isn’t affected. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…livery The publish/subscribe ACL authorization used topic::topic_matches (strict equal-segment: * is exactly one segment), while event DELIVERY uses astrid_events::TopicMatcher (a trailing * is a subtree wildcard — one or more segments at any depth). That divergence forced capsule manifests to enumerate wildcard depth (astrid.v1.admin.* / *.* / *.*.*) just to authorize publishing topics whose depth varies or is unknown (uuid suffixes, variable sub-paths), and it is the same asymmetry that made the cli run-loop crash so confusing to diagnose. Authorize via the route-layer matcher instead, so a declared trailing * covers the whole subtree: astrid.v1.admin.* now authorizes every admin topic at any depth with no enumeration. The matcher is now a single source of truth (TopicMatcher::matches_topic) shared by delivery and ACL authorization, so the two can never diverge again. Scope: only the two ACL checks (publish + subscribe) change; interceptor dispatch keeps strict topic_matches. The change is permissive — authorizes more, denies nothing previously allowed — and breadth is the operator's call (the manifest declares intent; capabilities + install review are the boundary). Verified end-to-end: a collapsed cli manifest (astrid.v1.admin.* / astrid.v1.request.*) authorizes admin commands and the mcp front doors.
532ffeb to
faef5da
Compare
Linked Issue
Closes #882
Summary
The publish/subscribe ACL authorization used the strict
topic::topic_matches(a*is exactly one segment) while event delivery usesastrid_events::TopicMatcher(a trailing*is a subtree wildcard — one or more segments at any depth). That divergence forced capsule manifests to enumerate wildcard depth (astrid.v1.admin.*/*.*/*.*.*) just to authorize publishing topics whose depth varies or is unknown (uuid suffixes, variable sub-paths), and it's the same matcher asymmetry that made the cli run-loop crash confusing. This authorizes via the route-layer matcher so a declared trailing*covers the whole subtree, and makesTopicMatcher::matches_topicthe single source of truth shared by delivery and ACL — they can never silently diverge again.Changes
astrid-events: extractTopicMatcher::matches_topic(&str)from the existing subtree logic;matches(&event)delegates to it. Added a direct subtree test.astrid-capsule: the two ACL checks (publish_inner,check_subscribe_acl) authorize viaTopicMatcher::new(pattern).matches_topic(topic)instead of stricttopic_matches.dispatcher.rs) keeps stricttopic_matches; the runtime "wildcard must be terminal" subscribe gate is unchanged.Permissive change — authorizes more, denies nothing previously allowed; delivery was already subtree, so nothing that worked stops working. Adversarial pass: ACL is per-capsule declared intent (no cross-principal/escalation path); no match-all hole (a bare
*stays single-segment; onlyprefix.*is subtree-under-prefix); subscribe pattern-vs-pattern is sound (a broad ACL authorizes a narrower request, an exact ACL does not authorize a broader wildcard request). Breadth is the operator's decision at install (the manifest declares intent; capabilities + install review are the boundary).Test Plan
Automated
cargo test --workspacepassescargo test -p astrid-events(matcher, incl. newmatches_topic_subtree_for_acl) and-p astrid-capsule --lib(ACL / topic / audit-scope) — green, no regressions.Manual
Rebuilt the daemon, collapsed the cli manifest to
astrid.v1.request.*+astrid.v1.admin.*(dropping the depth enumeration and the mcp front-door exacts), reinstalled.astrid agent list(a 6-segmentastrid.v1.admin.response.agent.listround-trip) andastrid mcp servetools/list(8 tools, a 6-segmentastrid.v1.request.mcp.tools.listpublish) both work — authorized by the single subtree patterns.Follow-up (separate, capsule-cli): collapse the cli manifest once this lands — it currently enumerates depth + lists the mcp exacts (capsule-cli #25) because it must work against today's strict kernel.
Checklist
[Unreleased]