Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The second table describes each protocol's own approach in prose, for direct com

| Protocol | Topology | Peer identity | Authorisation | Application content | Wire encoding |
|---|---|---|---|---|---|
| **wire-mesh** | Direct P2P mesh, with relay for NAT traversal and optional cross-mesh federation | Hash of a self-signed public key | Signed, revocable, delegatable capability tokens; delegation can only narrow authority | Opaque per-application payload; only identity, handshake, tokens and relay are protocol-native | CBOR/DAG-CBOR, signed via COSE, schema in CDDL |
| **wire-mesh** | Direct P2P, with relay for NAT traversal; no discrete mesh boundary — "mesh" is just the live connected set of reachable peers, and cross-scope sharing is ordinary capability-token delegation to a bearer outside the issuer's own group | Hash of a self-signed public key | Signed, revocable, delegatable capability tokens; delegation can only narrow authority | Opaque per-application payload; only identity, handshake, tokens and relay are protocol-native | CBOR/DAG-CBOR, signed via COSE, schema in CDDL |
| [libp2p](https://libp2p.io/) | Direct P2P mesh, with circuit relay and hole punching for NAT traversal | Multihash of a public key | Not built in; left to the application or its own protocols | Defined per protocol via multistream-select and application-registered protocol IDs | Protocol-specific; commonly Protobuf, which is not canonical across languages or library versions |
| [Secure Scuttlebutt](https://scuttlebutt.nz/) | P2P gossip replication of append-only logs; no relay primitive for NAT traversal | Ed25519 public key | None; a feed's own author key is its sole authority | Fixed: an append-only log of an identity's own signed messages | JSON, hashed for signing; no CBOR-style canonical encoding standard |
| [Syncthing](https://docs.syncthing.net/specs/bep-v1.html) (BEP) | Direct P2P mesh, with relay servers for NAT traversal | SHA-256 of a self-signed certificate | Access is device-list based, not token-scoped | Fixed: file blocks and index metadata for folder sync | Protobuf; not canonical across languages or library versions |
Expand All @@ -49,8 +49,10 @@ The second table describes each protocol's own approach in prose, for direct com
- **Relay and NAT traversal.** When two peers cannot connect directly, a relay carries their traffic as an opaque, unreadable byte pipe. The relay never holds the keys to decrypt what it forwards.
- **An opaque, per-application data domain.** The protocol carries structured content for the domains it defines natively (identity, handshake, tokens, relay). It leaves application-specific payloads — a chat message, a file block, an operation-log entry — as opaque bytes to any peer that does not need to interpret them. Different applications can therefore share one mesh without understanding each other's content.
- **A domain and capability registry**, so unrelated applications extending the protocol with their own capability domains or verbs do not collide on the same identifier.
- **Handle discovery.** A DNS-anchored resolution convention, mirroring WebFinger, for reaching an arbitrary handle you have never interacted with — resolving to a self-certifying record signed by the same key it claims, safe to serve through an untrusted intermediary. The same record's optional mailbox hint unifies discovery with offline delivery: fan-out and delivery to a currently-offline peer both fall out of any peer being able to answer for another device's data-domain log, not a separate mechanism.
- **Federation** between independent meshes, with selective, explicit cross-mesh sharing, distinct from ordinary intra-mesh relay.
- **Handle discovery.** Three bootstrap channels — a DNS-anchored resolution convention mirroring WebFinger, an out-of-band-delivered record, or local broadcast (mDNS/Bonjour) — for reaching a principal you have never interacted with, all resolving to the same self-certifying record signed by the same key it claims, safe to serve through an untrusted intermediary regardless of which channel delivered it. The same record's optional mailbox hints unify discovery with offline delivery: fan-out and delivery to a currently-offline peer both fall out of any peer being able to answer for another device's data-domain log, not a separate mechanism.
- **Coordinator election.** A lightweight, gossiped, term-based claim to the introduction/rendezvous role among whichever peers are currently reachable — any node can hold it, and any live peer can take over if it disappears, without relying on OS-level port contention to do the electing.

**The test for whether a proposed concept belongs in this list, rather than being left to applications:** does it do genuine, orthogonal work that identity, capability tokens, and connectivity/discovery don't already cover between them — or is it a bespoke wrapper around some combination of those three that would work exactly as well expressed directly? `mesh-id` and a dedicated federation link-and-share protocol both failed this test on review — reachability was already discovery's job, authorisation was already tokens' job, and the wrapper contributed no property either didn't already provide on its own; both were removed in favour of expressing the same outcomes directly. Coordinator election and mailbox delivery both passed it — a rendezvous role and a bootstrap anchor with an explicit trust signal are work identity, tokens, and discovery genuinely cannot do by themselves. Apply the same test to the next proposed addition before assuming a new frame, domain, or field is the right shape for it.

### Encoding

Expand Down Expand Up @@ -98,6 +100,8 @@ None yet. The schema exists (`spec/protocol.cddl`), and so does `conformance/`'s

`ts/packages/cloudflare-hub` is a reference deployment of a public, always-on mesh **node** — a coordinator-of-coordinators that other peers dial into for company- or community-wide reach beyond a single local mesh. It depends on `ts/packages/core` as an ordinary consumer, exactly as agent-comms and Cascade do. It lives in this repository for now, during early co-development with the spec, but is deliberately structured as its own package rather than folded into the core library — the spec itself must stay adoptable by anyone with no interest in ExaDev's specific deployment, and that boundary is what makes moving the hub to its own repository later a packaging change, not an architectural one.

Concretely, a single always-on hub like this can serve two remote peers with no other infrastructure at all, filling two distinct roles from `spec/` that a deployment like this is meant to combine: **relay** (`transport.cddl`'s `relay-offer`/`relay-connect`/`relay-data`/`relay-inbound` frames — an opaque byte pipe when two peers can't connect directly, the hub never holding the keys to read what it forwards) and **announcer** (`discovery.cddl`'s `mailboxes` field — the hub holds a peer's `handle-record` as a `core/data` entry so a peer with no address of its own, or one that's since moved networks, can still be found and resolved through it; a peer can name more than one mailbox for redundancy). Neither role requires the other; a deployment can offer just relay, just announcing, or both on the same connection. If the hub's own operator also controls a domain, the cleanest composition is DNS-anchored handles (`alice@exadev.io`) as the primary discovery path and the hub's `core/data`/mailbox role as the fallback for a peer with no registered handle of its own — but the hub still can't tell two arbitrary strangers about each other with zero other information: whichever side is resolving still needs to already know the other's handle or device-id from somewhere (an out-of-band invite, most commonly), the same bootstrap requirement discovery.cddl describes regardless of whether a hub is involved.

The web console a human actually opens in a browser — `ts/packages/web-console` — is a different thing, kept separate for the same reason: it is a *client* of a node, not a node itself. It may be served as static assets from the same origin as `cloudflare-hub` for deployment convenience, but it is not folded into it, and it is not tied to Cloudflare at all — it can equally connect to a purely local, laptop-hosted coordinator. This is also distinct from a third scenario this naming invites confusion with: an ordinary browser tab acting as its own genuine leaf peer (its own identity, its own `core`-implemented `MeshTransport`, entirely on an end user's device). That's not a package in this repository at all — it's just another consumer of `core`, built by whoever wants a browser-embedded node, the same as agent-comms or Cascade.

### Versioning
Expand All @@ -110,7 +114,7 @@ Two examples of what the design above already supports without further protocol

**A Tailscale-style mesh VPN.** The transport frames already cover the coordination layer a peer-to-peer VPN needs: peer discovery (`gossip-frame`/`peer-advert`), NAT traversal (`candidates-frame`, `sync-punch-frame`, `observed-address-frame`), and relay fallback when a direct connection fails (`relay-offer-frame`/`relay-connect-frame`/`relay-data-frame`) — structurally the same job Tailscale's control plane and DERP relays do around WireGuard. This would be a coordination layer *for* WireGuard (or an equivalent), not a replacement for it: wire-mesh specifies no tunnel-encryption layer of its own, and WireGuard's own cryptography is exactly the part worth leaving alone rather than reinventing. What a wire-mesh-based coordination layer could do better than Tailscale specifically:

- No single vendor's coordination server — any node can be a coordinator, coordinator handoff is already part of the design, and federation between independently-operated meshes is a first-class capability domain rather than an enterprise-tier afterthought (Tailscale's control plane is Tailscale's own hosted service; Headscale is a reverse-engineered clone, not a first-class alternative).
- No single vendor's coordination server — any node can claim the coordinator role via `coordinator-frame`'s gossiped, term-based election (a higher term always supersedes a lower one; equal terms break by lowest device-id), giving the same crash-recovery property a port-race coordinator has without relying on OS-level port contention. Cross-scope sharing needs no dedicated federation protocol at all: a capability token handed to a bearer outside the issuer's own group already is the explicit, signed, auditable act of agreement a bilateral link-negotiation step would only redundantly restate (Tailscale's control plane is Tailscale's own hosted service; Headscale is a reverse-engineered clone, not a first-class alternative).
- Capability tokens are delegable and narrowing rather than a flat, centrally-evaluated ACL file, and are self-certifying — a bearer can verify one offline with no callback to the issuer, where Tailscale's ACLs require reaching the control plane.
- The same mesh session (same identity, same tokens, same peer discovery) could carry this domain alongside file transfer or messaging at once, rather than needing an unrelated coordination system and trust domain per use case the way networking (Tailscale), file sync (Syncthing), and messaging (Slack) are three separate tools today.

Expand Down
140 changes: 35 additions & 105 deletions conformance/frames.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@
},
"wire_hex": "a264747970656d72656c61792d696e626f756e646d736f757263652d64657669636558202222222222222222222222222222222222222222222222222222222222222222"
},
{
"name": "coordinator_v1_with_capacity_hint",
"message": {
"type": "coordinator",
"term": 3,
"coordinator": {
"hex": "1111111111111111111111111111111111111111111111111111111111111111"
},
"capacity-hint": 64
},
"wire_hex": "a4647465726d0364747970656b636f6f7264696e61746f726b636f6f7264696e61746f72582011111111111111111111111111111111111111111111111111111111111111116d63617061636974792d68696e741840"
},
{
"name": "manage_request_v1_pty_spawn",
"message": {
Expand Down Expand Up @@ -174,21 +186,33 @@
"message": {
"type": "revocation-announce",
"entries": [
{
"token-id": {
"hex": "01010101010101010101010101010101"
[
{
"hex": "a2613126613458201111111111111111111111111111111111111111111111111111111111111111"
},
"revoked-at": 1861833700000
},
{
"token-id": {
"hex": "02020202020202020202020202020202"
{},
{
"hex": "a4666973737565725820111111111111111111111111111111111111111111111111111111111111111168746f6b656e2d696450010101010101010101010101010101016a6973737565722d6b6579a263616c67266a7075626c69632d6b6579584104aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb6a7265766f6b65642d61741b000001b17defb2a0"
},
"revoked-at": 1861833701000
}
{
"hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
],
[
{
"hex": "a2613126613458202222222222222222222222222222222222222222222222222222222222222222"
},
{},
{
"hex": "a4666973737565725820222222222222222222222222222222222222222222222222222222222222222268746f6b656e2d696450020202020202020202020202020202026a6973737565722d6b6579a263616c67266a7075626c69632d6b6579584104ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd6a7265766f6b65642d61741b000001b17defb688"
},
{
"hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
]
]
},
"wire_hex": "a26474797065737265766f636174696f6e2d616e6e6f756e636567656e747269657382a268746f6b656e2d696450010101010101010101010101010101016a7265766f6b65642d61741b000001b17defb2a0a268746f6b656e2d696450020202020202020202020202020202026a7265766f6b65642d61741b000001b17defb688"
"wire_hex": "a26474797065737265766f636174696f6e2d616e6e6f756e636567656e747269657382845828a2613126613458201111111111111111111111111111111111111111111111111111111111111111a058b7a4666973737565725820111111111111111111111111111111111111111111111111111111111111111168746f6b656e2d696450010101010101010101010101010101016a6973737565722d6b6579a263616c67266a7075626c69632d6b6579584104aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb6a7265766f6b65642d61741b000001b17defb2a05840ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845828a2613126613458202222222222222222222222222222222222222222222222222222222222222222a058b7a4666973737565725820222222222222222222222222222222222222222222222222222222222222222268746f6b656e2d696450020202020202020202020202020202026a6973737565722d6b6579a263616c67266a7075626c69632d6b6579584104ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd6a7265766f6b65642d61741b000001b17defb6885840ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
{
"name": "stream_data_v1_stdout_chunk",
Expand Down Expand Up @@ -262,100 +286,6 @@
]
},
"wire_hex": "a464706565725820111111111111111111111111111111111111111111111111111111111111111164747970656c646174612d656e747269657367656e74726965738243aabbcc44ddeeff006866726f6d2d7365711864"
},
{
"name": "federation_link_request_v1",
"message": {
"type": "federation-link-request",
"local-mesh": "exadev-internal",
"local-name": "exadev",
"offered-shares": [
{
"domain": "core/data",
"resource": {
"kind": "room",
"path": "general"
},
"direction": "outbound"
}
]
},
"wire_hex": "a464747970657766656465726174696f6e2d6c696e6b2d726571756573746a6c6f63616c2d6d6573686f6578616465762d696e7465726e616c6a6c6f63616c2d6e616d65666578616465766e6f6666657265642d73686172657381a366646f6d61696e69636f72652f64617461687265736f75726365a2646b696e6464726f6f6d64706174686767656e6572616c69646972656374696f6e686f7574626f756e64"
},
{
"name": "federation_link_accept_v1",
"message": {
"type": "federation-link-accept",
"remote-mesh": "example-partner",
"remote-name": "partner",
"accepted-shares": [
{
"domain": "core/data",
"resource": {
"kind": "room",
"path": "general"
},
"direction": "outbound"
}
]
},
"wire_hex": "a464747970657666656465726174696f6e2d6c696e6b2d6163636570746b72656d6f74652d6d6573686f6578616d706c652d706172746e65726b72656d6f74652d6e616d6567706172746e65726f61636365707465642d73686172657381a366646f6d61696e69636f72652f64617461687265736f75726365a2646b696e6464726f6f6d64706174686767656e6572616c69646972656374696f6e686f7574626f756e64"
},
{
"name": "federation_link_reject_v1",
"message": {
"type": "federation-link-reject",
"reason": "no shared domains accepted"
},
"wire_hex": "a264747970657666656465726174696f6e2d6c696e6b2d72656a65637466726561736f6e781a6e6f2073686172656420646f6d61696e73206163636570746564"
},
{
"name": "federation_share_v1",
"message": {
"type": "federation-share",
"share": {
"domain": "core/data",
"resource": {
"kind": "room",
"path": "incidents"
},
"direction": "bidirectional"
}
},
"wire_hex": "a264747970657066656465726174696f6e2d7368617265657368617265a366646f6d61696e69636f72652f64617461687265736f75726365a2646b696e6464726f6f6d647061746869696e636964656e747369646972656374696f6e6d6269646972656374696f6e616c"
},
{
"name": "federation_unshare_v1",
"message": {
"type": "federation-unshare",
"share": {
"domain": "core/data",
"resource": {
"kind": "room",
"path": "incidents"
},
"direction": "bidirectional"
}
},
"wire_hex": "a264747970657266656465726174696f6e2d756e7368617265657368617265a366646f6d61696e69636f72652f64617461687265736f75726365a2646b696e6464726f6f6d647061746869696e636964656e747369646972656374696f6e6d6269646972656374696f6e616c"
},
{
"name": "federation_envelope_v1_wrapping_a_ping",
"message": {
"type": "federation-envelope",
"origin-mesh": "example-partner",
"origin-device": {
"hex": "3333333333333333333333333333333333333333333333333333333333333333"
},
"resource": {
"kind": "room",
"path": "general"
},
"inner": {
"hex": "a164747970656470696e67"
}
},
"wire_hex": "a564747970657366656465726174696f6e2d656e76656c6f706565696e6e65724ba164747970656470696e67687265736f75726365a2646b696e6464726f6f6d64706174686767656e6572616c6b6f726967696e2d6d6573686f6578616d706c652d706172746e65726d6f726967696e2d64657669636558203333333333333333333333333333333333333333333333333333333333333333"
}
]
}
Loading