Identity and destinations
Identity
A Reticulum identity is the root cryptographic principal for signing announces, establishing links, and encrypting identity-layer messages.
Implementation: pkg/identity with primitives from pkg/cryptography.
Key material
| Component | Size | Purpose |
|---|---|---|
| X25519 private scalar | 32 bytes | ECDH, identity encrypt |
| Ed25519 seed | 32 bytes | Signing |
Together these form the 512-bit keyset described in Reticulum documentation.
Public form (64 bytes on wire)
X25519 public key (32 bytes) || Ed25519 public key (32 bytes)
Peers learn this blob from announces and proofs.
Identity hash
SHA-256 over the 64-byte public key, truncated to 128 bits (16 bytes). Used for addressing, HKDF salt context, and storage paths.
Software identity file (64 bytes on disk)
X25519 private (32) || Ed25519 seed (32)
Equivalent to Python RNS.Identity software persistence. Anyone with this file can impersonate the identity on the network. Store with restrictive permissions on encrypted media.
Load helpers:
id, err := identity.LoadIdentityFile(path, nil)
Identity backend (file, Secret Service, or kernel keyring)
Config key identity_backend in [reticulum]:
| Value | Behavior |
|---|---|
file (default) |
64-byte plaintext identity files |
secretservice |
Freedesktop Secret Service over D-Bus (GNOME Keyring, KDE Wallet Secret Service bridge, KeePassXC with Secret Service enabled) |
keyring |
Linux kernel keyring (user keyring, no D-Bus). Suitable for headless systemd user or system units |
With secretservice or keyring, ToFile stores the private blob in the backend and writes an 8-byte RSSI marker at the usual path. FromFile and LoadIdentityFile detect the marker and fetch the secret. If the backend is unavailable, persistence fails with a clear error (no silent plaintext fallback).
keyring is Linux-only. When a persistent user keyring is available, material can survive reboot for the same UID. Otherwise keys are cleared on reboot. Desktop sessions without D-Bus can use keyring instead of secretservice.
Headless units that cannot use the kernel keyring should keep identity_backend = file, or unlock a Secret Service collection / KeePassXC before start when using secretservice.
CLI migrate (path via -i):
rgoid -i ~/.reticulum-go/storage/transport_identity -to-secretservice
rgoid -i ~/.reticulum-go/storage/transport_identity -to-keyring
rgoid -i ~/.reticulum-go/storage/transport_identity -to-file
In-memory key handling
Long-term X25519 and Ed25519 material lives in pkg/securemem buffers with best-effort mlock and wipe on Identity.Close. Callers of GetPrivateKey should wipe the returned slice when finished.
What this protects against
| Control | Helps against | Does not stop |
|---|---|---|
identity_backend = secretservice |
Casual theft of the identity file path (backup copies, world-readable home dirs, malware that only reads ~/.reticulum-go/storage). Keyring unlock policies and KeePassXC master password raise the bar for offline disk images when the collection is locked. |
Root or same-user malware that can talk to an unlocked Secret Service session. Compelled unlock. Physical access while the keyring is unlocked. |
identity_backend = keyring |
Same file-path theft cases without requiring D-Bus. Fits systemd units that have a user keyring. | Root or same-UID processes that can call keyctl. Keys may not survive reboot if persistent keyring is unavailable. |
pkg/securemem mlock + wipe |
Keys lingering in swap after process exit, casual core dumps of freed heap (with RLIMIT_CORE=0 in the sandbox), accidental retention after Close. |
Live process memory inspection by root or a debugger attached to the running daemon. Full cold-boot attacks on RAM. |
File permissions 0600 + encrypted disk (operator practice) |
Other local users reading plaintext identity files. Disk theft when FDE is used and the volume is locked. | Attacks after the volume is unlocked and mounted. |
None of these replace HSM-backed signing (RHB1 / NewIdentityWithSigner) for high-assurance signing material, or host firewall and sandbox policy for network exposure.
Hardware-bound descriptor (RHB1, optional)
Reticulum-Go supports a 72-byte hardware-bound layout:
| Field | Size |
|---|---|
Magic RHB1 |
4 bytes |
| Version | 1 byte |
| Reserved | 3 bytes |
| X25519 private | 32 bytes |
| Ed25519 public | 32 bytes |
Signing uses an external cryptography.Ed25519Signer (for example PKCS#11 or cloud HSM). The on-wire Ed25519 public key matches a software identity so Python peers require no changes.
Python Identity.from_file does not read RHB1 today. See Compatibility.
id, err := identity.NewIdentityWithSigner(descriptor, signer)
Identity encryption
Outbound encrypted tokens use ephemeral X25519, HKDF-SHA256, AES-256-CBC, and HMAC-SHA256. See Cryptography.
Ratchets
Optional X25519 ratchet keys rotate for forward secrecy on identity-encrypted traffic. Ratchets persist under storage/ratchets/ per identity hash with expiry aligned to reference constants.
Destinations
A destination is an application endpoint on the network. Implementation: pkg/destination.
Destination types
| Type | Constant | Use |
|---|---|---|
| Single | SINGLE | One-to-one application endpoint |
| Group | GROUP | Group messaging |
| Plain | PLAIN | Unencrypted endpoint (rare) |
| Link | LINK | Link-mode endpoint |
Destination hash
The on-wire destination hash is derived from application name, aspects, and identity hash (for non-plain types) using a truncated SHA-256 construction in calculateHash. The exact byte layout matches Python and is verified in tests/crossref.
Creating a destination
dest, err := destination.NewDestination(
id,
destination.TypeSingle,
"myapp",
"subsystem",
)
Register with transport so inbound packets route to Destination.Receive.
Announces
Destinations publish signed announces so the network learns paths. Handlers register through pkg/announce:
transport.RegisterAnnounceHandler(handler)
Announce payloads can carry application-specific app_data visible to peers.
Request handlers
Destinations can register request paths. Incoming requests block the link goroutine until the handler responds or a timeout elapses. The control API exposes the same pattern over WebSocket events.
Incoming links
Register with accepts_links semantics. Incoming link requests dispatch to link.HandleIncomingLinkRequest.
Resolver
pkg/resolver resolves a human-readable full name string to a deterministic identity hash via SHA-256. Useful for configuration and display, not a substitute for trust on first use from announces.
Storage layout
| Artifact | Path |
|---|---|
| Identity blobs | storage/identities/ (per hash in Go) |
| Known destinations | storage/known_destinations/ |
| Ratchets | storage/ratchets/ |
Go writes identity files keyed by hash. Python may use per-name files. Go loads Python-format known destination files for interoperability.
Signing without holding the seed
Integrate HSM or cloud KMS via cryptography.NewEd25519SignerFromCryptoSigner. The public Ed25519 key in announces must match the 64-byte public blob.
Security practices
- Treat the 64-byte software file like a private key backup
- Use RHB1 when the signing key must not live in process memory
- Do not log identity material at high debug levels in production
- Rotate ratchets according to application policy and storage expiry
Related documents
- API reference
- Cryptography
- Transport for announce routing
- Links, channels, and resources for link-mode destinations