docs: four-section /docs/ restructure with new-user content, accuracy pass, and gateway feature-set rewrite
Restructures /docs/ by reader purpose (tutorials, how-to, reference, design), adds the new-user-progression and operator-recipe content the prior layout lacked, runs an accuracy pass against current source across the pre-existing design docs, and rewrites the gateway feature-set documentation end-to-end around its actual operational profile (a niche feature designed for systems already serving DHCP/DNS to a LAN, with two independent halves — outbound LAN→mesh, inbound mesh→LAN — sharing one nftables table, one binary, and one control socket). Top-level README and getting-started rewritten around two equally-weighted deployment modes (overlay on existing IP networks; ground-up over non-IP transports). ## Additions - 11 new tutorials in docs/tutorials/: an 8-step new-user progression from single-daemon test-mesh peering through to a ground-up two-device mesh, an IPv6-adapter side-trip walkthrough, an Advanced Tutorials index, and a hand-held OpenWrt walk-through for fips-gateway deployment that exercises both halves of the feature. - 12 new how-tos in docs/how-to/: firewall activation, Nostr discovery (resolve / advertise / open across five scenarios), Tor onion (directory + control_port modes), UDP buffer tuning, unprivileged-user setup, persistent identity, host aliases, Bluetooth LE peering, MTU diagnostics, manual Linux-host gateway deployment (covers both halves), gateway troubleshooting (organised by half), and a section index. - 9 new reference docs in docs/reference/: configuration, wire formats, control-socket protocol, four CLI references (fips, fipsctl, fipstop, fips-gateway), security posture matrix, and Nostr events catalog. Configuration and wire-formats are renamed-and-extended from prior design/ versions; the other seven are net-new. - 6 new design docs: fips-concepts, fips-architecture, and fips-prior-work split out of the deleted fips-intro.md; consolidated fips-mmp and fips-mtu aggregations; and a new generic port-advertisement-and-nat-traversal doc (Nostr-signaled port advertisement plus UDP NAT-traversal protocol, FIPS as an example implementation, suitable for eventual NIP submission). - Top-level docs/getting-started.md walking through the binary-installer-only Install story. - packaging/common/hosts pre-populated with the eight public test-mesh nodes so shortnames resolve out of the box on every fresh install. ## Changes - 23 wire-format diagrams relocated to reference/diagrams/ alongside the wire-formats move. - 4 design diagrams corrected against source code (fips-protocol-stack, fips-identity-derivation, fips-coordinate-discovery, fips-routing-decision). - 10 pre-existing design docs reconciled with current source. Numeric corrections: stale link-MMP report bounds (now [1s, 5s] with 200 ms cold-start floor); UDP default MTU (now 1280, IPv6 minimum); node_addr formula (SHA-256(pubkey)[..16]); Noise patterns (IK at link, XK at session); peer-ACL semantics (strict allowlist requires ALL in peers.deny); daemon DNS upstream ([::1]:5354); on-the-wire bloom-filter size (1,071 bytes); obsolete Cargo-feature references (PR #79 dropped them) removed. - Transport framing tightened across the docs: TCP is for UDP-filtered networks (not NAT traversal); Tor is a deployment mode (not failover); WebSocket dropped (not a shipped FIPS transport); WiFi promoted to Implemented via Ethernet in infrastructure mode; classic-Bluetooth row removed (BLE is the only Bluetooth-mode transport). - docs/design/fips-gateway.md rewritten end-to-end to lead with the niche-feature framing and the two-halves structure. Title moved from "FIPS Outbound LAN Gateway" to "FIPS Gateway"; architecture section describes the common machinery (the fips-gateway service, the nftables table, the control socket) before splitting into separate "Outbound Half" and "Inbound Half" sections of equal weight; security considerations split per-half; no Future Work section (speculative directions live in the project tracker, not in protocol design docs). Inbound port forwarding is a first-class half rather than a buried "Implemented Extensions" subsection. - Gateway terminology unified across all gateway docs as a separate Linux service running alongside the fips daemon (its own systemd unit / OpenWrt init script). Container- pattern terms (sidecar) are reserved for the Docker/Kubernetes sidecar deployment examples — the testing/sidecar/ tree, examples/k8s-sidecar/, examples/sidecar-nostr-relay/, examples/wireguard-sidecar-macos/, and the related CHANGELOG / top-level README entries — where the term carries its standard container meaning. - Net-new design body content: rekey section in fips-mesh-layer (Noise IK msg1/msg2 over the established link, K-bit cutover, drain window, smaller-NodeAddr-wins tie-breaker on dual-init); Mesh Size Estimation and Antipoison FPR Cap sections in fips-bloom-filters; Mesh-Interface Query Filter subsection in fips-ipv6-adapter; failure-suppression knobs and clock- skew tolerance in fips-nostr-discovery; loop-rejection and mid-chain ancestor swap added to spanning-tree propagation / stability rules; Priority Chain in fips-mesh-operation renumbered to match the routing-decision diagram. - Top-level README: dropped the stale nostr-discovery cargo-feature parenthetical. docs/README.md and the four section READMEs (tutorials, how-to, reference, design) refreshed for the new structure; index rows reflect both halves of the gateway feature and the new fips-gateway CLI reference. - Cargo.toml [package.metadata.deb] assets path updated for the fips-security.md move; .gitignore /reference/ rule anchored to repo root so docs/reference/ is trackable. - packaging/openwrt-ipk/files/etc/fips/fips.yaml configuration-doc URL updated to the new docs/reference/configuration.md location. ## Deletions - docs/design/fips-intro.md (split into the three new intro design docs). - docs/design/document-relationships.svg (orphan, no longer referenced). - docs/proposals/ tree removed; the only proposal it contained (the Nostr UDP hole-punch protocol) was rewritten as the new generic design/port-advertisement-and-nat-traversal.md.
@@ -0,0 +1,25 @@
|
||||
# Reference
|
||||
|
||||
Information-oriented technical descriptions for lookup on demand.
|
||||
Reference content describes *what is*: wire formats, configuration
|
||||
keys, command-line flags, control-socket commands, default values,
|
||||
file paths, exit codes. It is consulted, not read end-to-end.
|
||||
|
||||
Reference is austere by design: minimal narrative, no opinions, no
|
||||
guidance on when to use a feature. The "why" lives in design/; the
|
||||
"how do I accomplish X" lives in how-to/.
|
||||
|
||||
## Available Reference
|
||||
|
||||
| Document | Scope |
|
||||
| -------- | ----- |
|
||||
| [wire-formats.md](wire-formats.md) | All FMP and FSP message byte layouts, encapsulation walkthrough |
|
||||
| [configuration.md](configuration.md) | Full YAML configuration reference for the daemon and gateway |
|
||||
| [security.md](security.md) | nftables baseline, peer ACL, cryptographic primitives, rekey defaults, threat-resistance matrix |
|
||||
| [nostr-events.md](nostr-events.md) | Kind 37195 advert, Kind 21059 traversal signaling, Kind 10050 inbox relays |
|
||||
| [transports.md](transports.md) | Per-transport statistics counter inventory |
|
||||
| [control-socket.md](control-socket.md) | Line-delimited JSON control protocol for the daemon and gateway |
|
||||
| [cli-fips.md](cli-fips.md) | `fips` daemon CLI: options, exit codes, environment, files |
|
||||
| [cli-fipsctl.md](cli-fipsctl.md) | `fipsctl` control-client: subcommands, options, exit codes |
|
||||
| [cli-fipstop.md](cli-fipstop.md) | `fipstop` live-status TUI: tabs, keybindings |
|
||||
| [cli-fips-gateway.md](cli-fips-gateway.md) | `fips-gateway` service CLI: options, exit codes, files |
|
||||
@@ -0,0 +1,125 @@
|
||||
# `fips-gateway`
|
||||
|
||||
Long-running service that bridges a LAN segment into the FIPS mesh.
|
||||
|
||||
## Synopsis
|
||||
|
||||
```text
|
||||
fips-gateway [-c FILE] [-l LEVEL]
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
`fips-gateway` runs alongside `fips` on the same host, reads the same
|
||||
`fips.yaml`, and exposes two complementary functions to the LAN it
|
||||
fronts:
|
||||
|
||||
- **Outbound (LAN -> mesh).** Allocates a virtual IPv6 from a managed
|
||||
pool when a LAN client resolves `<npub>.fips`, installs nftables
|
||||
DNAT/SNAT/masquerade rules so the client's traffic is rewritten and
|
||||
carried into the mesh through the daemon's `fips0` adapter.
|
||||
- **Inbound (mesh -> LAN).** Installs nftables DNAT and LAN-side
|
||||
masquerade rules so mesh-side traffic arriving on `fips0` for the
|
||||
configured listen ports is rewritten to a LAN `host:port`, per the
|
||||
`gateway.port_forwards[]` block.
|
||||
|
||||
The service runs alongside `fips`, not as a replacement for it:
|
||||
the daemon must be running on the same host with the TUN adapter
|
||||
and DNS resolver enabled. The gateway is read-only with respect to the
|
||||
daemon's state, and connects to the daemon's resolver only — it is
|
||||
not a peer. For the architecture, see
|
||||
[../design/fips-gateway.md](../design/fips-gateway.md).
|
||||
|
||||
`fips-gateway` is **Linux-only**. The binary errors out and exits with
|
||||
status `1` on any other platform, since the NAT pipeline is built on
|
||||
nftables and proxy NDP. See
|
||||
[Configuration](#configuration) for the platform notes that follow
|
||||
from this.
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Argument | Default | Description |
|
||||
| ---- | -------- | ------- | ----------- |
|
||||
| `-c`, `--config` | `FILE` | *(default search paths)* | Use `FILE` as the configuration. Skips the default search paths. |
|
||||
| `-l`, `--log-level` | `LEVEL` | `info` | Tracing level: `trace`, `debug`, `info`, `warn`, `error`. Overridden by `RUST_LOG` if set (see [Environment](#environment)). |
|
||||
| `-V` | — | — | Print the short version. |
|
||||
| `--version` | — | — | Print the long version (short version plus build target triple). |
|
||||
| `-h`, `--help` | — | — | Print usage and exit. |
|
||||
|
||||
## Configuration
|
||||
|
||||
`fips-gateway` reads the same `fips.yaml` as `fips`; the gateway is
|
||||
configured under the top-level `gateway:` block. The block must
|
||||
include at minimum `enabled: true`, `pool`, and `lan_interface`. For
|
||||
each field — pool, LAN interface, DNS listener, conntrack overrides,
|
||||
and inbound `port_forwards[]` — see the
|
||||
[Gateway section](configuration.md#gateway-gateway) of the
|
||||
configuration reference.
|
||||
|
||||
The same default search paths apply as for `fips`
|
||||
(see [`fips`](cli-fips.md#files)); `-c FILE` overrides the search.
|
||||
The gateway must be able to read the same configuration file the
|
||||
daemon is reading, or the two will disagree about pool, DNS port,
|
||||
and LAN interface.
|
||||
|
||||
For deployment recipes, see
|
||||
[../how-to/deploy-gateway.md](../how-to/deploy-gateway.md) (manual
|
||||
Linux host) and
|
||||
[../tutorials/deploy-fips-gateway.md](../tutorials/deploy-fips-gateway.md)
|
||||
(OpenWrt walk-through).
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
| ---- | ------- |
|
||||
| `0` | Clean shutdown after `SIGINT` / `SIGTERM`. |
|
||||
| `1` | Non-Linux platform, configuration load failure, missing or invalid `gateway:` block, NAT/network setup failure, or control-socket bind failure. The reason is printed to stderr or the log before exit. |
|
||||
|
||||
## Environment
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `RUST_LOG` | Tracing filter directive. Takes precedence over `--log-level`. Examples: `info`, `debug`, `fips=trace,fips::gateway=debug`. |
|
||||
|
||||
## Files
|
||||
|
||||
| Path | Purpose |
|
||||
| ---- | ------- |
|
||||
| `/etc/fips/fips.yaml` | Gateway configuration (top-level `gateway:` block). Same file the daemon reads. |
|
||||
| `/run/fips/gateway.sock` | Gateway control socket. Hardcoded path; chowned to group `fips` (mode `0770`) at startup so members of that group can query without sudo. |
|
||||
| `inet fips_gateway` (nftables) | NAT table the gateway installs and tears down. View with `nft list table inet fips_gateway`. |
|
||||
|
||||
The gateway also adds and removes a `local <pool-cidr> dev lo` route
|
||||
in the local routing table so the kernel accepts pool addresses as
|
||||
locally-owned.
|
||||
|
||||
## Control Socket
|
||||
|
||||
`fips-gateway` exposes a JSON line-protocol control socket separate
|
||||
from the daemon's. The command set (`show_gateway`, `show_mappings`)
|
||||
and JSON shapes are documented in the
|
||||
[Gateway Command Catalog](control-socket.md#gateway-command-catalog).
|
||||
|
||||
There is no `fipsctl` subcommand for the gateway — query the socket
|
||||
directly with `nc -U`, or watch the **Gateway** tab in
|
||||
[`fipstop`](cli-fipstop.md), which polls the gateway socket
|
||||
automatically.
|
||||
|
||||
## See also
|
||||
|
||||
- [`fips`](cli-fips.md) — the daemon. Required to be running on the
|
||||
same host.
|
||||
- [`fipstop`](cli-fipstop.md) — the live-status TUI; its Gateway tab
|
||||
polls the gateway control socket.
|
||||
- [configuration.md § Gateway](configuration.md#gateway-gateway) —
|
||||
full `gateway.*` block reference.
|
||||
- [control-socket.md § Gateway Command Catalog](control-socket.md#gateway-command-catalog)
|
||||
— wire protocol for the gateway socket.
|
||||
- [../design/fips-gateway.md](../design/fips-gateway.md) — design,
|
||||
NAT pipeline, virtual IP pool lifecycle.
|
||||
- [../how-to/deploy-gateway.md](../how-to/deploy-gateway.md) — manual
|
||||
Linux deployment.
|
||||
- [../how-to/troubleshoot-gateway.md](../how-to/troubleshoot-gateway.md)
|
||||
— diagnostic recipes.
|
||||
- [../tutorials/deploy-fips-gateway.md](../tutorials/deploy-fips-gateway.md)
|
||||
— OpenWrt walk-through.
|
||||
@@ -0,0 +1,93 @@
|
||||
# `fips`
|
||||
|
||||
The FIPS mesh network daemon.
|
||||
|
||||
## Synopsis
|
||||
|
||||
```text
|
||||
fips [-c FILE]
|
||||
```
|
||||
|
||||
On Windows the same binary additionally accepts `--install-service`,
|
||||
`--uninstall-service`, and (used internally by the service control
|
||||
manager) `--service`.
|
||||
|
||||
## Description
|
||||
|
||||
`fips` is the FIPS daemon. It loads a YAML configuration, resolves an
|
||||
identity, brings up the TUN adapter, listens on configured transports,
|
||||
authenticates peers, maintains the spanning tree, and forwards mesh
|
||||
traffic. There is one daemon per node.
|
||||
|
||||
The daemon stays in the foreground, logging to stderr, until it
|
||||
receives `SIGINT` or `SIGTERM`. On Windows, the service variant is
|
||||
controlled through the standard service control manager.
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Argument | Description |
|
||||
| ---- | -------- | ----------- |
|
||||
| `-c`, `--config` | `FILE` | Use `FILE` as the configuration. Skips the default search paths. |
|
||||
| `-V` | — | Print the short version (e.g. `0.3.0-dev (rev abcdef1)`). |
|
||||
| `--version` | — | Print the long version: short version plus build target triple. |
|
||||
| `-h`, `--help` | — | Print usage and exit. |
|
||||
| `--install-service` | — | (Windows only) Install `fips` as a Windows service. Requires Administrator. |
|
||||
| `--uninstall-service` | — | (Windows only) Uninstall the Windows service. Requires Administrator. |
|
||||
| `--service` | — | (Windows only, internal) Run as a Windows service. Invoked by the service control manager — not for direct use. |
|
||||
|
||||
There are no other CLI flags; all daemon behaviour is governed by the
|
||||
YAML configuration. See [configuration.md](configuration.md).
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
| ---- | ------- |
|
||||
| `0` | Clean shutdown after `SIGINT` / `SIGTERM`. |
|
||||
| `1` | Failed to load configuration, resolve identity, construct the node, or start the node. The reason is printed to stderr before exit. |
|
||||
|
||||
## Environment
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `RUST_LOG` | Tracing filter directive. Overrides `node.log_level` from the config. Examples: `info`, `debug`, `fips=trace,fips::node::handlers::mmp=debug`. |
|
||||
| `XDG_RUNTIME_DIR` | Used to derive the default control-socket path when `/run/fips` does not exist. See [control-socket.md](control-socket.md). |
|
||||
| `FIPS_CONFIG` | (Windows service mode only) Path to the configuration file when the daemon runs under the service control manager. |
|
||||
|
||||
The daemon also clamps the `nostr_relay_pool`, `nostr_sdk`, and `nostr`
|
||||
log targets to `info` whenever the effective log level is below
|
||||
`trace`, so that `RUST_LOG=debug` does not flood the journal with raw
|
||||
relay frames. To see those frames, set the level to `trace`.
|
||||
|
||||
## Files
|
||||
|
||||
`fips` looks for `fips.yaml` in the following locations, lowest to
|
||||
highest priority. All present files are merged in priority order; the
|
||||
highest-priority value wins.
|
||||
|
||||
| Priority | Path | Purpose |
|
||||
| -------- | ---- | ------- |
|
||||
| 1 | `/etc/fips/fips.yaml` | System-wide defaults |
|
||||
| 2 | `~/.config/fips/fips.yaml` | User preferences |
|
||||
| 3 | `~/.fips.yaml` | Legacy user config |
|
||||
| 4 | `./fips.yaml` | Deployment-specific overrides |
|
||||
|
||||
Adjacent to the highest-priority config file the daemon reads (or
|
||||
writes, on first start) the identity files:
|
||||
|
||||
| File | Mode | Purpose |
|
||||
| ---- | ---- | ------- |
|
||||
| `fips.key` | `0600` | Bech32 nsec for the persistent identity (Unix only; Windows inherits parent ACLs). |
|
||||
| `fips.pub` | `0644` | Bech32 npub corresponding to `fips.key`. |
|
||||
|
||||
When `node.identity.persistent` is `false` (the default), a fresh
|
||||
keypair is written to these files on every start.
|
||||
|
||||
The control socket path is derived per
|
||||
[control-socket.md](control-socket.md).
|
||||
|
||||
## See also
|
||||
|
||||
- [`fipsctl`](cli-fipsctl.md) — control-socket client.
|
||||
- [`fipstop`](cli-fipstop.md) — live-status TUI.
|
||||
- [configuration.md](configuration.md) — YAML reference.
|
||||
- [control-socket.md](control-socket.md) — control-socket protocol.
|
||||
@@ -0,0 +1,148 @@
|
||||
# `fipsctl`
|
||||
|
||||
Command-line client for the FIPS daemon's control socket.
|
||||
|
||||
## Synopsis
|
||||
|
||||
```text
|
||||
fipsctl [-s SOCKET] <subcommand> [args...]
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
`fipsctl` connects to a running daemon over its control socket
|
||||
(Unix domain socket on Linux/macOS, TCP loopback on Windows), sends
|
||||
one JSON request, and pretty-prints the response. Exits with a
|
||||
non-zero status if the socket cannot be reached, the daemon returns an
|
||||
error, or the request times out.
|
||||
|
||||
`fipsctl keygen` is a special case: it does not contact the daemon and
|
||||
operates purely on local files.
|
||||
|
||||
For the line-delimited JSON wire protocol, see
|
||||
[control-socket.md](control-socket.md). For the YAML configuration
|
||||
that defines the socket location, see
|
||||
[configuration.md](configuration.md).
|
||||
|
||||
## Global Options
|
||||
|
||||
| Flag | Argument | Description |
|
||||
| ---- | -------- | ----------- |
|
||||
| `-s`, `--socket` | `PATH` | Override the control-socket path (Linux/macOS) or TCP port (Windows). |
|
||||
| `-V`, `--version` | — | Print the short version. |
|
||||
| `--version` | — | Print the long version. |
|
||||
| `-h`, `--help` | — | Print usage and exit. Per-subcommand help via `fipsctl <subcommand> --help`. |
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `show <what>`
|
||||
|
||||
Read-only queries against the daemon. Each subcommand maps 1:1 to a
|
||||
control-socket query (see [control-socket.md](control-socket.md)) and
|
||||
prints the response's `data` object as pretty JSON.
|
||||
|
||||
| Subcommand | Control-socket command | Returns |
|
||||
| ---------- | ---------------------- | ------- |
|
||||
| `show status` | `show_status` | Node-level status: identity, version, peer/link/session counts, TUN state, recent sparklines. |
|
||||
| `show peers` | `show_peers` | Authenticated peer list with link IDs, transport addresses, MMP metrics, Noise/rekey state. |
|
||||
| `show links` | `show_links` | Active links (one per FMP-authenticated peer): direction, state, byte counters. |
|
||||
| `show tree` | `show_tree` | Spanning-tree state: root, my coordinates, parent, peer declarations. |
|
||||
| `show sessions` | `show_sessions` | End-to-end FSP sessions: state, traffic counters, session-MMP metrics, path MTU. |
|
||||
| `show bloom` | `show_bloom` | Bloom-filter state: own filter sequence, leaf dependents, per-peer filter summaries. |
|
||||
| `show mmp` | `show_mmp` | MMP metrics summary: per-peer link-layer metrics and per-session session-layer metrics. |
|
||||
| `show cache` | `show_cache` | Coordinate cache: TTL, fill ratio, per-destination coords and path MTU. |
|
||||
| `show connections` | `show_connections` | Pending handshake connections: state, idle time, resend count. |
|
||||
| `show transports` | `show_transports` | Transport instances: type, state, MTU, local address, per-transport stats. |
|
||||
| `show routing` | `show_routing` | Routing summary: pending lookups, retry state, forwarding/discovery/error/congestion counters. |
|
||||
| `show identity-cache` | `show_identity_cache` | Cached `(node_addr → npub)` entries with last-seen timestamps. |
|
||||
|
||||
### `acl <what>`
|
||||
|
||||
| Subcommand | Control-socket command | Returns |
|
||||
| ---------- | ---------------------- | ------- |
|
||||
| `acl show` | `show_acl` | Loaded peer-ACL state: allow/deny files, effective mode, default decision, entry counts. |
|
||||
|
||||
### `stats <what>`
|
||||
|
||||
Time-series metrics from the in-process history rings.
|
||||
|
||||
| Subcommand | Control-socket command | Description |
|
||||
| ---------- | ---------------------- | ----------- |
|
||||
| `stats list` | `show_stats_list` | Enumerate available metrics, their units, and the per-ring retention windows. |
|
||||
| `stats peers` | `show_stats_peers` | List peers tracked in stats history (active or recently active). |
|
||||
| `stats history <metric> [options]` | `show_stats_history` | Fetch a time-series window for one metric. |
|
||||
|
||||
`stats history` options:
|
||||
|
||||
| Flag | Argument | Default | Description |
|
||||
| ---- | -------- | ------- | ----------- |
|
||||
| `--peer` | `npub` or hostname | *(none)* | Required for per-peer metrics; resolves through `/etc/fips/hosts` if not an npub. |
|
||||
| `--window` | `<N>s` / `<N>m` / `<N>h` | `10m` | Window duration. |
|
||||
| `--granularity` | `1s` or `1m` | `1s` | Ring resolution. `1s` uses the fast ring; `1m` uses the slow ring. |
|
||||
| `--plot` | — | off | Render a Unicode-block sparkline to stdout instead of JSON. |
|
||||
|
||||
### `keygen [options]`
|
||||
|
||||
Generate a new FIPS identity keypair locally. Does not contact the
|
||||
daemon.
|
||||
|
||||
| Flag | Argument | Default | Description |
|
||||
| ---- | -------- | ------- | ----------- |
|
||||
| `-d`, `--dir` | `DIR` | `/etc/fips` (Unix), `%APPDATA%\fips` (Windows) | Output directory for `fips.key` and `fips.pub`. |
|
||||
| `-f`, `--force` | — | off | Overwrite an existing `fips.key`. |
|
||||
| `-s`, `--stdout` | — | off | Print `nsec` then `npub` to stdout instead of writing files. |
|
||||
|
||||
`fips.key` is written with mode `0600` and `fips.pub` with mode `0644`
|
||||
on Unix. After running `keygen`, set `node.identity.persistent: true`
|
||||
in `fips.yaml` or the daemon will overwrite the keys on next start.
|
||||
|
||||
### `connect <peer> <address> <transport>`
|
||||
|
||||
Tell the daemon to dial a peer over a specific transport.
|
||||
|
||||
| Argument | Description |
|
||||
| -------- | ----------- |
|
||||
| `peer` | npub (bech32) or hostname from `/etc/fips/hosts`. |
|
||||
| `address` | Transport endpoint, e.g. `192.168.1.10:2121`, `[2001:db8::1]:2121`, or a Tor onion. FIPS-mesh ULAs (`fd00::/8`) are rejected for the IP-based transports (udp, tcp, ethernet). |
|
||||
| `transport` | One of `udp`, `tcp`, `tor`, `ethernet`. |
|
||||
|
||||
### `disconnect <peer>`
|
||||
|
||||
Tell the daemon to drop a peer link.
|
||||
|
||||
| Argument | Description |
|
||||
| -------- | ----------- |
|
||||
| `peer` | npub (bech32) or hostname from `/etc/fips/hosts`. |
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
| ---- | ------- |
|
||||
| `0` | Daemon returned `{"status":"ok",...}`. |
|
||||
| `1` | Argument parse failure, control-socket connection failure, daemon returned `{"status":"error",...}`, or local I/O failure (keygen). The error message is printed to stderr. |
|
||||
|
||||
## Environment
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `XDG_RUNTIME_DIR` | Used to derive the default control-socket path when `/run/fips` is absent. |
|
||||
|
||||
`fipsctl` does not consume `RUST_LOG`; logging is for the daemon.
|
||||
|
||||
## Files
|
||||
|
||||
| Path | Purpose |
|
||||
| ---- | ------- |
|
||||
| `/etc/fips/hosts` | Maps hostnames to npubs for the `connect`, `disconnect`, and `--peer` arguments. See [configuration.md](configuration.md). |
|
||||
| Control socket (default) | Same resolution as the daemon: `/run/fips/control.sock` if present, else `$XDG_RUNTIME_DIR/fips/control.sock`, else `/tmp/fips-control.sock` (Unix); TCP `localhost:21210` (Windows). |
|
||||
|
||||
If you get `Permission denied` connecting to the socket on Linux,
|
||||
add your user to the `fips` group (`sudo usermod -aG fips $USER`)
|
||||
and log out and back in.
|
||||
|
||||
## See also
|
||||
|
||||
- [`fips`](cli-fips.md) — the daemon.
|
||||
- [`fipstop`](cli-fipstop.md) — live-status TUI.
|
||||
- [control-socket.md](control-socket.md) — wire protocol.
|
||||
- [configuration.md](configuration.md) — YAML reference.
|
||||
@@ -0,0 +1,123 @@
|
||||
# `fipstop`
|
||||
|
||||
Live-status terminal UI for a running FIPS daemon.
|
||||
|
||||
## Synopsis
|
||||
|
||||
```text
|
||||
fipstop [-s SOCKET] [--gateway-socket PATH] [-r SECONDS]
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
`fipstop` is a `ratatui`-based dashboard. It opens the daemon control
|
||||
socket, polls a small set of `show_*` queries on a timer, and renders
|
||||
the state in a tabbed full-screen UI. A separate poll runs against the
|
||||
gateway control socket when the Gateway tab is active.
|
||||
|
||||
`fipstop` is read-only — it cannot mutate daemon state. Use
|
||||
[`fipsctl`](cli-fipsctl.md) for `connect` / `disconnect` and friends.
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Argument | Default | Description |
|
||||
| ---- | -------- | ------- | ----------- |
|
||||
| `-s`, `--socket` | `PATH` | (auto) | Daemon control-socket path / port. Same default as `fipsctl`. |
|
||||
| `--gateway-socket` | `PATH` | (auto) | `fips-gateway` control-socket path / port. Default: `/run/fips/gateway.sock` (Unix), TCP port `21211` (Windows). |
|
||||
| `-r`, `--refresh` | `SECONDS` | `2` | Poll interval. |
|
||||
| `-V`, `--version` | — | — | Print short version. |
|
||||
| `--version` | — | — | Print long version. |
|
||||
| `-h`, `--help` | — | — | Print usage and exit. |
|
||||
|
||||
## Tabs
|
||||
|
||||
Tabs cycle in this order. Each tab issues the listed control-socket
|
||||
query on its first activation and on every refresh tick while active.
|
||||
|
||||
| Tab | Query | Shows |
|
||||
| --- | ----- | ----- |
|
||||
| **Node** | `show_status` | Identity, version, uptime, peer/link/session counts, sparklines for mesh size, tree depth, peer count, bytes, loss. |
|
||||
| **Peers** | `show_peers` (+ `show_links`, `show_transports` cross-refs) | Authenticated peers in a table. Selecting a row and pressing Enter opens a detail view. |
|
||||
| **Transports** | `show_transports` (+ `show_links`, `show_peers` cross-refs) | Tree of transport instances with per-link children when expanded. |
|
||||
| **Sessions** | `show_sessions` | End-to-end FSP sessions. |
|
||||
| **Tree** | `show_tree` | Spanning-tree state and per-peer coordinates. |
|
||||
| **Filters** | `show_bloom` | Per-peer Bloom-filter state. |
|
||||
| **Performance** | `show_mmp` | Link-layer and session-layer MMP metrics. |
|
||||
| **Routing** | `show_routing` (+ `show_cache` cross-ref) | Forwarding/discovery counters, pending lookups, retry state. |
|
||||
| **Graphs** | `show_stats_history` family + `show_stats_peers` | Stacked time-series plots. Three modes: node-level metrics, one metric across peers, all metrics for one peer. |
|
||||
| **Gateway** | `show_gateway` and `show_mappings` against the gateway socket | Pool utilisation and per-mapping state when `fips-gateway` is running. Empty when the gateway socket is unreachable. |
|
||||
|
||||
The cycle order in the UI is: Node → Peers → Transports → Sessions →
|
||||
Tree → Filters → Performance → Routing → Graphs → Gateway. The Links
|
||||
and Cache tabs are not in the cycle but are fetched as cross-references
|
||||
to populate Peers, Transports, and Routing detail views.
|
||||
|
||||
## Keybindings
|
||||
|
||||
### Global
|
||||
|
||||
| Key | Action |
|
||||
| --- | ------ |
|
||||
| `q`, `Ctrl-C` | Quit. |
|
||||
| `Tab` | Next tab. |
|
||||
| `Shift-Tab` | Previous tab. |
|
||||
| `g` | Jump to the Graphs tab. |
|
||||
| `Esc` | Close detail view (if open). |
|
||||
|
||||
### Table tabs (Peers, Sessions, Transports, Gateway)
|
||||
|
||||
| Key | Action |
|
||||
| --- | ------ |
|
||||
| `Up`, `Down` | Move row selection. |
|
||||
| `Enter` | Open detail view for the selected row. |
|
||||
|
||||
### Transports tab (extra)
|
||||
|
||||
| Key | Action |
|
||||
| --- | ------ |
|
||||
| `Right`, `Space` | Expand the selected transport row to show its links. |
|
||||
| `Left` | Collapse the selected transport row. |
|
||||
| `e` | Expand all transports. |
|
||||
| `c` | Collapse all transports. |
|
||||
|
||||
### Graphs tab (extra)
|
||||
|
||||
| Key | Action |
|
||||
| --- | ------ |
|
||||
| `Up`, `Down` | Scroll within the stacked plots. |
|
||||
| `Right`, `Space` | Next time window. Cycles `1m / 1s` → `10m / 1s` → `1h / 1s` → `24h / 1m`. |
|
||||
| `Left` | Previous time window. |
|
||||
| `m` | Cycle view mode: `Node` (stacked node metrics) → `MetricByPeer` (one per-peer metric across all peers) → `PeerByMetric` (all per-peer metrics for one peer). |
|
||||
| `n` | Next selector (next per-peer metric in MetricByPeer; next peer in PeerByMetric). |
|
||||
| `Shift-N` | Previous selector. |
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
| ---- | ------- |
|
||||
| `0` | Normal quit. |
|
||||
| `1` | Failed to initialise the terminal. The reason is printed to stderr. |
|
||||
|
||||
A failure to reach the daemon socket is **not** fatal: the dashboard
|
||||
displays "Disconnected" in the status bar and retries on every refresh
|
||||
tick.
|
||||
|
||||
## Environment
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `XDG_RUNTIME_DIR` | Used to derive the default control-socket and gateway-socket paths when `/run/fips` is absent. |
|
||||
|
||||
## Files
|
||||
|
||||
Same control-socket resolution rules as
|
||||
[`fipsctl`](cli-fipsctl.md#files). The gateway socket follows the same
|
||||
pattern with `gateway.sock` in place of `control.sock`, falling back
|
||||
to `/tmp/fips-gateway.sock` if neither system path nor
|
||||
`XDG_RUNTIME_DIR` is available.
|
||||
|
||||
## See also
|
||||
|
||||
- [`fipsctl`](cli-fipsctl.md) — issue mutating commands.
|
||||
- [`fips`](cli-fips.md) — the daemon.
|
||||
- [control-socket.md](control-socket.md) — wire protocol fipstop polls.
|
||||
@@ -0,0 +1,166 @@
|
||||
# Control Socket Protocol
|
||||
|
||||
The FIPS daemon and `fips-gateway` each expose a local control socket
|
||||
that accepts line-delimited JSON requests and returns line-delimited
|
||||
JSON responses. `fipsctl` and `fipstop` are clients of this protocol;
|
||||
operators can also drive it directly with any tool that can speak
|
||||
length-bounded JSON over a stream socket.
|
||||
|
||||
## Connection
|
||||
|
||||
### Linux / macOS
|
||||
|
||||
A Unix domain socket. The default path is resolved in this order:
|
||||
|
||||
1. `/run/fips/control.sock` (or `/run/fips/gateway.sock` for the
|
||||
gateway), if `/run/fips` exists. This is what the `fips.service`
|
||||
systemd unit creates.
|
||||
2. `$XDG_RUNTIME_DIR/fips/control.sock` otherwise.
|
||||
3. `/tmp/fips-control.sock` if neither of the above is available.
|
||||
|
||||
The daemon `chown`s the socket file and its parent directory to the
|
||||
`fips` group at bind time and sets mode `0770`. Members of the `fips`
|
||||
group can therefore connect without root. Add a user with
|
||||
`sudo usermod -aG fips $USER` (re-login required).
|
||||
|
||||
The path can be overridden at the daemon side via
|
||||
`node.control.socket_path` in the YAML config, and at the client side
|
||||
via `fipsctl -s PATH` or `fipstop -s PATH`.
|
||||
|
||||
### Windows
|
||||
|
||||
A TCP listener bound to `127.0.0.1`. The daemon's port is `21210` by
|
||||
default; the gateway's is `21211`. Only loopback connections are
|
||||
accepted. Override via `node.control.socket_path` (which takes a port
|
||||
number string on Windows).
|
||||
|
||||
Windows TCP does not provide filesystem-level ACLs — any local user
|
||||
can connect. See the security note in
|
||||
[configuration.md](configuration.md#control-socket-nodecontrol).
|
||||
|
||||
## Request Format
|
||||
|
||||
One JSON object per line, terminated by `\n`. Maximum request size is
|
||||
4096 bytes; longer requests are dropped with `request too large`.
|
||||
|
||||
```json
|
||||
{"command": "<name>", "params": {<object>}}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ----- | ---- | -------- | ----------- |
|
||||
| `command` | string | yes | Command name. See [Daemon command catalog](#daemon-command-catalog) and [Gateway command catalog](#gateway-command-catalog). |
|
||||
| `params` | object | only for commands that take parameters | Parameter object. Unknown fields are ignored; missing required fields produce an error response. |
|
||||
|
||||
Unknown top-level fields in the request are silently ignored.
|
||||
|
||||
## Response Format
|
||||
|
||||
One JSON object per line.
|
||||
|
||||
```json
|
||||
{"status": "ok", "data": {<object>}}
|
||||
{"status": "error", "message": "<reason>"}
|
||||
```
|
||||
|
||||
| Field | Type | When present |
|
||||
| ----- | ---- | ------------ |
|
||||
| `status` | string | always; one of `"ok"` or `"error"`. |
|
||||
| `data` | object | on `ok` responses. |
|
||||
| `message` | string | on `error` responses. |
|
||||
|
||||
### I/O timeouts
|
||||
|
||||
The daemon enforces a 5-second timeout for both the request read and
|
||||
the response write. If the connection idles longer than that, the
|
||||
daemon closes it with no response.
|
||||
|
||||
### Common error messages
|
||||
|
||||
| Message | Cause |
|
||||
| ------- | ----- |
|
||||
| `empty request` | Connection closed before a newline was received. |
|
||||
| `invalid request: <serde error>` | Malformed JSON or missing `command`. |
|
||||
| `request too large` | Request exceeded 4096 bytes. |
|
||||
| `read timeout` / `read error: ...` | Slow client or transport failure. |
|
||||
| `unknown command: <name>` | Command not registered with this daemon. |
|
||||
| `missing params for <name>` | Command requires `params` but none were provided. |
|
||||
| `missing '<field>' parameter` | Required parameter missing. |
|
||||
| `query timeout` | Internal handler did not respond within 5 seconds. |
|
||||
| `node shutting down` | Daemon is exiting. |
|
||||
| `gateway not yet initialized` | (Gateway socket only) snapshot has not been published yet. |
|
||||
|
||||
## Daemon Command Catalog
|
||||
|
||||
Read-only queries are dispatched in `src/control/queries.rs`;
|
||||
mutating commands are dispatched in `src/control/commands.rs`. The
|
||||
table below lists every command currently registered.
|
||||
|
||||
### Read-only queries
|
||||
|
||||
| Command | Params | `data` shape (top-level keys) |
|
||||
| ------- | ------ | ----------------------------- |
|
||||
| `show_status` | — | `version`, `npub`, `node_addr`, `ipv6_addr`, `state`, `is_leaf_only`, `peer_count`, `session_count`, `link_count`, `transport_count`, `connection_count`, `tun_state`, `tun_name`, `effective_ipv6_mtu`, `control_socket`, `pid`, `exe_path`, `uptime_secs`, `estimated_mesh_size`, `forwarding`, `sparklines`. |
|
||||
| `show_acl` | — | `allow_file`, `deny_file`, `enforcement_active`, `effective_mode`, `default_decision`, `allow_all`, `deny_all`, `allow_file_entries`, `deny_file_entries`, `allow_entries`, `deny_entries`. |
|
||||
| `show_peers` | — | `peers[]` — per-peer object: `node_addr`, `npub`, `display_name`, `ipv6_addr`, `connectivity`, `link_id`, `direction`, `transport_addr`, `transport_type`, `is_parent`, `is_child`, `tree_depth`, `stats`, `noise`, `current_k_bit`, `mmp`, plus optional `nostr_traversal`, `rekey_in_progress`, `rekey_draining`. |
|
||||
| `show_links` | — | `links[]` — `link_id`, `transport_id`, `remote_addr`, `direction`, `state`, `created_at_ms`, `stats`. |
|
||||
| `show_tree` | — | `my_node_addr`, `root`, `is_root`, `depth`, `my_coords[]`, `parent`, `parent_display_name`, `declaration_sequence`, `declaration_signed`, `peer_tree_count`, `peers[]`, `stats`. |
|
||||
| `show_sessions` | — | `sessions[]` — `remote_addr`, `npub`, `display_name`, `state` (`established`, `initiating`, `awaiting_msg3`, `unknown`), `is_initiator`, `last_activity_ms`, `stats`, optional `mmp`, `current_k_bit`, `is_draining`. |
|
||||
| `show_bloom` | — | `own_node_addr`, `is_leaf_only`, `sequence`, `leaf_dependent_count`, `leaf_dependents[]`, `peer_filters[]`, `stats`. |
|
||||
| `show_mmp` | — | `peers[]` (link-layer per peer), `sessions[]` (session-layer per session). Each entry includes loss/RTT/ETX/goodput, smoothed values, trends. |
|
||||
| `show_cache` | — | `count`, `max_entries`, `fill_ratio`, `default_ttl_ms`, `expired`, `avg_age_ms`, `entries[]` — per-destination coords, depth, age, last-used, optional `path_mtu`. |
|
||||
| `show_connections` | — | `connections[]` — pending handshakes: `link_id`, `direction`, `handshake_state`, `started_at_ms`, `idle_ms`, `resend_count`, optional `expected_peer`. |
|
||||
| `show_transports` | — | `transports[]` — `transport_id`, `type`, `state`, `mtu`, `name`, `local_addr`, optional `tor_mode`, `onion_address`, `tor_monitoring`, `stats`. |
|
||||
| `show_routing` | — | `coord_cache_entries`, `identity_cache_entries`, `pending_lookups[]`, `pending_tun_destinations`, `pending_tun_packets`, `recent_requests`, `retries[]`, `forwarding`, `discovery`, `error_signals`, `congestion`. |
|
||||
| `show_identity_cache` | — | `entries[]`, `count`, `max_entries`. Each entry: `node_addr`, `npub`, `display_name`, `ipv6_addr`, `last_seen_ms`, `age_ms`. |
|
||||
| `show_stats_list` | — | `metrics[]` (each with `name`, `unit`, `scope`), `fast_ring_seconds`, `slow_ring_minutes`, `peer_retention_seconds`. |
|
||||
| `show_stats_history` | `metric` (req), `peer` (req for per-peer metrics), `window` (`<N>s` / `<N>m` / `<N>h`, default `10m`), `granularity` (`1s` / `1m`, default `1s`) | A single `Series`: `metric`, `unit`, `granularity_seconds`, `values[]`. |
|
||||
| `show_stats_all_history` | `peer` (optional npub), `window`, `granularity` | `granularity_seconds`, `window_seconds`, `peer`, `series[]` (one per metric). |
|
||||
| `show_stats_peers` | — | `peers[]`, `count`. Each entry: `npub`, `node_addr`, `display_name`, `is_active`, `first_seen_secs_ago`, `last_contact_secs_ago`. |
|
||||
| `show_stats_history_all_peers` | `metric` (req per-peer name), `window`, `granularity` | `metric`, `unit`, `granularity_seconds`, `window_seconds`, `peers[]` (each with `node_addr`, `display_name`, `is_active`, `values[]`). |
|
||||
|
||||
The schema of each query response is pinned by snapshot tests in
|
||||
`src/control/snapshots/`; intentional schema changes regenerate those
|
||||
fixtures.
|
||||
|
||||
### Mutating commands
|
||||
|
||||
| Command | Required params | Behaviour |
|
||||
| ------- | --------------- | --------- |
|
||||
| `connect` | `npub` (bech32), `address` (transport endpoint), `transport` (`udp`, `tcp`, `tor`, `ethernet`) | Asks the node to dial the peer over the named transport. Returns the API result on success or an error string on failure. |
|
||||
| `disconnect` | `npub` (bech32) | Asks the node to drop the link to the named peer. |
|
||||
|
||||
Both commands run on the daemon's main task and may block briefly
|
||||
while the node mutates its state.
|
||||
|
||||
## Gateway Command Catalog
|
||||
|
||||
`fips-gateway` exposes a separate control socket with its own command
|
||||
set. Dispatch lives in `src/gateway/control.rs`.
|
||||
|
||||
| Command | Params | `data` shape |
|
||||
| ------- | ------ | ------------ |
|
||||
| `show_gateway` | — | `pool_total`, `pool_allocated`, `pool_active`, `pool_draining`, `pool_free`, `nat_mappings`, `dns_listen`, `uptime_secs`, `pool_cidr`, `lan_interface`, `dns_upstream`, `dns_ttl`, `pool_grace_period`. |
|
||||
| `show_mappings` | — | `mappings[]` — `virtual_ip`, `mesh_addr`, `node_addr`, `dns_name`, `state` (`Allocated`, `Active`, `Draining`), `sessions`, `age_secs`, `last_ref_secs`. |
|
||||
|
||||
Until the first snapshot has been published (very early in startup),
|
||||
both commands return `gateway not yet initialized`.
|
||||
|
||||
## Driving the Socket Directly
|
||||
|
||||
```sh
|
||||
# Linux / macOS
|
||||
echo '{"command":"show_status"}' | sudo nc -U /run/fips/control.sock
|
||||
|
||||
# Windows (PowerShell with a TCP-capable tool of your choice)
|
||||
```
|
||||
|
||||
The newline at the end of the request is required: the daemon reads
|
||||
one line per connection. The connection is closed after the single
|
||||
response is written.
|
||||
|
||||
## See also
|
||||
|
||||
- [`fipsctl`](cli-fipsctl.md) — full-featured client.
|
||||
- [`fipstop`](cli-fipstop.md) — read-only TUI.
|
||||
- [configuration.md](configuration.md) — `node.control.*` keys.
|
||||
@@ -0,0 +1,28 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 192" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="192" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">AncestryEntry (32 bytes)</text>
|
||||
|
||||
<!-- Row 0 (0–15): node_addr -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–15</text>
|
||||
|
||||
<rect x="55" y="36" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">node_addr</text>
|
||||
<text x="315" y="76" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — routing identifier</text>
|
||||
|
||||
<!-- Row 1 (16–23): sequence -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">16–23</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">sequence</text>
|
||||
<text x="315" y="128" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Row 2 (24–31): timestamp -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">24–31</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">timestamp</text>
|
||||
<text x="315" y="180" fill="#d8a0f8" text-anchor="middle" font-size="10">8 bytes LE — Unix seconds</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,39 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 160" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="160" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="290" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">FMP/FSP Common Prefix (4 bytes)</text>
|
||||
|
||||
<!-- Bit ruler -->
|
||||
<text x="65" y="50" fill="#888" font-size="10" text-anchor="middle">0</text>
|
||||
<text x="130" y="50" fill="#888" font-size="10" text-anchor="middle">4</text>
|
||||
<text x="195" y="50" fill="#888" font-size="10" text-anchor="middle">8</text>
|
||||
<text x="325" y="50" fill="#888" font-size="10" text-anchor="middle">16</text>
|
||||
<text x="530" y="50" fill="#888" font-size="10" text-anchor="middle">32</text>
|
||||
|
||||
<!-- Byte 0: version (high nibble) -->
|
||||
<rect x="35" y="56" width="95" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="82" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ver</text>
|
||||
<text x="82" y="98" fill="#8ab4f8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<!-- Byte 0: phase (low nibble) -->
|
||||
<rect x="130" y="56" width="95" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="177" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">phase</text>
|
||||
<text x="177" y="98" fill="#8af8c8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<!-- Byte 1: flags -->
|
||||
<rect x="225" y="56" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="290" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="290" y="98" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<!-- Bytes 2-3: payload_len -->
|
||||
<rect x="355" y="56" width="200" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="455" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">payload_len</text>
|
||||
<text x="455" y="98" fill="#d8a0f8" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Byte offset labels -->
|
||||
<text x="82" y="130" fill="#666" font-size="10" text-anchor="middle">byte 0</text>
|
||||
<text x="290" y="130" fill="#666" font-size="10" text-anchor="middle">byte 1</text>
|
||||
<text x="455" y="130" fill="#666" font-size="10" text-anchor="middle">bytes 2-3</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 218" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="218" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">CoordsRequired (0x20) — 34 bytes payload</text>
|
||||
|
||||
<!-- Row 0 (0–1): msg_type + flags -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–1</text>
|
||||
|
||||
<rect x="55" y="36" width="260" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="185" y="76" fill="#8ab4f8" text-anchor="middle" font-size="10">0x20</text>
|
||||
|
||||
<rect x="315" y="36" width="260" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" rx="3"/>
|
||||
<text x="445" y="62" fill="#666" text-anchor="middle" font-size="12">flags (reserved)</text>
|
||||
|
||||
<!-- Row 1 (2–17): dest_addr -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">2–17</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">dest_addr</text>
|
||||
<text x="315" y="128" fill="#d8a0f8" text-anchor="middle" font-size="10">16 bytes — NodeAddr we couldn't route to</text>
|
||||
|
||||
<!-- Row 2 (18–33): reporter -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">18–33</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">reporter</text>
|
||||
<text x="315" y="180" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — NodeAddr of reporting router</text>
|
||||
|
||||
<!-- Note -->
|
||||
<text x="310" y="212" fill="#777" font-size="10" text-anchor="middle">plaintext error signal (U flag set) · total on wire: 38 bytes (4 prefix + 34 payload)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 114" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="114" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">Disconnect (0x50) — 2 bytes</text>
|
||||
|
||||
<!-- Row 0 (0–1): msg_type + reason -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–1</text>
|
||||
|
||||
<rect x="55" y="36" width="260" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="185" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x50</text>
|
||||
|
||||
<rect x="315" y="36" width="260" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">reason</text>
|
||||
<text x="445" y="78" fill="#f88aaa" text-anchor="middle" font-size="10">1 byte</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,91 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 498" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="498" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">Complete Encrypted Frame (37 + body bytes)</text>
|
||||
|
||||
<!-- Bit ruler -->
|
||||
<text x="85" y="50" fill="#888" font-size="10" text-anchor="middle">0</text>
|
||||
<text x="150" y="50" fill="#888" font-size="10" text-anchor="middle">4</text>
|
||||
<text x="215" y="50" fill="#888" font-size="10" text-anchor="middle">8</text>
|
||||
<text x="345" y="50" fill="#888" font-size="10" text-anchor="middle">16</text>
|
||||
<text x="550" y="50" fill="#888" font-size="10" text-anchor="middle">32</text>
|
||||
|
||||
<!-- Row 0: Common prefix (bytes 0-3) -->
|
||||
<text x="50" y="82" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="56" width="95" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="102" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ver</text>
|
||||
<text x="102" y="98" fill="#8ab4f8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="150" y="56" width="95" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="197" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">phase</text>
|
||||
<text x="197" y="98" fill="#8af8c8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="245" y="56" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="310" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="310" y="98" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="375" y="56" width="200" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="475" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">payload_len</text>
|
||||
<text x="475" y="98" fill="#d8a0f8" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 1: receiver_idx (bytes 4-7) -->
|
||||
<text x="50" y="134" fill="#666" font-size="10" text-anchor="end">4–7</text>
|
||||
|
||||
<rect x="55" y="108" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="132" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">receiver_idx</text>
|
||||
<text x="315" y="150" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 2: counter low (bytes 8-11) -->
|
||||
<text x="50" y="186" fill="#666" font-size="10" text-anchor="end">8–11</text>
|
||||
|
||||
<rect x="55" y="160" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="190" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">counter / lower 4 bytes LE</text>
|
||||
|
||||
<!-- Row 3: counter high (bytes 12-15) -->
|
||||
<text x="50" y="238" fill="#666" font-size="10" text-anchor="end">12–15</text>
|
||||
|
||||
<rect x="55" y="212" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="242" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">counter / upper 4 bytes LE</text>
|
||||
|
||||
<!-- AAD bracket -->
|
||||
<text x="10" y="166" fill="#777" font-size="10" text-anchor="middle" transform="rotate(-90, 10, 166)">AAD</text>
|
||||
<line x1="18" y1="56" x2="18" y2="264" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="56" x2="23" y2="56" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="264" x2="23" y2="264" stroke="#555" stroke-width="1"/>
|
||||
|
||||
<!-- Separator: cleartext above / ciphertext below -->
|
||||
<line x1="55" y1="272" x2="575" y2="272" stroke="#555" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="315" y="288" fill="#777" font-size="10" text-anchor="middle">── ciphertext (encrypted with ChaCha20-Poly1305) ──</text>
|
||||
|
||||
<!-- Row 4: timestamp (bytes 16-19, encrypted) -->
|
||||
<text x="50" y="322" fill="#666" font-size="10" text-anchor="end">16–19</text>
|
||||
|
||||
<rect x="55" y="296" width="520" height="52" fill="#3d4a2d" stroke="#8ad94a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="320" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">timestamp</text>
|
||||
<text x="315" y="338" fill="#c8f88a" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 5: msg_type (1 byte) + body starts -->
|
||||
<text x="50" y="374" fill="#666" font-size="10" text-anchor="end">20</text>
|
||||
|
||||
<rect x="55" y="348" width="130" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="372" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="390" fill="#f8d88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="185" y="348" width="390" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="380" y="374" fill="#666" text-anchor="middle" font-size="11">message body ...</text>
|
||||
|
||||
<!-- Row 6: body variable -->
|
||||
<text x="50" y="426" fill="#666" font-size="10" text-anchor="end">...</text>
|
||||
|
||||
<rect x="55" y="400" width="520" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="430" fill="#666" text-anchor="middle" font-size="11">message body (variable length)</text>
|
||||
|
||||
<!-- AEAD tag -->
|
||||
<text x="50" y="478" fill="#666" font-size="10" text-anchor="end">+16</text>
|
||||
|
||||
<rect x="55" y="452" width="520" height="30" fill="#4a2d4a" stroke="#b04ab0" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="472" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">Poly1305 AEAD tag (16 bytes)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.6 KiB |
@@ -0,0 +1,31 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 186" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="186" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">Established Frame Inner Header (5 bytes, plaintext after decrypt)</text>
|
||||
|
||||
<!-- Bit ruler -->
|
||||
<text x="85" y="50" fill="#888" font-size="10" text-anchor="middle">0</text>
|
||||
<text x="215" y="50" fill="#888" font-size="10" text-anchor="middle">8</text>
|
||||
<text x="345" y="50" fill="#888" font-size="10" text-anchor="middle">16</text>
|
||||
<text x="550" y="50" fill="#888" font-size="10" text-anchor="middle">32</text>
|
||||
|
||||
<!-- Row 0: timestamp (bytes 0-3) -->
|
||||
<text x="50" y="82" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="56" width="520" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">timestamp</text>
|
||||
<text x="315" y="98" fill="#8af8c8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 1: msg_type (byte 4) -->
|
||||
<text x="50" y="134" fill="#666" font-size="10" text-anchor="end">4</text>
|
||||
|
||||
<rect x="55" y="108" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="132" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="150" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<!-- Row 1: body starts -->
|
||||
<rect x="185" y="108" width="390" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="380" y="134" fill="#666" text-anchor="middle" font-size="11">message body ...</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,52 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 290" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="290" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">Established Frame Outer Header (16 bytes, AEAD AAD)</text>
|
||||
|
||||
<!-- Bit ruler -->
|
||||
<text x="85" y="50" fill="#888" font-size="10" text-anchor="middle">0</text>
|
||||
<text x="150" y="50" fill="#888" font-size="10" text-anchor="middle">4</text>
|
||||
<text x="215" y="50" fill="#888" font-size="10" text-anchor="middle">8</text>
|
||||
<text x="345" y="50" fill="#888" font-size="10" text-anchor="middle">16</text>
|
||||
<text x="550" y="50" fill="#888" font-size="10" text-anchor="middle">32</text>
|
||||
|
||||
<!-- Row 0: Common prefix (bytes 0-3) -->
|
||||
<text x="50" y="82" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="56" width="95" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="102" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ver</text>
|
||||
<text x="102" y="98" fill="#8ab4f8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="150" y="56" width="95" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="197" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">phase</text>
|
||||
<text x="197" y="98" fill="#8af8c8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="245" y="56" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="310" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="310" y="98" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="375" y="56" width="200" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="475" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">payload_len</text>
|
||||
<text x="475" y="98" fill="#d8a0f8" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 1: receiver_idx (bytes 4-7) -->
|
||||
<text x="50" y="134" fill="#666" font-size="10" text-anchor="end">4–7</text>
|
||||
|
||||
<rect x="55" y="108" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="132" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">receiver_idx</text>
|
||||
<text x="315" y="150" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 2: counter low (bytes 8-11) -->
|
||||
<text x="50" y="186" fill="#666" font-size="10" text-anchor="end">8–11</text>
|
||||
|
||||
<rect x="55" y="160" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="190" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">counter / lower 4 bytes LE</text>
|
||||
|
||||
<!-- Row 3: counter high (bytes 12-15) -->
|
||||
<text x="50" y="238" fill="#666" font-size="10" text-anchor="end">12–15</text>
|
||||
|
||||
<rect x="55" y="212" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="242" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">counter / upper 4 bytes LE</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,43 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 270" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="270" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">FilterAnnounce (0x20) — 11 + filter bytes</text>
|
||||
|
||||
<!-- Row 0 (0–3): msg_type(1) + sequence starts -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="130" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x20</text>
|
||||
|
||||
<rect x="185" y="36" width="390" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="380" y="66" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">sequence</text>
|
||||
|
||||
<!-- Row 1 (4–8): sequence continued -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–8</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="118" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE — monotonic counter</text>
|
||||
|
||||
<!-- Row 2 (9–10): hash_count + size_class -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">9–10</text>
|
||||
|
||||
<rect x="55" y="140" width="260" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">hash_count</text>
|
||||
<text x="185" y="180" fill="#8af8c8" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="315" y="140" width="260" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">size_class</text>
|
||||
<text x="445" y="180" fill="#d8a0f8" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<!-- Row 3 (11–): filter_bits -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">11–</text>
|
||||
|
||||
<rect x="55" y="198" width="520" height="40" fill="#1f1f3a" stroke="#4ad9d9" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="222" fill="#8af8f8" text-anchor="middle" font-size="11">filter_bits (variable, 512 << size_class bytes)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="258" fill="#777" font-size="10" text-anchor="middle">v1 payload: 1,035 bytes (11 header + 1,024 filter)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,97 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 524" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="524" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">FSP Complete Encrypted Message (34 + body bytes)</text>
|
||||
|
||||
<!-- Bit ruler -->
|
||||
<text x="85" y="50" fill="#888" font-size="10" text-anchor="middle">0</text>
|
||||
<text x="150" y="50" fill="#888" font-size="10" text-anchor="middle">4</text>
|
||||
<text x="215" y="50" fill="#888" font-size="10" text-anchor="middle">8</text>
|
||||
<text x="345" y="50" fill="#888" font-size="10" text-anchor="middle">16</text>
|
||||
<text x="550" y="50" fill="#888" font-size="10" text-anchor="middle">32</text>
|
||||
|
||||
<!-- Row 0: Common prefix (bytes 0-3) -->
|
||||
<text x="50" y="82" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="56" width="95" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="102" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ver</text>
|
||||
<text x="102" y="98" fill="#8ab4f8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="150" y="56" width="95" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="197" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">phase</text>
|
||||
<text x="197" y="98" fill="#8af8c8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="245" y="56" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="310" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="310" y="98" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="375" y="56" width="200" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="475" y="80" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">payload_len</text>
|
||||
<text x="475" y="98" fill="#d8a0f8" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 1: counter low (bytes 4-7) -->
|
||||
<text x="50" y="134" fill="#666" font-size="10" text-anchor="end">4–7</text>
|
||||
|
||||
<rect x="55" y="108" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="138" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">counter / lower 4 bytes LE</text>
|
||||
|
||||
<!-- Row 2: counter high (bytes 8-11) -->
|
||||
<text x="50" y="186" fill="#666" font-size="10" text-anchor="end">8–11</text>
|
||||
|
||||
<rect x="55" y="160" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="190" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">counter / upper 4 bytes LE</text>
|
||||
|
||||
<!-- AAD bracket -->
|
||||
<text x="10" y="134" fill="#777" font-size="10" text-anchor="middle" transform="rotate(-90, 10, 134)">AAD</text>
|
||||
<line x1="18" y1="56" x2="18" y2="212" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="56" x2="23" y2="56" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="212" x2="23" y2="212" stroke="#555" stroke-width="1"/>
|
||||
|
||||
<!-- Optional coords -->
|
||||
<text x="50" y="238" fill="#666" font-size="10" text-anchor="end">opt</text>
|
||||
|
||||
<rect x="55" y="220" width="520" height="40" fill="#1f1f3a" stroke="#4ad99a" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="244" fill="#8af8c8" text-anchor="middle" font-size="11">cleartext coordinates (if CP flag set, variable length)</text>
|
||||
|
||||
<!-- Separator: ciphertext below -->
|
||||
<line x1="55" y1="268" x2="575" y2="268" stroke="#555" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="315" y="284" fill="#777" font-size="10" text-anchor="middle">── ciphertext (encrypted with ChaCha20-Poly1305) ──</text>
|
||||
|
||||
<!-- Row 3: timestamp (4 bytes, encrypted) -->
|
||||
<text x="50" y="318" fill="#666" font-size="10" text-anchor="end">+0–3</text>
|
||||
|
||||
<rect x="55" y="292" width="520" height="52" fill="#3d4a2d" stroke="#8ad94a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="316" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">timestamp</text>
|
||||
<text x="315" y="334" fill="#c8f88a" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 4: msg_type (1) + inner_flags (1) + body starts -->
|
||||
<text x="50" y="370" fill="#666" font-size="10" text-anchor="end">+4</text>
|
||||
|
||||
<rect x="55" y="344" width="130" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="368" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="386" fill="#f8d88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="185" y="344" width="130" height="52" fill="#3d3d2d" stroke="#a0a04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="250" y="368" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">inner_flags</text>
|
||||
<text x="250" y="386" fill="#d8d88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="315" y="344" width="260" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="445" y="374" fill="#666" text-anchor="middle" font-size="11">body ...</text>
|
||||
|
||||
<!-- Row 5: body variable -->
|
||||
<text x="50" y="422" fill="#666" font-size="10" text-anchor="end">...</text>
|
||||
|
||||
<rect x="55" y="396" width="520" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="426" fill="#666" text-anchor="middle" font-size="11">message body (variable length)</text>
|
||||
|
||||
<!-- AEAD tag -->
|
||||
<text x="50" y="468" fill="#666" font-size="10" text-anchor="end">+16</text>
|
||||
|
||||
<rect x="55" y="452" width="520" height="30" fill="#4a2d4a" stroke="#b04ab0" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="472" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">Poly1305 AEAD tag (16 bytes)</text>
|
||||
|
||||
<!-- Totals -->
|
||||
<text x="310" y="510" fill="#777" font-size="10" text-anchor="middle">overhead: 34 bytes (12 header + 6 inner + 16 tag) · coords add variable length</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,66 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 620 500" font-family="monospace" font-size="13">
|
||||
<defs>
|
||||
<marker id="arrowR" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="userSpaceOnUse">
|
||||
<polygon points="0,0 10,4 0,8" fill="#e0e0e0"/>
|
||||
</marker>
|
||||
<marker id="arrowL" markerWidth="10" markerHeight="8" refX="1" refY="4" orient="auto" markerUnits="userSpaceOnUse">
|
||||
<polygon points="10,0 0,4 10,8" fill="#e0e0e0"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="620" height="500" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">FMP Handshake Flow (Noise IK)</text>
|
||||
|
||||
<!-- Column headers -->
|
||||
<text x="100" y="54" fill="#8ab4f8" text-anchor="middle" font-size="12" font-weight="bold">Initiator</text>
|
||||
<text x="520" y="54" fill="#8ab4f8" text-anchor="middle" font-size="12" font-weight="bold">Responder</text>
|
||||
|
||||
<!-- Lifelines -->
|
||||
<line x1="100" y1="62" x2="100" y2="480" stroke="#333" stroke-width="1" stroke-dasharray="4,4"/>
|
||||
<line x1="520" y1="62" x2="520" y2="480" stroke="#333" stroke-width="1" stroke-dasharray="4,4"/>
|
||||
|
||||
<!-- Step 1: Initiator prepares -->
|
||||
<text x="20" y="88" fill="#8af8c8" font-size="10">generates sender_idx</text>
|
||||
<text x="20" y="102" fill="#8af8c8" font-size="10">generates ephemeral keypair</text>
|
||||
|
||||
<!-- Arrow 1: msg1 (initiator -> responder) -->
|
||||
<line x1="100" y1="120" x2="520" y2="120" stroke="#4a90d9" stroke-width="1.5" marker-end="url(#arrowR)"/>
|
||||
<rect x="130" y="128" width="360" height="24" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1" rx="3"/>
|
||||
<text x="310" y="144" fill="#e0e0e0" text-anchor="middle" font-size="10">[0x01|flags=0|len] | sender_idx | noise_msg1</text>
|
||||
<text x="310" y="166" fill="#666" text-anchor="middle" font-size="9">phase 0x1 — 114 bytes</text>
|
||||
|
||||
<!-- Step 2: Responder processes (right-aligned to stay in bounds) -->
|
||||
<text x="600" y="192" fill="#8af8c8" font-size="10" text-anchor="end">validates msg1</text>
|
||||
<text x="600" y="206" fill="#8af8c8" font-size="10" text-anchor="end">learns initiator's static key</text>
|
||||
<text x="600" y="220" fill="#8af8c8" font-size="10" text-anchor="end">generates sender_idx</text>
|
||||
<text x="600" y="234" fill="#8af8c8" font-size="10" text-anchor="end">generates ephemeral keypair</text>
|
||||
|
||||
<!-- Arrow 2: msg2 (responder -> initiator) -->
|
||||
<!-- Draw line right-to-left so orient="auto" points the arrowhead left -->
|
||||
<line x1="520" y1="256" x2="100" y2="256" stroke="#4ad99a" stroke-width="1.5" marker-end="url(#arrowR)"/>
|
||||
<rect x="130" y="264" width="360" height="24" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1" rx="3"/>
|
||||
<text x="310" y="280" fill="#e0e0e0" text-anchor="middle" font-size="9">[0x02|flags=0|len] | sender_idx | receiver_idx | noise_msg2</text>
|
||||
<text x="310" y="300" fill="#666" text-anchor="middle" font-size="9">phase 0x2 — 69 bytes</text>
|
||||
|
||||
<!-- Step 3: Initiator completes -->
|
||||
<text x="20" y="324" fill="#8af8c8" font-size="10">validates msg2</text>
|
||||
<text x="20" y="338" fill="#8af8c8" font-size="10">derives session keys</text>
|
||||
|
||||
<!-- Handshake complete separator -->
|
||||
<line x1="30" y1="360" x2="230" y2="360" stroke="#d9904a" stroke-width="1.5"/>
|
||||
<text x="310" y="364" fill="#d9904a" text-anchor="middle" font-size="11" font-weight="bold">HANDSHAKE COMPLETE</text>
|
||||
<line x1="390" y1="360" x2="590" y2="360" stroke="#d9904a" stroke-width="1.5"/>
|
||||
|
||||
<!-- Arrow 3: first encrypted frame -->
|
||||
<text x="20" y="392" fill="#777" font-size="10">first encrypted frame:</text>
|
||||
<line x1="100" y1="406" x2="520" y2="406" stroke="#d94a6a" stroke-width="1.5" marker-end="url(#arrowR)"/>
|
||||
<rect x="115" y="414" width="390" height="24" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1" rx="3"/>
|
||||
<text x="310" y="430" fill="#e0e0e0" text-anchor="middle" font-size="9">[0x00|flags|len] | receiver_idx | counter=0 | ciphertext+tag</text>
|
||||
<text x="310" y="450" fill="#666" text-anchor="middle" font-size="9">phase 0x0 — established frame</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<text x="310" y="478" fill="#555" text-anchor="middle" font-size="9">Both parties hold identical symmetric keys. Epoch exchange enables restart detection.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,63 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 430" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="430" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">LookupRequest (0x30) — 46 + 16n bytes</text>
|
||||
|
||||
<!-- Row 0 (0–3): msg_type + request_id starts -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="130" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x30</text>
|
||||
|
||||
<rect x="185" y="36" width="390" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="380" y="66" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">request_id</text>
|
||||
|
||||
<!-- Row 1 (4–8): request_id continued -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–8</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="118" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE — unique random identifier</text>
|
||||
|
||||
<!-- Row 2 (9–24): target -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">9–24</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">target</text>
|
||||
<text x="315" y="180" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — NodeAddr being sought</text>
|
||||
|
||||
<!-- Row 3 (25–40): origin -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">25–40</text>
|
||||
|
||||
<rect x="55" y="192" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">origin</text>
|
||||
<text x="315" y="232" fill="#d8a0f8" text-anchor="middle" font-size="10">16 bytes — requester's NodeAddr</text>
|
||||
|
||||
<!-- Row 4 (41–43): ttl + min_mtu -->
|
||||
<text x="50" y="270" fill="#666" font-size="10" text-anchor="end">41–43</text>
|
||||
|
||||
<rect x="55" y="244" width="130" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="268" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ttl</text>
|
||||
<text x="120" y="286" fill="#8af8c8" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="185" y="244" width="390" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="380" y="268" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">min_mtu</text>
|
||||
<text x="380" y="286" fill="#f88aaa" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 5 (44–45): origin_coords_cnt -->
|
||||
<text x="50" y="322" fill="#666" font-size="10" text-anchor="end">44–45</text>
|
||||
|
||||
<rect x="55" y="296" width="260" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="326" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">origin_coords_cnt</text>
|
||||
|
||||
<!-- Row 6 (46–): origin_coords -->
|
||||
<text x="50" y="370" fill="#666" font-size="10" text-anchor="end">46–</text>
|
||||
|
||||
<rect x="55" y="348" width="520" height="40" fill="#1f1f3a" stroke="#4ad9d9" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="372" fill="#8af8f8" text-anchor="middle" font-size="11">origin_coords × n (16 bytes each — NodeAddr only)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="414" fill="#777" font-size="10" text-anchor="middle">total: 46 + (n × 16) bytes, where n = origin depth + 1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,56 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 374" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="374" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">LookupResponse (0x31) — 93 + 16n bytes</text>
|
||||
|
||||
<!-- Row 0 (0–3): msg_type + request_id starts -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="130" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x31</text>
|
||||
|
||||
<rect x="185" y="36" width="390" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="380" y="66" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">request_id</text>
|
||||
|
||||
<!-- Row 1 (4–8): request_id continued -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–8</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="118" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE — echoes the request's ID</text>
|
||||
|
||||
<!-- Row 2 (9–24): target -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">9–24</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">target</text>
|
||||
<text x="315" y="180" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — NodeAddr that was found</text>
|
||||
|
||||
<!-- Row 3 (25–28): path_mtu + target_coords_cnt -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">25–28</text>
|
||||
|
||||
<rect x="55" y="192" width="260" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">path_mtu</text>
|
||||
<text x="185" y="232" fill="#f88aaa" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<rect x="315" y="192" width="260" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">target_coords_cnt</text>
|
||||
<text x="445" y="232" fill="#f8d88a" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 4 (29–): target_coords -->
|
||||
<text x="50" y="270" fill="#666" font-size="10" text-anchor="end">29–</text>
|
||||
|
||||
<rect x="55" y="250" width="520" height="40" fill="#1f1f3a" stroke="#4ad9d9" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="274" fill="#8af8f8" text-anchor="middle" font-size="11">target_coords × n (16 bytes each — NodeAddr only)</text>
|
||||
|
||||
<!-- Row 5: proof -->
|
||||
<text x="50" y="316" fill="#666" font-size="10" text-anchor="end">+64</text>
|
||||
|
||||
<rect x="55" y="296" width="520" height="40" fill="#3d4a2d" stroke="#8ad94a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="320" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">Schnorr proof (64 bytes)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="358" fill="#777" font-size="10" text-anchor="middle">total: 93 + (n × 16) bytes · proof signs (request_id || target || target_coords)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,41 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 270" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="270" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">MtuExceeded (0x22) — 36 bytes payload</text>
|
||||
|
||||
<!-- Row 0 (0–1): msg_type + flags -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–1</text>
|
||||
|
||||
<rect x="55" y="36" width="260" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="185" y="76" fill="#8ab4f8" text-anchor="middle" font-size="10">0x22</text>
|
||||
|
||||
<rect x="315" y="36" width="260" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" rx="3"/>
|
||||
<text x="445" y="62" fill="#666" text-anchor="middle" font-size="12">flags (reserved)</text>
|
||||
|
||||
<!-- Row 1 (2–17): dest_addr -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">2–17</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">dest_addr</text>
|
||||
<text x="315" y="128" fill="#d8a0f8" text-anchor="middle" font-size="10">16 bytes — destination being forwarded to</text>
|
||||
|
||||
<!-- Row 2 (18–33): reporter -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">18–33</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">reporter</text>
|
||||
<text x="315" y="180" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — NodeAddr of reporting router</text>
|
||||
|
||||
<!-- Row 3 (34–35): mtu -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">34–35</text>
|
||||
|
||||
<rect x="55" y="192" width="260" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">mtu</text>
|
||||
<text x="185" y="232" fill="#f88aaa" text-anchor="middle" font-size="10">2 bytes LE — bottleneck MTU</text>
|
||||
|
||||
<!-- Note -->
|
||||
<text x="310" y="264" fill="#777" font-size="10" text-anchor="middle">plaintext error signal (U flag set) · total on wire: 40 bytes (4 prefix + 36 payload)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,67 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 426" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="426" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">Noise IK Message 1 — phase 0x1 (114 bytes)</text>
|
||||
|
||||
<!-- Row 0: Common prefix (bytes 0-3) -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="95" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="102" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ver</text>
|
||||
<text x="102" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="150" y="36" width="95" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="197" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">phase</text>
|
||||
<text x="197" y="78" fill="#8af8c8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="245" y="36" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="310" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="310" y="78" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="375" y="36" width="200" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="475" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">payload_len</text>
|
||||
<text x="475" y="78" fill="#d8a0f8" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 1: sender_idx (bytes 4-7) -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–7</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="112" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">sender_idx</text>
|
||||
<text x="315" y="130" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Noise IK msg1 label -->
|
||||
<text x="10" y="250" fill="#777" font-size="10" text-anchor="middle" transform="rotate(-90, 10, 250)">Noise IK msg1 (106 bytes)</text>
|
||||
<line x1="18" y1="140" x2="18" y2="348" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="140" x2="23" y2="140" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="348" x2="23" y2="348" stroke="#555" stroke-width="1"/>
|
||||
|
||||
<!-- Rows 2-3: ephemeral_pubkey (bytes 8-40, 33 bytes) -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">8–40</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="170" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ephemeral_pubkey</text>
|
||||
|
||||
<rect x="55" y="192" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="222" fill="#f8c88a" text-anchor="middle" font-size="10">33 bytes (compressed secp256k1)</text>
|
||||
|
||||
<!-- Rows 4-5: encrypted_static (bytes 41-89, 49 bytes) -->
|
||||
<text x="50" y="270" fill="#666" font-size="10" text-anchor="end">41–89</text>
|
||||
|
||||
<rect x="55" y="244" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="274" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">encrypted_static</text>
|
||||
|
||||
<rect x="55" y="296" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="326" fill="#f88aaa" text-anchor="middle" font-size="10">49 bytes (static key 33 + AEAD tag 16)</text>
|
||||
|
||||
<!-- Row 6: encrypted_epoch (bytes 90-113, 24 bytes) -->
|
||||
<text x="50" y="374" fill="#666" font-size="10" text-anchor="end">90–113</text>
|
||||
|
||||
<rect x="55" y="348" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="372" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">encrypted_epoch</text>
|
||||
<text x="315" y="390" fill="#d8a0f8" text-anchor="middle" font-size="10">24 bytes (epoch 8 + AEAD tag 16)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="418" fill="#777" font-size="10" text-anchor="middle">total: 114 bytes · payload_len = 110 · pattern: e, es, s, ss</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,65 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 374" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="374" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">Noise IK Message 2 — phase 0x2 (69 bytes)</text>
|
||||
|
||||
<!-- Row 0: Common prefix (bytes 0-3) -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="95" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="102" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ver</text>
|
||||
<text x="102" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="150" y="36" width="95" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="197" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">phase</text>
|
||||
<text x="197" y="78" fill="#8af8c8" text-anchor="middle" font-size="10">4 bits</text>
|
||||
|
||||
<rect x="245" y="36" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="310" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="310" y="78" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="375" y="36" width="200" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="475" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">payload_len</text>
|
||||
<text x="475" y="78" fill="#d8a0f8" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 1: sender_idx (bytes 4-7) -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–7</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="112" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">sender_idx</text>
|
||||
<text x="315" y="130" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 2: receiver_idx (bytes 8-11) -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">8–11</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="164" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">receiver_idx</text>
|
||||
<text x="315" y="182" fill="#8af8c8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Noise IK msg2 bracket -->
|
||||
<text x="10" y="270" fill="#777" font-size="10" text-anchor="middle" transform="rotate(-90, 10, 270)">Noise IK msg2 (57 bytes)</text>
|
||||
<line x1="18" y1="192" x2="18" y2="348" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="192" x2="23" y2="192" stroke="#555" stroke-width="1"/>
|
||||
<line x1="18" y1="348" x2="23" y2="348" stroke="#555" stroke-width="1"/>
|
||||
|
||||
<!-- Rows 3-4: ephemeral_pubkey (bytes 12-44, 33 bytes) -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">12–44</text>
|
||||
|
||||
<rect x="55" y="192" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="222" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ephemeral_pubkey</text>
|
||||
|
||||
<rect x="55" y="244" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="274" fill="#f8c88a" text-anchor="middle" font-size="10">33 bytes (compressed secp256k1)</text>
|
||||
|
||||
<!-- Row 5: encrypted_epoch (bytes 45-68, 24 bytes) -->
|
||||
<text x="50" y="322" fill="#666" font-size="10" text-anchor="end">45–68</text>
|
||||
|
||||
<rect x="55" y="296" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="320" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">encrypted_epoch</text>
|
||||
<text x="315" y="338" fill="#d8a0f8" text-anchor="middle" font-size="10">24 bytes (epoch 8 + AEAD tag 16)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="366" fill="#777" font-size="10" text-anchor="middle">total: 69 bytes · payload_len = 65 · pattern: e, ee, se</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,43 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 296" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="296" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">PathBroken (0x21) — 36 + 16n bytes payload</text>
|
||||
|
||||
<!-- Row 0 (0–1): msg_type + flags -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–1</text>
|
||||
|
||||
<rect x="55" y="36" width="260" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="185" y="76" fill="#8ab4f8" text-anchor="middle" font-size="10">0x21</text>
|
||||
|
||||
<rect x="315" y="36" width="260" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" rx="3"/>
|
||||
<text x="445" y="62" fill="#666" text-anchor="middle" font-size="12">flags (reserved)</text>
|
||||
|
||||
<!-- Row 1 (2–17): dest_addr -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">2–17</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">dest_addr</text>
|
||||
<text x="315" y="128" fill="#d8a0f8" text-anchor="middle" font-size="10">16 bytes — unreachable NodeAddr</text>
|
||||
|
||||
<!-- Row 2 (18–33): reporter -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">18–33</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">reporter</text>
|
||||
<text x="315" y="180" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — NodeAddr of reporting router</text>
|
||||
|
||||
<!-- Row 3 (34–35): last_coords_count -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">34–35</text>
|
||||
|
||||
<rect x="55" y="192" width="260" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="222" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">last_coords_count</text>
|
||||
|
||||
<!-- Row 4 (36–): last_known_coords -->
|
||||
<text x="50" y="264" fill="#666" font-size="10" text-anchor="end">36–</text>
|
||||
|
||||
<rect x="55" y="248" width="520" height="40" fill="#1f1f3a" stroke="#d9904a" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="272" fill="#f8c88a" text-anchor="middle" font-size="11">last_known_coords × n (16 bytes each — stale coords)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 114" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="114" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">PathMtuNotification (0x13) — 2 bytes body</text>
|
||||
|
||||
<!-- Row 0 (0–1): path_mtu -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–1</text>
|
||||
|
||||
<rect x="55" y="36" width="520" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">path_mtu</text>
|
||||
<text x="315" y="76" fill="#f88aaa" text-anchor="middle" font-size="10">2 bytes LE — minimum MTU observed along forward path</text>
|
||||
|
||||
<!-- Note -->
|
||||
<text x="310" y="108" fill="#777" font-size="10" text-anchor="middle">body only — carried inside FSP encrypted message (prefix + inner header + AEAD tag)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1000 B |
@@ -0,0 +1,106 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 634" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="634" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">ReceiverReport (0x02) — 68 bytes</text>
|
||||
|
||||
<!-- Row 0 (0–3): msg_type + reserved -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="130" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x02</text>
|
||||
|
||||
<rect x="185" y="36" width="390" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" rx="3"/>
|
||||
<text x="380" y="66" fill="#666" text-anchor="middle" font-size="12">reserved (3 bytes zero)</text>
|
||||
|
||||
<!-- Row 1 (4–11): highest_counter -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–11</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">highest_counter</text>
|
||||
<text x="315" y="128" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Row 2 (12–19): cumulative_packets_recv -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">12–19</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">cumulative_packets_recv</text>
|
||||
<text x="315" y="180" fill="#8af8c8" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Row 3 (20–27): cumulative_bytes_recv -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">20–27</text>
|
||||
|
||||
<rect x="55" y="192" width="520" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">cumulative_bytes_recv</text>
|
||||
<text x="315" y="232" fill="#8af8c8" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Row 4 (28–33): timestamp_echo + dwell_time -->
|
||||
<text x="50" y="270" fill="#666" font-size="10" text-anchor="end">28–33</text>
|
||||
|
||||
<rect x="55" y="244" width="260" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="266" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">timestamp_echo</text>
|
||||
<text x="185" y="284" fill="#d8a0f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<rect x="315" y="244" width="260" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="266" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">dwell_time</text>
|
||||
<text x="445" y="284" fill="#8af8f8" text-anchor="middle" font-size="10">2 bytes LE ms</text>
|
||||
|
||||
<!-- Row 5 (34–39): max_burst_loss + mean_burst_loss + reserved -->
|
||||
<text x="50" y="322" fill="#666" font-size="10" text-anchor="end">34–39</text>
|
||||
|
||||
<rect x="55" y="296" width="173" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="141" y="318" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">max_burst</text>
|
||||
<text x="141" y="336" fill="#f88aaa" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<rect x="228" y="296" width="174" height="52" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="318" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">mean_burst</text>
|
||||
<text x="315" y="336" fill="#f88aaa" text-anchor="middle" font-size="10">2 bytes u8.8</text>
|
||||
|
||||
<rect x="402" y="296" width="173" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" rx="3"/>
|
||||
<text x="488" y="322" fill="#666" text-anchor="middle" font-size="10">reserved</text>
|
||||
|
||||
<!-- Row 6 (40–47): jitter + ecn_ce_count -->
|
||||
<text x="50" y="374" fill="#666" font-size="10" text-anchor="end">40–47</text>
|
||||
|
||||
<rect x="55" y="348" width="260" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="370" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">jitter</text>
|
||||
<text x="185" y="388" fill="#f8d88a" text-anchor="middle" font-size="10">4 bytes LE µs</text>
|
||||
|
||||
<rect x="315" y="348" width="260" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="370" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ecn_ce_count</text>
|
||||
<text x="445" y="388" fill="#f8d88a" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 7 (48–55): owd_trend + burst_loss_count -->
|
||||
<text x="50" y="426" fill="#666" font-size="10" text-anchor="end">48–55</text>
|
||||
|
||||
<rect x="55" y="400" width="260" height="52" fill="#3d4a2d" stroke="#8ad94a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="422" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">owd_trend</text>
|
||||
<text x="185" y="440" fill="#c8f88a" text-anchor="middle" font-size="10">4 bytes i32 LE µs/s</text>
|
||||
|
||||
<rect x="315" y="400" width="260" height="52" fill="#3d4a2d" stroke="#8ad94a" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="422" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">burst_loss_count</text>
|
||||
<text x="445" y="440" fill="#c8f88a" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 8 (56–63): cumulative_reorder_count + interval_packets_recv -->
|
||||
<text x="50" y="478" fill="#666" font-size="10" text-anchor="end">56–63</text>
|
||||
|
||||
<rect x="55" y="452" width="260" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="474" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">reorder_count</text>
|
||||
<text x="185" y="492" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE cumulative</text>
|
||||
|
||||
<rect x="315" y="452" width="260" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="474" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">interval_pkts_recv</text>
|
||||
<text x="445" y="492" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 9 (64–67): interval_bytes_recv -->
|
||||
<text x="50" y="530" fill="#666" font-size="10" text-anchor="end">64–67</text>
|
||||
|
||||
<rect x="55" y="504" width="260" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="526" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">interval_bytes_recv</text>
|
||||
<text x="185" y="544" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="586" fill="#777" font-size="10" text-anchor="middle">total: 68 bytes</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
@@ -0,0 +1,66 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 478" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="478" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">SenderReport (0x01) — 48 bytes</text>
|
||||
|
||||
<!-- Row 0 (0–3): msg_type + reserved -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="130" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x01</text>
|
||||
|
||||
<rect x="185" y="36" width="390" height="52" fill="#1f1f3a" stroke="#444" stroke-width="1" rx="3"/>
|
||||
<text x="380" y="66" fill="#666" text-anchor="middle" font-size="12">reserved (3 bytes zero)</text>
|
||||
|
||||
<!-- Row 1 (4–11): interval_start_counter -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–11</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">interval_start_counter</text>
|
||||
<text x="315" y="128" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Row 2 (12–19): interval_end_counter -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">12–19</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">interval_end_counter</text>
|
||||
<text x="315" y="180" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Row 3 (20–27): interval_start_timestamp + interval_end_timestamp -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">20–27</text>
|
||||
|
||||
<rect x="55" y="192" width="260" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">interval_start_ts</text>
|
||||
<text x="185" y="232" fill="#d8a0f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<rect x="315" y="192" width="260" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">interval_end_ts</text>
|
||||
<text x="445" y="232" fill="#d8a0f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 4 (28–31): interval_bytes_sent -->
|
||||
<text x="50" y="270" fill="#666" font-size="10" text-anchor="end">28–31</text>
|
||||
|
||||
<rect x="55" y="244" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="266" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">interval_bytes_sent</text>
|
||||
<text x="315" y="284" fill="#8af8f8" text-anchor="middle" font-size="10">4 bytes LE</text>
|
||||
|
||||
<!-- Row 5 (32–39): cumulative_packets_sent -->
|
||||
<text x="50" y="322" fill="#666" font-size="10" text-anchor="end">32–39</text>
|
||||
|
||||
<rect x="55" y="296" width="520" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="318" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">cumulative_packets_sent</text>
|
||||
<text x="315" y="336" fill="#8af8c8" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Row 6 (40–47): cumulative_bytes_sent -->
|
||||
<text x="50" y="374" fill="#666" font-size="10" text-anchor="end">40–47</text>
|
||||
|
||||
<rect x="55" y="348" width="520" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="370" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">cumulative_bytes_sent</text>
|
||||
<text x="315" y="388" fill="#8af8c8" text-anchor="middle" font-size="10">8 bytes LE</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="422" fill="#777" font-size="10" text-anchor="middle">total: 48 bytes</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 322" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="322" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">SessionAck (phase 0x2) — Noise XK msg2</text>
|
||||
|
||||
<!-- Row 0 (0–3): FSP prefix -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="520" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">FSP common prefix</text>
|
||||
<text x="315" y="76" fill="#8ab4f8" text-anchor="middle" font-size="10">ver=0, phase=0x2, flags=0, payload_len</text>
|
||||
|
||||
<!-- Row 1 (4): flags -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="315" y="128" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte — reserved</text>
|
||||
|
||||
<!-- Row 2 (5–): src_coords (acknowledger) -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">5–</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="40" fill="#1f1f3a" stroke="#4ad9d9" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="164" fill="#8af8f8" text-anchor="middle" font-size="11">src_coords_count(2) + src_coords(16 × n) — acknowledger</text>
|
||||
|
||||
<!-- Row 3: dest_coords (initiator) -->
|
||||
<text x="50" y="206" fill="#666" font-size="10" text-anchor="end">...</text>
|
||||
|
||||
<rect x="55" y="186" width="520" height="40" fill="#1f1f3a" stroke="#b04ad9" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="210" fill="#d8a0f8" text-anchor="middle" font-size="11">dest_coords_count(2) + dest_coords(16 × m) — initiator</text>
|
||||
|
||||
<!-- Row 4: handshake_len + handshake_payload -->
|
||||
<text x="50" y="252" fill="#666" font-size="10" text-anchor="end">...</text>
|
||||
|
||||
<rect x="55" y="232" width="130" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="254" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">hs_len</text>
|
||||
<text x="120" y="272" fill="#f8d88a" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<rect x="185" y="232" width="390" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="380" y="254" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">handshake_payload</text>
|
||||
<text x="380" y="272" fill="#8af8c8" text-anchor="middle" font-size="10">Noise XK msg2 (57 bytes — ephemeral + epoch)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="308" fill="#777" font-size="10" text-anchor="middle">typical ~190 bytes (depth-dependent, carries both endpoints' coords)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 374" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="374" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">SessionDatagram (0x00) — 36 + payload bytes</text>
|
||||
|
||||
<!-- Row 0 (0–3): msg_type + ttl + path_mtu -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="130" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x00</text>
|
||||
|
||||
<rect x="185" y="36" width="130" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="250" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ttl</text>
|
||||
<text x="250" y="78" fill="#8af8c8" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="315" y="36" width="260" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">path_mtu</text>
|
||||
<text x="445" y="78" fill="#f8c88a" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 1-4 (4–19): src_addr -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–19</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">src_addr</text>
|
||||
<text x="315" y="128" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — source NodeAddr</text>
|
||||
|
||||
<!-- Row 5-8 (20–35): dest_addr -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">20–35</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">dest_addr</text>
|
||||
<text x="315" y="180" fill="#d8a0f8" text-anchor="middle" font-size="10">16 bytes — destination NodeAddr</text>
|
||||
|
||||
<!-- Row 9 (36–): payload -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">36–</text>
|
||||
|
||||
<rect x="55" y="198" width="520" height="40" fill="#1f1f3a" stroke="#444" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="222" fill="#666" text-anchor="middle" font-size="11">payload (variable — session-layer message)</text>
|
||||
|
||||
<!-- Notes -->
|
||||
<text x="310" y="262" fill="#777" font-size="10" text-anchor="middle">fixed header: 36 bytes · payload opaque to transit nodes</text>
|
||||
<text x="310" y="278" fill="#777" font-size="10" text-anchor="middle">ttl decremented and path_mtu min'd at each forwarding hop</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 244" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="244" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">SessionMsg3 (phase 0x3) — Noise XK msg3</text>
|
||||
|
||||
<!-- Row 0 (0–3): FSP prefix -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="520" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">FSP common prefix</text>
|
||||
<text x="315" y="76" fill="#8ab4f8" text-anchor="middle" font-size="10">ver=0, phase=0x3, flags=0, payload_len</text>
|
||||
|
||||
<!-- Row 1 (4–6): flags + handshake_len -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–6</text>
|
||||
|
||||
<rect x="55" y="88" width="130" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="120" y="128" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte</text>
|
||||
|
||||
<rect x="185" y="88" width="390" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="380" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">handshake_len</text>
|
||||
<text x="380" y="128" fill="#f8d88a" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<!-- Row 2 (7–): handshake_payload -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">7–</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">handshake_payload</text>
|
||||
<text x="315" y="180" fill="#8af8c8" text-anchor="middle" font-size="10">Noise XK msg3 (73 bytes — encrypted static + encrypted epoch)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="218" fill="#777" font-size="10" text-anchor="middle">~80 bytes · no coordinates (both endpoints already have them)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 322" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="322" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">SessionSetup (phase 0x1) — Noise XK msg1</text>
|
||||
|
||||
<!-- Row 0 (0–3): FSP prefix -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="520" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="58" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">FSP common prefix</text>
|
||||
<text x="315" y="76" fill="#8ab4f8" text-anchor="middle" font-size="10">ver=0, phase=0x1, flags=0, payload_len</text>
|
||||
|
||||
<!-- Row 1 (4): flags -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="110" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">flags</text>
|
||||
<text x="315" y="128" fill="#f8c88a" text-anchor="middle" font-size="10">1 byte — bit 0: REQUEST_ACK, bit 1: BIDIRECTIONAL</text>
|
||||
|
||||
<!-- Row 2 (5–): src_coords -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">5–</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="40" fill="#1f1f3a" stroke="#4ad9d9" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="164" fill="#8af8f8" text-anchor="middle" font-size="11">src_coords_count(2) + src_coords(16 × n)</text>
|
||||
|
||||
<!-- Row 3: dest_coords -->
|
||||
<text x="50" y="206" fill="#666" font-size="10" text-anchor="end">...</text>
|
||||
|
||||
<rect x="55" y="186" width="520" height="40" fill="#1f1f3a" stroke="#b04ad9" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="210" fill="#d8a0f8" text-anchor="middle" font-size="11">dest_coords_count(2) + dest_coords(16 × m)</text>
|
||||
|
||||
<!-- Row 4: handshake_len + handshake_payload -->
|
||||
<text x="50" y="252" fill="#666" font-size="10" text-anchor="end">...</text>
|
||||
|
||||
<rect x="55" y="232" width="130" height="52" fill="#4a3d2d" stroke="#d9b04a" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="254" fill="#e0e0e0" text-anchor="middle" font-size="11" font-weight="bold">hs_len</text>
|
||||
<text x="120" y="272" fill="#f8d88a" text-anchor="middle" font-size="10">2 bytes LE</text>
|
||||
|
||||
<rect x="185" y="232" width="390" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="380" y="254" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">handshake_payload</text>
|
||||
<text x="380" y="272" fill="#8af8c8" text-anchor="middle" font-size="10">Noise XK msg1 (33 bytes — ephemeral key)</text>
|
||||
|
||||
<!-- Total -->
|
||||
<text x="310" y="308" fill="#777" font-size="10" text-anchor="middle">typical ~170 bytes (depth-dependent)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,59 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 580 374" font-family="monospace" font-size="13">
|
||||
<!-- Background -->
|
||||
<rect width="580" height="374" fill="#1a1a2e" rx="4"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="310" y="26" fill="#e0e0e0" text-anchor="middle" font-size="14" font-weight="bold">TreeAnnounce (0x10) — 100 + 32n bytes</text>
|
||||
|
||||
<!-- Row 0 (0–3): msg_type(1) + version(1) + sequence starts -->
|
||||
<text x="50" y="62" fill="#666" font-size="10" text-anchor="end">0–3</text>
|
||||
|
||||
<rect x="55" y="36" width="130" height="52" fill="#2d4a7a" stroke="#4a90d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="120" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">msg_type</text>
|
||||
<text x="120" y="78" fill="#8ab4f8" text-anchor="middle" font-size="10">0x10</text>
|
||||
|
||||
<rect x="185" y="36" width="130" height="52" fill="#2d5a4a" stroke="#4ad99a" stroke-width="1.5" rx="3"/>
|
||||
<text x="250" y="60" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">version</text>
|
||||
<text x="250" y="78" fill="#8af8c8" text-anchor="middle" font-size="10">0x01</text>
|
||||
|
||||
<rect x="315" y="36" width="260" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="445" y="66" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">sequence</text>
|
||||
|
||||
<!-- Row 1 (4–9): sequence continued -->
|
||||
<text x="50" y="114" fill="#666" font-size="10" text-anchor="end">4–9</text>
|
||||
|
||||
<rect x="55" y="88" width="520" height="52" fill="#5a3d2d" stroke="#d9904a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="118" fill="#f8c88a" text-anchor="middle" font-size="10">8 bytes LE — monotonic counter</text>
|
||||
|
||||
<!-- Row 2 (10–17): timestamp -->
|
||||
<text x="50" y="166" fill="#666" font-size="10" text-anchor="end">10–17</text>
|
||||
|
||||
<rect x="55" y="140" width="520" height="52" fill="#4a2d5a" stroke="#b04ad9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="162" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">timestamp</text>
|
||||
<text x="315" y="180" fill="#d8a0f8" text-anchor="middle" font-size="10">8 bytes LE — Unix seconds</text>
|
||||
|
||||
<!-- Row 3 (18–33): parent -->
|
||||
<text x="50" y="218" fill="#666" font-size="10" text-anchor="end">18–33</text>
|
||||
|
||||
<rect x="55" y="192" width="520" height="52" fill="#2d5a5a" stroke="#4ad9d9" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="214" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">parent</text>
|
||||
<text x="315" y="232" fill="#8af8f8" text-anchor="middle" font-size="10">16 bytes — NodeAddr (self = root)</text>
|
||||
|
||||
<!-- Row 4 (34–35): ancestry_count -->
|
||||
<text x="50" y="270" fill="#666" font-size="10" text-anchor="end">34–35</text>
|
||||
|
||||
<rect x="55" y="250" width="260" height="40" fill="#5a2d3d" stroke="#d94a6a" stroke-width="1.5" rx="3"/>
|
||||
<text x="185" y="274" fill="#e0e0e0" text-anchor="middle" font-size="12" font-weight="bold">ancestry_count</text>
|
||||
|
||||
<!-- Row 5 (36–): ancestry entries -->
|
||||
<text x="50" y="316" fill="#666" font-size="10" text-anchor="end">36–</text>
|
||||
|
||||
<rect x="55" y="296" width="520" height="40" fill="#1f1f3a" stroke="#d9904a" stroke-width="1" stroke-dasharray="4,3" rx="3"/>
|
||||
<text x="315" y="320" fill="#f8c88a" text-anchor="middle" font-size="11">AncestryEntry × n (32 bytes each)</text>
|
||||
|
||||
<!-- Signature -->
|
||||
<text x="50" y="360" fill="#666" font-size="10" text-anchor="end">+64</text>
|
||||
|
||||
<rect x="55" y="342" width="520" height="26" fill="#3d4a2d" stroke="#8ad94a" stroke-width="1.5" rx="3"/>
|
||||
<text x="315" y="360" fill="#e0e0e0" text-anchor="middle" font-size="10" font-weight="bold">Schnorr signature (64 bytes)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,197 @@
|
||||
# Nostr Event Reference
|
||||
|
||||
The Nostr-protocol surface FIPS uses for discovery and signaling. For
|
||||
the design of the discovery runtime and the rationale behind these
|
||||
event shapes, see
|
||||
[../design/fips-nostr-discovery.md](../design/fips-nostr-discovery.md).
|
||||
For operator activation recipes, see
|
||||
[../how-to/enable-nostr-discovery.md](../how-to/enable-nostr-discovery.md).
|
||||
|
||||
FIPS uses three Nostr event kinds:
|
||||
|
||||
| Kind | Name | Encryption | Storage | Purpose |
|
||||
| ---- | ---- | ---------- | ------- | ------- |
|
||||
| 37195 | Overlay advert | None (signed only) | Replaceable | Publish reachable transport endpoints |
|
||||
| 21059 | Traversal signaling | NIP-44 inside NIP-59 gift wrap | Ephemeral | Carry `TraversalOffer`/`TraversalAnswer` payloads |
|
||||
| 10050 | NIP-17 inbox relay list | None (signed only) | Replaceable | Tell dialers where to publish offers |
|
||||
|
||||
All three are signed with the node's FIPS identity key (the same
|
||||
secp256k1 keypair Nostr uses); there is no separate Nostr key.
|
||||
|
||||
## Kind 37195 — Overlay Advert
|
||||
|
||||
A parameterized replaceable event in the application-defined
|
||||
replaceable range `30000–39999` (the digits visually spell `FIPS`:
|
||||
7=F, 1=I, 9=P, 5=S). Each node has a single in-place-updatable advert
|
||||
under its identity.
|
||||
|
||||
### Tags
|
||||
|
||||
- `d` — fixed to the literal `fips-overlay-v1` (the application
|
||||
identifier baked into the binary). Together with `pubkey`, this
|
||||
identifies the unique replaceable event slot.
|
||||
- `protocol` — the configured `node.discovery.nostr.app` value
|
||||
(default `fips-overlay-v1`). Distinct from the `d` tag so the
|
||||
application string can evolve without breaking the replaceable
|
||||
event slot.
|
||||
- `version` — protocol version string (currently `"1"`).
|
||||
- `expiration` — NIP-40 expiration timestamp set to now +
|
||||
`node.discovery.nostr.advert_ttl_secs` (default 3600 seconds).
|
||||
Conforming relays stop serving the event after this time.
|
||||
|
||||
### Content
|
||||
|
||||
The event content is a JSON document shaped as `OverlayAdvert`:
|
||||
|
||||
```json
|
||||
{
|
||||
"identifier": "fips-overlay-v1",
|
||||
"version": 1,
|
||||
"endpoints": [
|
||||
{"transport": "udp", "addr": "203.0.113.45:2121"},
|
||||
{"transport": "tor", "addr": "xxxxx.onion:8443"},
|
||||
{"transport": "udp", "addr": "nat"}
|
||||
],
|
||||
"signalRelays": ["wss://relay.damus.io", "wss://nos.lol"],
|
||||
"stunServers": ["stun:stun.l.google.com:19302"]
|
||||
}
|
||||
```
|
||||
|
||||
Field semantics:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `identifier` | string | Application namespace; must match the `d` tag. |
|
||||
| `version` | integer | Advert schema version (currently 1). |
|
||||
| `endpoints` | array | List of transport endpoints. Each is `{transport, addr}` where `transport` is `"udp"`, `"tcp"`, or `"tor"`, and `addr` is `"host:port"`, `".onion:port"`, or the literal `"nat"` (for UDP NAT-punch). |
|
||||
| `signalRelays` | array? | Optional. Relays the publisher prefers for offer/answer signaling. Present only when at least one endpoint is `udp:nat`. |
|
||||
| `stunServers` | array? | Optional. STUN servers the publisher uses for reflexive discovery. Present only when at least one endpoint is `udp:nat`. Informational — peers do not use these to choose their own STUN targets. |
|
||||
|
||||
### Signature scope
|
||||
|
||||
The Nostr event signature covers the standard Nostr event ID
|
||||
(serialized `[0, pubkey, created_at, kind, tags, content]`), so the
|
||||
content JSON, tags, kind, and timestamp are all bound to the signing
|
||||
identity.
|
||||
|
||||
### Replacement and deletion
|
||||
|
||||
Because kind 37195 is replaceable, publishing a new advert replaces
|
||||
the prior one in the same `(pubkey, d-tag)` slot. To withdraw an
|
||||
advert without publishing a successor, the node publishes a NIP-9
|
||||
kind 5 delete event referencing the prior advert.
|
||||
|
||||
## Kind 21059 — Traversal Signaling
|
||||
|
||||
An ephemeral event (kinds in the 20000–29999 range are not stored by
|
||||
conforming relays). Used to deliver gift-wrapped, NIP-44-encrypted
|
||||
`TraversalOffer` and `TraversalAnswer` payloads between dialer and
|
||||
responder during a UDP NAT hole-punch.
|
||||
|
||||
### Encryption envelope
|
||||
|
||||
The wire shape is the standard NIP-59 gift wrap:
|
||||
|
||||
1. **Rumor** — the unsigned `TraversalOffer`/`TraversalAnswer`
|
||||
payload (JSON), authored by the actual sender's identity.
|
||||
2. **Seal** — a kind 13 event whose content is the rumor
|
||||
NIP-44-encrypted to the recipient's pubkey, signed by the sender.
|
||||
3. **Gift wrap** — a kind 21059 event whose content is the seal
|
||||
NIP-44-encrypted to the recipient under an ephemeral key, signed
|
||||
by that ephemeral key. The outer `pubkey` of the kind 21059 event
|
||||
is the ephemeral identity, not the sender's real identity.
|
||||
|
||||
Only the intended recipient can decrypt the wrap to recover the seal,
|
||||
and only the recipient can decrypt the seal to recover the rumor.
|
||||
|
||||
### Wrapped payloads
|
||||
|
||||
The `TraversalOffer` carries:
|
||||
|
||||
- `type` — message-type tag.
|
||||
- `sessionId` — unique identifier correlating offer and answer.
|
||||
- `senderNpub` / `recipientNpub` — bech32-encoded pubkeys, repeated
|
||||
inside the encrypted payload (the outer wrap pubkey is ephemeral).
|
||||
- `issuedAt` / `expiresAt` — Unix-ms timestamps; `expiresAt` is
|
||||
`issuedAt + signal_ttl_secs * 1000`.
|
||||
- `nonce` — random per-offer value.
|
||||
- `reflexiveAddress` — `{protocol, ip, port}` observed via STUN, or
|
||||
`null` if STUN failed or returned no usable address.
|
||||
- `localAddresses` — array of `{protocol, ip, port}` private
|
||||
candidates, populated when `share_local_candidates` is enabled.
|
||||
- `stunServer` — the STUN server actually used (informational).
|
||||
|
||||
The `TraversalAnswer` echoes `sessionId` and carries:
|
||||
|
||||
- `type`, `senderNpub`, `recipientNpub`, `issuedAt`, `expiresAt`,
|
||||
`nonce` — same shape as the offer.
|
||||
- `inReplyTo` — the offer's event id.
|
||||
- `accepted` — boolean; false when the responder has no usable
|
||||
addresses.
|
||||
- `reflexiveAddress` and `localAddresses` — the responder's
|
||||
candidates, in the same shape as the offer.
|
||||
- `stunServer` — informational.
|
||||
- `punch` — a `PunchHint { startAtMs, intervalMs, durationMs }`
|
||||
telling both sides when to begin probing and how aggressively.
|
||||
Absent on rejected offers.
|
||||
- `reason` — optional rejection string when `accepted` is false.
|
||||
- `offerReceivedAt` — optional responder wall-clock (Unix ms) at
|
||||
the moment it received the offer; the initiator uses this to
|
||||
derive a clock-skew estimate.
|
||||
|
||||
### Relay selection
|
||||
|
||||
Dialer publishes offers to the recipient's NIP-17 inbox relays (kind
|
||||
10050) when available; otherwise to the local
|
||||
`node.discovery.nostr.dm_relays` list. The responder publishes the
|
||||
answer back through the same relay channel.
|
||||
|
||||
## Kind 10050 — NIP-17 Inbox Relay List
|
||||
|
||||
A standard NIP-17 event used by FIPS to advertise which relays this
|
||||
node prefers for receiving direct-message-style signaling — for FIPS,
|
||||
the gift-wrapped traversal offers (kind 21059).
|
||||
|
||||
This is **NIP-17** (`kind 10050`, inbox relays for DM delivery), not
|
||||
NIP-65 (`kind 10002`, general read/write relay list). The two serve
|
||||
different purposes:
|
||||
|
||||
- Kind 10002 (NIP-65) — general read/write relays for ordinary event
|
||||
publication and subscription.
|
||||
- Kind 10050 (NIP-17) — relays the recipient prefers for receiving
|
||||
DM-shaped (NIP-59 wrapped) events.
|
||||
|
||||
FIPS publishes its own kind 10050 on startup so dialers can discover
|
||||
where to send traversal offers. When dialing a peer, FIPS first
|
||||
fetches the peer's kind 10050 from the peer's `advert_relays`; on
|
||||
fetch failure it falls back to the local `dm_relays` list.
|
||||
|
||||
### Tags
|
||||
|
||||
Standard NIP-17 form: each relay is encoded as an `r` tag whose
|
||||
single value is the relay URL.
|
||||
|
||||
```text
|
||||
["r", "wss://relay.damus.io"]
|
||||
["r", "wss://nos.lol"]
|
||||
```
|
||||
|
||||
### Content
|
||||
|
||||
Empty per NIP-17.
|
||||
|
||||
## See also
|
||||
|
||||
- [../design/fips-nostr-discovery.md](../design/fips-nostr-discovery.md)
|
||||
— discovery runtime design and the five activation scenarios
|
||||
- [../how-to/enable-nostr-discovery.md](../how-to/enable-nostr-discovery.md)
|
||||
— operator recipes
|
||||
- [../tutorials/resolve-peers-via-nostr.md](../tutorials/resolve-peers-via-nostr.md),
|
||||
[../tutorials/advertise-your-node.md](../tutorials/advertise-your-node.md),
|
||||
[../tutorials/open-discovery.md](../tutorials/open-discovery.md)
|
||||
— hand-held tutorial walkthroughs of the three capabilities
|
||||
- [../design/port-advertisement-and-nat-traversal.md](../design/port-advertisement-and-nat-traversal.md)
|
||||
— generic protocol reference (event tags, NIP usage, on-the-wire
|
||||
offer/answer schema), with FIPS values as worked examples
|
||||
- [security.md](security.md) — how the FIPS identity key signs both
|
||||
adverts and Noise handshakes
|
||||
@@ -0,0 +1,235 @@
|
||||
# Security Reference
|
||||
|
||||
Consolidated security reference covering the nftables baseline, peer
|
||||
ACL file format, cryptographic primitives, rekey defaults, replay
|
||||
window, filesystem permissions, threat-resistance matrix, and default
|
||||
network exposures per transport. For the threat-model design and
|
||||
rationale, see [../design/fips-security.md](../design/fips-security.md).
|
||||
For the operator activation steps and drop-in recipes, see
|
||||
[../how-to/enable-mesh-firewall.md](../how-to/enable-mesh-firewall.md).
|
||||
|
||||
## nftables Baseline
|
||||
|
||||
The shipped baseline is `/etc/fips/fips.nft`. It defines a single
|
||||
nftables table `inet fips` with one chain hooked at `input`, structured
|
||||
as follows:
|
||||
|
||||
| Step | Rule | Effect |
|
||||
| ---- | ---- | ------ |
|
||||
| 1 | `iifname != "fips0" return` | Match only traffic arriving on `fips0`; everything else short-circuits. |
|
||||
| 2 | `ct state established,related accept` | Allow conntrack replies and related ICMPv6 errors. |
|
||||
| 3 | `icmpv6 type echo-request accept` | Allow IPv6 echo (ping6 reachability). |
|
||||
| 4 | `include "/etc/fips/fips.d/*.nft"` | Splice in operator drop-ins (empty matches nothing). |
|
||||
| 5 | `counter drop` | Default-deny everything else; counter increments on every drop. |
|
||||
|
||||
Outbound from `fips0` is unrestricted. The baseline is a documented
|
||||
dpkg conffile — operator edits to `/etc/fips/fips.nft` are preserved
|
||||
across upgrades.
|
||||
|
||||
The systemd unit is `fips-firewall.service` (oneshot). It is **not**
|
||||
enabled by default; activation is an explicit operator gesture
|
||||
documented in
|
||||
[../how-to/enable-mesh-firewall.md](../how-to/enable-mesh-firewall.md).
|
||||
|
||||
## Drop-In File Format
|
||||
|
||||
Operator extensions live under `/etc/fips/fips.d/` with the `.nft`
|
||||
suffix. Each file is included inline into the `inbound` chain at the
|
||||
marked point and may contain any nftables rule lines valid in that
|
||||
context.
|
||||
|
||||
Naming convention: `<purpose>-from-<source>.nft` keeps drop-ins easy
|
||||
to scan. Examples shipped in the design discussion:
|
||||
|
||||
- `ssh-from-bastion.nft` — accept TCP/22 from a single mesh-node address
|
||||
- `http-from-cluster.nft` — accept TCP/80 from a `/64` mesh-address prefix
|
||||
- `dns-public.nft` — accept UDP/53 and TCP/53 from any mesh node
|
||||
- `git-from-trusted.nft` — accept TCP/9418 from a set of mesh-node addresses
|
||||
|
||||
After editing, reload via
|
||||
`sudo systemctl reload-or-restart fips-firewall.service` (or
|
||||
equivalently `sudo nft -f /etc/fips/fips.nft` since the file is
|
||||
idempotent).
|
||||
|
||||
## Cryptographic Primitives
|
||||
|
||||
| Component | Choice | Where Used |
|
||||
| --------- | ------ | ---------- |
|
||||
| Curve | secp256k1 | FMP IK, FSP XK, Schnorr signatures |
|
||||
| Diffie-Hellman | ECDH on secp256k1 (x-only normalized) | Noise IK, Noise XK |
|
||||
| AEAD | ChaCha20-Poly1305 | FMP link encryption, FSP session encryption |
|
||||
| Hash | SHA-256 | NodeAddr derivation, Noise transcript |
|
||||
| Key derivation | HKDF-SHA256 | Noise key schedule |
|
||||
| Signatures | secp256k1 Schnorr | TreeAnnounce, LookupResponse proof, Nostr adverts |
|
||||
| Noise pattern (link) | `Noise_IK_secp256k1_ChaChaPoly_SHA256` | FMP link layer (IK with epoch payload) |
|
||||
| Noise pattern (session) | `Noise_XK_secp256k1_ChaChaPoly_SHA256` | FSP session layer (XK with epoch payload) |
|
||||
|
||||
These choices align with the Nostr cryptographic stack
|
||||
(secp256k1 + ChaCha20-Poly1305 + SHA-256) and the NIP-44 encrypted
|
||||
messaging standard.
|
||||
|
||||
## Rekey Defaults
|
||||
|
||||
Both link-layer and session-layer Noise sessions rekey under one of
|
||||
two triggers, configurable under `node.rekey.*`:
|
||||
|
||||
| Parameter | Default | Description |
|
||||
| --------- | ------- | ----------- |
|
||||
| `enabled` | `true` | Master switch. |
|
||||
| `after_secs` | `120` | Time-based rekey threshold. |
|
||||
| `after_messages` | `65536` | Message-count rekey threshold. |
|
||||
|
||||
In addition to the configurable triggers, the daemon retains the old
|
||||
session keys for a fixed **10-second drain window** after each
|
||||
cutover (compile-time constant `DRAIN_WINDOW_SECS` in
|
||||
`src/node/handlers/rekey.rs`). Rekey rotates the Noise key schedule
|
||||
and the session indices; old session keys are kept in
|
||||
`previous_session` for the drain window so in-flight packets
|
||||
encrypted under the old keys still decrypt.
|
||||
|
||||
## Replay Window
|
||||
|
||||
Both layers use explicit per-packet counters with a sliding bitmap
|
||||
window for replay protection. The bitmap is **2048 entries** at both
|
||||
layers — large enough to accommodate UDP reordering and packet loss
|
||||
without false-positive replay rejection. Counters older than the
|
||||
window are rejected. The same `ReplayWindow` and
|
||||
`decrypt_with_replay_check()` implementation is used at both the FMP
|
||||
and FSP layers.
|
||||
|
||||
## Peer ACL
|
||||
|
||||
Mesh-level ACL files at `/etc/fips/peers.allow` and
|
||||
`/etc/fips/peers.deny` give the operator allowlist/blocklist control
|
||||
over which npubs may complete the FMP Noise IK link handshake.
|
||||
|
||||
File format:
|
||||
|
||||
- One entry per line. An entry is either a bech32 `npub1...`,
|
||||
an alias defined in `/etc/fips/hosts`, or the literal `ALL`
|
||||
wildcard (case-insensitive).
|
||||
- Lines beginning with `#` are comments.
|
||||
- Blank lines are ignored.
|
||||
|
||||
Evaluation order (first match wins, default-allow on no match):
|
||||
|
||||
1. `peers.allow` — if the peer matches an entry here (or `ALL` is
|
||||
in `peers.allow`), the handshake is admitted, regardless of any
|
||||
`peers.deny` entry.
|
||||
2. `peers.deny` — if the peer matches an entry here (or `ALL` is
|
||||
in `peers.deny`), the handshake is refused.
|
||||
3. Otherwise the peer is admitted.
|
||||
|
||||
`peers.allow` is **not** an exclusive gate on its own: an unlisted
|
||||
peer falls through to step 3 and is admitted unless it appears in
|
||||
`peers.deny`. To turn `peers.allow` into a strict allowlist, place
|
||||
`ALL` in `peers.deny` so every unlisted peer is rejected at step 2.
|
||||
|
||||
The `ALL` wildcard makes the operator's posture explicit:
|
||||
|
||||
- `ALL` in `peers.allow` admits every peer (same effect as the
|
||||
default-allow behavior, but documented in the file).
|
||||
- `ALL` in `peers.deny` blocks every peer except those listed in
|
||||
`peers.allow` — the "allowlist-strict" posture.
|
||||
|
||||
In practice this collapses to a few common postures:
|
||||
|
||||
- **Default-allow with denylist**: leave `peers.allow` empty;
|
||||
populate `peers.deny`. All npubs may peer except those listed.
|
||||
- **Allowlist-strict**: populate `peers.allow` and put `ALL`
|
||||
in `peers.deny`. Only the listed npubs may peer; everyone else
|
||||
is rejected at step 2.
|
||||
|
||||
A populated `peers.allow` with an empty `peers.deny` is not a
|
||||
strict allowlist — it is equivalent to default-allow plus an
|
||||
explicit "always-admit" set. The strict variant requires `ALL`
|
||||
in `peers.deny`.
|
||||
|
||||
Aliases are resolved through `/etc/fips/hosts` at file-load
|
||||
time. If `peers.allow` lists `core-vm` and `/etc/fips/hosts`
|
||||
maps `core-vm` to a specific npub, that npub is admitted. If
|
||||
`core-vm` is later remapped to a different npub, the ACL
|
||||
re-resolves on the next mtime change. Operators should be aware
|
||||
that ACL semantics follow the `hosts`-file aliasing, not just
|
||||
the literal npubs visible in the file.
|
||||
|
||||
Both files are reloaded automatically when their mtime changes
|
||||
— no daemon restart or signal is needed. ACL evaluation runs
|
||||
after msg1 decryption but before any further peer-state
|
||||
mutation; rate-limited msg1s never reach the ACL.
|
||||
|
||||
## Filesystem Permissions
|
||||
|
||||
| Path | Owner | Mode | Purpose |
|
||||
| ---- | ----- | ---- | ------- |
|
||||
| `/etc/fips/fips.key` | root:root | `0600` | Persistent identity private key (sensitive). |
|
||||
| `/etc/fips/fips.pub` | root:root | `0644` | Public key (npub). |
|
||||
| `/etc/fips/fips.yaml` | root:root | `0644` | Daemon configuration (dpkg conffile). |
|
||||
| `/etc/fips/fips.nft` | root:root | `0644` | nftables baseline (dpkg conffile). |
|
||||
| `/etc/fips/fips.d/` | root:root | `0755` | Operator drop-in directory. |
|
||||
| `/etc/fips/hosts` | root:root | `0644` | Optional hostname → npub map (dpkg conffile). |
|
||||
| `/etc/fips/peers.allow` | root:root | `0644` | Optional peer allowlist. |
|
||||
| `/etc/fips/peers.deny` | root:root | `0644` | Optional peer denylist. |
|
||||
| `/run/fips/control.sock` | root:fips | `0770` | Control socket (members of `fips` group can use `fipsctl`). |
|
||||
| `/run/fips/` | root:fips | `0750` | Control socket parent directory. |
|
||||
|
||||
Adding a user to the `fips` group grants `fipsctl` access without
|
||||
requiring root. The daemon `chown`s the control socket and its parent
|
||||
directory at bind time.
|
||||
|
||||
## Threat-Resistance Matrix
|
||||
|
||||
The link layer's threat-resistance matrix is consolidated here from
|
||||
the FMP design document:
|
||||
|
||||
| Threat | Mitigation |
|
||||
| ------ | ---------- |
|
||||
| Connection exhaustion | Token-bucket rate limit + connection count limit |
|
||||
| CPU exhaustion (msg1 flood) | Rate limit before crypto operations |
|
||||
| Replay attacks | Counter-based nonces with sliding window (2048 entries) |
|
||||
| State confusion | Strict handshake state machine validation |
|
||||
| Spoofed encrypted packets | Index lookup + AEAD verification |
|
||||
| Spoofed msg2 | Index lookup + Noise ephemeral key binding |
|
||||
| Address spoofing | Cryptographic authority, not address-based |
|
||||
| Session correlation | Index rotation on rekey |
|
||||
| Inbound exposure on `fips0` | Default-deny nftables baseline (operator opt-in) |
|
||||
| Sybil identities | Discretionary peering + handshake rate limiting + optional peer ACL |
|
||||
| Eclipse attack | Diverse peering across independent operators and transports |
|
||||
| Unauthorized peer admission | Optional `peers.allow` allowlist consulted before handshake |
|
||||
|
||||
See [../design/fips-mesh-layer.md](../design/fips-mesh-layer.md) for
|
||||
the unauthenticated-attack-surface analysis (only handshake msg1 is
|
||||
reachable by unauthenticated parties), and
|
||||
[../design/fips-mesh-operation.md](../design/fips-mesh-operation.md#privacy-considerations)
|
||||
for the metadata-privacy model and the rejection of onion routing.
|
||||
|
||||
## Default Network Exposures by Transport
|
||||
|
||||
| Transport | Default Inbound | Default Bind | Opt-in |
|
||||
| --------- | --------------- | ------------ | ------ |
|
||||
| UDP | None until `bind_addr` set | `0.0.0.0:2121` typical | Operator sets `transports.udp.bind_addr` |
|
||||
| TCP | None until `bind_addr` set | None — outbound-only without bind | Operator sets `transports.tcp.bind_addr` |
|
||||
| Ethernet | Listens on configured interface (raw `AF_PACKET`) | EtherType 0x2121 on selected interface | Per-flag `discovery`, `announce`, `auto_connect`, `accept_connections` |
|
||||
| Tor | None until `directory_service` configured | `127.0.0.1:8443` (loopback only) | Operator sets `transports.tor.directory_service` and configures `HiddenServiceDir` in `torrc` |
|
||||
| BLE | Off by default | n/a | Operator enables `transports.ble.*` |
|
||||
| Nostr discovery | Off by default | n/a (relay client, not a listener) | Operator sets `node.discovery.nostr.enabled: true` |
|
||||
|
||||
The mesh-layer `fips0` interface is reachable from any mesh node that
|
||||
can route to you, not only direct peers — your direct peers forward
|
||||
traffic from any reachable mesh node onto your `fips0`. The
|
||||
default-deny nftables baseline (operator opt-in) is the recommended
|
||||
way to restrict inbound traffic on `fips0`. See
|
||||
[../how-to/enable-mesh-firewall.md](../how-to/enable-mesh-firewall.md).
|
||||
|
||||
## See also
|
||||
|
||||
- [../design/fips-security.md](../design/fips-security.md) — threat
|
||||
model and design rationale for the `fips0` baseline
|
||||
- [../design/fips-mesh-layer.md](../design/fips-mesh-layer.md) — FMP
|
||||
link encryption, replay protection, rate limiting
|
||||
- [../design/fips-session-layer.md](../design/fips-session-layer.md)
|
||||
— FSP end-to-end encryption, Noise XK, replay window
|
||||
- [../how-to/enable-mesh-firewall.md](../how-to/enable-mesh-firewall.md)
|
||||
— operator activation and drop-in recipes
|
||||
- [configuration.md](configuration.md) — full `node.rekey.*`,
|
||||
`node.rate_limit.*` parameter tables
|
||||
@@ -0,0 +1,92 @@
|
||||
# Transport Statistics Reference
|
||||
|
||||
Per-transport statistics counter inventories. Counters are exposed
|
||||
through the daemon control socket (`fipsctl show transports`) and the
|
||||
`fipstop` operator UI. For the transport-layer design (services
|
||||
provided to FMP, transport categories, the trait surface, connection
|
||||
model), see
|
||||
[../design/fips-transport-layer.md](../design/fips-transport-layer.md).
|
||||
|
||||
All transports report counters via `fipsctl show transports`; the
|
||||
tables below are source-extracted from each transport's `stats.rs`
|
||||
module.
|
||||
|
||||
## UDP
|
||||
|
||||
| Counter | Description |
|
||||
| ------- | ----------- |
|
||||
| `packets_sent` / `bytes_sent` | Successful sends |
|
||||
| `packets_recv` / `bytes_recv` | Successful receives |
|
||||
| `send_errors` / `recv_errors` | Send/receive failures |
|
||||
| `mtu_exceeded` | Packets rejected for MTU violation |
|
||||
| `kernel_drops` | Kernel `SO_RXQ_OVFL` drop count (feeds ECN congestion detection) |
|
||||
|
||||
## TCP
|
||||
|
||||
| Counter | Description |
|
||||
| ------- | ----------- |
|
||||
| `packets_sent` / `bytes_sent` | Successful sends |
|
||||
| `packets_recv` / `bytes_recv` | Successful receives |
|
||||
| `send_errors` / `recv_errors` | Send/receive failures |
|
||||
| `mtu_exceeded` | Packets rejected for MTU violation |
|
||||
| `connections_established` | Successful outbound connections |
|
||||
| `connections_accepted` | Accepted inbound connections |
|
||||
| `connections_rejected` | Rejected inbound connections (limit exceeded) |
|
||||
| `connect_timeouts` | Connection timeout count |
|
||||
| `connect_refused` | Connection refused count |
|
||||
|
||||
## Ethernet
|
||||
|
||||
| Counter | Description |
|
||||
| ------- | ----------- |
|
||||
| `frames_sent` / `frames_recv` | Successful frame send/receive |
|
||||
| `bytes_sent` / `bytes_recv` | Byte counters |
|
||||
| `send_errors` / `recv_errors` | Send/receive failures |
|
||||
| `beacons_sent` / `beacons_recv` | Peer-discovery beacon traffic |
|
||||
| `frames_too_short` | Frames below minimum length, dropped |
|
||||
| `frames_too_long` | Frames above transport MTU, dropped |
|
||||
|
||||
## Tor
|
||||
|
||||
| Counter | Description |
|
||||
| ------- | ----------- |
|
||||
| `packets_sent` / `bytes_sent` | Successful sends |
|
||||
| `packets_recv` / `bytes_recv` | Successful receives |
|
||||
| `send_errors` / `recv_errors` | Send/receive failures |
|
||||
| `connections_established` | Successful SOCKS5 connections |
|
||||
| `connect_timeouts` | Connection timeout count |
|
||||
| `connect_refused` | Connection refused count |
|
||||
| `socks5_errors` | SOCKS5 protocol errors |
|
||||
| `mtu_exceeded` | Packets rejected for MTU violation |
|
||||
| `connections_accepted` | Accepted inbound connections via onion service |
|
||||
| `connections_rejected` | Rejected inbound connections (limit exceeded) |
|
||||
| `control_errors` | Tor control port errors |
|
||||
|
||||
## Bluetooth
|
||||
|
||||
| Counter | Description |
|
||||
| ------- | ----------- |
|
||||
| `packets_sent` / `bytes_sent` | Successful L2CAP CoC sends |
|
||||
| `packets_recv` / `bytes_recv` | Successful L2CAP CoC receives |
|
||||
| `send_errors` / `recv_errors` | Send/receive failures |
|
||||
| `mtu_exceeded` | Packets rejected for MTU violation |
|
||||
| `connections_established` | Successful outbound L2CAP connections |
|
||||
| `connections_accepted` | Accepted inbound L2CAP connections |
|
||||
| `connections_rejected` | Rejected inbound (limit exceeded) |
|
||||
| `connect_timeouts` | Connection timeout count |
|
||||
| `pool_evictions` | Connection-pool entries evicted |
|
||||
| `advertisements_sent` | BLE advertisements emitted |
|
||||
| `scan_results` | BLE scan results observed |
|
||||
|
||||
## See also
|
||||
|
||||
- [../design/fips-transport-layer.md](../design/fips-transport-layer.md)
|
||||
— transport-layer design, trait surface, per-transport sections
|
||||
- [configuration.md](configuration.md) — `transports.*` configuration
|
||||
blocks
|
||||
- [../how-to/tune-udp-buffers.md](../how-to/tune-udp-buffers.md) —
|
||||
host-side `net.core.rmem_max` / `net.core.wmem_max` setup for UDP
|
||||
- [../how-to/deploy-tor-onion.md](../how-to/deploy-tor-onion.md) —
|
||||
Tor `directory` mode operator setup
|
||||
- [../how-to/set-up-bluetooth-peer.md](../how-to/set-up-bluetooth-peer.md)
|
||||
— Linux BLE peer config
|
||||
@@ -0,0 +1,938 @@
|
||||
# FIPS Wire Formats
|
||||
|
||||
This document is the comprehensive wire format reference for all three
|
||||
protocol layers. It covers transport framing, link-layer message formats,
|
||||
and session-layer message formats, with an encapsulation walkthrough showing
|
||||
how application data is wrapped through each layer.
|
||||
|
||||
## FMP Message Type Catalog
|
||||
|
||||
The FMP link layer defines the following message types, dispatched by the
|
||||
`msg_type` byte in the encrypted inner header:
|
||||
|
||||
| Type | Name | Forwarding |
|
||||
| ---- | ---- | ---------- |
|
||||
| 0x00 | SessionDatagram | Routed hop-by-hop toward the destination |
|
||||
| 0x01 | SenderReport | Peer-to-peer (MMP, link-layer instance) |
|
||||
| 0x02 | ReceiverReport | Peer-to-peer (MMP, link-layer instance) |
|
||||
| 0x10 | TreeAnnounce | Peer-to-peer (spanning-tree gossip) |
|
||||
| 0x20 | FilterAnnounce | Peer-to-peer (bloom-filter gossip) |
|
||||
| 0x30 | LookupRequest | Forwarded — bloom-guided through tree peers |
|
||||
| 0x31 | LookupResponse | Forwarded — reverse-path via `recent_requests` |
|
||||
| 0x50 | Disconnect | Peer-to-peer (orderly link teardown) |
|
||||
| 0x51 | Heartbeat | Peer-to-peer (link liveness) |
|
||||
|
||||
Handshake messages travel before encryption is established and are identified
|
||||
by the FMP common-prefix `phase` field rather than a `msg_type` byte
|
||||
(phase 0x1 = Noise IK msg1, phase 0x2 = Noise IK msg2).
|
||||
|
||||
## Packet Type Summary
|
||||
|
||||
A higher-level summary that includes typical sizes and forwarding category:
|
||||
|
||||
| Message | Typical Size | When | Forwarded? |
|
||||
| ------- | ------------ | ---- | ---------- |
|
||||
| TreeAnnounce | Variable (depth-dependent) | Topology changes | No (peer-to-peer) |
|
||||
| FilterAnnounce | ~1 KB | Topology changes | No (peer-to-peer) |
|
||||
| LookupRequest | ~300 bytes | First contact, recovery | Yes (bloom-guided tree) |
|
||||
| LookupResponse | ~400 bytes | Response to discovery | Yes (reverse-path) |
|
||||
| SessionDatagram + SessionSetup | ~232–402 bytes | Session establishment | Yes (routed) |
|
||||
| SessionDatagram + SessionAck | ~170 bytes | Session confirmation | Yes (routed) |
|
||||
| SessionDatagram + Data (minimal) | 77 bytes + IPv6 payload | Bulk IPv6 traffic (compressed) | Yes (routed) |
|
||||
| SessionDatagram + Data (with CP) | 77 + coords + IPv6 payload | Warmup/recovery (compressed) | Yes (routed) |
|
||||
| SessionDatagram + CoordsRequired | 70 bytes | Cache miss error | Yes (routed) |
|
||||
| SessionDatagram + PathBroken | 70+ bytes | Dead-end error | Yes (routed) |
|
||||
| Disconnect | 2 bytes | Link teardown | No (peer-to-peer) |
|
||||
|
||||
## Encoding Rules
|
||||
|
||||
- All multi-byte integers are **little-endian** (LE)
|
||||
- NodeAddr is **16 bytes** — truncated SHA-256 hash of public key
|
||||
- Signatures are **64 bytes** — secp256k1 Schnorr
|
||||
- Variable-length arrays use a **2-byte u16 LE count prefix** followed by
|
||||
that many items
|
||||
- Public keys are **33 bytes** — compressed secp256k1 (02/03 prefix + 32
|
||||
bytes)
|
||||
|
||||
## Transport Framing
|
||||
|
||||
Datagram-oriented transports (UDP, raw Ethernet, radio) preserve natural
|
||||
packet boundaries and require no additional framing. Stream-oriented
|
||||
transports (TCP, Tor) must delineate FIPS packets within the byte
|
||||
stream; the common prefix `payload_len` field provides this framing
|
||||
directly. TCP and Tor share a common stream reader (`tcp/stream.rs`)
|
||||
that implements this framing.
|
||||
|
||||
**Ethernet data frame header.** The Ethernet transport prepends a 3-byte
|
||||
header before the FMP payload on data frames: a 1-byte frame type
|
||||
(`0x00`) followed by a 2-byte little-endian payload length. The length
|
||||
field allows the receiver to trim Ethernet minimum-frame padding that
|
||||
would otherwise corrupt AEAD verification. Beacon frames (`0x01`) have
|
||||
no length field (fixed 34-byte payload). These bytes are consumed by the
|
||||
transport layer and are not visible to FMP. The effective MTU for FMP is
|
||||
the interface MTU minus three bytes (typically 1497).
|
||||
|
||||
## Link-Layer Formats
|
||||
|
||||
All FMP packets begin with a **4-byte common prefix** that identifies the
|
||||
protocol version, session lifecycle phase, per-packet flags, and payload
|
||||
length.
|
||||
|
||||
### Common Prefix (4 bytes)
|
||||
|
||||

|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| version | 4 bits (high) | Protocol version. Currently 0x0 |
|
||||
| phase | 4 bits (low) | Session lifecycle phase (see table) |
|
||||
| flags | 1 byte | Per-packet signal flags (zero during handshake) |
|
||||
| payload_len | 2 bytes LE | Length of payload after phase-specific header, excluding AEAD tag |
|
||||
|
||||
### Phase Table
|
||||
|
||||
| Phase | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| 0x0 | Established frame | Post-handshake encrypted traffic |
|
||||
| 0x1 | Noise IK msg1 | Handshake initiation |
|
||||
| 0x2 | Noise IK msg2 | Handshake response |
|
||||
|
||||
### Flags (Established Phase Only)
|
||||
|
||||
| Bit | Name | Description |
|
||||
| --- | ---- | ----------- |
|
||||
| 0 | K (key epoch) | Selects active key during rekeying |
|
||||
| 1 | CE | Congestion Experienced echo |
|
||||
| 2 | SP (spin bit) | RTT measurement |
|
||||
| 3-7 | — | Reserved (must be zero) |
|
||||
|
||||
Flags must be zero in handshake packets (phase 0x1 and 0x2).
|
||||
|
||||
### Established Frame (phase 0x0)
|
||||
|
||||
All post-handshake traffic between authenticated peers. Contains one
|
||||
encrypted link-layer message.
|
||||
|
||||
**Outer header** (16 bytes, used as AEAD AAD):
|
||||
|
||||

|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| common prefix | 4 bytes | ver=0, phase=0, flags, payload_len |
|
||||
| receiver_idx | 4 bytes LE | Session index for O(1) lookup |
|
||||
| counter | 8 bytes LE | Monotonic nonce, used as AEAD nonce and for replay detection |
|
||||
|
||||
The entire 16-byte header is authenticated as Associated Data (AAD) in the
|
||||
ChaCha20-Poly1305 AEAD construction.
|
||||
|
||||
**Encrypted inner header** (5 bytes, first bytes of plaintext):
|
||||
|
||||

|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| timestamp | 4 bytes LE | Session-relative milliseconds (u32) |
|
||||
| msg_type | 1 byte | Link-layer message type |
|
||||
|
||||
After decryption, the plaintext begins with the 4-byte timestamp followed by
|
||||
the 1-byte message type and message-specific fields.
|
||||
|
||||
**Complete encrypted frame**:
|
||||
|
||||

|
||||
|
||||
### Message Type Table
|
||||
|
||||
| Type | Message | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| 0x00 | SessionDatagram | Encapsulated session-layer payload for forwarding |
|
||||
| 0x01 | SenderReport | MMP sender-side metrics report (48 bytes) |
|
||||
| 0x02 | ReceiverReport | MMP receiver-side metrics report (68 bytes) |
|
||||
| 0x10 | TreeAnnounce | Spanning tree state announcement |
|
||||
| 0x20 | FilterAnnounce | Bloom filter reachability update |
|
||||
| 0x30 | LookupRequest | Coordinate discovery request |
|
||||
| 0x31 | LookupResponse | Coordinate discovery response |
|
||||
| 0x50 | Disconnect | Orderly link teardown |
|
||||
| 0x51 | Heartbeat | Link liveness probe |
|
||||
|
||||
### Noise IK Message 1 (phase 0x1)
|
||||
|
||||
Handshake initiation from connecting party.
|
||||
|
||||

|
||||
|
||||
Common prefix: ver=0, phase=0x1, flags=0, payload_len=110 (4 + 106).
|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| common prefix | 4 bytes | ver=0, phase=1, flags=0, payload_len |
|
||||
| sender_idx | 4 bytes LE | Initiator's session index (becomes receiver's `receiver_idx`) |
|
||||
| noise_msg1 | 106 bytes | Noise IK first message |
|
||||
|
||||
**Noise msg1 breakdown** (106 bytes):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | ephemeral_pubkey | 33 bytes | Initiator's ephemeral key (compressed secp256k1) |
|
||||
| 33 | encrypted_static | 49 bytes | Initiator's static key (33) + AEAD tag (16) |
|
||||
| 82 | encrypted_epoch | 24 bytes | Startup epoch (8) + AEAD tag (16) |
|
||||
|
||||
Noise pattern: `-> e, es, s, ss` with epoch payload
|
||||
|
||||
### Noise IK Message 2 (phase 0x2)
|
||||
|
||||
Handshake response from responder.
|
||||
|
||||

|
||||
|
||||
Common prefix: ver=0, phase=0x2, flags=0, payload_len=65 (4 + 4 + 57).
|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| common prefix | 4 bytes | ver=0, phase=2, flags=0, payload_len |
|
||||
| sender_idx | 4 bytes LE | Responder's session index |
|
||||
| receiver_idx | 4 bytes LE | Echo of initiator's sender_idx from msg1 |
|
||||
| noise_msg2 | 57 bytes | Noise IK second message |
|
||||
|
||||
**Noise msg2 breakdown** (57 bytes):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | ephemeral_pubkey | 33 bytes | Responder's ephemeral key (compressed secp256k1) |
|
||||
| 33 | encrypted_epoch | 24 bytes | Startup epoch (8) + AEAD tag (16) |
|
||||
|
||||
Noise pattern: `<- e, ee, se` with epoch payload
|
||||
|
||||
After msg2, both parties derive identical symmetric session keys. The
|
||||
encrypted epoch in msg1 and msg2 enables peer restart detection — if a
|
||||
peer's epoch changes, the other side knows it restarted and must
|
||||
re-establish the link.
|
||||
|
||||
### Index Semantics
|
||||
|
||||
Each party in a link session maintains two indices:
|
||||
|
||||
| Index | Chosen By | Used By | Purpose |
|
||||
| ----- | --------- | ------- | ------- |
|
||||
| our_index | Us | Them | They include this as `receiver_idx` in packets to us |
|
||||
| their_index | Them | Us | We include this as `receiver_idx` in packets to them |
|
||||
|
||||
### Handshake Flow
|
||||
|
||||

|
||||
|
||||
## Link-Layer Message Types
|
||||
|
||||
These messages are carried as plaintext inside encrypted frames (phase 0x0).
|
||||
After decryption of the AEAD ciphertext, the plaintext begins with a 4-byte
|
||||
session-relative timestamp followed by the 1-byte message type and
|
||||
message-specific fields.
|
||||
|
||||
### TreeAnnounce (0x10)
|
||||
|
||||
Spanning tree state announcement, exchanged between direct peers only.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x10 |
|
||||
| 1 | version | 1 byte | 0x01 (v1) |
|
||||
| 2 | sequence | 8 bytes LE | Monotonic counter, increments on parent change |
|
||||
| 10 | timestamp | 8 bytes LE | Unix seconds |
|
||||
| 18 | parent | 16 bytes | NodeAddr of selected parent (self = root) |
|
||||
| 34 | ancestry_count | 2 bytes LE | Number of AncestryEntry records |
|
||||
| 36 | ancestry | 32 x n bytes | AncestryEntry array (self -> root) |
|
||||
| 36 + 32n | signature | 64 bytes | Schnorr signature over entire message |
|
||||
|
||||
**AncestryEntry** (32 bytes):
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | node_addr | 16 bytes | Node's routing identifier |
|
||||
| 16 | sequence | 8 bytes LE | Node's sequence number |
|
||||
| 24 | timestamp | 8 bytes LE | Node's Unix timestamp |
|
||||
|
||||
**Size**: `100 + (n x 32)` bytes, where n = `ancestry_count` (depth + 1,
|
||||
includes self)
|
||||
|
||||
| Tree Depth | Payload | With Link Overhead |
|
||||
| ---------- | ------- | ------------------ |
|
||||
| 0 (root) | 132 bytes | 169 bytes |
|
||||
| 3 | 228 bytes | 265 bytes |
|
||||
| 5 | 292 bytes | 329 bytes |
|
||||
| 10 | 452 bytes | 489 bytes |
|
||||
|
||||
### FilterAnnounce (0x20)
|
||||
|
||||
Bloom filter reachability update, exchanged between direct peers only.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x20 |
|
||||
| 1 | sequence | 8 bytes LE | Monotonic counter for freshness |
|
||||
| 9 | hash_count | 1 byte | Number of hash functions (5 in v1) |
|
||||
| 10 | size_class | 1 byte | Filter size: `512 << size_class` bytes |
|
||||
| 11 | filter_bits | variable | Bloom filter bit array |
|
||||
|
||||
**Size class table**:
|
||||
|
||||
| size_class | Bytes | Bits | Status |
|
||||
| ---------- | ----- | ---- | ------ |
|
||||
| 0 | 512 | 4,096 | Reserved |
|
||||
| 1 | 1,024 | 8,192 | **v1 (MUST use)** |
|
||||
| 2 | 2,048 | 16,384 | Reserved |
|
||||
| 3 | 4,096 | 32,768 | Reserved |
|
||||
|
||||
**v1 payload**: 1,035 bytes (11 header + 1,024 filter).
|
||||
With link overhead: 1,072 bytes.
|
||||
|
||||
### LookupRequest (0x30)
|
||||
|
||||
Coordinate discovery request, routed through the spanning tree via
|
||||
bloom-filter-guided forwarding. Each transit node forwards only to tree
|
||||
peers (parent + children) whose bloom filter contains the target.
|
||||
Request_id dedup in recent_requests handles edge cases from tree
|
||||
restructuring.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x30 |
|
||||
| 1 | request_id | 8 bytes LE | Unique random identifier |
|
||||
| 9 | target | 16 bytes | NodeAddr being sought |
|
||||
| 25 | origin | 16 bytes | Requester's NodeAddr |
|
||||
| 41 | ttl | 1 byte | Remaining hops (default 64) |
|
||||
| 42 | min_mtu | 2 bytes LE | Minimum transport MTU the origin requires (0 = no requirement) |
|
||||
| 44 | origin_coords_cnt | 2 bytes LE | Number of coordinate entries |
|
||||
| 46 | origin_coords | 16 x n bytes | Requester's ancestry (NodeAddr only) |
|
||||
|
||||
**Size**: `46 + (n x 16)` bytes, where n = origin depth + 1
|
||||
|
||||
| Origin Depth | Payload |
|
||||
| ------------ | ------- |
|
||||
| 3 | 110 bytes |
|
||||
| 5 | 142 bytes |
|
||||
| 10 | 222 bytes |
|
||||
|
||||
### LookupResponse (0x31)
|
||||
|
||||
Coordinate discovery response, reverse-path routed back to the
|
||||
requester via the transit nodes that forwarded the request.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x31 |
|
||||
| 1 | request_id | 8 bytes LE | Echoes the request's ID |
|
||||
| 9 | target | 16 bytes | NodeAddr that was found |
|
||||
| 25 | path_mtu | 2 bytes LE | Minimum MTU along response path (transit-annotated) |
|
||||
| 27 | target_coords_cnt | 2 bytes LE | Number of coordinate entries |
|
||||
| 29 | target_coords | 16 x n bytes | Target's ancestry (NodeAddr only) |
|
||||
| 29 + 16n | proof | 64 bytes | Schnorr signature over `(request_id \|\| target \|\| target_coords)` |
|
||||
|
||||
**Size**: `93 + (n x 16)` bytes
|
||||
|
||||
| Target Depth | Payload |
|
||||
| ------------ | ------- |
|
||||
| 3 | 141 bytes |
|
||||
| 5 | 173 bytes |
|
||||
| 10 | 253 bytes |
|
||||
|
||||
The `path_mtu` field is initialized to `u16::MAX` by the target and each
|
||||
transit hop applies `min(path_mtu, outgoing_link_mtu)`, giving the
|
||||
originator an MTU estimate for the discovered path.
|
||||
|
||||
**Proof coverage**: Signs `(request_id || target || target_coords)` —
|
||||
`path_mtu` is excluded from the proof because it is a transit annotation
|
||||
modified at each hop. Coordinates are included because proof verification
|
||||
at the source confirms the target actually holds the claimed tree position.
|
||||
The source verifies the proof upon receipt.
|
||||
|
||||
### SessionDatagram (0x00)
|
||||
|
||||
Encapsulated session-layer payload for multi-hop forwarding.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x00 |
|
||||
| 1 | ttl | 1 byte | Remaining hops, decremented each hop |
|
||||
| 2 | path_mtu | 2 bytes LE | Path MTU, min'd at each forwarding hop |
|
||||
| 4 | src_addr | 16 bytes | Source NodeAddr |
|
||||
| 20 | dest_addr | 16 bytes | Destination NodeAddr |
|
||||
| 36 | payload | variable | Session-layer message |
|
||||
|
||||
**Fixed header**: 36 bytes (`SESSION_DATAGRAM_HEADER_SIZE`)
|
||||
|
||||
The `path_mtu` field is initialized to `u16::MAX` by the sender and each
|
||||
forwarding hop applies `min(path_mtu, outgoing_link_mtu)`, giving the
|
||||
receiver an estimate of the minimum MTU along the path.
|
||||
|
||||
The payload is opaque to transit nodes — session-layer encrypted
|
||||
independently of link encryption.
|
||||
|
||||
### Disconnect (0x50)
|
||||
|
||||
Orderly link teardown with reason code.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x50 |
|
||||
| 1 | reason | 1 byte | Disconnect reason code |
|
||||
|
||||
**Reason codes**:
|
||||
|
||||
| Code | Name | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| 0x00 | Shutdown | Normal operator-requested stop |
|
||||
| 0x01 | Restart | Restarting, may reconnect soon |
|
||||
| 0x02 | ProtocolError | Protocol error encountered |
|
||||
| 0x03 | TransportFailure | Transport failure |
|
||||
| 0x04 | ResourceExhaustion | Memory or connection limit |
|
||||
| 0x05 | SecurityViolation | Authentication or policy violation |
|
||||
| 0x06 | ConfigurationChange | Peer removed from configuration |
|
||||
| 0x07 | Timeout | Heartbeat liveness timeout |
|
||||
| 0xFF | Other | Unspecified reason |
|
||||
|
||||
### SenderReport (0x01)
|
||||
|
||||
Sent by the frame sender to provide interval-based transmission statistics.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Encoding |
|
||||
| ------ | ----- | ---- | -------- |
|
||||
| 0 | msg_type | 1 | `0x01` |
|
||||
| 1 | reserved | 3 | Zero |
|
||||
| 4 | interval_start_counter | 8 | u64 LE — first counter in this interval |
|
||||
| 12 | interval_end_counter | 8 | u64 LE — last counter in this interval |
|
||||
| 20 | interval_start_timestamp | 4 | u32 LE — timestamp at interval start |
|
||||
| 24 | interval_end_timestamp | 4 | u32 LE — timestamp at interval end |
|
||||
| 28 | interval_bytes_sent | 4 | u32 LE — payload bytes sent in interval |
|
||||
| 32 | cumulative_packets_sent | 8 | u64 LE — total packets sent on this link |
|
||||
| 40 | cumulative_bytes_sent | 8 | u64 LE — total bytes sent on this link |
|
||||
|
||||
**Total: 48 bytes.**
|
||||
|
||||
### ReceiverReport (0x02)
|
||||
|
||||
Sent by the frame receiver to provide loss, jitter, and timing feedback.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Encoding |
|
||||
| ------ | ----- | ---- | -------- |
|
||||
| 0 | msg_type | 1 | `0x02` |
|
||||
| 1 | reserved | 3 | Zero |
|
||||
| 4 | highest_counter | 8 | u64 LE — highest counter value received |
|
||||
| 12 | cumulative_packets_recv | 8 | u64 LE — total packets received |
|
||||
| 20 | cumulative_bytes_recv | 8 | u64 LE — total bytes received |
|
||||
| 28 | timestamp_echo | 4 | u32 LE — echoed sender timestamp for RTT |
|
||||
| 32 | dwell_time | 2 | u16 LE — time between receive and echo (ms) |
|
||||
| 34 | max_burst_loss | 2 | u16 LE — largest loss burst in interval |
|
||||
| 36 | mean_burst_loss | 2 | u16 LE — mean burst length (u8.8 fixed-point) |
|
||||
| 38 | reserved | 2 | Zero |
|
||||
| 40 | jitter | 4 | u32 LE — interarrival jitter (microseconds) |
|
||||
| 44 | ecn_ce_count | 4 | u32 LE — cumulative ECN-CE marked packets |
|
||||
| 48 | owd_trend | 4 | i32 LE — one-way delay trend (µs/s, signed) |
|
||||
| 52 | burst_loss_count | 4 | u32 LE — number of loss bursts in interval |
|
||||
| 56 | cumulative_reorder_count | 4 | u32 LE — total reordered packets |
|
||||
| 60 | interval_packets_recv | 4 | u32 LE — packets received in interval |
|
||||
| 64 | interval_bytes_recv | 4 | u32 LE — bytes received in interval |
|
||||
|
||||
**Total: 68 bytes.**
|
||||
|
||||
## Session-Layer Message Formats
|
||||
|
||||
Session-layer messages are carried as the payload of a SessionDatagram (0x00).
|
||||
All FSP messages begin with a **4-byte common prefix** that identifies the
|
||||
protocol version, session lifecycle phase, per-packet flags, and payload length.
|
||||
|
||||
### FSP Common Prefix (4 bytes)
|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| version | 4 bits (high) | Protocol version. Currently 0x0 |
|
||||
| phase | 4 bits (low) | Session lifecycle phase (see table) |
|
||||
| flags | 1 byte | Per-packet signal flags (zero during handshake) |
|
||||
| payload_len | 2 bytes LE | Length of payload after phase-specific header |
|
||||
|
||||
### FSP Phase Table
|
||||
|
||||
| Phase | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| 0x0 | Established | Post-handshake encrypted traffic or plaintext error signals |
|
||||
| 0x1 | Handshake msg1 | SessionSetup (Noise XK msg1) |
|
||||
| 0x2 | Handshake msg2 | SessionAck (Noise XK msg2) |
|
||||
| 0x3 | Handshake msg3 | SessionMsg3 (Noise XK msg3) |
|
||||
|
||||
### FSP Flags (Established Phase Only)
|
||||
|
||||
| Bit | Name | Description |
|
||||
| --- | ---- | ----------- |
|
||||
| 0 | CP (coords present) | Source and destination coordinates follow the header in cleartext |
|
||||
| 1 | K (key epoch) | Selects active key during rekeying |
|
||||
| 2 | U (unencrypted) | Payload is plaintext (error signals) |
|
||||
| 3-7 | — | Reserved (must be zero) |
|
||||
|
||||
Flags must be zero in handshake packets (phase 0x1, 0x2, and 0x3).
|
||||
|
||||
### FSP Encrypted Message (phase 0x0, U flag clear)
|
||||
|
||||
Post-handshake encrypted data. The 12-byte cleartext header is used as AEAD
|
||||
AAD. Coordinates may appear in cleartext between the header and ciphertext
|
||||
when the CP flag is set.
|
||||
|
||||
**Cleartext header** (12 bytes, used as AEAD AAD):
|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| common prefix | 4 bytes | ver=0, phase=0, flags, payload_len |
|
||||
| counter | 8 bytes LE | Monotonic nonce, used as AEAD nonce and for replay detection |
|
||||
|
||||
**Optional cleartext coordinates** (when CP flag is set):
|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| src_coords_count | 2 bytes LE | Number of source coordinate entries |
|
||||
| src_coords | 16 x n bytes | Source's ancestry (NodeAddr, self -> root) |
|
||||
| dest_coords_count | 2 bytes LE | Number of dest coordinate entries |
|
||||
| dest_coords | 16 x m bytes | Destination's ancestry |
|
||||
|
||||
Transit nodes parse the CP flag and extract coordinates without decryption.
|
||||
|
||||
**Encrypted inner header** (6 bytes, first bytes of AEAD plaintext):
|
||||
|
||||
| Field | Size | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| timestamp | 4 bytes LE | Session-relative milliseconds (u32) |
|
||||
| msg_type | 1 byte | Session-layer message type |
|
||||
| inner_flags | 1 byte | Bit 0: SP (spin bit for RTT measurement) |
|
||||
|
||||
After the inner header, the remaining plaintext is the message-type-specific
|
||||
body.
|
||||
|
||||
**Complete encrypted message**:
|
||||
|
||||

|
||||
|
||||
### FSP Session Message Types
|
||||
|
||||
| Type | Message | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| 0x10 | Data | Port-multiplexed service payload (see DataPacket below) |
|
||||
| 0x11 | SenderReport | MMP sender-side metrics report |
|
||||
| 0x12 | ReceiverReport | MMP receiver-side metrics report |
|
||||
| 0x13 | PathMtuNotification | End-to-end path MTU echo |
|
||||
| 0x14 | CoordsWarmup | Standalone coordinate cache warming |
|
||||
| 0x20 | CoordsRequired | Error: transit node lacks destination coordinates |
|
||||
| 0x21 | PathBroken | Error: greedy routing reached dead end |
|
||||
| 0x22 | MtuExceeded | Error: forwarded packet exceeds next-hop MTU |
|
||||
|
||||
Message types 0x10-0x14 are carried inside the AEAD ciphertext (dispatched
|
||||
by the `msg_type` field in the encrypted inner header). Types 0x20-0x22 are
|
||||
plaintext error signals (U flag set, no encryption).
|
||||
|
||||
Session-layer SenderReport (0x11) and ReceiverReport (0x12) carry the same
|
||||
metric fields as their link-layer counterparts (0x01 and 0x02), but the
|
||||
body framing differs because the FSP encrypted inner header already
|
||||
carries the message-type byte. The session body therefore omits the
|
||||
msg_type byte and uses 2 reserved bytes (not 3) before the fields:
|
||||
|
||||
| Layer | Wire size | Header inside body |
|
||||
| ----- | --------- | ------------------ |
|
||||
| Link SenderReport (0x01) | 48 bytes | msg_type(1) + reserved(3) + fields(44) |
|
||||
| Session SenderReport (0x11) | 46 bytes | reserved(2) + fields(44) |
|
||||
| Link ReceiverReport (0x02) | 68 bytes | msg_type(1) + reserved(3) + fields(64) |
|
||||
| Session ReceiverReport (0x12) | 66 bytes | reserved(2) + fields(64) |
|
||||
|
||||
Dispatch happens at the session level via the `msg_type` byte in the FSP
|
||||
encrypted inner header.
|
||||
|
||||
### SessionSetup (phase 0x1)
|
||||
|
||||
Establishes a session and warms transit coordinate caches. Contains the
|
||||
first message of the Noise XK handshake (ephemeral key only — the
|
||||
initiator's static identity is not revealed until msg3).
|
||||
|
||||
SessionSetup, SessionAck, and SessionMsg3 are identified by the **phase**
|
||||
field in the FSP common prefix (0x1, 0x2, 0x3), not by a message type
|
||||
byte. The `msg_type` field in the encrypted inner header applies only to
|
||||
established-phase (0x0) messages.
|
||||
|
||||
Encoded with FSP prefix: ver=0, phase=0x1, flags=0, payload_len.
|
||||
|
||||

|
||||
|
||||
**Body** (after 4-byte FSP prefix):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | flags | 1 byte | Bit 0: REQUEST_ACK, Bit 1: BIDIRECTIONAL |
|
||||
| 1 | src_coords_count | 2 bytes LE | Number of source coordinate entries |
|
||||
| 3 | src_coords | 16 x n bytes | Source's ancestry (NodeAddr, self -> root) |
|
||||
| ... | dest_coords_count | 2 bytes LE | Number of dest coordinate entries |
|
||||
| ... | dest_coords | 16 x m bytes | Destination's ancestry |
|
||||
| ... | handshake_len | 2 bytes LE | Noise payload length |
|
||||
| ... | handshake_payload | variable | Noise XK msg1 (33 bytes — ephemeral key only) |
|
||||
|
||||
### SessionAck (phase 0x2)
|
||||
|
||||
Second message of the Noise XK handshake. The responder sends its
|
||||
ephemeral key and encrypted epoch.
|
||||
Encoded with FSP prefix: ver=0, phase=0x2, flags=0, payload_len.
|
||||
|
||||

|
||||
|
||||
**Body** (after 4-byte FSP prefix):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | flags | 1 byte | Reserved |
|
||||
| 1 | src_coords_count | 2 bytes LE | Number of acknowledger coordinate entries |
|
||||
| 3 | src_coords | 16 x n bytes | Acknowledger's ancestry (for cache warming) |
|
||||
| ... | dest_coords_count | 2 bytes LE | Number of initiator coordinate entries |
|
||||
| ... | dest_coords | 16 x m bytes | Initiator's ancestry (for return-path cache warming) |
|
||||
| ... | handshake_len | 2 bytes LE | Noise payload length |
|
||||
| ... | handshake_payload | variable | Noise XK msg2 (57 bytes — ephemeral key + encrypted epoch) |
|
||||
|
||||
### SessionMsg3 (phase 0x3)
|
||||
|
||||
Third and final message of the Noise XK handshake. The initiator reveals
|
||||
its encrypted static identity and epoch. After msg3, both parties derive
|
||||
identical symmetric session keys and the session is established.
|
||||
Encoded with FSP prefix: ver=0, phase=0x3, flags=0, payload_len.
|
||||
|
||||

|
||||
|
||||
**Body** (after 4-byte FSP prefix):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | flags | 1 byte | Reserved |
|
||||
| 1 | handshake_len | 2 bytes LE | Noise payload length |
|
||||
| 3 | handshake_payload | variable | Noise XK msg3 (73 bytes — encrypted static + encrypted epoch) |
|
||||
|
||||
**Noise XK msg3 breakdown** (73 bytes):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | encrypted_static | 49 bytes | Initiator's static key (33) + AEAD tag (16) |
|
||||
| 49 | encrypted_epoch | 24 bytes | Startup epoch (8) + AEAD tag (16) |
|
||||
|
||||
SessionMsg3 does not carry coordinates — both endpoints already have each
|
||||
other's coordinates from SessionSetup (msg1) and SessionAck (msg2).
|
||||
|
||||
### Data (0x10) — DataPacket Port Multiplexing
|
||||
|
||||
DataPacket is the primary application data carrier. The body after the
|
||||
6-byte encrypted inner header contains a 4-byte port header followed by
|
||||
the service payload:
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | src_port | 2 bytes LE | Source service port |
|
||||
| 2 | dst_port | 2 bytes LE | Destination service port |
|
||||
| 4 | payload | variable | Service-specific payload |
|
||||
|
||||
The receiver dispatches by `dst_port` to the registered service handler.
|
||||
|
||||
**Port registry (three tiers):**
|
||||
|
||||
| Range | Purpose |
|
||||
| ----- | ------- |
|
||||
| 0–255 (0x00–0xFF) | Reserved, protocol use |
|
||||
| 256–1023 (0x100–0x3FF) | Reserved, FIPS standard services |
|
||||
| 1024–65535 (0x400–0xFFFF) | Application use |
|
||||
|
||||
**Initial assignment**: Port 256 (0x100) = IPv6 shim.
|
||||
|
||||
#### IPv6 Shim Payload Format (Port 256)
|
||||
|
||||
The IPv6 shim defines its own payload format with a leading format byte:
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | format | 1 byte | Compression format (0x00 = mesh-internal compressed) |
|
||||
| 1 | fields | variable | Format-specific residual fields |
|
||||
|
||||
**Format 0x00 — mesh-internal compressed (default):**
|
||||
|
||||
Strips source and destination IPv6 addresses (32 bytes) and payload length
|
||||
(2 bytes) from each packet. Carries residual fields that cannot be derived
|
||||
from session context:
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | format | 1 byte | 0x00 |
|
||||
| 1 | traffic_class | 1 byte | IPv6 Traffic Class (DSCP + ECN) |
|
||||
| 2 | flow_label | 3 bytes | IPv6 Flow Label (20 bits, big-endian, zero-padded) |
|
||||
| 5 | next_header | 1 byte | IPv6 Next Header (protocol identifier) |
|
||||
| 6 | hop_limit | 1 byte | IPv6 Hop Limit |
|
||||
| 7 | upper_payload | variable | Upper-layer payload (TCP, UDP, ICMPv6, etc.) |
|
||||
|
||||
The receiver reconstructs the full 40-byte IPv6 header from session context
|
||||
(source and destination addresses derived from session npubs, version = 6,
|
||||
payload length from outer packet length) plus the 6 bytes of residual fields,
|
||||
then delivers the complete IPv6 packet to the TUN interface.
|
||||
|
||||
**Format 0x01+**: Reserved for future use (e.g., full-header gateway traffic).
|
||||
|
||||
**Compression savings**: 29 bytes per packet (34 bytes stripped, 7 bytes
|
||||
format + residual added). Net overhead for IPv6 traffic: 77 bytes
|
||||
(`FIPS_IPV6_OVERHEAD`), down from 110 bytes base DataPacket overhead.
|
||||
|
||||
### PathMtuNotification (0x13)
|
||||
|
||||
Sent by the destination to report the observed forward-path MTU.
|
||||
|
||||

|
||||
|
||||
| Offset | Field | Size | Encoding |
|
||||
| ------ | ----- | ---- | -------- |
|
||||
| 0 | path_mtu | 2 | u16 LE — minimum MTU observed along the forward path |
|
||||
|
||||
**Total body: 2 bytes** (plus FSP common prefix + encrypted header + AEAD tag).
|
||||
|
||||
### CoordsWarmup (0x14)
|
||||
|
||||
Standalone coordinate cache warming message. Sent when piggybacking coordinates
|
||||
via the CP flag on a data packet would exceed the transport MTU, or as an
|
||||
immediate response to CoordsRequired/PathBroken signals (rate-limited).
|
||||
|
||||
CoordsWarmup is an encrypted FSP message with the CP flag set and an empty
|
||||
body. Transit nodes extract coordinates via the existing CP-flag parsing
|
||||
path — no transit-side changes required.
|
||||
|
||||
**Wire format**:
|
||||
|
||||
```text
|
||||
FSP header (12 bytes, AAD): ver=0, phase=0, flags=CP, counter, payload_len
|
||||
Cleartext coords: src_coords + dst_coords (same encoding as CP flag)
|
||||
AEAD ciphertext: inner_header(6) + Poly1305 tag(16) = 22 bytes
|
||||
|
||||
Total FSP payload: 12 + coords + 22
|
||||
```
|
||||
|
||||
The cleartext coords section uses the same variable-length encoding as any
|
||||
CP-flagged message: `src_coords_count(2) + src_coords(16×n) +
|
||||
dest_coords_count(2) + dest_coords(16×m)`.
|
||||
|
||||
**Typical size** (depth-3 tree): 12 + (2+64+2+64) + 22 = **166 bytes** FSP
|
||||
payload. With SessionDatagram + link overhead: 166 + 36 + 37 = **239 bytes**
|
||||
on the wire.
|
||||
|
||||
### CoordsRequired (0x20)
|
||||
|
||||
Plaintext error signal — transit node lacks coordinates for destination.
|
||||
Identified by the FSP phase field, not a separate message type byte
|
||||
(same as SessionSetup/SessionAck/SessionMsg3).
|
||||
Encoded with FSP prefix: ver=0, phase=0x0, U flag set, payload_len=34.
|
||||
|
||||

|
||||
|
||||
**Payload** (after 4-byte FSP prefix):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x20 |
|
||||
| 1 | flags | 1 byte | Reserved |
|
||||
| 2 | dest_addr | 16 bytes | NodeAddr we couldn't route to |
|
||||
| 18 | reporter | 16 bytes | NodeAddr of reporting router |
|
||||
|
||||
**Payload size**: 34 bytes. **Total on wire**: 38 bytes (4 prefix + 34 payload).
|
||||
|
||||
### PathBroken (0x21)
|
||||
|
||||
Plaintext error signal — greedy routing reached a dead end.
|
||||
Encoded with FSP prefix: ver=0, phase=0x0, U flag set, payload_len.
|
||||
|
||||

|
||||
|
||||
**Payload** (after 4-byte FSP prefix):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x21 |
|
||||
| 1 | flags | 1 byte | Reserved |
|
||||
| 2 | dest_addr | 16 bytes | Unreachable NodeAddr |
|
||||
| 18 | reporter | 16 bytes | NodeAddr of reporting router |
|
||||
| 34 | last_coords_count | 2 bytes LE | Number of stale coordinate entries |
|
||||
| 36 | last_known_coords | 16 x n bytes | Stale coordinates that failed |
|
||||
|
||||
### MtuExceeded (0x22)
|
||||
|
||||
Plaintext error signal — forwarded packet exceeds the next-hop link MTU.
|
||||
Sent by a transit router back to the source when a SessionDatagram cannot
|
||||
be forwarded because its size exceeds the outgoing link's MTU.
|
||||
Encoded with FSP prefix: ver=0, phase=0x0, U flag set, payload_len=36.
|
||||
|
||||

|
||||
|
||||
**Payload** (after 4-byte FSP prefix):
|
||||
|
||||
| Offset | Field | Size | Description |
|
||||
| ------ | ----- | ---- | ----------- |
|
||||
| 0 | msg_type | 1 byte | 0x22 |
|
||||
| 1 | flags | 1 byte | Reserved |
|
||||
| 2 | dest_addr | 16 bytes | NodeAddr of the destination being forwarded to |
|
||||
| 18 | reporter | 16 bytes | NodeAddr of the router that detected the MTU violation |
|
||||
| 34 | mtu | 2 bytes LE | Bottleneck MTU at the reporting router |
|
||||
|
||||
**Payload size**: 36 bytes. **Total on wire**: 40 bytes (4 prefix + 36 payload).
|
||||
|
||||
The source uses the reported MTU to adjust its session-layer path MTU
|
||||
estimate. MtuExceeded is the reactive complement to the proactive
|
||||
`path_mtu` field in SessionDatagram and LookupResponse.
|
||||
|
||||
## Encapsulation Walkthrough
|
||||
|
||||
A complete picture of how application data is wrapped through each layer.
|
||||
|
||||
### Application Data -> Wire
|
||||
|
||||
Starting with an IPv6 application sending a 1024-byte TCP payload to a
|
||||
destination (the original IPv6 packet at the TUN is 1064 bytes: 40-byte
|
||||
header + 1024-byte payload):
|
||||
|
||||
```text
|
||||
Layer 5: Application data
|
||||
1024 bytes (TCP payload inside 1064-byte IPv6 packet)
|
||||
|
||||
Layer 4: IPv6 shim compression (port 256)
|
||||
Strip IPv6 addresses (32) + payload length (2), keep residual fields
|
||||
format (1) + residual (6) + upper payload (1024) = 1031 bytes
|
||||
|
||||
Layer 3: Session encryption (FSP)
|
||||
FSP header (12) + AEAD(inner_hdr (6) + port_hdr (4) + shim (1031)) + tag (16)
|
||||
= 12 + 1041 + 16 = 1069 bytes
|
||||
|
||||
Layer 2: SessionDatagram envelope (FMP routing)
|
||||
msg_type (1) + ttl (1) + path_mtu (2) + src_addr (16) + dest_addr (16) + payload (1069)
|
||||
= 1105 bytes
|
||||
|
||||
Layer 1: Link encryption (FMP per-hop)
|
||||
outer header (16) + encrypted(inner_hdr (5) + datagram (1105)) + AEAD tag (16)
|
||||
= 1142 bytes
|
||||
|
||||
Layer 0: Transport
|
||||
UDP datagram containing 1142 bytes
|
||||
```
|
||||
|
||||
Total overhead for IPv6 traffic: 1142 − 1064 = 78 bytes per packet. The
|
||||
difference from the `FIPS_IPV6_OVERHEAD` constant (77 bytes) is the 1-byte
|
||||
FMP `msg_type` counted in the link inner header rather than the
|
||||
SessionDatagram body.
|
||||
|
||||
### Overhead Budget
|
||||
|
||||
| Layer | Overhead | Component |
|
||||
| ----- | -------- | --------- |
|
||||
| Link encryption | 37 bytes | 16 outer header (AAD) + 5 inner header (timestamp + msg_type) + 16 AEAD tag |
|
||||
| SessionDatagram body | 35 bytes | 1 ttl + 2 path_mtu + 16 src + 16 dest (msg_type counted in inner header) |
|
||||
| FSP header | 12 bytes | 4 prefix + 8 counter |
|
||||
| FSP inner header | 6 bytes | 4 timestamp + 1 msg_type + 1 inner_flags (inside AEAD) |
|
||||
| Session AEAD tag | 16 bytes | Poly1305 tag on session-encrypted payload |
|
||||
| **Protocol envelope** | **106 bytes** | `FIPS_OVERHEAD` constant |
|
||||
| Port header | 4 bytes | src_port + dst_port (DataPacket only) |
|
||||
| **DataPacket total** | **110 bytes** | Base overhead for any port-multiplexed service |
|
||||
| IPv6 compression | −33 bytes | 40-byte IPv6 header → 7-byte format + residual |
|
||||
| **IPv6 data path total** | **77 bytes** | `FIPS_IPV6_OVERHEAD` constant |
|
||||
|
||||
### At Each Transit Node
|
||||
|
||||
```text
|
||||
1. Receive UDP datagram
|
||||
2. Parse common prefix -> version, phase, flags, payload_len
|
||||
3. Phase 0x0 -> established frame
|
||||
4. Look up (transport_id, receiver_idx) -> session
|
||||
5. Check replay window (counter)
|
||||
6. Decrypt with link keys (16-byte header as AAD) -> plaintext
|
||||
7. Strip inner header -> timestamp, msg_type
|
||||
8. msg_type 0x00 -> SessionDatagram
|
||||
9. Read dest_addr -> routing decision
|
||||
10. Decrement ttl, min path_mtu
|
||||
11. Re-encrypt with next-hop link keys
|
||||
12. Send via next-hop transport
|
||||
```
|
||||
|
||||
Transit nodes see the SessionDatagram envelope (src_addr, dest_addr,
|
||||
ttl, path_mtu) but cannot read the session-layer payload (encrypted with
|
||||
endpoint session keys).
|
||||
|
||||
## Size Summary
|
||||
|
||||
### FMP Handshake Messages (Noise IK)
|
||||
|
||||
| Message | Raw Noise | Wire Frame |
|
||||
| ------- | --------- | ---------- |
|
||||
| IK msg1 (ephemeral + encrypted static + encrypted epoch) | 106 bytes | 114 bytes |
|
||||
| IK msg2 (ephemeral + encrypted epoch) | 57 bytes | 69 bytes |
|
||||
|
||||
### FSP Handshake Messages (Noise XK)
|
||||
|
||||
| Message | Raw Noise | Notes |
|
||||
| ------- | --------- | ----- |
|
||||
| XK msg1 (ephemeral only) | 33 bytes | Carried in SessionSetup |
|
||||
| XK msg2 (ephemeral + encrypted epoch) | 57 bytes | Carried in SessionAck |
|
||||
| XK msg3 (encrypted static + encrypted epoch) | 73 bytes | Carried in SessionMsg3 |
|
||||
|
||||
### Link-Layer Messages (inside encrypted frame)
|
||||
|
||||
| Message | Size | Notes |
|
||||
| ------- | ---- | ----- |
|
||||
| TreeAnnounce | 100 + 32n bytes | n = depth + 1 |
|
||||
| FilterAnnounce | 1,035 bytes | v1 (1KB filter) |
|
||||
| LookupRequest | 46 + 16n bytes | n = origin depth + 1 |
|
||||
| LookupResponse | 93 + 16n bytes | n = target depth + 1 |
|
||||
| SessionDatagram | 36 + payload bytes | Fixed 36-byte header |
|
||||
| Disconnect | 2 bytes | |
|
||||
|
||||
### Session-Layer Messages (inside SessionDatagram)
|
||||
|
||||
| Message | Typical Size | Notes |
|
||||
| ------- | ------------ | ----- |
|
||||
| SessionSetup | ~170 bytes | Depth-dependent (XK msg1 = 33 bytes) |
|
||||
| SessionAck | ~190 bytes | Depth-dependent, carries both endpoints' coords (XK msg2 = 57 bytes) |
|
||||
| SessionMsg3 | ~80 bytes | Fixed (XK msg3 = 73 bytes, no coords) |
|
||||
| Data (minimal) | 12 + 6 + 4 + payload + 16 bytes | Steady state (port header included) |
|
||||
| Data (with coords) | 12 + ~130 + 6 + 4 + payload + 16 bytes | Warmup/recovery (port header included) |
|
||||
| SenderReport | 12 + 6 + 46 + 16 bytes | MMP metrics |
|
||||
| ReceiverReport | 12 + 6 + 66 + 16 bytes | MMP metrics |
|
||||
| PathMtuNotification | 12 + 6 + 2 + 16 bytes | MTU signal |
|
||||
| CoordsWarmup | 12 + coords + 6 + 16 bytes | Standalone warmup (empty body) |
|
||||
| CoordsRequired | 38 bytes | Fixed (prefix + msg_type + body) |
|
||||
| PathBroken | 35 + 16n bytes | Includes stale coords |
|
||||
| MtuExceeded | 40 bytes | Fixed (prefix + msg_type + body) |
|
||||
|
||||
### Complete Packet Sizes (link + session)
|
||||
|
||||
| Scenario | Wire Size | Notes |
|
||||
| -------- | --------- | ----- |
|
||||
| Encrypted frame minimum | 37 bytes | Empty body |
|
||||
| SessionDatagram + Data (minimal) | 37 + 35 + 12 + 6 + 4 + payload + 16 | 110 + payload (any service) |
|
||||
| SessionDatagram + IPv6 Data (minimal) | 110 + 7 + upper_payload − 34 | 77 + IPv6 payload (compressed) |
|
||||
| SessionDatagram + Data (with coords) | 110 + coords + payload | Coords vary with tree depth |
|
||||
| SessionDatagram + SessionSetup | ~275 bytes | Depth-3, both dirs |
|
||||
| SessionDatagram + CoordsRequired | 37 + 36 + 38 = 111 bytes | Including link overhead |
|
||||
|
||||
## References
|
||||
|
||||
- [../design/fips-mesh-layer.md](../design/fips-mesh-layer.md) — FMP behavioral specification
|
||||
- [../design/fips-session-layer.md](../design/fips-session-layer.md) — FSP behavioral specification
|
||||
- [../design/fips-transport-layer.md](../design/fips-transport-layer.md) — Transport framing
|
||||
- [../design/fips-mesh-operation.md](../design/fips-mesh-operation.md) — How messages work together
|
||||
- [../design/fips-ipv6-adapter.md](../design/fips-ipv6-adapter.md) — MTU enforcement
|
||||
- [../design/fips-bloom-filters.md](../design/fips-bloom-filters.md) — FilterAnnounce parameters and FPR analysis
|
||||
- [../design/fips-mtu.md](../design/fips-mtu.md) — How `path_mtu` and MtuExceeded fit together
|
||||