- Rust 48.9%
- TypeScript 21.6%
- JavaScript 20.6%
- Dart 4.6%
- Shell 1.3%
- Other 2.9%
|
Some checks failed
CI / Detect Changed Paths (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
Meridian Harness / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Meridian Harness / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Isolated DB Gate (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Admin Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Server Cross-Compile (push) Has been cancelled
CI / Server Cross-Compile-1 (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Meridian Harness / Publish rolling release (push) Has been cancelled
Meridian Harness / Publish tagged release (push) Has been cancelled
|
||
|---|---|---|
| .agents/skills | ||
| .beads | ||
| .cargo | ||
| .claude | ||
| .codex/skills | ||
| .github | ||
| .goose/skills | ||
| .intersect | ||
| .settings | ||
| .vscode | ||
| benchmarks | ||
| bin | ||
| crates | ||
| deploy | ||
| docs | ||
| examples | ||
| migrations | ||
| patches | ||
| perf | ||
| REMAPPING | ||
| schema | ||
| script | ||
| scripts | ||
| skills | ||
| .dockerignore | ||
| .env.example | ||
| .env.local.example | ||
| .gitattributes | ||
| .gitignore | ||
| AGENTS.md | ||
| ARCHITECTURE.md | ||
| biome.json | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| ct.yaml | ||
| deny.toml | ||
| DIAGRAM.md | ||
| docker-compose.harness.yml | ||
| docker-compose.override.yml.example | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Dockerfile.control-plane | ||
| Dockerfile.push-gateway | ||
| GOVERNANCE.md | ||
| intro.html | ||
| Justfile | ||
| lefthook.yml | ||
| LICENSE | ||
| NOSTR.md | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| preview-features.json | ||
| prometheus.yml | ||
| README.md | ||
| RELEASING.md | ||
| renovate.json | ||
| run.sh | ||
| rust-toolchain.toml | ||
| SECURITY.md | ||
| skills-lock.json | ||
| TASKS.md | ||
| TESTING.md | ||
| VISION.md | ||
| VISION_ACTIVITY.md | ||
| VISION_AGENT.md | ||
| VISION_MESH.md | ||
| VISION_MODERATION.md | ||
| VISION_PROJECTS.md | ||
| VISION_SOVEREIGN.md | ||
MERIDIAN
Multidomain Exchange for Realtime Integration of Data Innovation with Allied Nations
A low-friction, open-protocol, sovereign and decentralized exchange for real-time visibility and integration — humans, servers, agents, machines and IoT on one wire.
Quick start · Repository map · Protocols · Architecture · System map · Program plan · Design Law · MIT
Contents
Orientation — What MERIDIAN is · The mental model · System shape · What runs today
Working in the repo — Quick start · Repository map · Quality gates · Documentation
Protocol — Interoperability, protocols and payloads · Protocol surface · Sovereignty and decentralization
Numbers — The two rules that govern them · Ceilings, with profiles attached
Constraints — Design Law · What we deliberately do not build · Programme and roadmap
New here and want the shape of the codebase rather than the argument for it? Start at the repository map.
What MERIDIAN is
An exchange, not an application. One relay binary carries two kinds of traffic that share a process and almost nothing else:
- The human, agent and service path — every message, reaction, review, workflow step and git object is a signed Nostr NIP-01 event in one Postgres log. Verified, ordered, stored, indexed, audited, and provable to a third party years later.
- The machine and IoT path — tracks, telemetry and sensor frames ride the
ephemeral range: never stored, never indexed, never audited, and never on the
Postgres write path. Specified but not yet shipped: the opaque-frame
envelope that carries them as bytes behind a fixed-width routing header the
relay routes on without ever parsing the payload
(MIP-OF,
DRAFT). Until it lands this traffic is still signature-verified per event — which is the cost the frame path exists to remove, not a cost it has already removed.
Anything that can hold a keypair or reach a bridge can participate: a person at a desktop, a service posting over HTTP, an AI agent with its own identity, an airframe emitting kinematic state at 100 Hz, a constrained device that only speaks MQTT. Participation is a protocol, not a product integration.
The goals, and what each one actually means here
Adjectives are cheap. Each row below names the mechanism that makes the claim true, or says plainly that it is still a design.
| Goal | The mechanism |
|---|---|
| Low friction | A keypair is the only credential. The wire is published, so no vendor SDK is required to join. Devices that cannot be reflashed reach the exchange through an MQTT bridge at the edge rather than a firmware programme |
| Open protocol | NIP-01 on the wire, plus a published spec suite — draft NIPs in docs/nips/ for anything the wider ecosystem wants, MIPs for what only this deployment wants. Nothing proprietary between two participants |
| Sovereign | Each participant runs its own relay and holds its own keys. The tenancy fence is derived from the request host, bound before any handler sees data, and immutable on every row. No external service is required to operate, and no operator can read another's traffic |
| Decentralized | No central broker, no mandatory message-bus hop, and no second durable log. Topology is hub-and-leaf: edge relays terminate device links and forward upstream; the edge never routes for the spine |
| Real-time | Local subscribers are served from an in-process registry with no broker hop. Ephemeral traffic classes skip storage, indexing and audit by compile-time fence. Interest scoping cuts cross-pod ingress 64×, measured |
| Scalable | Four sanctioned scaling axes — replica, function, tenant, traffic class — and never vocabulary. Every ceiling is published with its profile, and optimisation waits for its measurement |
| Interoperable | One event log for humans, agents and services; opaque frames for machines; an MQTT bridge for the device ecosystem; payload specs for AIS, ADS-B, kinematics and scalar telemetry; symbology carried faithfully and never interpreted by the relay |
Who and what participates
| Participant | Speaks | State |
|---|---|---|
| Humans | NIP-01 over WebSocket via the desktop, mobile and web clients | shipping |
| Agents | meridian-cli, the ACP harness (Goose, Codex, Claude Code), dev-MCP — with their own keypairs, memberships and audit trail |
shipping |
| Servers and services | POST /events · /query · /count, workflow webhooks, git smart HTTP, the typed Rust SDK |
shipping |
| Machines — platforms, sensors, surveillance feeds | Opaque MIP-OF frames through a feeder that decodes, dedupes and attests |
designed |
| IoT and constrained devices | MQTT at the edge, bridged by MIP-MQ; the broker keeps offline queues and QoS state |
designed |
Two rules that govern every number in this repository
Both are binding contract in AGENTS.md, not editorial preference.
- No throughput figure without its profile — traffic class, auth profile, whether the payload is parsed, whether it is stored. One relay has two ceilings roughly two orders of magnitude apart. Quoting the good one without its qualifier is how you acquire an obligation the binary cannot meet.
- Nothing is resolved until deployed to a live relay and re-probed. 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.
Every claim below carries one of these labels, and they are not interchangeable:
| Label | Means |
|---|---|
| measured | a committed benchmark in this repo produced this number |
| estimate | derived from measured parts, not measured end to end |
| target | a goal behind a gate, not a result |
| designed | specified, no code in tree |
| open | a real question we have not answered |
| refused | deliberately not built, with a written reason |
The mental model: two relays in one binary
HUMAN + AGENT + SERVICE 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, estimate)
~10–30k/s once stored AND indexed (estimate)
MACHINE + IOT PATH ── opaque frames, ephemeral kinds, trusted link ──────
read ~44 B fixed header → match key expression → forward bytes, unparsed
└─ no signature, no store, no index, no audit ─┘
ceiling: ~600k msg/s ≈ 0.44 Gbps in (estimate, not in tree)
SHARED: tenancy fence · admission · interest routing · the bus
NOT SHARED: parsing · crypto · durability · query surface · client shape
The machine path's defining property is a refusal: the relay never reads the payload. That is the enabling property for zero-copy fan-out, not a limitation to work around — so anything the relay must act on has to live in the header.
| Header field | Width | Why the relay needs it |
|---|---|---|
| community | 16 B | the tenancy fence, bound before any handler sees data |
| kind | 4 B | selects the QoS lane and the class floor |
| geo cell | 4 B | packed geohash — this is the routing key |
| conflation key | 8 B | target id; ICAO24 (24 b) and MMSI (30 b) both fit |
| sequence | 4 B | gap detection without reading content |
| timestamp | 8 B | TTL and staleness without reading content |
≈ 44 B fixed-width, no allocation, no parse tree. Everything else is bytes the relay forwards and never inspects. Content-aware work — deduplication above all — moves upstream to the feeder, because a relay that never reads the payload categorically cannot dedupe by content.
Accept the consequence deliberately: opaque machine frames are not consumable by a generic Nostr client. Their consumers are purpose-built — a track display, a fusion engine, a recorder.
Ceilings, with profiles attached
| Profile | Figure | Basis |
|---|---|---|
| Opaque pre-decoded frames · trusted link · never parsed · not stored | ~600k msg/s | estimate — the path is not yet in tree; the pre-/post-dedup question behind it is open at ~10× |
| Signed NIP-01 events · per 16-core pod · BIP-340 bound | ~250–320k events/s | measured per-core, extrapolated — just bench puts verify_event at ~30k/s on one core, observed 21.6–33.1k over 3 runs (M3 Max, release) |
| Stored and indexed events · one Postgres | ~10–30k/s | estimate — persistence latency is measured (p95 74.6 ms direct, 77.1 ms nested) |
| Fan-out, per event | ~4.1 µs at N=1 → ~56 µs at N=64 | measured — fanout_cost.rs; past ~40 recipients fan-out exceeds signature verification |
| Bus interest scoping (Dragonfly) | 64× cluster ingress reduction | measured — perf/relay_bus_scaling.py --mode redis |
| Bus (Zenoh) at 256 B | 1.15–1.51× Redis — target withdrawn, measured short | measured — the ≥2M msg/s / ~10–20× target was falsified by meridian-ctg. 1.51× vs Dragonfly-in-Docker, 1.15× [0.98–1.34] vs same-footing host Redis, 0.71× with express. The ≥4 KB crossover is WITHDRAWN — shared memory was on and its 3,072 B threshold gave every larger payload a POSIX-SHM fast path the relay build cannot take. Below 4 KB stands. Profile: opaque bytes, nothing signed/parsed/stored, Python binding — perf/RELAY_BUS_SCALING.md § Phase 0A.2 |
| Symmetric MAC vs. per-event signature | ~120× (HMAC-SHA256) · ~400× (keyed BLAKE3) | measured at batch 1, bands ~110–170× and ~360–550×. Compare at a matched batch size: at batch 64 it is ~200× and ~420× (HMAC-SHA256 0.19 µs, keyed BLAKE3 0.089 µs vs. 37.7 µs Schnorr) |
Two consequences worth internalising before planning against these numbers:
- At scale this system is fan-out bound, not crypto bound. At 10M users / 1M concurrent, human message volume is ~3.3k events/s — one verification pod covers it. Fan-out at 50 average recipients is ~1M frames/s.
- The way past the verification ceiling is to skip verification on links that qualify for a trusted exchange profile, never to make BIP-340 faster. Batch Schnorr verification does not exist at any level of the pinned tree, and measured on ed25519 — where batching does exist — batch-64 amortises to only ~2.9×.
Reproduce any of it:
cargo bench -p meridian-core --bench event_cost # BIP-340 ceiling, per core
cargo bench -p meridian-relay --bench fanout_cost # fan-out at N = 1 / 8 / 64
./perf/relay_bus_scaling.py --mode redis # interest-scoping reduction
Interoperability — protocols and payloads
The spec suite
Two registers, one rule for choosing between them: does anyone outside this deployment want the spec? Yes → it is a NIP and goes upstream, but only after it runs. No → it is a MIP, ours, versioned here. Calling a deployment-specific spec a NIP is how you end up owing an ecosystem a design built for one operator.
| Spec | Layer | Owns | Status |
|---|---|---|---|
| MIP-RG | Process | Proposal registry, lifecycle and numbering | designed |
| MIP-XP | Link | Negotiated exchange profiles — per-link auth floors P0–P3, negotiated once, enforced per message | designed |
| MIP-OF | Wire | The opaque frame envelope: the ~44 B header, batch framing, the three movement contracts. This is the adapter contract — get it right and a second feed is configuration, not a new spec | designed |
| MIP-SF | Wire | Session frames — after NIP-42 AUTH the socket is the attribution channel, so an HMAC replaces the per-message Schnorr signature on ephemeral kinds | designed |
| MIP-QC | Scheduling | QoS classes and congestion contracts — eight lanes, class-aware shed | designed |
| MIP-LF | Topology | Leaf forwarding — hub-and-leaf, store-and-forward, no peering | designed |
| MIP-MQ | Interop | MQTT interoperability — topic mapping by registry lookup, QoS mapping, session semantics | designed |
| MIP-KN | Payload | Kinematic state — position, velocity, heading, altitude per platform | designed |
| MIP-TL | Payload | Scalar telemetry and sensor readings | designed |
Layering is strict and one-directional: a payload spec may never require the relay to read it.
Those nine are the transport stack — the part the rest of this section explains.
The register carries more, and it is the authority rather than this table:
feed and payload profiles (MIP-AS AIS, MIP-ML MAVLink, MIP-RI Remote ID,
MIP-DS ROS 2 / DDS, MIP-NM NMEA 0183, MIP-CT drone backbone telemetry,
MIP-MI STANAG 4609 motion imagery), the media control plane (MIP-MS), the
DDIL and delivery contracts (MIP-DD, MIP-CU), and the adapter contract that
pins units, CRS, time base and lossiness (MIP-AD).
Every one of them is DRAFT today, and the lifecycle is earned rather than
asserted: just check-mips decides each row against the tree, and ENFORCED —
the only state that permits NIP-11 advertisement — additionally requires a
reader in the relay's allowlist plus a live re-probe, because green CI
proves nothing about a running relay. Full register and lifecycle in
docs/mips/README.md; dependency graph and sprint
mapping in TASKS.md § 5.
Payload families
One kinematic schema serves both shapes of traffic, because two schemas would mean every fusion implementation writes two parsers plus a converter — and the converter is where provenance gets lost.
| Family | Shape | Movement contract |
|---|---|---|
| Drone / platform kinematics | temporal — N samples, 1 platform, 10–1000 Hz | conflated, key = platform id |
| AIS / ADS-B surveillance states | spatial — N platforms, ~1 sample each, 0.1–2 Hz × 10⁵ | conflated, key = ICAO24 / MMSI |
| Sensor and health telemetry | temporal | volatile |
| Health beacons | low-rate liveness | conflated, key = platform id |
Proposed kinds 21000–21002 sit in the ephemeral range 20000–29999, and
that is not cosmetic: session frames are fenced by a compile-time
assert!(is_ephemeral(k)), so a persistent kind number forfeits them and pays
30–70 µs of BIP-340 per sample instead of ~0.2 µs of HMAC. A ~350× cost
decided entirely by which integer someone picks. A persistent kind on a
session frame is rejected, never upgraded — extending session frames to
persistent kinds would turn "the relay can lie about who is typing" into "the
relay can forge history."
Two payload fields that must never default
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.fidelity: realmeans not simulated — it does not mean trustworthy. An unauthenticated broadcast faithfully relayed is stillrealand still spoofable. Spoof detection is an explicit non-goal of the transport layer, doubly so for a relay that has forsworn reading. Detection belongs to fusion.
Stream labels — a safety primitive, not governance
Three closed lanes applied per stream, never per message, resolved at subscribe time at zero per-event cost:
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 measured 70–84% defect rates in published audits; per-stream is cheaper and more correct.
Symbology — SIDC / MIL-STD-2525 / APP-6
open — the newest and least-settled part of the programme. What the Design Law already decides:
- The SIDC struct belongs in the payload, never in the header. The relay does not route by affiliation, does not filter by battle dimension and does not render. Putting a symbol code in the header would be a request that the relay read semantics, forfeiting everything the opaque-frame design buys.
- A symbol code must never become a routing, topic or storage key. Affiliation is mutable; a track can be re-designated. Freeze it into a key and every re-designation is a fleet-wide rename plus a hole in the history. Route by geo cell; render by symbol.
- Standard and version are explicit and non-defaulting. 2525D / APP-6(D) is a 20-digit numeric SIDC; 2525C / APP-6(B) is a 15-character alphanumeric code. They are not interconvertible by inspection, and guessing wrong flips affiliation.
- One authoritative simulation marker. 2525/APP-6 carries its own
exercise/simulation amplifiers and we carry
fidelityandcontextstream labels. Two independent ways to assert "simulated" will drift silently, and the drift is safety-relevant. The standing recommendation — for the council to accept or reject before either ships — is that the stream label is authoritative and any symbology amplifier is rendered from it, never read back as a second source of truth.
MQTT: what MERIDIAN replaces, and what stays at the edge
The claim is narrower than "broker replacement", and the narrowing is ratified in DIAGRAM.md § Part III by the Architecture & Scale council.
| Replaced by MERIDIAN | Kept at the edge (a broker, legitimately) |
|---|---|
| The mandatory message-bus hop on every message | Broker-held offline queues and persistent sessions |
| Per-message topic-ACL authorization | MQTT QoS 1/2 delivery state for constrained devices |
| A second durable log beside the query store | Retained messages and Last Will & Testament |
| Topic-prefix multi-tenancy | Shared subscriptions ($share/group/topic) |
| Unauthenticated-past-the-broker messages | The embedded MQTT client SDK ecosystem |
The right-hand column is a standing architectural boundary, not a gap list to close. A device offline for a day needs broker-held state; a MERIDIAN client re-runs a query over stored events, which works for a chat client and does not work for a constrained device that cannot page history.
The one irreducible difference: an MQTT message is authenticated by its connection, so downstream of the broker nothing proves authorship. A signed event carries its own proof to every consumer, forever — across organizations that share data but not trust. That is what the ~37.7 µs buys, and what the audit chain, third-party verification and any future federation rest on.
Sovereignty and decentralization
flowchart LR
classDef dev fill:#4a4a4a,stroke:#2b2b2b,color:#fff
classDef edge fill:#1f6aa5,stroke:#123f63,color:#fff
classDef feed fill:#7d6608,stroke:#4d3f05,color:#fff
classDef spine fill:#b03030,stroke:#6e1c1c,color:#fff
classDef bad fill:#8a1c1c,stroke:#500f0f,color:#fff
DEV["Constrained devices<br/>MQTT clients · intermittent links"]:::dev
BRK["EDGE — MQTT bridge + leaf relay<br/>persistent sessions · QoS 1/2 · retained<br/>LWT · store-and-forward across outages"]:::edge
FD["FEEDER — the boundary<br/>decode · dedup · routing header · MAC or sign · batch"]:::feed
SP["SPINE — authoritative relay<br/>admission · verification · ordering ·<br/>tenancy fence · audit · fan-out"]:::spine
DEV --> BRK --> FD --> SP
X["REFUSED: the leaf as a routing peer.<br/>A leaf outage would become a spine routing failure,<br/>and a compromised leaf could transit between communities."]:::bad
SP -.->|never| X
- The tenancy fence is the sovereignty boundary. A relay is one
operator's world — its members, channels, repos, streams, search index and
audit chain. The fence is resolved from the request host before any handler
runs, an unknown host fails closed rather than falling through to a default,
and the tenancy id is immutable and part of every primary key. Two tenants
sharing infrastructure cannot observe each other — not events, profiles, DMs,
search results, audit chains, or error strings. That property is mechanized in
TLA+ and its authorization in Tamarin, mutation-tested. (In the schema and
the internal docs that fence is still spelled
community/community_id; the user-facing rename to "relay" is in flight —meridian-1lv.) - Identity is portable; standing is local and dated. A keypair is yours
everywhere. Membership is modelled as
[since, until)rows in one grant store, never baked into a credential that goes stale the moment it is issued. - The boundary is one-directional. The spine ingests from the edge. The edge never routes for the spine.
- Federation is designed and deliberately blocked. Multi-master peering would let a concurrent grant resurrect a revoked one by wall-clock timestamp. The defect is dormant while a single relay is the only writer per tenant; the fix — epoch-monotonic, absorbing revocation — lands before the second writer, because after is too late and the resurrection is silent.
System shape
flowchart TB
classDef client fill:#4a4a4a,stroke:#2b2b2b,color:#fff
classDef relay fill:#b03030,stroke:#6e1c1c,color:#fff
classDef store fill:#1e7a45,stroke:#0f4527,color:#fff
classDef next fill:#2b2b2b,stroke:#1e7a45,color:#8fd6ab,stroke-dasharray: 5 3
subgraph CL["PARTICIPANT TIER — two edges, one enforcement point"]
direction LR
subgraph CN["Nostr edge — NIP-01 JSON / WebSocket · permanent"]
direction LR
D["Desktop · Tauri 2 + React 19"]:::client
M["Mobile · Flutter"]:::client
W["Web · repo browser + invite"]:::client
end
subgraph CM["Machine edge — Zenoh native · NIP-XP profile"]
direction LR
A["Agents · meridian-cli · ACP · dev-MCP"]:::client
F["Feeders · machine + IoT frames"]:::next
end
end
subgraph RL["RELAY TIER — one Axum process, the ONLY enforcement point"]
direction TB
B0["community bind — resolve_host → TenantContext<br/>before AUTH, EVENT, REQ, REST, media, git"]:::relay
B1["admission · NIP-42 / NIP-98 · exchange profile"]:::relay
B2["EVENT — kind gate · BIP-340 verify · scope · membership"]:::relay
B3["REQ — access checked BEFORE registration"]:::relay
B4["SubscriptionRegistry — 3-tier fan-out index"]:::relay
B0 --> B1 --> B2 --> B4
B1 --> B3 --> B4
end
subgraph ST["STACK TIER — one system of record per fact"]
direction LR
PG[("Postgres 17 — THE EVENT LOG<br/>events · members · workflows · audit · FTS")]:::store
DF[("Dragonfly — STATE, never events<br/>presence · rate windows · pub/sub today")]:::store
S3[("MinIO / S3 — BYTES, never events<br/>media blobs · git objects")]:::store
end
subgraph BT["BUS TIER — delivery between pods; the edge cannot tell"]
direction LR
EB{{"EventBus seam — Phase 1"}}:::next
ZP["Zenoh peer — Phase 2<br/>IN-PROCESS in every pod<br/>pod ↔ pod direct, no broker hop"]:::next
ZR["zenohd routers — Phase 4<br/>OPTIONAL: scale-out · cross-region<br/>only where a peer mesh stops scaling"]:::next
EB --> ZP -.-> ZR
end
subgraph XT["REPLICA + ARTIFACT — downstream, never truth"]
direction LR
IP["IPFS — CID replication<br/>planned, contract unwritten"]:::next
RS["ReductStore — REST artifact tier<br/>opt-in, no call sites"]:::next
end
CL --> RL
RL <--> PG
RL <--> DF
RL <--> S3
DF -.->|"bus migrates"| EB
S3 -.->|"replication"| IP
RL -.-> RS
Dashed nodes are decided but not built — there is no
zenohdependency in anyCargo.tomltoday, and IPFS has no spec yet. Dragonfly keeps presence, rate windows and the fenced arbiter after the bus moves: Zenoh is a pub/sub fabric, not a keyspace, so onlyPUBLISH/PSUBSCRIBEmigrates.
| Tier | Owns | Must never |
|---|---|---|
| Participant | Rendering, drafts, local settings, key custody | Hold authority, or talk to anything but its relay |
| Relay | Host-derived tenant binding, admission, verification, ordering, membership, fan-out | Delegate an access decision — it is the only enforcement point |
| Postgres | The event log and every fact derived by transaction | Be second. Nothing else may claim truth for a fact it stores |
| Dragonfly | Ephemeral state with a TTL, plus cross-pod delivery | Become a durable log |
| Object store | Bytes too large for a row | Hold anything the relay must interpret to decide |
| Bus | Delivery between pods, QoS classes, interest routing | Decide anything. A transport swap the NIP-01 edge can detect is not a transport swap |
| Replica / artifact | Downstream copies and time-indexed artifact bytes | Become a resolution path. A CID that answers a fetch S3 refused is an un-revocable blob |
The relay orchestrates every subsystem by direct call, and no subsystem reaches
sideways for a capability the relay owns — meridian-workflow never calls
meridian-pubsub, and meridian-search never calls meridian-db, so fan-out
and storage stay the relay's decisions. (meridian-workflow does depend on
meridian-db for its own run and approval records; that is its store, not a
lateral hop.) Every cross-subsystem interaction is a line of code in the relay,
which is why one process can be reasoned about.
Design Law
Binding constraints on new protocol, identity and access-control surface. Each is cheap to honour up front, expensive to retrofit, and written down because the failure it prevents is silent rather than loud. Full text in AGENTS.md § Design Law; the conformance register is DIAGRAM.md § Part II.
| Law | What it forbids |
|---|---|
| Names never encode ownership | org/team/channel paths — or symbol codes — in topic, storage or namespace keys. A reorg or re-designation becomes a fleet-wide rename |
| Standing is registry-derived and effective-dated | Baking org or role into a credential; membership is [since, until), never a frozen claim |
| Add attributes, not mechanisms | A policy DSL or rules engine. New capability = new attribute + new event kind |
| No third shim | A mapping table or sync job between two enforcement points — one grant store, everyone reads it |
| Explicit denial, never a silent empty result | A decision that cannot name the record that made it |
| Nothing is resolved until deployed and re-probed | Closing work on green CI alone |
| No vocabulary without an enforcement point | A kind, tag or grant name nothing reads — enforced by just check-kinds |
| Revocation is monotonic and absorbing | Timestamp-wins resolution that could resurrect a revoked grant. Decided now, built at the second node — before any federation, never after |
| Scale by replica / function / tenant / traffic class | Sharding by kind or NIP. A kind is a column in every shard, never a shard |
Refusals, declined by citation rather than re-argued — a second durable log
behind the relay (R2), a second read path for data the spine stores (R3),
sharding telemetry kinds onto their own relays (R1), the edge peering with the
spine (R4), caching grants at the bridge (R5), and building machinery for
load nobody has measured yet (R8).
What runs today
The human, agent and service path is in production use; the machine and IoT path is specified and gated on measurement.
| Surface | State |
|---|---|
| Relay, channels, threads, DMs, canvases, media, search, audit chain | shipping |
| Desktop app (Tauri 2 + React 19) | shipping |
meridian-cli — agent-first, JSON in / JSON out — and the ACP harness |
shipping |
| YAML workflows — message / reaction / schedule / webhook triggers | shipping |
| Git hosting + NIP-34 events (patches, repo announcements, status), Nostr-signed push | shipping |
| Huddles — WebSocket Opus voice relay, no external SFU | shipping (recording, per-track publishing planned) |
| Shared compute mesh — relay-gated, OpenAI-compatible to agents | shipping |
| Mobile (Flutter, iOS + Android) | in development |
| Workflow approval gates | partial — schema, API, MCP tool and UI exist; the executor does not yet suspend and resume |
| Opaque-frame data plane, MIP suite, MQTT bridge, symbology | designed — see TASKS.md |
Screenshots — the human and agent surface
![]() Agents are members, not bots. Added to a channel the same way a person is. |
![]() Rooms are cheap. Name it, describe it, scope it. |
![]() Media with frame-anchored comments, stored via Blossom on S3/MinIO. |
|
Quick start
Requires Docker and
Hermit, which pins the whole toolchain
(Rust, Node, pnpm, just) and downloads it on first use.
git clone https://git.office.ilab.zone/RAID/R2D2-MERIDIAN.git
cd R2D2-MERIDIAN
. ./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
run.sh wraps just; it does not replace it. What it adds is port resolution —
every port in the profile is probed by attempting a bind and walked upward until
free, then written to the two existing override layers (.env.local and
docker-compose.override.yml) so builds stay cache-warm. On a machine running
several stacks, that removes an entire class of quiet failure: a lost port bind,
or a relay dialling :5432 and reaching someone else's database.
Prefer the raw recipes for split-terminal work:
just setup # deps, Docker services, migrations
just relay # relay on ws://localhost:3000
just desktop-dev # web-only dev server (fast iteration)
just dev # full desktop app with native shell
just ci # the complete local gate — run before every PR
For agents and services, set MERIDIAN_PRIVATE_KEY and MERIDIAN_RELAY_URL,
then drive everything through meridian-cli — JSON on
stdout, structured errors on stderr, designed for tool calls. The ACP harness
injects both variables into managed agent subprocesses automatically.
Deploying rather than developing? Use the production Compose bundle in
deploy/compose/ or the Helm charts in
deploy/charts/. The root docker-compose.yml is for
day-to-day development only.
Windows prerequisites
The agent shell tool runs commands under bash. Install
Git for Windows — MERIDIAN resolves Git Bash
at runtime. To point it at a different bash-compatible shell, set
MERIDIAN_SHELL to its path; the agent's tool description updates to match.
Repository map
Everything MERIDIAN ships is built from this tree — the protocol, the relay, four client surfaces, two standalone services, the deployment charts and the specs. There is no sibling build pipeline to coordinate with, and no directory here is a vendored copy of something released elsewhere.
Top level
R2D2-MERIDIAN/
│
├── crates/ Rust workspace — the relay and every binary shipped beside it
├── REMAPPING/meridian-desktop/ Tauri 2 + React 19 — the primary human client
├── REMAPPING/meridian-web/ Browser client served by the relay: repo browser + invite accept
├── REMAPPING/meridian-admin-web/ Read-only operator dashboard, served on the admin host
├── REMAPPING/meridian-mobile/ Flutter (iOS + Android) — Riverpod + hooks
│
├── migrations/ Forward-only Postgres schema, applied on relay startup
├── schema/ schema.sql — the flattened current schema, for reading only
├── deploy/ Helm charts, the production Compose stack, deploy scripts
│
├── docs/ Draft NIPs, the MIP register, formal models, operator runbooks
├── skills/ Project skill suite — how agent contributors route work
├── .settings/ Working docs: feature specs, gauntlet baselines, brand, references
├── .beads/ Issue database (bd) — the source of truth for task state
│
├── scripts/ Dev tooling, CI guard cores, release and contract scripts
├── perf/ Relay bus scaling harness
├── benchmarks/ harbor-meridian-orchestra multi-agent benchmark
├── examples/ countdown-bot, meadow-core, sample workflows
│
├── bin/ Hermit-pinned toolchain — `. ./bin/activate-hermit`
├── patches/ pnpm patches for pinned upstream fixes
├── .github/ CI workflows, issue and PR templates, CODEOWNERS
│
├── Justfile Every verification command — the entry point for all of them
├── run.sh Local launcher: wraps `just` and deconflicts ports
└── docker-compose.yml Development backing stack — Postgres, Dragonfly, MinIO
repos/, test-results/, .meridian-run/ and .control-plane/ appear once you
run something and are gitignored. They are state, not source.
The components
Seven deployable things come out of this repo. Each one has exactly one home.
| Component | Built with | Lives in | Run it |
|---|---|---|---|
| Relay — the server, and the only enforcement point | Rust · Axum · Tokio | crates/meridian-relay |
just relay → ws://localhost:3000 |
| Desktop — the primary human client | Tauri 2 · React 19 · Vite · Tailwind | REMAPPING/meridian-desktop/ |
just dev (native shell) · just desktop-dev (browser) |
| Web — the narrow browser surface | React 19 · Vite | REMAPPING/meridian-web/ |
built to REMAPPING/meridian-web/dist, served by the relay |
| Operator dashboard — read-only ops view | React 19 · Vite · plain CSS | REMAPPING/meridian-admin-web/ |
just admin → served on the admin host |
| Mobile | Flutter · Riverpod · hooks | REMAPPING/meridian-mobile/ |
just mobile-dev |
| Control plane — relay provisioning | Rust · Axum | crates/meridian-control-plane |
./run.sh control-plane |
| Push gateway — APNs, blind by design | Rust · Axum | crates/meridian-push-gateway |
own image, own deployment |
The relay, the control plane and the push gateway are three separate binaries
with three separate container images (registry.r2d2.office.ilab.zone/meridian-*)
and, in the control plane's case, its own database. They are not one service with
flags: sharing the relay's database would collide two migration histories, and
the push gateway is deliberately blind to message content.
The relay — crates/meridian-relay
One Axum process. It terminates the WebSocket protocol, answers the narrow HTTP
surface, hosts git and huddle audio, and serves the REMAPPING/meridian-web/ and REMAPPING/meridian-admin-web/
bundles. Every access decision in the system is made here — no client, no
bridge and no downstream consumer may make one.
crates/meridian-relay/src/
├── tenant.rs host → TenantContext. Runs BEFORE any handler sees data
├── admission.rs NIP-42 / NIP-98 admission and exchange profile
├── connection.rs per-socket lifecycle
├── protocol.rs NIP-01 frame parsing (EVENT / REQ / CLOSE / COUNT / AUTH)
├── handlers/ what each verb does — event, req, count, auth, close,
│ ingest, moderation commands + authz, reports, feedback,
│ push leases, relay admin, community provisioning
├── api/ the HTTP surface — events, media (Blossom), git smart HTTP,
│ invites, nip05, operator, admin/, docs (OpenAPI), bridge
├── subscription.rs SubscriptionRegistry — the 3-tier fan-out index
├── audio/ huddle voice relay (Opus over WebSocket, no external SFU)
├── tunnel/ inter-relay mesh sessions — fenced ownership, generations
├── conformance/ runtime trace emission for the TLA+ replay checker
├── router.rs route table · state.rs shared app state
├── metrics.rs the Prometheus registry the operator dashboard reads
├── push_runtime.rs push lease evaluation · workflow_sink.rs workflow triggers
└── storage_sweep.rs retention and TTL enforcement for ephemeral kinds
The relay orchestrates every subsystem crate by direct call, and no subsystem reaches sideways for a capability the relay owns. That is why one process can be reasoned about — see System shape.
Desktop — REMAPPING/meridian-desktop/
A Tauri 2 app: a Rust native shell around a React 19 webview. The split matters — anything touching the OS keychain, the filesystem, a native socket or a subprocess lives in Rust; everything else is React.
REMAPPING/meridian-desktop/
├── src/ the React 19 front end
│ ├── app/ shell, router, community remount boundary, shortcuts
│ ├── features/ one folder per product area (channels, chat, agents,
│ │ huddle, forum, projects, search, settings, workflows,
│ │ moderation, onboarding, keys, mesh-compute, …)
│ ├── shared/ api client, UI primitives, hooks, theme, kind constants
│ ├── brand/ globe + wordmark renderers the control plane inlines
│ └── testing/ the E2E mock Tauri bridge
├── src-tauri/ the Rust native shell
│ ├── src/commands/ every Tauri IPC command the front end may call
│ ├── src/relay*.rs native WebSocket client and relay admission
│ ├── src/secret_store.rs OS keychain custody for private keys
│ ├── src/media_proxy.rs local proxy so the webview can load relay media
│ ├── src/managed_agents/ agent subprocess supervision
│ ├── src/huddle/ native audio capture and playback
│ └── src/migration/ local database migration on upgrade
└── tests/e2e/ Playwright specs (mock bridge + live relay)
Two traps this layout sets, both documented in REMAPPING/meridian-desktop/AGENTS.md:
REMAPPING/meridian-desktop/src-tauriis excluded from the root Cargo workspace.cargo testat the repo root does not run it. Usecargo test --manifest-path REMAPPING/meridian-desktop/src-tauri/Cargo.toml.- Switching community remounts React but does not reload the page. Any
module-level cache you add must be reset in
resetCommunityState(), or data from the previous community leaks into the next one.
Web — REMAPPING/meridian-web/
Deliberately narrow, and kept that way. It is not a browser port of the desktop app; it exists so that a link works without an install:
src/features/repos/— the repo browser behind git smart HTTPsrc/features/invite/— invite acceptance
The relay serves the built bundle. Adding a third feature here is a decision, not a routine change — the desktop app is where client surface belongs.
Operator dashboard — REMAPPING/meridian-admin-web/
About ten files, no state library, no design system, no Tailwind. Its value is that an operator can read the whole thing in one sitting, so it stays dependency-light on purpose.
Read-only by contract. Moderation actions are Nostr command kinds handled
by the relay; a write added here would bypass the audit and authorization
pipeline. src/types.ts mirrors the relay's admin response contract in
crates/meridian-relay/src/api/admin/ — the two are updated together and
tests/routes.spec.ts fails on drift. A metric the relay does not report renders
as "Not reported", never as zero: an absent metric is not evidence of health.
Mobile — REMAPPING/meridian-mobile/
Flutter, Riverpod + flutter_hooks, Catppuccin theming matched to desktop.
Features under lib/features/ (activity, channels, forum, home, invites,
pairing, profile, pulse, search, settings), shared code in lib/shared/.
Event kinds in lib/shared/relay/nostr_models.dart must stay in sync with
REMAPPING/meridian-desktop/src/shared/constants/kinds.ts. Agents may run flutter test,
flutter analyze and dart format — never flutter run, build, clean
or upgrade.
Control plane and push gateway
meridian-control-plane provisions relays: account sessions, Nostr identity
binding, and the <name>.relays.meridian.localtest.me:PORT URLs handed back to
clients. It enforces an Origin check against MERIDIAN_CONTROL_PUBLIC_ORIGIN,
and it rebuilds a signed operator string that must match the relay's
RELAY_OPERATOR_API_ORIGIN byte-for-byte — a relay left running across a port
change rejects every provisioning call as unauthorized. Restart it rather than
debugging the signature.
meridian-push-gateway is a blind, capability-gated NIP-PL gateway to APNs. It
forwards a wake signal; it does not learn what the message said.
The Rust workspace — crates/
Twenty-eight crates. The split is deliberate: meridian-core has no I/O so the
protocol can be tested without a database, and every subsystem is a library the
relay calls rather than a service that calls back.
Relay and protocol
| Crate | Owns |
|---|---|
meridian-relay |
The WebSocket relay server — main entry point; also hosts git and huddle audio |
meridian-core |
Zero-I/O core: event types, BIP-340 verification, filter matching, the kind registry |
meridian-db |
Postgres event store and data access layer |
meridian-auth |
Authentication and authorization (NIP-42 / NIP-98) |
meridian-pubsub |
Cross-pod event bus — the EventBus seam, its Dragonfly backend, fan-out, presence |
meridian-search |
Postgres full-text search, scoped by community |
meridian-audit |
Hash-chain audit log |
meridian-media |
Blossom/S3 media storage, validation, thumbnails |
meridian-relay-mesh |
Inter-relay QUIC mesh: transport, membership, the fenced wire contract |
meridian-conformance |
Trace schema and replay checker for docs/spec/MultiTenantRelay.tla |
meridian-openapi |
Vendored Scalar renderer and OpenAPI reference pages |
Standalone services
| Crate | Owns |
|---|---|
meridian-control-plane |
Relay provisioning: account sessions, identity binding, community creation |
meridian-push-gateway |
Blind, capability-gated NIP-PL gateway to APNs |
meridian-pair-relay |
Ephemeral sidecar relay for NIP-AB device pairing handshakes |
Agent surface
| Crate | Owns |
|---|---|
meridian-cli |
The agent-first CLI — JSON in, JSON out, structured exit codes |
meridian-acp |
ACP harness bridging Meridian events to AI agents |
meridian-agent |
Minimal ACP-compliant agent — non-streaming, tool-calls-as-output |
meridian-dev-mcp |
Developer MCP server — shell and file-edit tools |
meridian-workflow |
YAML-as-code workflow engine (evalexpr conditions) |
meridian-persona |
Parser and loader for .persona.md persona packs |
meridian-harness |
All-in-one bundle of ACP, agent and dev MCP |
Git over Nostr
| Crate | Owns |
|---|---|
git-sign-nostr |
NIP-GS commit and tag signing with a Nostr secp256k1 key |
git-credential-nostr |
Git credential helper producing NIP-98 auth headers |
Shared libraries and tooling
| Crate | Owns |
|---|---|
meridian-sdk |
Typed Nostr event builders for Meridian operations |
meridian-ws-client |
Shared NIP-42 WebSocket client — connect, auth, publish |
meridian-admin |
Operator CLI for relay administration |
meridian-pairing-cli |
CLI for NIP-AB device pairing interop testing |
meridian-test-client |
Integration test client and the E2E suite |
meridian-test-db |
Test-database resolution, and the guard that refuses to destroy a non-disposable database |
This table is checked against the Cargo workspace in both directions by
just check-architecture-map: a crate added without a row fails CI, and so does
a row for a crate that no longer exists.
macOS note for git-credential-nostr
Configure a host-scoped empty helper entry before the nostr entry. This
resets the inherited osxkeychain helper and prevents spurious
fatal: failed to store output on success. See the
credential-helper setup.
Where the rules live
Every directory above carries an AGENTS.md that is a binding contract for its
subtree, not a description of it. The root AGENTS.md holds Design
Law and the child index; each child owns its local rules and indexes its own
children. Before editing a file, read the root document plus every AGENTS.md on
the path to it — the closer document wins on local detail, and no child may
weaken a parent's constraint.
| Contract | Governs |
|---|---|
crates/AGENTS.md |
The Rust workspace, with nested contracts for relay, core, db, acp, agent, cli, test-client |
REMAPPING/meridian-desktop/AGENTS.md |
The desktop app; nested under src/features/, src-tauri/, tests/ |
REMAPPING/meridian-mobile/AGENTS.md |
Flutter rules, the file-size ceiling, which flutter commands are safe |
REMAPPING/meridian-web/AGENTS.md · REMAPPING/meridian-admin-web/AGENTS.md |
The two browser surfaces and the relay admin API contract |
migrations/AGENTS.md |
Forward-only schema, checksum-frozen files, partition and fence invariants |
docs/AGENTS.md |
NIPs, MIPs, TLA+/Tamarin models, operator guides |
deploy/AGENTS.md |
Helm charts and the Compose stack; values, schema and test contract |
scripts/AGENTS.md |
Dev tooling, the shared lint guard cores, release and contract scripts |
.github/AGENTS.md |
Required checks, release automation, DCO, action pinning |
.settings/AGENTS.md |
Feature specs, gauntlet baselines, the brand kit, reference material |
Protocol surface
NIP-01 over WebSocket is the primary API. Everything on the human, agent and
service path is an event; a new capability is a new kind integer in
meridian-core/src/kind.rs plus a handler, never a new endpoint-specific JSON
API. That buys realtime fan-out, NIP-29 scoping and the existing auth pipeline
for free.
The HTTP surface is deliberately narrow and preserves the same host-derived
community boundary: NIP-11/NIP-05 metadata, POST /events, POST /query,
POST /count, workflow webhooks, Blossom media, git smart HTTP, git policy
hooks, health probes.
That surface publishes an OpenAPI 3.1 document and renders it, and both are
served by the process they describe — the relay at /docs, the control plane
at its port root. The document is generated from the handlers that answer the
requests (utoipa_axum derives each route from its operation, so an endpoint
cannot ship undocumented), and the renderer is vendored into the binary rather
than loaded from a CDN, so a deployment with no egress gets the same page as
one on the open internet. The WebSocket API is not in it — a REQ
subscription is not an HTTP operation, and a reference that implied otherwise
would read as a complete description of an API it only partly covers.
Repo-local extensions live as draft NIPs in docs/nips/, with
TLA+ and Tamarin models for the multi-tenant isolation and authorization
guarantees in docs/formal/. just check-kinds fails CI on
any kind, tag or grant name that no enforcement point reads — declared
vocabulary without an enforcement point is the standing risk when a repo carries
this many drafts.
Programme and roadmap
- DIAGRAM.md is the system view: Part I the running system, Part II the GOAT/EFDI conformance register, Part III the throughput case against a central broker, Part IV the maturation map from one relay that does everything to a thin spine plus independently deployable consumers — adding no second read path, no second policy store, and no client-visible protocol change.
- TASKS.md is the 10-week decentralized-relay programme that executes Part III's ratified call: a frozen MIP specification suite, a reference implementation of the opaque-frame data plane, an MQTT interoperability bridge at the edge, and a conformance suite. It mirrors Bead IDs; it never leads them.
- Beads is the source of truth for task state.
bd readyfor available work,bd show <id>for any ID cited in the docs. Do not keep a parallel TODO list.
The live gate is measurement, not design. Zenoh's direction is settled —
zenohd routers are the bus end-state — but the Phase 0 benchmark still decides
which traffic classes move first and what may be claimed in public: the
circulating ~4–5M msg/s figures are measured against Kafka, MQTT and CycloneDDS,
not against Dragonfly, so the gap over our path is still unmeasured. The
opaque-frame path must likewise be measured before anything downstream is
optimised. Machinery built for load nobody has measured is inventory (R8).
Quality gates
just ci # fmt + clippy + desktop lint + unit tests + builds
just test-unit # unit tests, no infrastructure
just test # integration suite (requires Postgres + Dragonfly)
just check # every guard: kinds, skills, brand, migrations, contracts
Read the exit code, not the output — piping a gate into tail or grep
reports the filter's status, so a failed run looks clean. Clippy passing does not
mean fmt passes, and a green cargo test says nothing about the lint gate: a
test build compiles through an unused import that -D warnings rejects.
Pre-commit hooks auto-fix formatting and re-stage; pre-push runs clippy and fast
unit tests. Commit with git commit -s — the DCO check fails any PR with a
commit missing a Signed-off-by trailer.
Additional standing rules: no unsafe; no new unwrap() or expect() in
production paths; new public API carries doc comments.
What we deliberately do not build
- A second system of record. Postgres holds the events. Derived views are legitimate downstream of the spine — cursor-bearing, freshness-stamped, and invisible at the NIP-01 edge — but nothing else claims truth for a fact the spine stores.
- A policy engine. Grants are enumerable dated rows precisely so that a denial can name the record that decided it. A rule- or formula-based DSL is un-auditable by enumeration, and is refused by name.
- Payload interpretation on the hot path. The relay does not decode a track, fuse a picture, detect a spoof, or render a symbol. Those are consumer jobs, and the refusal to read is what makes the throughput possible.
- Federation before monotonic revocation. Multi-master peering would let a concurrent grant resurrect a revoked one by wall-clock timestamp. The fix lands before the second writer, because after is too late.
- A throughput number without its profile. Including in this file.
Documentation
| Document | Covers |
|---|---|
| ARCHITECTURE.md | Component reference — crates, pipelines, kind ranges, subsystem boundaries |
| DIAGRAM.md | System view, conformance register, throughput case, maturation map |
| TASKS.md | The 10-week decentralized-relay programme and the MIP suite |
| AGENTS.md | Design Law, agent conventions, the STELLAR doc hierarchy |
| CONTRIBUTING.md | Setup, code style, PR process, how to add kinds / commands / endpoints |
| TESTING.md | Multi-agent E2E guide |
| RELEASING.md | Release flow, candidate tags, deployment provenance |
| SECURITY.md · GOVERNANCE.md · CODE_OF_CONDUCT.md | Disclosure, decision rights, conduct |
| VISION.md and companions | Sovereign · Projects · Agents · Mesh · Activity · Moderation |
MERIDIAN — Multidomain Exchange for Realtime Integration of Data Innovation with Allied Nations
MIT · Built by STELLAR


