ao.webhook.v1 records as
the AO Webhook:
For systems that don’t run on a Toolkit host, such as a SaaS CI service,
use the AO Webhook instead. Records are
identical, so an emitter can switch transports without changing them.
The local socket
The Toolkit listens on a Unix stream socket and speaks plain HTTP/1.1, socurl and every language’s standard HTTP client can use it:
The socket is created with mode
0660. Whether a process may connect is
an ordinary file-permission decision: add the emitting service’s user to
the socket’s group, or run the emitter as root. The Toolkit never opens a
TCP listener for local events. A loopback port can’t tell callers apart,
and every local process can reach it.
A minimal event body:
event.json
affected_entities, the event attaches to the Toolkit’s own host.
A successful request returns 200:
source is how the Toolkit identified your process (see
Who sent it).
The Toolkit answers only after the event is written to its on-disk
delivery queue. An acknowledged event survives a Toolkit restart or
upgrade and is delivered once the Toolkit reconnects.
Toolkit in a container
When the Toolkit itself runs in a container, two things are required:- The host PID namespace (
--pid host,pid: host, orhostPID: true). The Toolkit identifies callers by their process ID. Without the host PID namespace it cannot see processes in other containers and rejects them withunidentified_peer(or labels them only by user ID, as your policy allows). - A shared socket directory. Bind-mount the host’s
/run/ao-toolkitinto the Toolkit container, and the same directory into every emitter container.
0660): run it as root, or give it the socket group’s GID.
Who sent it
On every connection the Toolkit reads the caller’s process ID and user ID from the kernel and resolves them to the systemd unit or container the process belongs to. It records that on every event as tags the emitter cannot override:
Events are also stored with that source, so events from different
services never overwrite each other, even if they use the same
source_event_id.
Policy: who may send what
Without a policy file, any process that can open the socket may send any namespaced event kind about its own host and that host’s services. To restrict sources, pointAO_EVENTS_POLICY at a YAML file. Once a
policy file exists, everything it does not allow is denied:
/etc/ao-toolkit/events-policy.yaml
A request with a kind outside the source’s list is rejected with
forbidden_kind, and a reference outside its entities with
forbidden_entity. Nothing from a rejected request is stored.
Attach events to the host and its services
You never need AO identifiers. Useself and the Toolkit fills in its
own host:
Host, HostService, Service) are matched case-insensitively and stored in that spelling.
Events about other hosts
An orchestrator such as a configuration-management master or a deploy runner reports on other machines that it knows by name. A source whose policy allowsany_host can reference a host by any one of these keys:
AO resolves the reference to the one host in your organization with that
value and stores the canonical host reference. A reference that matches
no host, or several, is kept as sent and listed as unresolved, so you can
see it instead of the event silently attaching to nothing.
Updates, closing, and retries
Events are identified bysource_event_id within a source on a host.
Sending the same ID again updates the event, which makes retries safe:
- To close an event, send the same
source_event_idwithclosed_atset. - AO keeps the version with the latest
observed_at, so setobserved_atto the time of each update.
The spool directory
For cron jobs and scripts, drop a file instead of making a request:- Write the
ao.webhook.v1body to<anything>.json.tmpin the spool directory. - Rename it to
<anything>.json. Renaming is atomic, so the Toolkit never reads a half-written file.
*.json files, validates them, queues the events,
and deletes the files. A file that fails validation moves to
spool/rejected/ with a matching .error file explaining why. Files
written while the Toolkit is down are processed when it starts.
Spooled events are attributed to the file’s owner (uid:<n>). Policy
rules can match on uid.
ao-toolkit emit
For shell scripts and configuration-management states, the Toolkit binary
sends an event for you. It uses the socket and falls back to the spool
when the Toolkit isn’t running:
--entity HostService:<unit> attaches the event to one of the host’s
systemd services.
Limits
Each source has its own rate limit (default 50 events per second with a burst of 500, configurable withAO_EVENTS_RATE and AO_EVENTS_BURST).
A source over its limit receives rate_limited and should back off. One
noisy source never delays other sources or the Toolkit’s own monitoring.
Check the Toolkit
ao-toolkit status shows the host ID, the socket and spool paths, and
per-source accepted and rejected counts.
Verify with your AI assistant
If your assistant is connected to the AO MCP server, it can do the whole integration from there. These are discovery tools: ask the assistant to callfind_tools if it doesn’t see them.
integration_guide: step-by-step instructions for your task, for example “emit events from a containerized app on a host”.host_identity: the host’s ID and the exactaffected_entitiesreferences for it and its services.validate_event: a dry run that checks your records and resolves every reference against your graph, for example “attaches to Host 847cdd30-…” or “orphaned: no Service with name=checkout”. Nothing is stored.- Send the event.
event_searchwith anaffectedSelectoron the host finds it.
docs_search and docs_get tools.
Reference
Event record (ao.webhook.v1)
Envelope: {"schema": "ao.webhook.v1", "records": [ … ]}. Ingested record types: event. Recognized but not ingested yet (unsupported_type): graph.attribute_affirm, graph.attribute_remove, graph.edge, graph.edge_remove, graph.node, graph.node_remove, telemetry.
Each
affected_entities entry:
kind must match ^[a-z][a-z0-9_-]*[:.][a-z][a-z0-9_-]+$.
Severity
Settags.severity. These labels are recognized (case-insensitive); anything else leaves the event without a severity.
Limits
Error codes: AO Webhook
Errors answer{"error": {"code": "…", "message": "…"}}.
Error codes: toolkit socket
Errors answer{"error": {"code": "…", "message": "…"}}.
Host references
Cross-host keys (Local sources whose policy allows any_host. On the webhook, reference a host by host_id (host_identity returns it).):
hostname, fqdn, instance_name, toolkit_id. Resolved at ingest to the one host with that value in the organization and stored as {“kind”:“Host”,“identity_tags”:{“host_id”:…}}. Zero or several matches: stored as sent and listed by integration_unresolved_refs (reason no_match / ambiguous).