diff --git a/content/docs/guides/external-agent-registration.md b/content/docs/guides/external-agent-registration.md index 81f5686..aad1099 100644 --- a/content/docs/guides/external-agent-registration.md +++ b/content/docs/guides/external-agent-registration.md @@ -277,15 +277,38 @@ The platform enforces strict hierarchy-based access control via | Relationship | Allowed | |---|---| | Same workspace (self-call) | Yes | -| Siblings (same `parent_id`) | Yes | -| Root-level siblings (both `parent_id` is NULL) | Yes | +| Siblings (same non-null `parent_id`) | Yes | +| Root-level workspaces (both `parent_id` is NULL) | **No** | | Parent to child | Yes | | Child to parent | Yes | +| Ancestor to descendant (any depth) | Yes | +| Descendant to ancestor (any depth) | Yes | | Everything else | **Denied** | +The sibling rule requires **both** workspaces to share a non-null `parent_id`, so two +root-level workspaces (each with `parent_id = NULL`) **cannot** communicate — the +root-level bypass was removed (`molecule-core` #1961). Communication also extends up and +down the full ancestor chain, not just direct parent/child. + Canvas requests (no `X-Workspace-ID` header) and system callers (`webhook:*`, `system:*`, `test:*` prefixes) bypass this check. +### Checking access programmatically + +To test whether one workspace may message another without sending a real A2A call, +`POST /registry/check-access`: + +```json +POST /registry/check-access +{ "caller_id": "", "target_id": "" } + +→ 200 OK +{ "allowed": true } +``` + +It evaluates the same `CanCommunicate` rule above and returns `{ "allowed": }` — +useful for previewing the topology before wiring up delegation. + --- ## Canvas Appearance