Two networks
Boundary Architecture Between Mesh & Private Infrastructure
Operators joining the mesh often maintain existing infrastructure. Reusing an existing production cluster directly as a mesh node is a dangerous shortcut. That shortcut produces three predictable failures: a network that collapses if a business fails, private users exposed to unvetted traffic, and a single operator gaining an unearned veto. Fleetmesh defines the junction between private estates and the public mesh as a strict security boundary. These rules apply universally, from commercial cloud operators to home servers.
The gateway
A gateway is a separate process, in its own isolation boundary, holding its
own node key, its own storage and its own resource budget. Its only interface to the
operator's own services is wss:// plus NIP-77 range sync — the same
protocol any third-party client speaks. That is deliberately the weakest coupling available,
and it buys three things:
- The estate cannot be broken by the mesh. A malfunctioning or attacked gateway appears to internal relays as a single misbehaving client. Relays handle this natively with existing rate limits and connection drops.
- The mesh cannot be broken by the estate. The gateway is one anchor among many. If the entire estate behind it is down, the mesh loses one node's coverage.
- The boundary is testable. Internal isolation is mechanically provable. The gateway holds no credentials beyond an ordinary relay WebSocket connection. This is a verifiable deployment property rather than an assumption about application logic.
What crosses, in each direction
| Direction | Rule |
|---|---|
| estate → mesh | Per-shard, opt-in, and the opt-in belongs to the account that owns the data — never to the operator carrying it. A relay publishes nothing into the mesh until the shard is turned on, and turning it off stops new publication immediately. Public events only; the never-crosses list below is enforced at the gateway and again at every receiving node. |
| mesh → estate | Quarantined. Mesh-sourced events land in a separate store and are never auto-promoted into the relay behind the gateway. Backfill is requested per shard and arrives as an explicit import with its provenance recorded. Anything else lets strangers write into a service whose users never consented to them. |
| reputation |
Does not cross in either direction. A pubkey the mesh has judged hostile keeps
whatever service its own relay grants it; an account in good standing on that relay
starts at probation in the mesh like everyone else. Delegating
moderation of your own users to a permissionless network is not a feature, and
importing a stranger's standing is not either.
|
| identity | Separate keys. A mesh node key is generated for the mesh and lives only in the gateway's own secret store. Existing service identities MUST NOT be reused, so a mesh key compromise never produces a signature anyone trusted for something else. |
| money & status | Mesh traffic is never metered to a user and never counts against a quota. Mesh incidents are not incidents of the service beside it, and no service commitment anywhere should make a statement about the mesh. |
Never crosses, in any version
| Data | Why |
|---|---|
| custodial keys | Encrypted nsecs, the secret that unlocks them, and signer session state. Replicating them turns custody into distribution. |
| kind 24133 | NIP-46 signer traffic. Ephemeral, and federating it would expose every pairing and every signing event of every reachable signer. |
| kind 1059 | Gift wraps, under the rule in § Replication. Stated there once and normatively; repeated here only because the boundary table would be misleading without it. |
| billing & push | Payment records, balances, push subscriptions. Commercial and contact data belonging to people who never joined the mesh. |
| client IPs | Connection metadata stays on the node that observed it. Reports carry counts, never addresses. |
Nothing is a constant
relay.example.org appears in this specification exactly as an example. Three
rules keep it that way:
-
Bootstrap is a file, not a constant. Default peers ship as a plain, editable file.
A single
--bootstrapCLI flag bypasses this file entirely. A build shipping an empty bootstrap list MUST operate without error. The composition rule is staged progressively (see § Governance) to allow network bootstrap from genesis without artificial barriers. - DNS seeds are optional. A node MAY discover peers through DNS TXT records, the way Bitcoin does, and MUST NOT require them. Losing every seed domain slows discovery for new nodes and does nothing to established ones.
-
Protocol identifiers are neutral. The
https://URIs naming the DIDComm protocols, the gossipsub topics and the transport ALPNs all sit underfleetmesh.orgrather than a product domain. They are identifiers, never dereferenced, so this buys nothing at runtime — it buys not having to hold a flag day later, when renaming stops being free. See § Governance.
The conformance suite includes a dedicated run with all default anchors and fleetmesh-operated infrastructure removed.
The test disables all gateways, seed domains, and sets fleetmesh.org to resolve to nothing.
Discovery, bilateral peering, state synchronization, grant issuance, and conformance reporting must all execute successfully.
This test runs in continuous integration on every commit to enforce absolute infrastructural independence.
Governance
Protocol Assets & Decentralized Governance
Network governance spans four distinct operational assets. Treating them as a single problem creates unnecessary bureaucracy. Separating them reveals that three are already decentralized by design, and the fourth requires no central authority.
| Asset | What it actually risks | Disposition |
|---|---|---|
| protocol identifiers | Nothing technical — they are never dereferenced. Only that a network meant to outlive a company carries its name. | registered fleetmesh.org, held as the project’s own
name rather than a product’s. |
| kind registrations | Nothing, once submitted. A merged entry in registry-of-kinds lives in a repository
neither project owns nor can revoke. |
self-neutralising Submitting them is itself an act of decentralisation. |
bootstrap.toml |
Soft power over who a new node meets first. Not a takeover vector — established nodes have
persisted peers, and one --bootstrap address bypasses it entirely. |
unsigned A convenience whose composition is disclosed. See below. |
| protocol versions | Looks like the dangerous one: whoever decides what the protocol is decides what everyone must implement. It stops being dangerous when nobody decides. | declared, not decreed |
Versions are declared, not decreed
There is no release authority, and there is no signer set. A k-of-n scheme, under which a change is valid once k of n signers endorse the announcement hash, operates by decree. Everything else in this document relies on empirical verification, and a version is not the one place to make an exception.
Everything else here works by declaration. A grant is a published budget. Coverage is a falsifiable claim. Conformance is your signed statement measured against my observation. A compute result is a hash the seller signs and a re-run can contradict. In every case the shape is the same: declare, be observed, and let divergence be the offence. A protocol version is exactly that kind of claim, so it gets exactly that treatment.
Each node declares its protocol version in its own descriptor.
The descriptor names the protocols the node implements, each with a version, and two nodes
peer on the ones they share: same major, the peer's minor at or above this node's floor.
The required peering and budget protocols must be compatible or
the peering is declined with UNSUPPORTED_PROTOCOL; a peering whose policy needs
another protocol the peer lacks is declined the same way. The schema digest records which
schema a node was built from and is surfaced to the operator, not refused on its own.
There is no governance announcement, notice period, or signing committee.
A version exists when active nodes declare it and disappears when they stop.
Adoption is measured directly from public on-wire data.
A version is a digest, and a protocol is a version
Version names alone are insufficient.
Two independent operators could deploy incompatible implementations both labelled fleetmesh/1 and discover discrepancies only after corrupting state.
Declarations therefore carry a canonical digest over the whole schema, which says exactly what a node was built from.
Compatibility, though, is decided one protocol at a time: each protocol identifier carries a version
(sync/1.0) that MUST move when its wire moves, schema/PROTOCOLS records a digest of each
protocol's block against that version and CI refuses a block that changes under an unchanged version. Nodes
peer on the protocols whose versions are compatible and surface a differing whole-schema digest to the operator
instead of refusing it.
what the version digest covers — and what it deliberately does not
kinds.json every event kind this version defines: number, class,
required and optional tags, content type
messages.json every DIDComm protocol: piuri, message types, body schemas
constants.json every number the arithmetic depends on: the 110% threshold,
the divergence floor, AIMD step, tier weights, hop limit,
window sizes, sampling defaults
digest := sha256(kinds.json || messages.json || constants.json)
each canonicalised, concatenated in that order
NOT covered: this document's prose, examples, rationale, section order,
or any wording anywhere. A typo fix MUST NOT fork the network.
Scoping the digest to the machine-readable schema is the whole trick. Too narrow and incompatible implementations still collide silently; too broad and correcting a sentence becomes a breaking change. What two implementations must agree on byte-for-byte is exactly what belongs in the hash, which is the same rule § Governance already applies to deciding which names are the protocol's.
Each protocol identifier carries a version of its own, as in sync/1.0. Those are
informative labels; the digest is normative, and a node negotiates nothing below it. An
identifier's version is bumped when its messages or fields change, so a reader can see which
protocol moved without diffing the schema.
This mechanism is not new to the document either. Kind 11801 already pins an operator's policy document by digest, and changing that document without republishing the digest is already an S2 misreport. Version pinning is the identical mechanism pointed at the protocol instead of at the policy.
What freezes fleetmesh/0, and who says so
Nobody says so, because declaration is the only mechanism there is.
fleetmesh/0.1-draft becomes canonical fleetmesh/0 when
at least three independently-operated anchors declare and peer with an
identical protocol digest for 30 consecutive days with zero adverse divergence reports
between them. Independence is judged under the Three-Layer Operator Independence Model
below.
That is an observation about the network rather than a decision taken on its behalf. The condition is checkable by anyone holding the descriptors, no announcement makes it true, and no announcement is required. Until it holds, the digest may change on any day and nobody is owed a migration — see § Alpha contract.
Starting at fleetmesh/0.1-draft
0-draft with different
digests are simply incompatible — correctly, and with neither at fault. Nobody
should build anything durable against it, and saying so in the version string itself is
more honest than a warning in a document people skim.
fleetmesh/1, not an amended 0.
What event constitutes the freeze, and who gets to say it has happened, is genuinely
unresolved — see § Open questions.
A node that declares a version it cannot speak commits a misreport. This triggers an S2 finding under the severity ladder. The peer's signed descriptor serves as direct cryptographic evidence. No new severity class, evidence type, or out-of-band machinery is required.
What the signing key was also doing
It had three jobs, and only one of them was versioning. The other two need answers:
bootstrap.tomlFragmentation moves from prevented to visible. Nothing stops the network splitting into islands that never converge. The digest only guarantees that a split is loud and immediate rather than silent and slow. Deprecation gets slower too: without a flag day, an old version dies only when peers individually stop granting it capacity, which is a local reputation decision like every other one here. A network that cannot be steered also cannot be steered well, and that is the trade. Every other decision in this document has already been made in the same direction.
What the shipped bootstrap list may contain, and when
The obvious rule — "name at least three independently-operated anchors, no operator holding a majority" — is the right end state and an impossible beginning. At genesis there is one operator. A rule nobody can satisfy is not a safeguard; it is a line everyone learns to ignore. So the composition requirement is staged, and what holds at every stage is disclosure rather than diversity:
| Independent operators | What bootstrap.toml MUST do |
|---|---|
| 1 — genesis | Carry operators = 1 and name that operator. A node reading it can then
see, without asking anyone, that its first view of the network comes from a single
party. That is an acceptable start and an unacceptable thing to leave implicit. |
| 2 | Name both, and MUST NOT be sole-operator. The second operator is the point at which "diverse" starts meaning something, and the file should reflect it the day it becomes true rather than at the next release. |
| 3 or more | The full rule binds: at least three, with no single operator holding a majority of the entries. From here the staging is over and the requirement is absolute. |
Every stage maintains the manual escape hatch.
A single --bootstrap address supplied via CLI bypasses the file completely, and builds shipping empty bootstrap files operate normally.
The file is an optional convenience whose composition is disclosed. It never functions as a gate.
The three-layer operator independence model
Operator identity is an empirical socio-economic fact. Node identity is a cryptographic key. Because key generation is free, treating "independent operator" as a purely mechanical wire primitive is a category error. Fleetmesh resolves operator independence across three decoupled layers:
operator pubkeys in kind 11801, advertise endpoints on separate BGP
Autonomous Systems (ASNs) or /24 IP prefixes, and hold distinct
keyAgreement keys. This protects against physical routing outages and
single-datacenter failure modes.
probation or revoked.
Where this document lives
The specification is published at www.fleetmesh.org/spec.html and developed in the open. The source code, brand assets, and build configurations reside in a public repository dedicated to the public domain under CC0 1.0. This mirrors the open licensing of official Nostr NIPs. A public standard must remain unencumbered by proprietary claims, allowing independent implementations to build freely.
Changes arrive as merge requests against that repository. The line that matters is not
editorial: anything altering kinds.json, messages.json or
constants.json changes the version digest, and therefore changes the protocol
version a conformant node declares. Prose, examples and rationale do not. Merging a patch is
never by itself what makes a version real — nodes declaring it is.
Which name goes where
One rule, applied without exception: fleetmesh names the protocol, and reference implementations adhere strictly to it. A string that two independent implementations must agree on byte-for-byte belongs to the protocol; everything else belongs to whoever is accountable for the code.
| fleetmesh owns | reference implementation owns |
|---|---|
DIDComm PIURIs · gossipsub topics · transport ALPNs · the fleetmesh://
invitation scheme · the kind definitions · this document |
crates/mesh* in reference workspace · the fleetmesh-node daemon binary
· the gateway · anchors on operator hostnames |
The crates use standard names without colliding with third-party implementations, and a bug report should be unambiguous about whose code it is against.
What fleetmesh.org is, and what it must never become
Domain names frequently become centralized retrieval bottlenecks for peer lists, release binaries, and schemas. A single canonical host creates an unacceptable single point of failure.
Everything fleetmesh.org hosts MUST be a convenience mirror of something that
already exists as signed data reachable without DNS. No node may require it to start, to
find peers, to validate a release, or to resolve any identifier. It is a faster path to
bytes that are obtainable anyway — and the unplug test now deletes it too.
| Served at fleetmesh.org | Canonical source, always |
|---|---|
| /.well-known/nostr.json | NIP-05 for the project identity. A lookup aid and nothing more: no claim in this protocol has ever been validated by a DNS answer. |
| /bootstrap.toml | A mirror of an unsigned file — there is no signer set, per § Governance. Fetching it from here confers no authority on it whatever, exactly as if it had arrived on a USB stick. What a node verifies is each named anchor's own signed descriptor at connection setup; the list is a hint about who to ask first, and nothing else. |
| _seed.fleetmesh.org TXT | DNS seeds, one of four discovery paths and required by none. Losing them slows a first run and does nothing to an established node. |
| /npk/<sha256>.npk | Release artifacts, keyed by their own hash and therefore immutable. A mirror, not a registry: the bytes are verified against the signed release, so serving them confers no authority and losing them costs a download location rather than a package. |
| /spec, /schema | This document and the kind definitions. The kinds’ real home is registry-of-kinds,
which nobody here controls. |
| nothing else | No API, no directory service, no registry, no node that other nodes are expected to reach. The moment the domain serves something with no offline equivalent, it has become the hub this design exists to avoid. |
Mirror hosting is an operational convenience. A node's declared protocol digest is authoritative over external mirror files. When declarations diverge from mirror files, nodes trust the direct peer declaration and distrust the mirror.
Fleetmesh functions today as an open project identity and protocol namespace. The domain registration is held for the project. Maintaining a dedicated, neutral domain ensures eventual stewardship transfer occurs smoothly. When governance triggers fire, the project transfers an established name rather than conducting contentious rebranding negotiations.
The trigger for revisiting
Written down so it is not a judgement call made under pressure later. The question of a neutral organisation — entity, domain transfer, formal membership — is reopened on whichever comes first:
- A second independent implementation of this specification ships. This is also the threshold the NIPs repository requires before a submission is even eligible, so the two milestones arrive together and should be handled together.
- A third independent anchor operator joins the mesh, evaluated under the Three-Layer Operator Independence Model (distinct Nostr operator pubkeys, separate BGP ASNs, and independent production infrastructure for ≥ 90 days).
Until one of those happens, an organisation would be one group plus paperwork, and organisations formed before their communities are difficult to unwind. After one of them happens, holding everything is no longer defensible. The trigger exists so that the transition is scheduled rather than conceded.