mores.
The inter-agent signal standard — the shared conventions by which AI agents announce what they've done, what changed, and that they're alive.
01Two planes, one convention
Agent estates need two distinct communication planes, and most conflate them. Plane 1 is governed invocation: an agent calls a tool, policy is evaluated, a response returns. Plane 2 is signalling: an agent announces something — no request, no response, no data transfer. Mores is Plane 2: one lightweight, fire-and-forget schema every agent can speak, and any hub, broker or peer can consume.
Governed invocation
Synchronous request/response through a governance gateway (e.g. ai‑gap). Policy-evaluated, allow/deny, audited.
Signalling
Asynchronous, one-way telemetry + coordination: “I refreshed grid‑ef”, “source drift detected”, “I'm online”. Never blocks real work.
Design principle: ride a standard, own the semantics. The envelope is stock CloudEvents 1.0 JSON — brokers and gateways consume Bowdlr signals with zero bespoke tooling. The value is the taxonomy of agent signals, not wire framing.
02The envelope
Every signal is one CloudEvents 1.0 JSON object. Bowdlr adds five extension
attributes (all optional except moresversion and principal),
named per the CloudEvents extension rules — lowercase alphanumerics, ≤20 chars.
{
"specversion": "1.0",
"id": "0b9f2c64-8f1e-4d67-9c1a-7f3b2a9e5d10",
"source": "mcp://refdata.bowdlr.com/grid-ef",
"type": "com.bowdlr.refdata.product.refreshed",
"subject": "grid-ef@v1",
"time": "2026-06-28T09:14:03Z",
"datacontenttype": "application/json",
"dataschema": "https://contracts.bowdlr.com/mores/1/product.refreshed.json",
"data": { "product": "grid-ef", "version": "v1",
"source": "EPA eGRID", "rows_changed": 12,
"digest": "sha256:4f2a…" },
"moresversion": "1",
"principal": "svc:refdata-curation",
"correlationid": "req-7731",
"sequence": "10432"
}
idis the idempotency key — delivery is at-least-once; consumers dedupe on it.principalis the emitting service identity, mappable to a governance principal.correlationidcross-links a signal to a governance event or request on Plane 1.sequencegives strict per-source ordering;timegives best-effort global order.signature(optional) ised25519:+ 128-hex Ed25519 over the canonical envelope — the event minussignature, serialised as compact JSON with sorted keys, UTF-8. Unsigned signals are accepted but lower-trust.
03The vocabulary (v1)
Signal types are reverse-DNS, hierarchical, and versioned as a reference-data
product (Bowdlr master #38, mores-vocabulary). Bold data fields are
required; each type links its JSON Schema wire contract.
Reference-data lifecycle
com.bowdlr.refdata.product.refreshedactive | A product's data was refreshed from source. data: product, version, source, rows_changed, digest, cadence |
com.bowdlr.refdata.product.version.publishedactive | A new product version was released. data: product, from_version, to_version, changelog_url, breaking |
com.bowdlr.refdata.source.drift.detectedactive | The watcher flagged drift in an upstream source. data: product, source, drift_kind, severity, detail |
com.bowdlr.refdata.source.fetch.failedactive | A source fetch or integrity check failed. data: product, source, error, http_status, retry_at, attempt |
com.bowdlr.refdata.licence.changedactive | A product's redistribution posture changed. data: product, from_posture, to_posture, redistributable, reason |
com.bowdlr.refdata.health.statusactive | Periodic heartbeat / health of a ref-data surface. data: status, products_ok, products_total, note |
Agent coordination
com.bowdlr.agent.presenceactive | Agent came online / went offline. data: agent_id, state, surface_uri, capabilities_digest |
com.bowdlr.agent.statusactive | Free-form agent status report. data: agent_id, state, message, metrics |
com.bowdlr.agent.capability.changedactive | An agent's advertised capabilities changed. data: agent_id, added, removed, catalog_uri |
New types are additive (a vocabulary entry + a wire schema). Removing or retyping a
field, or changing a type's meaning, is a new major (/mores/2/…).
04Transport & the hub contract
v1 binding is the CloudEvents HTTP structured mode. A hub is anything that accepts:
| Request | Response |
|---|---|
POST /mores/v1/signalsContent-Type: application/cloudevents+jsonbody = the envelope above |
202 Accepted — stored |
| schema violation (envelope or data) | 400 + JSON error |
| other content type | 415 |
- Fire-and-forget: emitting never blocks or fails the caller's real work. Emitters buffer + retry; the hub is on nobody's critical path.
- At-least-once: consumers dedupe on
id. Emitters keepidstable per logical signal. - No payloads: signals carry metadata about data — digests, counts, versions — never the data itself, so they are safe on the observability plane and licence-clean by construction.
- A durable bus (NATS / Kafka via the CloudEvents bindings) is a drop-in later; the schema does not change.
05Adopting Mores
Three steps, no SDK required:
- Emit — build the CloudEvents object for a vocabulary type and
POSTit to your hub. Any CloudEvents SDK works. - Validate — run the conformance validator against your emitter's output:
python -m bowdlr_platform.reference_data.mores.conformance signal.json # or against downloaded artefacts, verifying signatures too: … conformance --schemas ./mores/1 --vocabulary ./vocabulary.yaml \ --pubkey <64-hex ed25519 key> signal.json - Discover — the vocabulary is queryable as reference data: the
mores-vocabularymaster on the Bowdlr refdata MCP/REST surface answers “what signals exist?” with the same file the emitter is driven by.
Wire schemas resolve under
contracts.bowdlr.com/mores/1/;
the vocabulary master ships as mores-vocabulary@v1.