In one line: an E2E-encrypted, forward-private, compostable messenger that rides any of seven chains — you talk wallet-to-wallet, money and message in the same atomic transaction, and everything rots on your schedule. No central server. No VC. No token.
Want to send your first message? → docs/ONBOARDING.md
Two commands: spore init then spore msg send-e2 …. (spore demo runs with
no chain and no wallet if you just want to see it work.)
Install (checksum-verified, spore + spore-peer, all of
linux/macOS/Windows, amd64 + arm64):
curl -fsSL https://raw.githubusercontent.com/liqdmetal/spore/main/scripts/install.sh | sh # linux/macOS/WSLirm https://raw.githubusercontent.com/liqdmetal/spore/main/scripts/install.ps1 | iex # WindowsThen spore demo — the full send → receive → burn lifecycle, no wallet, no
setup. Releases also carry SLSA keyless provenance
(multiple.intoto.jsonl) verifiable with
slsa-verifier.
| Version | Date | Highlights |
|---|---|---|
| v0.8.0 | 2026-09-27 | The EVM carrier readied for the wire: local EIP-155 signing, the loopback spore evm-proxy in front of read-only public RPCs, the per-chain evm_mailbox config seam, and a durable settlement notice outbox pinned by crash-window tests — plus the relay work-order prepare/identity boundary, DEL-escaping canonical JSON, and a reaper-watchdog staleness fix. Pointing the carrier at a real chain moves to v0.9.0. |
| v0.7.0 | 2026-09-23 | First release shipping the full stack: spore (Go) for all six targets (linux/macOS/Windows × amd64/arm64) plus the Rust spore-peer transport for five of them, per-asset .sha256 checksums, and SLSA keyless provenance for every binary. Installers verify before they swap anything into place. |
Pin the install with SPORE_RELEASE=v0.8.0, and verify a downloaded binary
the way CI does with slsa-verifier … --source-tag v0.8.0 — full recipe in
docs/ONBOARDING.md. One honest
caveat: spore-peer for Windows arm64 is not shipped yet (the Rust
cross-linker for that target isn't trustworthy); Go spore for arm64 is.
| Signal | Session | Telegram+TON | Spore | |
|---|---|---|---|---|
| Forward secrecy (Double Ratchet) | ✓ | ✓ | partial | ✓ |
| Post-compromise healing | ✓ | ✓ | ✗ | ✓ |
| No central server | ✗ | ✓ (onion) | ✗ | ✓ (chain + your own node) |
| Money moves WITH the message, same atomic tx | ✗ | ✗ | custodial bots | ✓ native (DERO/EVM) |
| Messages compost (bodies expire, mailbox burns, local panic-wipe) | ✗ | ✗ | ✗ | ✓ |
| Single-use prekeys served without exposing your identity key | — | — | — | ✓ |
Settlement-native, compostable, self-hosted private messaging is an empty category. Spore is the messaging fruiting body on the Relay/Sap settlement rail — money and words become the same atomic object, and both rot.
In a forest, trees look separate — underground they are joined by a shared mycorrhizal network exchanging nutrients and warnings. That is Spore:
- Trees = endpoints. Each an independent wallet + node on its own chain.
- Spore = the substrate underneath. A no-relay transport letting any tree signal another — quietly, point-to-point.
- m³ = the network that emerges: trees on different chains, joined through one underground fabric.
Spore carries only an opaque 74-byte pointer on-chain. The ratcheted ciphertext, the handshake, and the message body stay off-chain in a TTL-bound store and are reaped after expiry. A permanent chain can't forget a transaction — so Spore never pretends to. What it guarantees:
The message body composts. The permanent carrier retains only an opaque, non-decryptable pointer scrap.
- X3DH + Double Ratchet (
internal/ratchet,internal/ratchetwire): every new conversation is0xE2, forward-private, post-compromise healing. The legacy0xE1envelope, DERO-native whispers, and one-shot long-body path remain compatibility-only and are documented as not forward-private. - Off-chain bodies (
internal/store): content-addressed, TTL-evicted, crash-safe (the expiry record is written before the body, so a crash can never leave an un-reapable ciphertext). - Single-use prekeys (
internal/mailbox):GET /prekeypops one pre-signed public bundle per sender — two senders never get the same OPK. The mailbox never touches your identity/SPK private keys; bundles are signed offline (spore prekeybatch). - Durable local state: ratchet sessions are endpoint-local, encrypted at rest, anti-rollback (append-only sequence log survives restart), and inactivity-expiring.
- No downgrade: an unsupported carrier refuses rather than silently falling back to a legacy plaintext-forever path.
| Carrier | Backend | Pointer transport | Compost | Status |
|---|---|---|---|---|
| DERO | internal/dero |
native encrypted tx payload carrying opaque E2 pointer | body TTL | live, mainnet |
| EVM | internal/evm |
mailbox contract / calldata | burn(to,seq) after delivery |
live-verified (local Anvil); deployment pending |
| Solana | internal/solana |
inbox PDA (program v2) | burn(idx) after delivery |
live, mainnet; self-messaging verified |
| Nostr | internal/nostr |
signed event content | NIP-09 delete (best-effort) | carrier impl |
| Bitcoin | internal/bitcoin |
OP_RETURN (≤80B) |
body-only (chain immutable) | carrier impl, signer-injected |
| Cosmos SDK | internal/cosmos |
configurable memo field | chain-specific | configurable seam |
| TON | internal/ton |
configurable comment | no universal burn | configurable seam |
| Monero (XMR) | internal/xmr |
8-byte payment id | off-chain rendezvous | mock-verified; too small for E2 pointer — refused, not downgraded |
Full carrier matrix, invariants, and deployment order:
docs/CARRIER_MATRIX.md.
Money rides the same transaction as the pointer on DERO and EVM-calldata — atomically, trustlessly, no custody. Spore never holds your funds; your wallet signs.
spore msg send-e2 -amount 5.5dero …— pay with the message.spore msg invoice -amount 25dero …— request payment in-thread.spore msg pay -invoice <id> …— settle: money + proof ride one atomic tx.
Bitcoin/TON value carriage is refused today (their backends discard the amount hint) rather than silently sending an unpaid message as if paid.
spore init # one-shot onboarding: identity kit + config.json
spore demo # see it work — no chain, no wallet
# Forward-private E2 (the real messenger):
spore msg send-e2 -to ADDR (-bundle F | -bundle-url URL) -pinned-sig HEX [-ringsize 8|16] [-amount 5.5dero] [-msg-file F|-]
spore msg recv-e2 [-auto-ack] [-maildb F] [-out-dir D] [-ntfy URL]
spore msg reply-e2 -to ADDR -session HEX # continue a thread
spore msg forward-e2 -to ADDR -file F … # new session, same body
spore msg sessions # list thread/session ids
spore msg invoice|pay -session HEX -amount N # in-thread settlement
spore msg compose | flush # offline send queue (HMAC-sealed)
spore msg mail add|list|block|threads|search|purge # local contacts + search
# Prekey discovery (single-use):
spore prekeybatch gen|push|status
# Infra:
spore mailbox host|list|get # hosted ciphertext/prekey service
spore msg recv-e2 # client-side E2 receive/decrypt
spore relay run # store-and-forward hop (auth + backoff)
spore status | doctor # health HUD + preflight
# Serverless (no home node): bodies live on a public Nostr relay commons.
# -store nostr://relay.damus.io,nos.lol -store-key ~/.spore/store.key
# (any E2 command; dedicated key required — see docs/CARRIER_MATRIX.md)
# Compostability as a user feature:
spore panic [-home ~/.spore] [-confirm] # verifiable local wipe of keys/state/maildb/spool
# Explicit continuity vault (no automatic fund movement):
spore continuity create -owner-key FILE -recipient-pub HEX[,HEX,...] -file PAYLOAD -out VAULT
spore continuity check-in -vault VAULT -owner-key FILE
spore continuity status -vault VAULT [-at UNIX]
spore continuity release -vault VAULT -recipient-key FILE -out PAYLOAD [-at UNIX]
spore continuity verify -vault VAULT
spore continuity watch-init -vault VAULT -observer-key KEY -out WATCH_STATE
spore continuity watch -vault VAULT -observer-key KEY -state WATCH_STATE -notice NOTICE -outbox OUTBOX -webhook URL [-at UNIX] [-flush]
spore continuity recovery-create -vault VAULT [-policy POLICY] [-quorum QUORUM] [-anchor ANCHOR] [-receipt RECEIPT] [-watch WATCH_STATE] [-notice NOTICE] -out BUNDLE
spore continuity recovery-verify -bundle BUNDLE
spore continuity recovery-restore -bundle BUNDLE -dir EMPTY_DIR
# N-of-M independent observer release:
spore continuity quorum-create -vault VAULT -threshold N -attester-pub HEX[,HEX,...] -out POLICY
spore continuity attest -vault VAULT -policy POLICY -observer-key KEY -out ATTESTATION [-at UNIX]
spore continuity quorum -policy POLICY -attestations A1[,A2,...] -out QUORUM
spore continuity verify-quorum -quorum QUORUM [-vault VAULT]
spore continuity release-quorum -vault VAULT -quorum QUORUM -recipient-key KEY -out PAYLOAD [-at UNIX]
# Optional chain commitment (opaque IDs only; posting is explicit):
spore continuity anchor-create -vault VAULT -policy POLICY -out ANCHOR
spore continuity anchor-verify -anchor ANCHOR -vault VAULT -policy POLICY
spore continuity anchor-post -anchor ANCHOR -vault VAULT -policy POLICY -to DERO_ADDR [-rpc URL] [-rpc-user USER] [-ringsize 8|16] [-receipt RECEIPT] # password is prompted securely
spore continuity anchor-check -receipt RECEIPT -anchor ANCHOR [-rpc URL] [-rpc-user USER] # wallet-history payload readback
Plaintext is never an argv flag (shell history, ps, and crash reports
read argv) — use -msg-file or stdin. Run spore with no args for full usage.
- Forward-private + compostable on the E2 path; legacy paths are not. DERO E2 posts accept only
-ringsize 8or-ringsize 16, defaulting to 16. - No relay: a whisper is a real tx that P2P-fans to the recipient's node.
- Metadata is visible: "a tx happened at ~time" is chain-wide public. DERO's ring sigs hide the sender; EVM/Solana/Bitcoin/TON expose tx metadata (content stays private via the off-chain ratchet body). ntfy sees "you got a message" + a short txid, never the body.
- Immutable carriers keep the pointer scrap forever — deleting an off-chain body does not erase the on-chain pointer. No protocol can promise otherwise.
- Local plaintext:
maildbstores decrypted snippets for search (0600, purge-able); the spool stores queued plaintext (0600, HMAC-sealed).panicwipes both. - Threat model:
docs/SENDER_AUTH.md· wire formats:docs/WIRE_SPEC.md· ratchet:docs/RATCHET.md.
Three deployment postures, from zero infrastructure to fully self-hosted:
| Serverless | Home node (encouraged) | Hosted (Model B) | |
|---|---|---|---|
| You run | nothing | chain node + spore mailbox host (+ optional spore relay run) |
nothing — you pay the operator |
| Off-chain bodies | nostr:// public relay commons (-store nostr://relay1,relay2) |
your mailbox over TLS + token | the service's mailbox (blind courier) |
| Prekey discovery | manual bundle exchange (-bundle FILE), or your own mailbox |
your mailbox serves GET /prekey |
the service's mailbox |
| Who's in the middle | nobody you pay; relays see ciphertext-by-CID | nobody | the service sees traffic + timing, never content |
| Trade-off | 256 KiB body cap, deletion is best-effort (the ratchet is the real erasure) | needs an always-on box | you trust the operator with metadata |
The serverless posture is the no-servers endgame: point -store at
sporepeer:// (the two endpoints run everything — see the store table
above) or at nostr:// relays, exchange bundles out-of-band, and no one
operates anything
for you. Bodies are content-addressed ciphertext on a public commons; deletion
is best-effort, so the ratchet's erased keys are what actually makes old
messages unreadable — see
docs/CARRIER_MATRIX.md
for the honest limits.
→ docs/HOME_NODE.md (copy-paste) ·
docs/MODEL_B_SERVICE.md ·
docs/MODEL_B_RUNBOOK.md
go build ./... # builds clean
go vet ./... # clean
go test ./... # all packages green
go test -race ./internal/ratchetwire ./internal/mailbox ./internal/relay # race-clean
Go 1.23.1+. No CGO.
One command runs the whole pre-push suite — gofmt/vet/tests, the doc-refs
pin checker, and (when a spore-peer checkout is present next to this one)
its cargo build/fmt/clippy/test gates plus the -race cross-binary interop
run against the freshly built Rust binary:
scripts/gates.sh # the full suite
scripts/gates.sh --quick # inner loop: skips the Rust gates and -race/interop
A lefthook pre-push hook runs the full suite automatically on every
git push (escape hatches: LEFTHOOK=0 git push … or --no-verify).
The global hooks setup is backed up in this repo:
scripts/install-global-hooks.sh recreates it on a new machine and
self-verifies; scripts/verify-hooks.sh detects drift on any machine
against the committed scripts/hooks-manifest.sha256.
Spore is BSD-3 free software. It stays free. The operator (not the protocol)
can earn from optional convenience — see docs/BUSINESS.md:
hosted Model-B mailboxes, settlement rake on in-chat escrow/swaps (sap/relay-dex),
and an optional business tier. None of it is required to use Spore privately and
forever-free.
See ROADMAP.md. Shipped recently: E2 (0xE2) forward-private
transport, 4 new carriers, pay-with-message + in-thread invoices, local maildb
(contacts/threads/search), offline compose queue, single-use prekey batches,
one-shot onboarding, panic wipe, multi-device state sync via e2-device, and
serverless bodies over spore-peer (sporepeer:// store backend, spore serve daemon, cross-binary Go↔Rust interop tests). Next: tokenized search,
in-chat settlement (escrow/swap), and real-chain EVM deployment.
BSD 3-Clause. Spore is clean-room Go; it imports no derohe source. derohe-rs (the Rust port used for L1) and spore-peer are separately BSD-3-Clause.