Netherchat netherchat
Read the docs
The war room that vanishes.

Messaging that lives
below the surface

Self-hostable, end-to-end encrypted coordination for incidents and sensitive operations — with signed, verifiable records of what was decided.

Self-hostable, end-to-end encrypted, real-time messaging. A blind-relay server that cannot read your messages — proven by the build graph, not a promise. Your server. Your keys. Your rules.

$ curl -fsSL https://netherchat.com/install | bash

macOS · Linux · Windows (PowerShell) · or docker run -p 3000:3000 salkreiner/netherchat

“We cannot read your messages.” Said literally.

The encryption lives in the client. The server is a blind relay — it routes ciphertext and sealed key blobs it has no key to open.

⛓️

Enforced by the build graph

The crypto package lives under tui/internal; Go's rules make it physically unreachable from the server binary. CI fails if that ever changes. It's a property of the import graph, not marketing.

🔑

Keys never touch the server

Identities are generated and stored on each device. Room keys are wrapped per-recipient with nacl/box and relayed as opaque blobs. No escrow, no recovery — by design.

📡

Zero telemetry, zero persistence

No analytics, no phone-home, ever. By default nothing is written to disk — rooms evaporate when empty — and out of the box the relay makes no outbound network calls. Two opt-in features add egress once an operator configures them: a route's reply_url and --tor. One further call never leaves the host — --healthcheck GETs /health on 127.0.0.1 so the FROM scratch image can declare a Docker healthcheck. Message bodies cross the wire as ciphertext it has no key to open.

Honest about limits: metadata is not hidden — the relay sees who is in which room, message sizes and timing, and over a plain ws:// hop so does the network, because the routing fields around the ciphertext are plaintext JSON (terminate TLS at a reverse proxy in front of the relay). Forward secrecy is per-epoch (not per-message), and the group key-distribution layer is custom over audited primitives — it should get an independent cryptographic review before any paid tier, with MLS (RFC 9420) as the migration target. See encryption.md.

Built for engineering teams

Designed to scale to everyone else without compromising the developer experience.

End-to-end encrypted

X25519 + XChaCha20-Poly1305 + Ed25519 + HKDF. Pure Go in the terminal, byte-compatible in the browser — the same crypto on both sides, checked by shared interop vectors.

Static binaries, no runtime

No runtime, no dependencies, no libc. The relay is a ~12 MB FROM scratch image holding a single binary; each release archive carries both the netherchat client and the netherchat-server relay, for macOS, Linux and Windows. curl … | bash installs the client — add --with-server for the relay.

Terminal & browser

A modern TUI for power users and a link-join browser client for guests — both speak one documented wire protocol to the same relay.

Eight themes, instant

nether, abyss, ember, ghost, sprinkles, dracula, gruvbox, solarized — switch with no reload. Try the picker above.

Pipes & webhooks

echo "deploy done" | netherchat send ops. Give a room a webhook token in netherchat.toml and it accepts inbound posts from CI and alerts. Any alert source can open a locked-down war room — a monitoring page, a CI failure, or a security tool posting a finding.

Ephemeral by default

/vanish rotates the room key and clears history. Room TTLs, one-time invite tokens, invite-only rooms.

Proven by a check you run

netherchat doctor --paranoid stands up a frame-tapped relay running the production relay code, round-trips a canary between two real clients, and shows the canary in none of the frames the relay saw — with the ciphertext at 7.5+ bits/byte of entropy. Not a claim. A test you run yourself.

Incident timeline, two audiences

netherchat report renders the sealed record as a standalone HTML timeline — human-readable for leadership, cryptographically verifiable by engineers. One file, no server, works forever.

Relay-less when it matters

netherchat pair --lan forms an encrypted war room with no server at all — same keys, same crypto, zero infrastructure. When even the relay is suspect, the conversation continues.

Decisions you can prove

🔗

Signed, attributed decisions

Every /decide, /action, and approval is Ed25519-signed by the person who said it, hash-chained to everything before it, and sealed into a record you can verify offline — forever, with no server and no account.

✓✓

Cryptographic two-person rule

High-stakes actions — scuttle a room, run a runbook, approve an artifact — require N-of-M independent Ed25519 signatures before they fire. Not a policy. A protocol primitive.

📋

AI drafted, human approved

An agent proposes an artifact (hash only — content never crosses). A named human approves under the two-person rule. The sealed record proves who drafted it, who approved it, and when — verifiable offline after the room is gone.

🔍

Verify with no server

netherchat verify record.json checks the full hash chain and every signature offline, after the room is gone, on any machine. VALID or TAMPERED — no account, no relay, no trust required.

🔥

Proof of destruction

Scuttle a room and a signed receipt — co-signed by the members present — is written before the keys are zeroized, proving what was destroyed and when. The one artifact that outlives the room, verifiable offline. Ephemerality you can prove, not just promise.

Run it yourself in one line

1

Start the relay

$ docker run -p 3000:3000 salkreiner/netherchat
2

Install the terminal client

$ curl -fsSL https://netherchat.com/install | bash
3

Join from a browser

Share a one-time /break-glass link. Anyone clicks it, types a name, and they're in the room — no install, no account.

Make it yours