How AxiOwl Uses Enrollment as an Agent Allow-List

AxiOwl does not treat every discovered chat, display name, or stale session hint as a valid message target. It uses enrollment as a practical allow-list: a provider session has to be represented in the local registry, enabled, and marked sendable before AxiOwl will route normal delivery to it.

That detail is small but important. AxiOwl is built for agent-to-agent messaging across provider sessions, and names alone are not strong enough for that job. A chat title can be reused. A provider session can become stale. A copied path can survive after the real target is gone. Enrollment gives AxiOwl a durable place to record what it knows, where that knowledge came from, and whether the row is currently trusted enough to receive a message.

The Registry Is The Allow-List

The AxiOwl source describes the product as a local Windows coordinator with a CLI, MCP server, durable local registry, provider discovery, and provider-specific delivery edges. The registry is not a convenience cache. The developer docs call it durable local state that maps names and aliases to provider sessions.

In the implementation, an AgentRecord carries the fields that make enrollment useful:

That means enrollment is more precise than "AxiOwl once saw a name." A row can be known but not usable. A row can be enabled but not sendable. A row can carry a last error that explains why it should not be used. This gives operators an inspectable allow-list rather than an invisible pile of guessed targets.

Discovery Adds Evidence, Not Blind Trust

AxiOwl discovery modules search provider-specific surfaces and merge what they find into the registry. The user docs summarize the basic behavior: AxiOwl discovers provider sessions and chats, records them in a local registry, sends messages to selected provider sessions, exposes MCP tools for replies, and records receipts and delivery evidence.

The important constraint is that discovery does not automatically make every row safe. The developer docs state the rule directly: do not mark a row sendable unless discovery or direct proof justifies it, and do not let stale paths or stale display names become stronger than provider-owned session IDs.

The code follows that rule. In discovery.cpp, enrollment decisions distinguish rows with proof from rows that are only evidence. A discovered row must be sendable, must not be archived, and must not carry a last error to pass the general enrollment-proof test. Provider-specific discovery can be stricter. Local Codex-related policy has its own proof rules. Cursor, VS Code, OpenCode CLI, remote discovery, and other provider modules set sendable, has_enrollment_proof, status, evidence, and last-error fields based on what that provider can prove.

For example, Cursor CLI and OpenCode CLI discovery mark rows sendable when they find an active owned session with stable identity and usable workspace context. If the workspace directory no longer exists, those modules downgrade the row to not sendable and record a clear last error. VS Code native discovery records ownership evidence from the native bridge path. Remote discovery only treats a remote row as enrollment proof when the remote row is sendable and has a provider session ID.

That is the allow-list in action: discovery can add candidates, but only proof-bearing rows become delivery targets.

Send Resolution Checks The Allow-List

The message pipeline is where enrollment becomes a hard routing rule. A normal send request enters MessagePipeline, which validates the target and body, resolves sender identity, resolves the target registry row, optionally runs one targeted discovery repair, and then dispatches through the provider edge.

The key behavior is explicit:

  1. AxiOwl looks up the target in the registry.
  2. If the target is missing or appears to require repair, it runs targeted discovery once.
  3. If the target is still missing, the send is rejected.
  4. If the target is known but sendable is false, the send is rejected.
  5. Only after a sendable row is found does AxiOwl mark the request accepted_by_axiowl and hand it to the delivery layer.

That makes the registry more than a name index. It is the gate between a requested target and provider delivery. If AxiOwl knows about a session but does not have enough current proof to treat it as sendable, the correct result is not a best-effort delivery. The correct result is a loud failure: "target is known but not sendable," with the row's last error appended when available.

This is useful in daily operation because many failures are safer when they are early and specific. A stale session should not receive a message meant for a current chat. A display name that looks valid should not override a provider-owned session ID. A known broken bridge should not be quietly treated as a valid route.

Created Agents Are Enrolled Deliberately

Enrollment also appears in the create flow. When AxiOwl creates a new provider chat or agent, it only saves a sendable registry row after the provider returns a stable provider session ID. If the provider accepts the create operation but does not return a session ID, the CLI reports that no sendable registry row was created and records the lifecycle state as pending correlation.

When the provider does return a stable session ID, the create path builds an AgentRecord, sets it enabled and sendable, records the provider, session ID, node ID, display name, timestamps, and source, then writes the row into the registry. It also appends evidence that the row was enrolled.

That distinction matters. "A provider accepted a create request" and "AxiOwl now has a safe target for future sends" are not identical states. Enrollment requires a stable identity that can be used for future routing.

Manual Enrollment Is Powerful And Visible

AxiOwl also exposes registry commands for explicit operator action. The CLI usage includes:

axiowl registry add-agent --name <agent> --provider <provider> --session <provider-session-id> [--node <node>]

Manual registry insertion sets the row enabled and sendable, with manual as the default source when no source is provided. That is intentionally powerful: it lets an operator enroll a known target when discovery cannot infer it automatically.

Because manual enrollment is powerful, it should be treated like editing an allow-list. The row should name the real provider, the real provider-owned session ID, the correct node, and a display name the operator will recognize later. The implementation preserves manual and protected rows during discovery instead of casually replacing them with weaker automatic evidence.

Sender Identity Uses The Same Discipline

Allow-listing recipients is only half of reliable agent-to-agent messaging. AxiOwl also needs to know who sent the message so replies can route to a real session.

The architecture and developer docs define the sender identity priority: provider-owned MCP metadata first, then an explicit provider session ID that matches a registry row, then an explicit sender address or alias that maps to a registry row, then targeted discovery repair. The registry code is careful about session identifiers. A raw UUID-like session ID or provider session marker is not treated as a healthy human-facing sender name just because it appears in a field.

This is why AxiOwl's MCP path matters. Provider sessions can send through the axiowl_send_message MCP tool, and MCP replies are preferred because they include provider/session identity metadata. A correct reply proves more than a local CLI receipt: it proves the target received the message, had AxiOwl MCP available, replied through AxiOwl, and could be identified by session metadata.

Operators Get Clear Inventory Checks

The allow-list model is visible in operator commands. axiowl list agents shows enrolled registry rows. axiowl doctor inventory counts rows and local sendable targets by provider. The smoke-test code also searches for the newest local sendable target for each provider before trying to send.

Those checks are not cosmetic. They answer the operational question that matters before a message is sent: "Do we have at least one current sendable target for this provider?" If the answer is no, AxiOwl should not pretend the provider path is healthy.

The same pattern helps debug multi-provider setups. If a provider appears in discovery but has zero sendable rows, the problem is probably not the user's message body. It is enrollment proof, provider bridge health, stale workspace state, missing metadata, a broken patch/config path, or a provider-specific discovery failure.

The Practical Value Of Enrollment As A Gate

Using enrollment as an allow-list gives AxiOwl four practical benefits.

First, it prevents accidental delivery to stale or weakly identified targets. A display name alone is not enough.

Second, it makes routing explainable. The registry stores provider, session ID, node, source, timestamps, sendability, and last error in one durable row.

Third, it makes repair bounded. AxiOwl may run targeted discovery once when a target is missing or stale, but it still rejects the send if no sendable exact row appears.

Fourth, it supports real end-to-end proof. AxiOwl can separate "accepted by AxiOwl" from provider delivery and from MCP reply proof because each stage has a target identity and sender identity to attach evidence to.

Closing

AxiOwl's enrollment model is intentionally conservative. The system can discover sessions, create new chats, merge evidence, and repair missing targets, but the final routing question is simple: is this target enrolled, enabled, and sendable?

That is what makes enrollment an agent allow-list rather than a passive inventory. It gives AxiOwl a concrete boundary between known names and valid delivery targets, and it gives operators a clear place to inspect, repair, or deliberately enroll the sessions they want the system to use.