Multidomain Exchange for Realtime Integration of Data Innovation with Allied Nations
A low-friction, open-protocol, sovereign, decentralized exchange for real-time visibility and
integration across humans, servers, agents, machines and IoT. That sentence is
the product definition of record in AGENTS.md; every refusal later in this document
is a refusal because it would cost one of those five words.
One binary that carries two very different kinds of traffic: signed, stored, indexed events for humans and agents, and opaque, ephemeral, header-routed frames for machines. It is a modular replacement for the bus, policy and audit role of a central MQTT broker — and a decentralization story that a broker cannot tell.
ARCHITECTURE.md, DIAGRAM.md, TASKS.md,
AGENTS.md § Design Law, docs/mips/,
.settings/gauntlet/register.md, .settings/features/,
.settings/reference-docs/goat/, and the
.beads issue database. Bead IDs are cited inline so you can run
bd show <id> and read the source.
Both are binding contract in AGENTS.md, not editorial preference. If you take
nothing else from this page, take these — they are why the document is longer than a slide.
Traffic class, auth profile, whether the payload is parsed, and whether it is stored. One relay has two ceilings roughly two orders of magnitude apart. A bare number misleads in both directions, and quoting the good one without the qualifier is how you acquire an obligation the binary cannot meet.
Green CI is not evidence that an enforcement path runs in production. A deployed image predating the enforcement code makes a feature flag a silent no-op; a trusted proxy header is forgeable unless something strips it at the edge.
Prose cannot forbid an agent — or an engineer — from declaring a component complete, secure or
production-ready by self-assessment. A checker can.
.settings/gauntlet/register.md carries 21 workstream rows (G01–G21), each in
exactly one state — UNSTARTED → BASELINE → SLICE →
REDTEAM → PASSED — with a gate class that decides what evidence
counts: ci needs a named just … command, live needs a
probe: against a real relay, bench needs a benchmark and its
profile. just check-gauntlet fails the build on a row claiming a state its
evidence does not support, and PASSED is unreachable without a recorded
REDTEAM pass, because a self-graded gate is the exact failure the rules above name.
Read it before believing anything on this page. Today
14 of 21 rows are UNSTARTED. G02 and G17 are at BASELINE;
G01, G03, G14 and G21 are at SLICE; G15 is at REDTEAM; and no row
has reached PASSED. That is the honest maturity picture, and it is deliberately
more conservative than the design maturity the rest of this document describes. The findings
ledger now runs to F097 — 52 fixed, 45 open; each is a bead, because a markdown task list
is forbidden by the root contract.
Three rows are worth reading in full, because they are what the
loop actually buys you. G03 wired an isolated-DB gate that runs 157 previously
#[ignore]d meridian-db tests in CI — and running them cost six
findings, including the gate's green being a property of test order, proven by a
drain→pass→4→6 sequence and by one module failing 5/5 alone while green in the full selection. It
also retracted its own blocker: the "~2.5 h" cost estimate that had withheld the wiring
re-measured at 119–224 s. G01 is parked at SLICE after 7 iterations
— every regression repaired, remaining findings risk-class — which is the register
admitting that a guard can absorb more effort than the thing it guards.
G03 went to REDTEAM and came back to SLICE. Three fresh
critics were spawned with the artefacts and the repo's failure catalogue and no narrative
from the author. They confirmed the engineering — the drain restructure is
equivalent, no skipped or doubled goodbye, no hot spin; all 20 select! sites
across both crates swept with no other live hazard; the cancel-safety mechanism real and in
fact understated by its own comment.
Then they broke the ledger. The evidence cell asserted
cargo test -p meridian-relay --lib exit 0. The tree exited 101 — one test
defaulted to redis://127.0.0.1:6379, which on this host is a neighbour's
container, not Meridian's. The claim was false in exactly the way this whole register
exists to catch, and the row lost its state over it while the code it described was fine.
That is the difference between a gate and a green checkmark.
Before the traffic-class argument makes sense, the shape has to. MERIDIAN is
one self-hosted repository: source, builds and deployment all live in it, and
there is no sibling pipeline to coordinate with. What runs is three tiers with one enforcement
point between them. Source: DIAGRAM.md § Part I, ARCHITECTURE.md.
CLIENT TIER — TWO EDGES, ONE RELAY
NOSTR CLIENT EDGE [in tree] MACHINE EDGE [committed]
Desktop Mobile Web Agents Feeders · IoT · server-to-server
Tauri 2 + Flutter + repo meridian-cli peers. Speaks Zenoh natively —
React 19 Riverpod browser ACP·dev-MCP NOT NIP-01. MIP-XP link profile.
│ │ │ │ │
└──────────┴────┬─────┴───────────┘ │
│ │
WSS · NIP-01 EVENT / REQ / CLOSE / AUTH opaque frames · fixed header
HTTPS · /events /query /count /media never parsed · never stored
/git /hooks /info MIP-OF envelope
│ │
└────────────────┬─────────────────────────┘
│
RELAY TIER — meridian-relay, ONE Axum process, the ONLY enforcement point
STEP 0 · community bind resolve_host → TenantContext
runs BEFORE auth, event, req, REST, media, git
│
STEP 1 · admission NIP-42 challenge / NIP-98 · connection semaphore
│ handler semaphore (1024)
├─────────────────────────────┬───────────────────────────────
▼ ▼
STEP 2 · EVENT pipeline STEP 3 · REQ pipeline
kind gate · BIP-340 verify access check BEFORE registration
drift · size · identity historical query, then EOSE
scope · membership
└─────────────┬───────────────┘
▼
STEP 4 · SubscriptionRegistry — DashMap, 3-tier fan-out index
SUBSYSTEMS, called only by the relay and never by each other:
meridian-core · -db · -auth · -pubsub · -search · -audit · -workflow · -media
│
▼
STACK TIER — one system of record per fact
Postgres 17 Dragonfly MinIO / S3 Opt-in profiles
THE EVENT LOG STATE, never events BYTES, never keycloak · prometheus
events · channels pub/sub · presence events adminer · artifacts
members · workflows typing · rate windows media blobs │
audit chain · FTS caches git objects (CAS) │
│ ▼
▼ ReductStore
IPFS [planned] [opt-in · no
CID replicas call sites]
meridian-workflow never calls meridian-pubsub;
meridian-search never calls meridian-db. And both edges
terminate on the same enforcement point and the same event log. A second edge is a
traffic class, not a second authorizer; the moment it decides access on its own it has become
one, which the no-third-shim law refuses. The workspace is 29 crates (plus
examples/countdown-bot), asserted against the map in AGENTS.md in
both directions by just check-architecture-map — every crate is a member, every
member has a row, and the README's crate table is held to the same contract. Adding a crate
touches three files and omitting any one fails CI rather than an audit.
| Tier | Owns | Must never |
|---|---|---|
| Client | Rendering, local drafts, local-only settings, key custody, optimistic UI | Hold authority. A client is never asked to enforce membership, and talks to nothing but the relay — no client connects to Postgres, Dragonfly, S3, or a consumer |
| Relay | Community binding, admission, signature verification, ordering, membership enforcement, fan-out, re-authorization of every candidate | Delegate an access decision. Being the only enforcement point is what makes the no-third-shim law free rather than expensive |
| Postgres | The event log, and every fact derived by transaction — channels, membership, workflow runs, the audit chain | Be second. Nothing else may claim truth for a fact it stores |
| Dragonfly | Ephemeral state with a TTL — presence, typing, rate windows, replay set, caches — plus cross-pod delivery | Become a durable log. It is a keyspace and a transport, never a system of record |
| Object store | Bytes whose size makes them unfit for a row — media blobs, git packs | Hold anything the relay must interpret to make a decision |
Boxes and arrows are reproducible from any codebase; the must never column is the part that decays silently. Every architectural refusal later in this document — a second durable log, a second read path, a policy engine beside the relay — is this table applied. If you are proposing something and cannot say which row permits it, that is the finding.
meridian-relay is the entry point and also hosts git and huddle audio. Around
it: -core (types, verification, filter matching, the kind registry),
-db (Postgres event store), -auth, -pubsub (Dragonfly
fan-out, presence, typing), -search (Postgres FTS), -audit
(hash-chain log), -media (Blossom/S3).
-acp bridges events to AI agents; -agent is a minimal
ACP-compliant agent; -dev-mcp carries shell and file-edit tools;
-persona, -workflow (YAML-as-code, evalexpr conditions), and
-harness bundling the three.
REMAPPING/meridian-desktop/ (Tauri 2 + React 19), REMAPPING/meridian-mobile/ (Flutter),
REMAPPING/meridian-web/ (repo browser), REMAPPING/meridian-admin-web/ (read-only operator dashboard),
plus meridian-cli (agent-first), -sdk, -admin,
git-sign-nostr and git-credential-nostr.
Deployment is in-repo too: deploy/charts/ (Helm) and
deploy/compose/, images at registry.r2d2.office.ilab.zone/meridian-*,
hosts under *.meridian.r2d2.office.ilab.zone. A separate
control plane provisions per-account relays and is deliberately
not a second source of truth for tenancy — it stores the
community_id → host bridge and executes every mutation against the relay's own
NIP-98 operator API.
Dragonfly, not Redis. It speaks the Redis wire protocol, so REDIS_URL,
the redis crate, Lua scripts and pub/sub all work unchanged — but the container is
meridian-dragonfly and reintroducing a redis service is a regression,
not a rollback. The artifact tier holds no truth. ReductStore is opt-in, serves bytes on
the consumer axis, and currently has no call sites — wire a consumer or remove it, exactly as a
self-hosted Convex tier was removed rather than left as inventory.
Almost every wrong intuition about this system comes from collapsing these two paths into one.
They share a process and almost nothing else. Making that seam a deliberate module boundary —
rather than an emergent property discovered when someone adds a parse() to the hot
loop — is tracked as meridian-tg1. ARCHITECTURE.md states the same fact
from the ingress side — "one binary, two data planes", reached through the
two edges in §1 — and the two framings are the same seam seen from opposite ends.
HUMAN + AGENT PATH ── NIP-01 over WebSocket ─────────────────────────────────
parse JSON → BIP-340 verify → Postgres INSERT → FTS index → audit chain
└─ 34–37.7 µs ─┘ └───── the durability boundary ─────┘
ceiling: ~250–320k events/s per 16-core pod (verify-bound)
~10–30k/s once stored AND indexed
MACHINE PATH ── opaque frames, ephemeral kinds, trusted link ───────────────
read ~44 B fixed header → match key expression → forward bytes, never parsed
└─ no signature, no store, no index, no audit ─┘
ceiling: ~600k msg/s ≈ 0.44 Gbps in (ESTIMATE — path not yet in tree)
target: 1.0–1.5M msg/s per pod after the batching refactor (TARGET, gated)
SHARED: the community/tenant fence · admission · interest routing · the bus
NOT SHARED: parsing · crypto · durability · query surface · client shape
Anything the relay must act on MUST live in the header; everything else is bytes it forwards and never reads. That single sentence allocates the whole design: dedup moves upstream to the feeder (it requires comparing content), conflation stays at the relay (its key is a header field), NIP-01 tag filters are ruled off the hot path, and zero-copy fan-out becomes possible.
| Field | Width | Why the relay needs it |
|---|---|---|
| community | 16 B | the existing tenant fence — bound before any handler sees data |
| kind | 4 B | selects the QoS lane and the class floor. This is also payload_type — MIP-AS returned that resolution to MIP-OF, because the relay's only two uses of either are the lane and consumer discrimination, and the 44-byte budget has room for one field, not two |
| geo cell | 4 B | geohash-3/4 packed; this is the routing key |
| conflation key | 8 B | target id — ICAO24 (24 b) and MMSI (30 b) both fit. Makes anti-shredding implementable |
| sequence | 4 B | gap detection without reading content |
| timestamp | 8 B | TTL / staleness without reading content |
≈ 44 B fixed-width, no allocation, no parse tree. Spec:
MIP-OF (meridian-tdq) — DRAFT. The encoder is
postcard, already a workspace dependency. A worked budget exists: MIP-CT's ≤37 B core
track payload on this header is ≤81 B fixed-width, ~75 B after varints — for a track
carried without source attachment, which is exactly the kind of profile this page refuses to
drop.
Opaque machine frames are not consumable by a generic Nostr client. Consumers are purpose-built — a track display, a fusion engine, a recorder. That is the intended shape (general-use relay, specialty MIPs), and it belongs in the spec rather than being discovered in integration.
The honest version of the claim, and the one the Architecture & Scale council approved: Meridian replaces the broker's bus, policy and audit role. A broker remains legitimate — often correct — at the constrained-device edge, feeding the spine through a feeder and never as a routing peer. Read the three tables below in order; the third is the one that keeps us honest.
| MQTT / TBMQ | Meridian equivalent | Status |
|---|---|---|
| PUBLISH to a topic | signed event, or an opaque frame on a key expression | runs |
| SUBSCRIBE with wildcards | NIP-01 REQ filters (human path) · key-expression subscription (machine path) | runs / designed |
| Topic ACL evaluated per message | bound once at connect and at subscribe, from a host-derived tenant fence | runs |
| Kafka retention behind the broker | the events table is the log, the query store and the FTS index | runs |
| Broker-vouched publisher identity | per-event BIP-340 signature that survives the relay, storage, and third-party re-verification years later | runs |
| Cluster bridging between broker nodes | interest-scoped cross-pod fan-out over one bus, no N² bridge config | runs — 64× ingress reduction measured, at the subscribe side; the publish side is unconditional today |
| QoS 0 fire-and-forget telemetry | ephemeral kind range 20000–29999 — skips storage, audit and search by compile-time fence | runs |
| What a broker gives you | What we do instead | Cost / owner |
|---|---|---|
| Broker-held offline queues, persistent sessions (QoS 1/2, non-clean session) | Clients reconnect and re-run a REQ over stored events. Works for a chat client; does not work for a constrained device that cannot page history. |
will not add a second durable queue — keep a broker at the edge. Now partly specified: MIP-DD (meridian-bm32) defines DDIL session resume — a client on an intermittent link resumes a session that never logically ended, rather than reconnecting. It is not a queue and does not become one |
| Device SDK ecosystem | Every embedded platform ships an MQTT client; almost none ships a Nostr client. | specified MIP-AD, the adapter contract (meridian-qi7f) — this is the reusable asset (§6). Profiles built on it: MIP-ML MAVLink, MIP-NM NMEA 0183, MIP-RI Remote ID, MIP-DS ROS 2/DDS, MIP-AS AIS |
Shared subscriptions $share/group/topic |
Every matching subscription receives every event; competing consumers must coordinate themselves. | still no equivalent — MIP-MQ is where it would land, and it does not carry one yet. Name it before a workload depends on it |
| Retained messages, Last Will & Testament | Presence with a 90-second TTL does part of LWT's job. NIP-33 addressable events resemble retained messages without being them. | partial — gap is real, not papered over. The machine-path answer is a health beacon, not a broker primitive (§7) |
| QoS 2 exactly-once | At-most-once on the bus, idempotent at the store (ON CONFLICT DO NOTHING) → at-least-once-with-dedup end to end. |
different contract — but no longer an undescribed one. MIP-CU (meridian-8qkw) separates the five things NIP-01 OK conflates: accepted, stored, authorized, delivered, acted on. A caller can finally tell which it received |
Eight arguments, all decidable from shape rather than from a benchmark:
| Argument | Mechanism |
|---|---|
| 1 · The hop is mandatory; ours is architecturally conditional — but not yet conditional in code | Every TBMQ message crosses Kafka even when publisher and subscriber sit on the same node. We serve local subscribers from an in-process DashMap, so the delivery path for a local subscriber never touches the bus. Correction, and it is ours to own: this page previously said we "touch the bus only for cross-pod reach, only on retained topics." Redis PUBLISH is unconditional today — neither condition is built, and no topic-retention concept exists in the codebase (DIAGRAM.md:215). The shape argument survives — broker cost grows with total traffic, ours can grow with cross-pod traffic, and tenant sharding reduces that term — but it is a property of the design, not a property of the running binary, and the 64× measurement is of interest scoping at the subscribe side. |
| 2 · Kafka is a second durable log | We already have one. A second buys retention we have and costs an operational tier plus truth ambiguity. This is refusal R2, written before TBMQ entered the conversation. |
| 3 · Authorization binds per message | MQTT topic ACLs are evaluated on every PUBLISH and SUBSCRIBE. We bind at connect and subscribe. Stated honestly: we are better placed, not clean — filter_fanout_by_access is our own per-recipient residual. |
| 4 · Message authenticity dies at the broker | An MQTT message is authenticated by its connection. Downstream there is no proof of authorship, so audit chains, third-party verification and cross-operator federation must be rebuilt above the transport. This is what the ~37.7 µs buys. |
| 5 · QoS is a delivery dial, not a congestion dial | QoS says how hard to try to deliver. It says nothing about which class to shed under load. A 100 Hz telemetry stream and a chat message should not share a movement contract. Caveat: our eight-class map and conflated are designed, not built — today we too have one undifferentiated queue. |
| 6 · Tenancy is a topic-prefix convention | MQTT has no tenant boundary in the protocol, so multi-tenancy becomes prefixes plus ACLs — exactly the "names encode ownership" failure our Design Law refuses, where a reorg becomes a fleet-wide ACL edit. Our community is host-derived, immutable on the row, and part of every primary key. |
| 7 · One scaling axis, not five | TBMQ scales by adding broker nodes and Kafka partitions. It offers nothing for function-split, tenant-shard, traffic-class or trust-domain federation. Those four are what §4 is built on. |
| 8 · Bridging is O(N²) | MQTT bridges are point-to-point configuration; N interconnected networks need N² hand-maintained bridges. Interest-based transitive routing replaces that at O(N) — and is blocked here on cryptographic enforcement work, which we say rather than imply. |
Refusing a second durable log is right, and the reason is that the events
table is already the log. It is not yet right for the reason a reader might infer
from a durability diagram. DIAGRAM.md now opens its write-path section with
TARGET STATE, NOT AS-BUILT: ingest_seq, the commit watermark and the
consumer cursor do not exist — grep -rn ingest_seq over
migrations/ and crates/**/*.rs returns zero.
What exists today is a client-driven REQ backfill capped at 2000
(handlers/req.rs:25) with no gap detection. So "consumer down? no message is
lost" is a property this design would buy, not one the system has. Do not cite the
durable cursor as the reason no broker is needed until the column exists
(meridian-5riu). Cite the events table, which is real.
THE RATIFIED TOPOLOGY — hub and leaf, never peer
constrained devices EDGE — MQTT bridge FEEDER SPINE
MQTT clients, ──► + LEAF RELAY ──► decode ──► ONE authoritative
intermittent links persistent sessions dedup relay per community
need offline queues QoS 1/2 · retained routing header admission
LWT · shared subs MAC or sign verification
store-and-forward batch tenancy fence
across outages audit · fan-out
✕ REFUSED: the leaf as a routing peer.
A leaf outage would become a spine routing failure, and a compromised
leaf could transit between communities. (Refusal R4 · Law 2)
meridian-3wh, §4) stays dormant rather than becoming
live. A topology that made leaves into peers would activate it — which is why this is a
ratified constraint and not a deployment preference.
There is no head-to-head benchmark. Nothing here is a measurement of TBMQ, and nothing here is a measurement of Meridian against TBMQ on one workload. The architectural argument is decidable from shape; the numeric comparison is not. Publish this as an architecture and cost-model comparison, never as a benchmark result.
A broker scales one way: more broker nodes, more partitions. We scale five ways, and the discipline that makes that safe is the refusal register — the moves this architecture will not make, each with its reason and its review-time tell.
Shard by who owns the data and by what job runs — never by what the data is called. A kind is a column in every shard, never a shard.
| Axis | You add | Failure story | Status |
|---|---|---|---|
| A1 · Replica — identical spine pods behind one URL | a pod | lose a pod → nothing happens | today |
| A2 · Function — truth on the spine, derived views in consumers | a consumer container | lose a view → staleness, published | proposed |
| A3 · Tenant — shard whole communities | a community shard | lose one tenant → the others keep running | key already immutable |
| A4 · Traffic class — persistent vs ephemeral, QoS lanes | a lane, an edge pod | lose droppable traffic → by design | range fenced; lanes unbuilt |
| A5 · Trust domain — networks federate rather than merge | a peered network | lose a peer → the local network stays whole | blocked — see below |
If a proposed split's failure story is not one of those five sentences, it is on the wrong axis.
NIP-33 addressable events resolve last-write-wins by created_at. Membership
already lives in that range (39001/39002). So a revoke and a
concurrent grant healing across a partition, or across two relays, resolve by later timestamp
and resurrect the revoked grant — silently.
The rule is banked in Design Law: revocation is monotonic and absorbing, carried by an
explicit epoch/generation counter over an append-only log, resolved fail-closed. Once revoked at
epoch n, no grant at epoch ≤ n reinstates. Decided now, built at the
second node (meridian-3wh) — dormant while one relay is the only writer,
and it must land before any relay-to-relay federation, because after is too late.
| Refusal | Why it fails here | The tell in review |
|---|---|---|
| R1 shard by kind/NIP | reads are cross-kind by construction; gift wrap encrypts the inner kind so the router cannot see its own key; kind volume is skewed → one hot shard, N idle | a deployment map or ACL keyed by kind |
| R2 a second durable log | the events table is already the log; a second buys retention we have and costs truth ambiguity | a broker with a retention setting |
| R3 a second read path for data the spine stores | "a correctness liability for zero measured gain" | a consumer answering filters Postgres already serves |
| R4 consumers promoted to routing peers | a consumer outage becomes a spine routing failure; a compromised consumer can transit between communities | a consumer in router peering config |
| R5 grant copies in the scaled-out part | the third shim — a second policy store drifts from the first | an ACL cache with no freshness stamp; a sync job between two stores |
| R6 client-side multiplexing as the scale story | works in open Nostr precisely because there is no membership, tenancy, ordering or audit contract — that contract is this product | a client holding a per-capability relay list |
| R7 parallelizing what serializes by meaning | the audit chain and per-community ordering are serial because their semantics are serial; the sanctioned move is A3 | "shard the chain" / "split the arbiter" |
| R8 scale-out ahead of a measurement | machinery for load that does not exist is inventory | a new tier whose PR carries no Phase 0 number |
This is the table to memorize. Note how many rows say estimate. That is the point: the discipline is what makes the measured rows worth quoting.
| Profile | Figure | Basis |
|---|---|---|
| Opaque pre-decoded frames · trusted P0 link · payload never parsed · not stored | ~600k msg/s ≈ 0.44 Gbps |
estimate The path does not exist in tree. The pre-/post-dedup question behind it is open at ~10×. |
| Same profile, after the batching refactor — the program's stated goal | 1.0–1.5M msg/s per pod |
target MRDN-810 is the gate. The scoping figure behind it is 1.337M msg/s, and that number is TBMQ-class vendor-published, not measured on our stack — MRDN-101 establishes our own baseline before anything commits to it. |
| Signed NIP-01 events · per 16-core pod · BIP-340 bound | ~250–320k events/s |
measured per-core, extrapolated verify_event = ~30k/s on one core, observed 21.6–33.1k over 3 runs (M3 Max, release, one core, batch 1/8/64 — just bench). At ~30k/core a 16-core pod tops out near ~480k/s for verification alone, 350–530k across the observed spread. |
| Stored and indexed chat events · one Postgres | ~10–30k/s | estimate Persistence latency is measured — p95 74.6 ms direct, 77.1 ms nested, synchronous_commit, concurrency 1. |
| Per-event BIP-340 verify (single; no batch API exists) | 34–37.7 µs | measured meridian-core/benches/event_cost.rs. Flat across batch 1→64. |
| Fan-out, per event | ~4.1 µs @ N=1 ~56 µs @ N=64 |
measured fanout_cost.rs — a fixed ~2–3 µs JSON serialize plus ~430 ns per recipient. Past ~40 recipients, fan-out exceeds signature verification. |
| Symmetric MAC alternative to per-event signatures compare at a matched batch size |
~120× / ~400× at batch 1 ~200× / ~420× at batch 64 |
measured HMAC-SHA256 / keyed BLAKE3. Honest bands at batch 1 are ~110–170× and ~360–550×, because they divide by the BIP-340 number whose observed spread is 21.6–33.1k/s. schnorr_verify_nostr is flat across the 1/8/64 sweep; the MAC groups are not, so a MAC figure without its batch size is a figure without its profile. |
| Bus — Dragonfly today, interest scoping | 64× ingress reduction |
measured perf/relay_bus_scaling.py --mode redis, 64 communities, 1 subscribed, pods 1/2/4. |
| Bus — Zenoh, peer mode, one pod pair, 256 B payload | ≥2M msg/s | target, unproven Gated on Phase 0 (meridian-ctg). If Zenoh is under 5× on this named profile, we stop and defer the whole chain. |
The verification ceiling used to read 26.5–29.4k events/s. Re-running the same
bench on the same machine reproduces 21.6–33.1k, so the published interval was
narrower than the measurement supports — and nothing isolated the bench from host load.
The number was never wrong; its precision was. Every derived figure moved with it: the
pod ceiling from ~420–470k to ~480k with a 350–530k band, and the MAC ratios from exactly
125×/405× to ~120×/~400× with bands, because they divide by the number whose spread this
is (meridian-9fqe).
That is rule ① applied to ourselves rather than to a vendor, and it is the reason
just bench now exists as a recipe: it prints the CPU, core count, OS, traffic class,
auth profile and parsed/stored status alongside the numbers, so a figure cannot
be lifted out of its output bare. Seven bench groups run — every group backing a
published figure on this page.
The same species of defect was found twice more while reconciling this page, and both are
now fixed at the source. (1) AGENTS.md stated the MAC advantage twice —
~120–400× in one paragraph and 179–382× two paragraphs later, as though
they were the same quantity. (2) ARCHITECTURE.md quoted HMAC at
3.67 M/s while TASKS.md quoted 5.27 M/s / 0.19 µs, and neither said
which batch size it meant.
One root cause, and it is instructive. The bench sweeps batch 1 / 8 / 64 and records
both endpoints. schnorr_verify_nostr is flat across that sweep — which is
the whole point of sweeping it, since with no batch API cost is strictly linear in N — but
the MAC groups are not flat: HMAC-SHA256 runs 3.67 M/s at batch 1 and 5.27 M/s
at batch 64. ARCHITECTURE was quoting batch 1, TASKS batch 64; both were right and neither said
so. And 179–382× turned out to be neither — it divided MAC throughput at batch
64 by Schnorr throughput at batch 1, a mixed profile matching no row of the bench's
own table. It had propagated from the bench's doc comment into four documents.
Matched-profile ratios, which is what this page now carries: batch 1 → 125× and 405×; batch 64 → 199× and 423×. A ratio carries its batch size for exactly the reason a throughput carries its traffic class — which is rule ① again, one level down. Neither error changed a conclusion: BLAKE3 wins on every reading, by two orders of magnitude.
Ingest CPU required to sustain 1,337,000 msg/s on one host, profile ingress, QoS 0 equivalent, small payload, never stored:
| Path | Per-msg | Cores | Verdict |
|---|---|---|---|
| Signed NIP-01 events | 37.7 µs m | 50.4 | Not viable — ~3.2 × 16-core pods for verification alone, before routing, framing or egress |
| Opaque frame + HMAC-SHA256 @ batch 64 | 0.19 µs m | 0.25 | viable |
| Opaque frame + keyed BLAKE3 @ batch 64 | 0.089 µs m | 0.12 | viable — specify BLAKE3. It is currently a meridian-core dev-dependency and must be promoted to a production one (MRDN-312) |
| Opaque frame, P0 trusted link, no MAC | 0 | 0 | viable where the link is trust-domain-internal |
| Pipeline overhead, unbatched | ~2 µs e | 2.67 | the dominant cost today |
| Pipeline overhead, batched at 64 | ~150 ns t | 0.20 | the meridian-0zd win |
The target is architecturally reachable on the opaque-frame path and architecturally unreachable on the signed path. That is the whole case, and it must always be published with both halves — a figure quoted without the second row is exactly the obligation rule ① exists to prevent.
At 10M users / 1M concurrent, human message volume is only ~3.3k events/s; with agents at 5× human volume, ~20k/s. One verification pod covers it. Fan-out at an average 50 recipients is ~1M frames/s.
At vast scale this system is fan-out bound, not crypto bound — the opposite of the per-pod ceiling story, and the number that should drive any comparison. Large-channel fan-out is named as the honest open problem: it is not solved, and fan-out-on-read or a broadcast class is the unbuilt answer.
egress = R_in × Σ f_s f_s = the fraction of the keyspace
s subscriber s declared interest in
Ingest is a fixed ~0.44 Gbps at 600k msg/s. EGRESS MULTIPLIES.
10 subscribers, all want everything (f=1) → 6M msg/s ≈ 4.4 Gbps
10 subscribers, 1% of cells each → 60k msg/s ≈ 44 Mbps
100 subscribers, 1% of cells each → 600k msg/s ≈ 0.44 Gbps
| Cost | At 600k/s | Note |
|---|---|---|
| Signature (BIP-340) | 0 | ephemeral + session frames; not on this path |
| Link auth (P0 profile) | 0 | mTLS/QUIC peer identity on a trusted feeder link |
| MAC (P1 profile, if required) | ~9% of a core | ~150 ns each |
| Header parse + key match | negligible | fixed-width, no parse tree |
| Async pipeline overhead | ~0.6–1.8 cores | the dominant ingest cost — ~1–3 µs/msg estimate |
| Bandwidth | ~55 MB/s | 600k × (32 B header + ~60 B state) |
Ingest is cheap; the relay can carry the firehose directly — if and only if it
never parses it. Aggregation is an optimisation, not a precondition. The single structural fix for
the dominant row is meridian-0zd: make the batch the type flowing through the
pipeline (mpsc::Sender<FrameBatch>, not <Frame>), with a
synchronous inner loop. Estimated 10–20×.
Two pieces of work sit between a device feed and the spine, and they are the highest-leverage reusable assets in the program.
The Zenoh peer runs inside the relay process. That is the whole point, and
zenohd is not the destination. Today every cross-pod event takes two hops
(pod A → Dragonfly → pod B). In peer mode pods connect directly and the broker hop
disappears — a latency win that is independent of any throughput figure. The
zenohd router daemon is added only where a full peer mesh stops being
viable, because peer links grow with the square of the pod count; a small or single-region
deployment may never run one at all. When it is added it goes under a new opt-in
bus compose profile, default off, because core services must stay profile-free —
just _ensure-services blocks on core health and would hang 120s against a disabled
service.
And the seam matters more than the vendor. The sequence is an
EventBus trait seam first (meridian-8rn), then a
ZenohEventBus in peer mode behind an env gate with dual-publish shadow mode
(meridian-bem), then QoS classes, then routers (meridian-0ps). Nothing
below that seam exists in the workspace today — there is no zenoh dependency in
any Cargo.toml, and Zenoh is not authorized by the program plan until
meridian-ctg clears.
Zenoh is the transport spine for everything except the Nostr client edge. That exception is not a compromise; it is what lets the spine exist at all, and it keeps Nostr interoperability — the project's stated core value — intact.
| Surface | Zenoh's role | Axis | Gate before it lands |
|---|---|---|---|
| Inter-pod bus | replaces Dragonfly PUBLISH/PSUBSCRIBE, peer mode in-process | A1 | ctg → 8rn → bem |
| Machine / IoT / agent ingress | native Zenoh edge, MIP-XP profile — not NIP-01 | A4 | MIP-XP + MIP-SF (owl) |
| Relay ↔ relay mesh | candidate consolidation with the existing iroh QUIC mesh | A1 | must be decided, not deferred |
| Cross-org federation | zenohd routers peering across trust domains | A5 | blocked on epoch-monotonic revocation |
| Nostr client edge | none — NIP-01 JSON over WebSocket stays | — | permanently out of scope |
Three properties keep this checkable in review: Zenoh adds no event kind and no
client-facing NIP (MIP-SF adds a message type, SFRAME, in the existing ephemeral
range); Zenoh is never a store — zenoh-backend-* storages exist and are not a
transactional store with FTS, partitioning and an audit chain; Dragonfly is narrowed, not
displaced — presence TTLs, rate-limit INCR+EXPIRE, the NIP-98 replay
set and the fenced arbiter all need a keyspace with atomics, and Zenoh is a pub/sub fabric, not a
keyspace.
MIP-AD (meridian-qi7f) is now a written spec, and it is the mechanism
that makes every subsequent protocol profile thin. It pins the four things every feed
argues about and no envelope can infer: units, coordinate reference system, time base, and
declared lossiness. Profiles built on it — MAVLink, NMEA 0183, Remote ID, ROS 2/DDS,
AIS — add no mechanism of their own. Six obligations:
The relay never parses. All content-aware work happens here, before the spine.
Dedup compares content; the relay has forsworn content. Shard it geographically so the dedup key and the routing key are the same key — no shared state.
Geohash cell, conflation key, sequence, timestamp. The feeder already decoded the position, so this is free here and impossible downstream.
Per the destiny rule, not per a performance preference — see the profile ladder below.
64 states per frame turns ~600k pipeline traversals/s into ~9.4k, and header overhead from ~35% to ~6% (~25% bandwidth saving).
The author is the feeder: "I, receiver R, observed these states at time T." Enforced by NIP-70 protected events.
MIP-XP, meridian-2e2)The profile floor is set by the message's destiny, not by a performance preference. You cannot negotiate away the signature on data that leaves the trust domain, because the signature is what makes it leave-able. Negotiated once per link; enforced per message.
P0 none — link identity only (mTLS/QUIC), 0 cost, 0 bytes · P1 MAC ~50–200 ns ·
P2 ed25519 batch-verified ~5–8 µs · P3 BIP-340 ~30–70 µs, NIP-01 native.
Per-class floors: ephemeral = P0 · persistent = P2 ·
stored-federated = P3.
Today all of these share one undifferentiated lane, so a burst of typing indicators can consume buffer that chat needs. The map below is designed (Zenoh Phase 3); the bottom two rows are the proposed drone/machine additions.
| Class | Priority | Congestion | Reliability | express |
|---|---|---|---|---|
| Fence / control — ban enforcement, cache invalidation | RealTime | Block | Reliable | yes |
| Huddle audio — Opus frames | RealTime | Drop | BestEffort | yes |
| Huddle control · moderation 9000–9044 | InteractiveHigh | Block | Reliable | — |
| Agent observer (24200) | InteractiveLow | Drop | BestEffort | — |
| Jobs / workflows | DataHigh | Block | Reliable | — |
| Chat · reactions · deletes | Data | Block | Reliable | — |
| Typing · presence (20001/20002) | DataLow | Drop | BestEffort | — |
| Push leases · backfill · reindex | Background | Block | Reliable | — |
| Kinematic state frames (proposed 21000) | RealTime | Drop → conflate | BestEffort | yes |
| Sensor / health telemetry (proposed 21001/21002) | InteractiveLow | Drop | BestEffort | — |
Do not merge a kind constant without a QoS class. kind_to_qos()
lives next to the constants in meridian-core/src/kind.rs, is exhaustive over the
documented ranges, and carries a compile-time assertion per range. It is the twin of the
existing rule that no kind may merge without a reader.
| Knob | What it buys |
|---|---|
transport/link/tx/queue/size/{control,real_time,interactive_high,…} | Deep queues for Data, shallow for RealTime — so audio and kinematics drop rather than delay. A deep queue on a realtime lane is latency debt disguised as reliability. |
transport/link/tx/queue/congestion_control/{drop,block}/wait_before_close | The exact knob for "shed typing indicators under load instead of buffering them." |
transport/link/tx/batch_size (default 65535) + adaptive batching | Coalesces small messages into one network batch when the link is busy and sends immediately when idle. This is where the msg/s figures come from, and it is precisely what one-PUBLISH-per-event cannot do. |
Zenoh already batches at the transport layer. That win is completely destroyed
by for msg in batch { tx.send(msg).await } immediately above it. The batch must
survive all the way to fan-out, or the transport batching buys nothing. It also amortises TLS
record framing for free — one record per batch rather than per message, which matters because
per-record overhead at 600k records/s is significant while bulk AEAD at 55 MB/s is not.
zenohd optionally enters
TODAY Pod A ──┐ two hops per cross-pod event
[in tree] Pod B ──┼──► Dragonfly ──► SPOF + broker hop
Pod C ──┘
PHASE 2 Pod A ◄──► Pod B peer mode, ONE hop.
[committed] ▲ ╲ ╱ ▲ THE PEER IS IN-PROCESS in
meridian-bem │ ╳ │ every relay pod — this is
└─► Pod C ┘ the destination, not a
waypoint.
Dragonfly stays — STATE ONLY:
presence TTL, rate limits,
replay set, fence generation
PHASE 4 region 1: Pod A ─┐ ┌─ Pod C :region 2
[OPTIONAL] ├─ zenohd ◄─► zenohd ─┤
meridian-0ps region 1: Pod B ─┘ └─ Pod D :region 2
Added ONLY where a full peer mesh stops being viable
(peer links grow with the square of the pod count).
Opt-in `bus` compose profile, default off.
Today the relay does not re-verify signatures on events arriving over the bus, justified by a documented trust assumption: the bus only ever carries events between nodes inside the relay trust domain. That holds because Dragonfly is a private, in-cluster service.
zenohd routers make cross-org federation cheap, and cheap federation is how
that assumption dies quietly. The moment a router peers with a relay outside the trust
domain, "trusted bus" becomes false without any code changing. This must be fenced explicitly
before Phase 4, not after.
This is the trade the brief names, and it deserves its own vocabulary because it is the single most likely thing for a team arriving from MQTT to get backwards.
QoS 0/1/2 is a promise about a message: deliver it at most once, at least once, exactly once. Under congestion the broker's options are to queue it or lose it. Neither option knows that message #941 for target X has been made meaningless by message #942.
A kinematic frame is a superseding snapshot: the newest state for a target replaces the previous one, and the older has no residual value. The correct behaviour under congestion is therefore not to queue and not to shed randomly — it is to keep the latest state per target and discard superseded entries.
For superseding state, a conflating buffer under congestion loses nothing. Every target is still delivered at its newest position; only the update rate degrades. No target ever disappears from the picture. A dropping buffer shreds the picture — targets blink out at random. A conflating buffer ages it — every target is present, possibly a second stale.
| Contract | Under congestion | What the subscriber is promised | Status |
|---|---|---|---|
persisted | never dropped | complete, ordered, stored | runs |
volatile | dropped | gaps possible — samples genuinely lost; a target may vanish from your display | range fenced, contract undeclared |
conflated | superseded entries dropped, by key | no target lost, rate degrades; every target present, possibly a second stale | DRAFT MIP-QC · meridian-9j8 |
An operator watching a tracking picture has to know which guarantee they have. An undeclared stream forces the consumer to assume the weaker one — which throws away the entire benefit. So the contract and its conflation key are part of the wire spec, not an implementation note. And it is only implementable at all because the conflation key lives in the header.
Applied at ingest rather than only under load, conflation becomes a throughput mechanism. Ten receivers reporting target X within one flush window produce ten writes to the same header-keyed slot — nine are overwritten, one leaves. That is a content-free ~10× on a redundant feed, using only the header conflation key, and it de-risks the open pre-/post-dedup question below. Fan-out is the expensive stage because it multiplies by subscriber count, so every message collapsed before that stage is saved once per subscribed keyspace fraction.
design proposal The relay-side contract is specified
(meridian-9j8); the client-side twin below is derived from it here and is
not yet a bead. It is the first thing this team should file.
sequence for gap detection, not for reassembly — a gap in a conflated
stream is expected and benign; a gap in a volatile stream is data loss. Same field,
two different meanings, decided by the declared contract.21002) says the platform is alive. Position silence says
nothing new to report, or the link is down, and the client cannot distinguish them
without the beacon. This is the closest thing we have to Last Will & Testament, and it is a
beacon rather than a broker primitive precisely because we are time-based.It asked whether the 400–600k msg/s feed figure was pre- or post-dedup, because the answer
moves egress sizing by ~10×. MIP-AS settles it: the deployment feed is
pre-dedup at roughly 10× receiver redundancy — ~400k raw frames/s resolve to ~40k unique
vessel states/s. Dedup is therefore the single highest-leverage operation in the path,
it removes ~90% of every downstream cost, and it lives at the feeder because it compares content
and the relay has forsworn content.
The spec does not just assert the ratio, it makes the deployment keep proving it: a feeder
MUST publish meridian_ais_frames_received_total and
meridian_ais_states_emitted_total, whose ratio is the dedup factor. That is
normative precisely because a deployment which cannot answer this from its own metrics will
answer it from memory and be wrong.
| Stage | Rate | B/report | Wire | Label |
|---|---|---|---|---|
| A — as observed today: MQTT JSON, pre-dedup, 4 topics | 400k/s | 2384 | 7.63 Gbps | measured payload size |
| B — MIP-OF format only, still pre-dedup | 400k/s | 68 | 0.218 Gbps | estimate 35× |
| C — plus feeder dedup at 10× | 40k/s | 68 | 0.022 Gbps | estimate 351× |
| D — plus spatial batching at 64 | 40k/s | 32.7 | 0.011 Gbps | target 729× |
The three multipliers are independent and compose — format ×35, dedup ×10, batching ×2.1. Dedup is the cheapest of the three to implement and the second largest: do it first.
At 40k post-dedup states/s, ingest is not the problem on any path. Even the fully signed path costs only ~1.5 cores. So the argument for the opaque frame path at this workload is not ingest CPU, and the reverse claim — "we need MIP-OF because AIS ingest is too expensive to verify" — is false at 40k/s and would be caught by the first reviewer who does the arithmetic.
The three real reasons, in order: (a) zero-copy fan-out, the only term that scales with
subscriber count — 2.2 cores of serde_json at N=64 versus one buffer and N
refcounted sends; (b) key-expression interest scoping, which requires the routing key to
sit in a header the relay can read without parsing; (c) 40k/s exceeds the
stored-and-indexed ceiling (~10–30k/s), so this traffic must be ephemeral regardless of which
path carries it.
This section has grown more than any other since the page was written. It described four specs under an editorial naming convention; it now describes a governed register with a state machine, a CI guard, and a hard rule about what a relay may advertise. Read the lifecycle before the register — the register's size is a measure of design work, not of shipped work, and the two are easy to confuse in exactly the direction that flatters us.
This page used to carry a constituency test — "does anyone outside this deployment
want it?" — which required a judgement call on every proposal. MIP-RG replaced it
with something mechanical: upstream Nostr NIPs are numeric (NIP-01,
NIP-42); repo-local proposals are two uppercase letters (MIP-OF,
MIP-AS). Digits and letters are disjoint, so the namespaces cannot collide
and no registry negotiation with upstream is needed. Not MIP-0001: a numeric scheme
invites the reader to assume ordering and completeness, and it conflicts on every concurrent
proposal — a two-letter mnemonic is stable, greppable, and conflicts loudly rather than silently.
MIP now expands to Meridian Implementation Possibility, and it is optional by
construction exactly as an upstream NIP is — which is why an implementation has to be able to say
which MIPs it actually honours.
docs/nips/ is retained only for profiles of genuinely upstream NIPs (17 files
today); everything repo-local lives in docs/mips/.
| State | Means |
|---|---|
DRAFT | Problem and wire shape stated. No code. |
REVIEW | Council seated; reversal evidence named. |
IMPLEMENTABLE | Wire format frozen; golden vectors published. |
ENFORCED | A reader exists; just check-kinds green. |
WITHDRAWN | Reason recorded. |
ENFORCED is the only state that permits NIP-11 advertisement — and this rule was bought with a real bugNIP-17 was advertised for months while kind:10050 was rejected by the ingest
allowlist. Conformant third-party clients feature-detected support, and then failed
silently. A negotiation field that promises frames the relay will reject
reproduces exactly that bug on the hot path. A relay advertises its MIPs in NIP-11
supported_extensions as mip-xx, and the operator console renders that
list — so the register and what a relay claims are checkable against each other.
Every MIP below is DRAFT. None is ENFORCED. Nothing here may be
advertised yet.
Mechanism MIPs add machinery. A profile MIP adds none — it binds an existing envelope, payload schema and link profile to one real-world feed, and states which enforcement point rejects a non-conforming frame. That distinction is what keeps the register from becoming a wish list.
| MIP | Layer | Owns | Bead |
|---|---|---|---|
| MIP-RG Registry | Process | Numbering, lifecycle, the advertisement rule above. Governs the rest. | — |
| MIP-OF Opaque frame envelope | Wire | The ~44 B header, batch framing, the opaque-payload contract, the conflation key. The load-bearing spec. | tdq |
| MIP-XP Exchange profiles | Link | The P0–P3 ladder with per-class floors, negotiated once per link and enforced per message. Keeps transport auth and event identity as two layers. | 2e2 |
| MIP-SF Session frames | Wire | Removes BIP-340 from the ephemeral hot path — after AUTH the WebSocket is the attribution channel. ~350× on the class that dominates message count. Adds a message type (SFRAME), not a kind, and no new crypto dependency. | owl |
| MIP-QC QoS classes | Scheduling | Eight lanes, class-aware shed, and the three movement contracts (§7). | 9j8 |
| MIP-LF Leaf forwarding | Topology | Hub-and-leaf, store-and-forward, no peering — the topology in §3. | — |
| MIP-MQ MQTT interop | Interop | Topic mapping, QoS mapping, session semantics. Identifiers travel as attributes, never mapped into the routing key by string manipulation. | — |
| MIP-CU Custody + app ack | Delivery | Separates the five things NIP-01 OK conflates: accepted / stored / authorized / delivered / acted on. | 8qkw |
| MIP-DD DDIL sessions | Link | Resume for intermittent links, and four counters deliberately not collapsed into one — see below. | bm32 |
| MIP-AD Adapter contract | Ingest | Units, CRS, time base, declared lossiness. The reusable asset — it is what makes every profile below thin. | qi7f |
| MIP-KN Kinematic payload | Payload | Position, velocity, heading, altitude. One schema serves drones and surveillance — drones batch temporally, AIS/ADS-B spatially. Two schemas would mean every fusion implementation writes two parsers plus a converter, and the converter is where provenance gets lost. | ss1 |
| MIP-TL Scalar telemetry | Payload | Typed readings — unit, value, quality, sensor id. | — |
| MIP-MS Media sessions | Media | How a relay learns continuous media exists and who may consume it — control plane only, bytes never on the event path. | 7xh3.1 |
| MIP-CT Symbol identity + Compact Track Code | Profile | SIDC/XSIDC on the backbone, and a compressed track code for disadvantaged links. This is §9, and it is the largest spec in the register. | qtl8 |
| MIP-AB ADS-B | Profile | The cooperative air picture — 1090ES, UAT 978, GDL 90. | 768v |
| MIP-TK Cursor on Target / TAK | Profile | TAK interoperability. Note the tension with §9: CoT's fused type string is named there as the canonical example of what not to do — so this profile's job is to translate it at the adapter, never to adopt its shape. | doty |
| MIP-AS AIS feed | Profile | The first real workload on the machine surface — the reason the suite stops being inventory. | hgjj |
| MIP-MI Motion imagery | Profile | STANAG 4609. | 7xh3.1 |
| MIP-ML MAVLink v1/v2 | Profile | The autopilot protocol PX4, ArduPilot and effectively every open GCS speak. | is42 |
| MIP-RI Remote ID | Profile | ASTM F3411 / prEN 4709-002. | n4jr |
| MIP-DS ROS 2 / DDS | Profile | — | fsjp |
| MIP-NM NMEA 0183 | Profile | — | fqeu |
| MIP-CN Channel canvas | Document | The shared per-channel document, kind 40100. Read the entry below — this one arrived from the opposite direction to every other row. | d7o |
Layering is strict and one-directional:
MIP-XP → MIP-OF → MIP-QC → MIP-LF →
MIP-MQ, with MIP-SF, MIP-KN and MIP-TL hanging
off the envelope. A payload MIP may never require the relay to read it — that is the
enabling property, not a limitation.
Every other row in the register is a spec waiting for a reader. MIP-CN is a reader that had
no spec. Kind 40100 was defined in kind.rs, enforced at relay
ingest, and consumed by the ACP agent pool — with no written contract. Three
independent readers each inferred one, and they did not agree: on two writes inside the same
second, meridian-cli and the desktop rendered different documents, and
neither was wrong by any rule that existed.
The page's standing law is no vocabulary without an enforcement point, and
just check-kinds enforces it. MIP-CN is the converse failure, which nothing was
checking: an enforcement point with no vocabulary written down. A CI guard can catch a
kind that nothing reads; it cannot catch a kind that three things read differently. That one
needs a spec, which is what MIP-CN now is.
Onboarding offers domain packs — AIS, ADS-B, APP-6E,
STANAG-4609, MAVLINK, TAK, REMOTE-ID — seven
of them. A pack is a named bundle of MIPs a deployment has moved to
ENFORCED. Partial support is not support: a pack that lights up
while one member MIP is still DRAFT is the NIP-17 failure above wearing a friendlier
label. just check-mips regenerates the client's registry from
docs/mips/README.md and fails when the two disagree, so the register stays the one
registry.
On 2026-08-06 an external architecture review with no access to this repository independently derived the wire shape of ten registered MIPs — the binary envelope, negotiated per-link auth, no-signature-per-sample, eight delivery classes, fusion as ordinary clients, client-side symbology, media outside the payload path, and both payload schemas. It converged on Design Law too: opaque routing keys with no human-readable mission names, and a hot path that calls no store synchronously.
Two designs agreeing raises the cost of re-litigating the wire shape. It says nothing
about whether a reader exists — only ENFORCED says that. The review's two
genuine gaps became MIP-DD and MIP-CU; its other two proposals — a
second durable event journal and per-region provisioning counts — were withdrawn under
refusals R2 and R8, by agreement with the reviewer.
All four look like "a version number" and only one is absorbing. Collapsing
them is how revocation dies at reconnection, because a disconnected client is the second
node, arriving early: it holds writes made while partitioned, and merging them by
created_at resurrects anything revoked during the outage.
session_epoch reconnect incarnations, assigned by the relay ·
source_epoch source restarts, assigned by the source ·
policy_generation authorization version, authority only, monotonic ·
revocation_floor minimum acceptable generation, authority only,
monotonic and absorbing.
A client is never authoritative for policy_generation.
It reports the last generation it observed; the authority supplies the signed current policy. A
resume that trusts the client's claim has invented a second authorizer, which the no-third-shim
law refuses by name. Resume ordering is normative and policy-and-revocation delta comes before
any data frame — a relay that delivers state first is non-conformant. The governing
invariant: historical repair must never delay or temporally overwrite current state.
Backfill is additive to the past, never to the present.
What MIP-DD deliberately does not decide: how
revocation_floor advances, who signs it, and how it propagates. That is a Design Law
change and goes to council before this MIP may reach IMPLEMENTABLE.
provenance: self-reported | observed. A drone signs its own telemetry; an
ADS-B target signs nothing and the feeder attests on its behalf. That distinction must
not be droppable by omission — no default value, ever.
The companion honesty rule: fidelity: real means "not simulated", not
"trustworthy". An unauthenticated broadcast faithfully relayed is still real
and still spoofable. And spoof detection is an explicit non-goal of the transport
layer — doubly so now, because a relay that never reads the payload
categorically cannot detect an implausible track. Detection belongs to fusion. Stating
that is not a caveat; it is the honest description of a relay that has forsworn reading.
This page previously proposed 21000/21001/21002 split by
payload type — kinematic frame, telemetry frame, health beacon (that proposal survives on
meridian-41a and is now superseded). The register consolidated on a single
ephemeral machine block, 24400–24419, and split it by symbol set —
that is, by domain:
| Kind | Constant | Domain | Nominal refresh | Spec |
|---|---|---|---|---|
| 24400 | KIND_AIS_POSITION | AIS vessel position | ~0.1–2 Hz × 10⁵ | MIP-AS |
| 24401 | KIND_AIS_STATIC | vessel static / voyage — carries the durable IMO | low | MIP-AS |
| 24402 | KIND_AIS_AID_TO_NAV | aid to navigation — kinematic, zero velocity | low | MIP-AS |
| 24403 | KIND_TRACK_AIR | air | 1–10 Hz | MIP-CT |
| 24404 | KIND_TRACK_LAND | land | 0.1–1 Hz | MIP-CT |
| 24405 | KIND_TRACK_SEA_SURFACE | sea surface | 0.1–1 Hz | MIP-CT |
| 24406 | KIND_TRACK_SUBSURFACE | subsurface | ~0.01 Hz | MIP-CT |
| 24407 | KIND_TRACK_SPACE | space | snapshot only | MIP-CT |
All five track kinds are conflated. Why domain and not payload
type: air refreshes at 1–10 Hz and subsurface at ~0.01 Hz — three orders of magnitude apart —
so the domain genuinely is a traffic class, which is sanctioned axis A4. Splitting by
payload type would have put a 10 Hz and a 0.01 Hz stream in one lane.
24400 is a narrower profile of 24405, not a competitor: AIS is one
source of sea-surface tracks, so a consumer wanting all surface tracks subscribes to both.
KIND_TRACK_SPACE carries a hard consumer obligation — orbital motion MUST NOT be
dead-reckoned; course and speed encode unknown by construction.
These kinds MUST be allocated in the ephemeral range
20000–29999, and 24400–24419 is inside it. MIP-SF's fence is a compile-time
assert!(is_ephemeral(k)) per eligible kind, so a persistent kind number
forfeits session frames and pays 30–70 µs of BIP-340 per sample instead of ~0.2 µs of HMAC — a
~350× difference decided entirely by which integer someone picks. A persistent kind on a
session frame is rejected, never upgraded, because extending session frames to persistent
kinds would convert "the relay can lie about who is typing" into "the relay can forge chat
history."
State of the block in kind.rs today: none of
24400–24407 is committed yet. The MIP-AS three were authored, compile- and guard-verified,
then deliberately backed out (meridian-bjgs) because a second agent was editing the
same file — splitting one file across two agents would have committed their incomplete work and
pushed a red just check-kinds. Re-apply once kind.rs is quiet.
Ephemeral kinds actually allocated today: 20001 presence, 20002 typing,
20003 media-session heartbeat, 22242 NIP-42 auth, 24134 pairing, 24200 agent observer,
24242 Blossom auth, 24243 identity binding, 24810 huddle reactions, 27235 HTTP auth, 28936 NIP-43
leave request. just check-kinds is green.
A worked example of why a BASELINE is a snapshot, not a
fact. Gauntlet row G02 pinned 133 kind constants at 133 distinct values and still
says so. The tree has since moved: it now declares 137 constants matching the guard's own
pattern, at 136 distinct values — and that last number is not a duplicate-kind defect,
it is PARAM_REPLACEABLE_KIND_MIN sharing 30000 with KIND_FOLLOW_SET,
a range bound coinciding with the first kind in its range. The guard stays green because it
checks readers and duplicates, not a hand-written total. Cite the register for what it
pinned and when; re-derive anything you need to be current.
meridian-06p — stream labels are a safety primitiveThree closed lanes applied per stream, never per message:
fidelity ∈ {notional, simulated, real},
context ∈ {experiment, exercise, operational},
access ∈ {community, organization, controlled}.
This is the mechanism that stops a simulated track entering an operational picture.
Per-message marking produced 70–84% defect rates in DoD IG/GAO audits; per-stream is cheaper
and more correct, and costs zero per event because it resolves at subscribe time.
Filed under "governance" it looks optional; filed correctly it is part of the machine bus.
meridian-luy — the recorder is unownedIt appears in three separate documents — drone post-flight reconstruction, AIS history, general cold storage — every time as someone else's job. Ephemeral kinds mean never stored, and the recorder is what answers the post-flight-analysis objection without putting telemetry in a persistent range. Shape: subscribe to the live stream, write durable signed segments out of band, store opaque blobs plus headers and decode lazily. Columnar/object storage, not Postgres — ~60k unique states/s at ~80 B is roughly 415 GB/day raw. The recorder is the signed P3 boundary; the live bus never is. Still open, still P2, still unowned — the one condition on this page that has not moved.
meridian-7xh3 — drone video is a P0 epic, and it is two buildsStated need: "we need media support for the drones — video." Video as a chat
attachment already works (meridian-media, Blossom/S3, desktop imeta).
What does not exist: no NIP-71 video kinds, no NIP-68 picture kind, no NIP-53
live-streaming kinds. Two materially different builds hide under one phrase — (a) a
live ISR feed (NIP-53 live event plus HLS or WebRTC; the relay already hosts huddle
audio, so there is transport precedent) and (b) recorded post-mission clips
(NIP-71 addressable video over existing Blossom storage). MIP-MS and
MIP-MI are the specs, and the bytes stay off the event path in both.
meridian-k6b)MIP-SF's own trust table concedes that the relay can fabricate ephemeral events attributed to a
client — and shrugs, because the worked example is a typing indicator. Applied to a
kinematic frame, the same capability means the relay can inject targets into a tracking
picture. It is arguably not a new exposure, since a P0 feeder link has the same
property. But inheriting that reasoning silently from a document about typing indicators is
exactly how it never gets written down. It lands as a section of MIP-KN.
This page previously reported that .settings/reference-docs/sidc-2525-APP6/ was
empty, that no symbology bead existed, and that nothing was decided. All three are now
false. MIP-CT (meridian-qtl8) is written and is the largest
spec in the register; the reference directory holds the CTC calculator, record format
v3.1, which is the normative reference for every field width, band table and
hydration rule; and the gauntlet carries symbology as row G08.
And it is no longer only a spec — a client is being built against it.
meridian-9qll is the tactical-picture epic: an Apps region in the left nav keyed
off the MIP register (landed), a Map app on MapLibre GL v5 with 2D and globe modes, a
milsymbol renderer bound to XSIDC with amplifiers, an XSIDC track store that conflates by
instance, and an honest degraded/empty state. APP-6 hulls for AIS, a zoom detail ladder,
clustering, per-layer opacity, table export, Esri Shapefile export and a motion-imagery video
wall have landed alongside it. The epic is still 1 of 6 children complete — the shipped
surface has outrun its own acceptance criteria — and gauntlet rows G06 and G08 remain
UNSTARTED. Shipping UI does not move a gate; evidence does.
G14 is the exception, and it moved the right way. It reached SLICE on
named tests for mapping, symbology, degraded/staleness rendering and tactical UX — and in the
same pass the word alerts was cut from the row's title. A search found
the string twice under the apps tree, both times a lucide AlertTriangle icon: no
alert rule, geofence, proximity, tripwire or watchbox concept exists anywhere in
REMAPPING/meridian-desktop/src/ or crates/, and kind.rs declares no alert
kind, so nothing could carry one over the wire. Rather than let the row keep claiming a
capability nobody had built, the capability was scoped out and its absence written down
(meridian-in50) — which is the honest version of a green gate, and worth more than
the gate.
What follows is what MIP-CT decided, including the one place it overturned this page's earlier recommendation, and the one question it still leaves open.
The desktop rendered the chat NIP surface and nothing else, so a deployment that
moved MIP-CT to ENFORCED would have had no way to see the tracks those
MIPs carry — MIP state was visible only as a read-only Settings card. That is the
"no vocabulary without an enforcement point" law reaching its natural conclusion: a
reader is not only a parser in the relay, it is eventually a human looking at a
screen. meridian-9qll.1 is the concrete consequence. MIP-CT § 6.5 now specifies
the full 22-field APP-6 text-amplifier extension — including the commonly omitted
AG · Auxiliary Equipment Indicator — while its writer/validator/decoder remains to be
implemented.
Full fidelity, carried on the backbone: the MIP-OF frame is a 44 B header plus a full-fidelity payload. This is the interior representation, and it is what fusion, recorders and displays consume.
A quantized, band-tabled record for genuinely disadvantaged links. It hydrates back to a track using mission defaults. Never treat a hydrated CTC record as equal in fidelity to the record it came from — the loss is quantified, not hand-waved, and the CTC profile explicitly drops both source attachment and the complete text-amplifier group.
The earlier answer was a clean blanket rule: the SIDC struct belongs in the payload, never in the header. MIP-CT keeps the reasoning and rejects the blanket. The discriminator is not "is it symbology" — it is mutability.
| SIDC element | Mutable? | Placement | Why |
|---|---|---|---|
| Symbol set (pos 5–6) — air, land, sea surface, subsurface, space | effectively immutable for a platform | Header, via kind | Domains have genuinely different QoS profiles — air 1–10 Hz, subsurface ~0.01 Hz, space a snapshot that must not be dead-reckoned. That is traffic class — sanctioned axis A4, not vocabulary sharding |
| Standard identity (pos 4) — pending, unknown, friend, suspect, hostile | highly mutable — re-assessment is the normal case | Payload | An affiliation in the routing key means re-classifying a track changes its routing key — with the added edge that the track would vanish and reappear on a subscriber's display at the moment of re-classification |
| Entity code (pos 11–16) | refined as classification improves | Payload | Same argument, plus it is a type code with ~10⁶ values; it cannot be a routing dimension |
| Version, context, HQ/TF, echelon, modifiers | mission-constant | Neither — mission config | Not sent on any link |
| Instance identifier | immutable for the track's life | Header — conflation key | This is what conflation supersedes on. On the backbone it is the full identifier, never the CTC alias |
| Position | — | Header — geo cell and payload | The cell routes; the payload carries the measurement |
| Observation time | — | Header — timestamp | LWW resolves on it |
The relay routes by where a thing is and what domain it is in. It never routes by what someone currently believes about it.
Ten of the thirty digits are information-bearing and travel;
10¹⁰ < 2³⁴, so the wire base is a 34-bit field plus a
sidc_defaults_version naming which mission table restores the other twenty.
Cursor-on-Target's type="a-f-A-M-F-Q" fuses affiliation (f) into the
type string that TAK routing is built from. That is exactly the failure above,
shipped and widely deployed — which is why the mutability rule is written as law here rather than
left to reviewer judgement.
| Group | Fields |
|---|---|
| Identity | sidc_wire (the 10 digits as one 34-bit field) and sidc_defaults_version |
| Kinematic (MIP-KN) | latitude, longitude, elevation, course, speed, plus their measured accuracies — unquantized |
| Track quality | phase (STANAG 4817 TrackPhase: tracked / dead-reckoned / lost / inactive) and confidence |
| Provenance | provenance — mandatory, non-defaulting (self-reported | observed) |
| Source attachment | source_format, source_bytes |
| Payload extensions | independently length-delimited groups; extension 1, version 1, carries all 22 APP-6 text amplifiers |
Three design calls worth knowing before you argue with them.
(1) Track phase is not SIDC status. SIDC status (pos 7) is operational condition —
present, planned, damaged, destroyed. Phase is track quality. A destroyed target can
still be a well-tracked object, so both travel.
(2) Confidence has no "unknown" codepoint — ten buckets, and an unsourced confidence still
picks one: the lowest defensible. Confidence ≤ 0.35 SHOULD pair with standard identity
Pending or Unknown.
(3) Elevation is f32 metres, not fixed-point — the kinematic core spans crush
depth to beyond GEO, eleven orders of magnitude, and a fixed-point integer that reaches 36 000 km
cannot also resolve a drone at 840 m. Relative precision is what elevation needs.
Byte budget: ≤37 B fixed-width core plus the empty extension-vector byte, so header
plus bare payload is ≤82 B, landing near ~76 B after postcard varints —
for a track carried without source attachment or populated payload extensions.
Quoting 76 B for an attachment- or amplifier-bearing track would be exactly the profile-free
figure rule ① forbids.
A SAPIENT detection carries sensor-level structure — classification vectors, detection
geometry, sensor identity — that no SIDC digit can express. Discarding it at the adapter
makes the transform irreversible and silently lossy. So: an adapter MUST carry the source
record's original bytes in source_bytes, tagged with source_format,
whenever the source is not itself XSIDC. The backbone has the bandwidth and the relay
never reads it, so the cost is bytes and nothing else — and what it buys is an auditable
transform and a mis-mapped adapter that is diagnosable after the fact rather than a permanent
loss.
The transform itself is a table lookup, never a string transformation: a source format's identifiers, topic strings and platform names travel as attributes and are never mapped into the routing key by string manipulation.
| Source | What the adapter must supply | Coverage today |
|---|---|---|
| STANAG 4817 — the NATO track/contact model this layer compresses and reconstructs; the closest thing to a native input | 4817 track fields → SIDC digits + extension; phase maps directly, since TrackPhase is 4817's | gauntlet G07 UNSTARTED |
| SAPIENT — sensor-level detections from modular autonomous sensors | Detection → track association. This is a fusion step, not a format conversion | F005 — prose in MIP-CT only; no adapter, mapping or fixture |
| AIS | ship type → entity code; MMSI → instance identifier | MIP-AS written |
| ADS-B | emitter category → entity code; ICAO24 → instance identifier; provenance = observed | no ADS-B profile MIP yet — the pack binds through MIP-AD |
| STANAG 4559 — ISR library discovery and retrieval | — | F004 — zero coverage in any MIP, crate, spec or fixture |
The risk was that 2525/APP-6's own exercise/simulation amplifier and our
fidelity/context stream labels (meridian-06p) would drift,
silently and safety-relevantly. MIP-CT narrows it substantially: the SIDC Context digit
(pos 3 — Reality / Exercise / Simulation) is mission-wide with no per-record override
and never travels on the wire at all. So the per-frame contradiction this page feared
cannot occur.
What is left is smaller and still real. The mission table and the stream label remain
two independent assertions of the same fact, at two different scopes, and MIP-CT does not cite
meridian-06p or say which one wins. MIP-CT adds its own sharp warning in the same
breath — an exercise track that inherits 0 - Reality is indistinguishable from a
live one, so 1 - Exercise is the correct default for any non-operational
mission.
The recommendation stands, unchanged and still unratified: the stream label is authoritative — it is enforced, closed-enum, CI-checked, and resolved at subscribe time at zero per-event cost — and the mission-table context is derived from it, never read back as an independent source of truth. This is the one genuinely new decision this document surfaces, and it belongs to the Protocol council, not to a recommendation in a slide.
MIP-CT, extending MIP-KN.just check-kinds.
The five track kinds each need a QoS row for the same reason — do not merge a kind constant
without a QoS class.sidc_defaults_version). Subscribers on the old version simply never see the new one.13) with 2525D/APP-6(D) as version 10. Which do consumers actually
render? That answer still sizes the client work, and it is not written down.The binding constraint on this program is WIP, not knowledge, and it has grown since this page was first written — and it is still growing faster than it drains. Live tracker state: 485 issues total — 305 open, 235 ready to work, 16 in progress, 70 blocked, 164 closed.
Across the three days this page has been reconciled, the tracker went 303 → 402 → 485 issues and 139 → 197 → 235 ready. It closed 39 then 38 in those two days, and opened 99 then 83. The backlog is growing roughly twice as fast as it drains, and the ready queue has grown 69% while in-progress held flat at 15–16.
Two hundred and thirty-five ready items is not a plan; it is a menu that takes longer to read than most of its entries take to do. Pick one track, and start a second only when the first has shipped something. This is the Outsider's seat in §11, and it is the one warning on this page that has got worse at every single reconciliation.
TASKS.md, and it is a 10-week programThis section used to carry a hand-ordered bead list. That list has been superseded by a
costed program plan that executes the ratified council call in DIAGRAM.md § Part III
and does not re-argue it. Beads remains the source of truth for task state —
TASKS.md mirrors Bead IDs, never the reverse — and the JIRA project key is
MRDN.
Three assumptions in it require confirmation before Sprint 1 planning, and each changes sizing rather than direction: a 6-engineer team at ~55 points/sprint (~260 points total); one dedicated 16-core load-generation host and one 16-core system-under-test host with ≥25 GbE between them — the NIC is a first-class dependency here, not an afterthought; and the fact that the 1.337M scoping figure is TBMQ-class vendor-published, not measured on our stack.
| Weeks | Track |
|---|---|
| 1–6 | Greenfield opaque-frame core. Native MIP frames end to end. Hits the throughput number on a path with no MQTT semantics in it |
| 6–10 | MQTT compatibility bridge. Ingress adapter at the edge, so devices that cannot be reflashed migrate without firmware work |
Building the bridge first would let MQTT semantics — per-message topic ACLs, broker-held delivery state, topic-string tenancy — leak into the core, which is precisely what this program exists to remove.
| Epic | Title | Pri | Pts | Sprints |
|---|---|---|---|---|
MRDN-100 | Measurement baseline and capacity model — the gate; nothing optimizes before this has a number | P0 | 34 | 1 |
MRDN-200 | MIP specification suite | P0 | 47 | 1–4 |
MRDN-300 | Opaque frame data plane | P0 | 63 | 2–5 |
MRDN-400 | Traffic class, QoS, and congestion | P1 | 34 | 3–4 |
MRDN-500 | Hub-and-leaf topology and store-and-forward | P1 | 42 | 3–5 |
MRDN-600 | MQTT compatibility bridge | P1 | 47 | 3–5 |
MRDN-700 | Client SDK and operator console | P2 | 34 | 2–5 |
MRDN-800 | Conformance, load, and handoff — runs continuously | P0 | 42 | 1–5 |
Not discovered in Sprint 4. The named cut line: E7 drops to CLI-only, E6 descopes MQTT 5.0
to 3.1.1, and the MIP-TL payload schema defers — which leaves 258.
Do not resolve it by cutting E1 or E8. Those are the gates that make every other
number on this page true.
| Sprint | Theme | Exit gate |
|---|---|---|
| S1 · wk 1–2 | Measure and specify | Baseline numbers published; MIP-OF and MIP-XP at IMPLEMENTABLE; go/no-go on the whole program |
| S2 · wk 3–4 | Frame path exists | Codec fuzzed and golden-vectored; relay parses and routes a frame end to end |
| S3 · wk 5–6 | Batch and class | Batch survives to fan-out; 8 QoS lanes wired; class-aware shed proven under a slow consumer |
| S4 · wk 7–8 | Edge and interop | Leaf relay forwards upstream with store-and-forward; MQTT 3.1.1 bridge publishes into the spine |
| S5 · wk 9–10 | Prove and hand off | Sustained-rate capacity run with attached profile; conformance suite green; handoff frozen |
If the Sprint 1 measurements show the opaque-frame path is dominated by something unlisted — a
TLS setting, an accidental debug! in the fan-out loop, a clone() on a
hot struct — then the batching and QoS work in S3 is optimizing the wrong term
and must be re-planned before it starts. The three named outcomes:
| Measurement | If it lands | If it does not |
|---|---|---|
| Per-message frame-path cost within ~2× of the 1–3 µs estimate | proceed as planned | re-scope E3 around the actual dominant term |
| Batching projects ≥10× on pipeline traversal count | proceed with MRDN-310 | drop meridian-0zd; find the real cost |
Zenoh ≥5× over Dragonfly on one fixed topology (meridian-ctg) | Zenoh becomes a candidate | stay on Dragonfly; defer the whole implementation chain |
C-1 every published figure carries its profile ·
C-2 no vocabulary without an enforcement point ·
C-3 names never encode ownership — no org/site/device prefixes in key
expressions, storage keys or ACLs ·
C-4 add attributes, not mechanisms — no policy DSL, rules engine or plugin runtime in the
bridge ·
C-5 no third shim — the bridge holds no ACL copy and no sync job ·
C-6 explicit denial, never a silent drop ·
C-7 revocation is monotonic — no second writer or peering link before
meridian-3wh ·
C-8 nothing closes on green CI alone ·
C-9 scale by replica / function / tenant / traffic class, never by vocabulary ·
C-10 measure before optimizing; machinery for absent load is inventory.
And four refusals this program will be asked to reverse, named now so they are declined by citation rather than re-argued: "put Kafka behind the relay for durability" (R2) · "shard telemetry kinds onto their own relays" (R1) · "let the edge relay peer with the spine" (R4 / Law 2) · "cache grants at the bridge for speed" (R5 / C-5).
Parking is not rejection. Each of these is designed, sized and cross-linked, and ready the day a driver appears:
| Parked | Un-parks when… |
|---|---|
| Grants, discovery loop, restriction-drift meter | a named party wants cross-organisation sharing |
| Grant-keyed envelope encryption | a stream must genuinely exclude a member of its own community |
| Participant classes | the first external / partner tenant |
| Federation, negentropy backfill | a second network that wants to federate |
| Compliance criteria | someone asks us to prove conformance |
| Tasking orders · fusion products | a platform to task, or a second sensor to fuse |
. ./bin/activate-hermit # pinned toolchain — do this first, always
cp .env.example .env
./run.sh doctor # toolchain, Docker, port conflicts, stale processes, env drift
./run.sh all # services + relay + web in background, desktop in foreground
just ci # the full local gate — run before every PR
# The four guards that keep this page from rotting. Read them before trusting it.
just check-gauntlet # every G-row's state against its evidence
just check-mips # the MIP register against the client's generated registry
just check-kinds # no vocabulary without an enforcement point
just check-architecture-map # the crate map, asserted in both directions
bd ready # what is actually available to work on
bd show meridian-tdq # read any bead cited on this page
just bench # ALL seven bench groups, and it prints the profile with
# them — CPU, cores, OS, traffic class, auth profile,
# parsed/stored — so no figure can be lifted out bare
just bench --quick # same groups, shorter sample
./perf/relay_bus_scaling.py --mode redis # interest-scoping reduction
Prefer just bench over a bare cargo bench. The recipe
exists precisely because cargo bench appeared in no recipe and no workflow while its
numbers were being published (finding F062), and because a figure without its profile is an
unsupported claim under rule ①.
Commit with git commit -s — the DCO check fails any PR with a commit
missing a Signed-off-by trailer. run.sh wraps just and adds
port deconfliction; it does not replace it.
Convened per AGENTS.md § User Preferences. Decision under review: publish
this page as the standing introduction for the incoming Program Development Team, framing Meridian
as a modular replacement for a TBMQ central-broker deployment and as a scalable alternative for
decentralized adoption. Each seat gives a position, one falsifiable objection, and what would
change it.
| Seat | Position | Strongest objection / named risk | What would change it |
|---|---|---|---|
| Contrarian | approve, amended | "Drop-in replacement" is the phrase that will get quoted back to us in a procurement meeting, and the very next question is offline queueing for constrained devices — which we do not have and cannot add without a second durable log. Amendment applied: §3 leads with the narrowed claim (bus, policy, audit role) and gives the gaps their own table before the argument table. Second point, also applied: §9 stated plainly that no symbology design existed rather than implying one. Since ratified: that narrowing is now written law in TASKS.md § 1 as a standing architectural boundary with its own kept-at-the-edge column, and §9's gap has closed — MIP-CT exists, so §9 now reports a design rather than a blank page. |
A customer workload dominated by intermittently-connected devices — then the broker is being kept, not replaced, and this becomes an integration document. |
| First Principles Thinker | approve | Strip it back: the relay moves bytes to interested parties with an identity attached. The page must sort the same way — throughput and interest routing are the product; governance is a feature — with the one exception that stream labels are safety, not governance, and belong on the product side. Applied: §8 carries the reclassification explicitly, and §9 Trap 2 makes the label authoritative over the symbology amplifier for exactly that reason. Update: Trap 2 has narrowed but not closed — MIP-CT keeps the SIDC context digit off the wire entirely, so the per-frame contradiction cannot occur, but the mission table and the stream label remain two assertions of one fact and MIP-CT does not say which wins. | Evidence that a renderer cannot honour a stream label — then the authority direction in Trap 2 inverts and the recommendation is wrong. |
| Expansionist | approve | The reusable asset is not the AIS path, it is the adapter contract — every high-rate feed terminates the same way, so the second feed should be configuration. The page under-sold that in draft. Applied: §6 opens on the adapter contract as six numbered obligations, and MIP-OF is described as the asset rather than as a spec. Self-check honoured: federation still has no second party, so §4 marks A5 blocked rather than upcoming. Vindicated, and then some: the adapter contract is now its own spec — MIP-AD — with five protocol profiles already registered against it (MAVLink, NMEA 0183, Remote ID, ROS 2/DDS, AIS), which is precisely the "second feed is configuration" shape. |
The reversal evidence has arrived in the form this seat named: MIP-AD is being designed against several callers rather than one, which is better — so the sequencing question is now which profile is first, not whether the envelope generalizes. |
| Outsider | approve, amended | Three things a newcomer will not otherwise be told: the recorder is the hidden common dependency and nobody owns it; the two-ceilings problem is commercial, not technical, and is the cheapest item on the list to get right and the most expensive to get wrong; and five competing programs against nine in-progress items is the organisation's actual constraint. Applied: the recorder gets its own card with the 415 GB/day sizing, §0 makes the ceilings rule the first thing on the page, and §10 opens with live tracker counts rather than the plan-of-record snapshot. This seat's warning has aged the worst, which is to say the best: WIP has grown from 50 open / 9 in progress to 201 open / 15 in progress / 62 blocked, and the recorder is still unowned. | Someone taking ownership of the recorder — then it stops being a hidden dependency and moves into the ordered track. Not yet: meridian-luy is still open, still P2, still unowned. |
| Executor | condition cleared | A page this comprehensive risks becoming the deliverable. The failure mode of the whole GOAT exercise was a beautiful plan nobody builds — five documents, twenty-five issues, zero lines of code. Condition applied: §10 is an ordered track with gates, not a menu, and §9 closes with concrete artefacts to produce before any code. Condition was: the symbology work was in no tracker, so bd ready could not surface it. It has cleared twice over — meridian-qtl8 (MIP-CT) and the meridian-9qll client epic are filed, symbology is gauntlet row G08, and a MapLibre/milsymbol Map app is being built. Restated concern, and it has got worse at every reconciliation: 23 MIPs are DRAFT and zero are ENFORCED; no gauntlet row has reached PASSED; and over two days the backlog grew 99 then 83 while closing 39 then 38. Specification is outrunning enforcement, which is this seat's failure mode wearing a register instead of five documents. The counter-evidence, and it is real: G14 reached SLICE on named tests and cut an unbuilt capability out of its own title rather than carry it — that is the loop working exactly as this seat wanted. |
The first MIP reaching ENFORCED with a reader and a green just check-kinds — that is what converts the register from plan to product. Until then, treat the register's size as a risk indicator, not a progress indicator. |
| Sr. Full Stack Developer | approve | Sizing gets underestimated in exactly one direction, so the page must not imply that §8's specs are comparable in cost: lq7/06p are days, 0zd is weeks and touches 21 mpsc sites in the hottest path in the relay, and grants/encryption/federation are a quarter each. Applied, then superseded by something better: §10 no longer carries a hand-written size column — it carries story points per epic, and the plan admits up front that it is 343 points against a 260-point budget with a named cut line. That is the honest version of the same concern. One precision I want kept: the desktop e2e suite does not cover connection.rs — the relevant safety net for the batching refactor is relay-level fan-out coverage, which is a different suite, and conflating them is how a refactor ships without a net. |
Relay-level fan-out coverage landing before 0zd starts — that is the condition, and it is already pinned by two mutation-verified tests. |
| Network Engineer & Architect | approve | Two architecture facts must be owned explicitly rather than discovered. One: we are building two relays in one binary, and that should be a deliberate module boundary — §2 now leads the entire document with it. Two: the metric that judges every roadmap item is does it improve interest scoping?, because egress is the binding constraint and f_s is the only term we control — §5 carries the equation and the three-scenario table. Sequencing is already correct: Phase 0 measurement gates the rest; leave it. Both have hardened since: "two relays in one binary" is now "two edges, one relay" in ARCHITECTURE.md, with the machine edge speaking Zenoh natively rather than NIP-01 — and the egress point is now carried by a measured ladder in §7 (format ×35, dedup ×10, batching ×2.1, composing to 729×). |
A measurement showing pipeline overhead, not egress, dominating at realistic subscriber counts — then the optimisation order inverts. |
Approve. The original outstanding condition has cleared, and one has replaced it. Publish this as the standing introduction. The Contrarian's amendment is the load-bearing one and is applied: the claim is modular replacement of the broker's bus, policy and audit role, with the gaps given their own table before the argument for the replacement, and a broker legitimate — often correct — at the device edge, feeding the spine and never routing for it. Every figure carries its profile and its measured-or-estimated status. No head-to-head throughput claim is made anywhere on the page, because no head-to-head benchmark exists.
The First Principles sort is adopted: throughput and interest routing are the product; governance is a feature — except stream labels, which are safety and move to the product side. That sort is what decides §9's Trap 2, and Trap 2 is still the one genuinely new decision this document surfaces — narrowed by MIP-CT, not closed by it — so it goes to the Protocol council as its own item rather than being settled here by a recommendation in a slide.
Condition cleared (Executor): the symbology work is now in the tracker
(meridian-qtl8, meridian-9qll, gauntlet row G08), MIP-CT is written, and
a client is being built against it.
Condition now outstanding, and it is the same failure mode one register later: the MIP
register has grown to 23 proposals and not one has reached ENFORCED;
the gauntlet has 21 rows and not one has reached PASSED; and the backlog grew
by 99 then 83 issues across the two days this page was last reconciled. Move one MIP to
ENFORCED and one gauntlet row to PASSED before adding a twenty-fourth
MIP, or the register becomes the deliverable. The condition was stated as "before a
twenty-third" one reconciliation ago; the twenty-third arrived first, which is the datum.
Reversal evidence — any one of these reopens the call:
MIP-DD specifies session resume, which is
not a queue and must not be sold as one.ctg) measuring under 5× against the current bus on the named
profile. Then §6 loses its premise and the transport chain defers. Still unrun — this is the
live gate, and it is Sprint 1's third named outcome.MIP-AS answered it — pre-dedup, ~10×
receiver redundancy — and made the ratio a metric the deployment must keep publishing
rather than a fact it may misremember.