Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(ipc): publish/subscribe ACL trailing * is subtree, matching delivery - #883

Merged
joshuajbouw merged 1 commit into
mainfrom
feat/acl-subtree-wildcard
Jun 10, 2026
Merged

joshuajbouw merged 1 commit into
mainfrom
feat/acl-subtree-wildcard

Conversation

@joshuajbouw

@joshuajbouw joshuajbouw commented Jun 10, 2026

Copy link
Copy Markdown
Member

Linked Issue

Closes #882

Summary

The publish/subscribe ACL authorization used the strict topic::topic_matches (a * 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'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 makes TopicMatcher::matches_topic the single source of truth shared by delivery and ACL — they can never silently diverge again.

Changes

  • astrid-events: extract TopicMatcher::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 via TopicMatcher::new(pattern).matches_topic(topic) instead of strict topic_matches.
  • Scope: only the two ACL sites change. Interceptor dispatch (dispatcher.rs) keeps strict topic_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; only prefix.* 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 --workspace passes
  • No new clippy warnings

cargo test -p astrid-events (matcher, incl. new matches_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-segment astrid.v1.admin.response.agent.list round-trip) and astrid mcp serve tools/list (8 tools, a 6-segment astrid.v1.request.mcp.tools.list publish) 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

  • Linked to an issue
  • CHANGELOG.md updated under [Unreleased]

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 TopicMatcher, it enables more flexible and maintainable ACL patterns where a trailing wildcard correctly represents a full subtree. This change simplifies manifest management for capsules and ensures consistent behavior across the system.

Highlights

  • Unified Topic Matching: Extracted the subtree matching logic from TopicMatcher into a reusable matches_topic method, establishing it as the single source of truth for both event delivery and ACL authorization.
  • ACL Authorization Improvements: Updated astrid-capsule to use the route-layer TopicMatcher for publish and subscribe ACL checks, allowing trailing * wildcards to cover entire subtrees without requiring depth enumeration.
  • Verification: Added comprehensive unit tests in astrid-events to verify subtree matching behavior and confirmed end-to-end functionality with collapsed manifest patterns.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +202 to +205
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))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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:

  1. In HostState (defined in crates/astrid-capsule/src/engine/wasm/host_state.rs), change the types of ipc_publish_patterns and ipc_subscribe_patterns from Vec<String> to Vec<astrid_events::TopicMatcher>.
  2. Populate them by mapping the manifest strings to TopicMatcher::new(pat) during capsule initialization.
  3. Update these check functions to directly call .matches_topic(...) on the pre-compiled matchers without any new allocations.
References
  1. Flag excessive or unnecessary allocations (e.g. .clone(), .to_string(), or Box where references/lifetimes or impl Trait would suffice). (link)

Comment thread crates/astrid-capsule/src/engine/wasm/host/ipc.rs Outdated
Comment thread crates/astrid-events/src/route/matcher.rs Outdated
@joshuajbouw
joshuajbouw force-pushed the feat/acl-subtree-wildcard branch 3 times, most recently from db51075 to 532ffeb Compare June 10, 2026 01:41
@joshuajbouw
joshuajbouw requested a review from Copilot June 10, 2026 01:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 out TopicMatcher::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 via TopicMatcher::matches_topic instead of the strict topic_matches.
  • Updates CHANGELOG.md with 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.

Comment thread crates/astrid-capsule/src/engine/wasm/host/ipc.rs
Comment thread crates/astrid-capsule/src/engine/wasm/host/ipc.rs
…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.
@joshuajbouw
joshuajbouw force-pushed the feat/acl-subtree-wildcard branch from 532ffeb to faef5da Compare June 10, 2026 02:04
@joshuajbouw
joshuajbouw merged commit 8b01087 into main Jun 10, 2026
13 checks passed
@joshuajbouw
joshuajbouw deleted the feat/acl-subtree-wildcard branch June 10, 2026 02:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Publish/subscribe ACL wildcard matching diverges from event delivery (forces manifest depth-enumeration)

2 participants