a messaging protocol

Every participant
is a URL.

To send Alice a message, you POST to her URL. When her server receives a message claiming to be from Bob, it verifies the signature against the public key Bob publishes at his URL. No relays, no directory, no PKI — HTTPS bootstraps the trust.

How it works

Three things every URL agrees to do.

1

POST to send

Sending Alice a message is an HTTP POST to https://alice.example/inbox, carrying a signed JSON envelope in the body.

2

GET to identify

The same URL, on GET, returns an actor doc with the participant's published public keys. The URL is the identity; everything else is a label.

3

Sign the body

An Ed25519 signature over the raw POST body. No header canonicalization, no string-to-sign rules — read bytes, verify, parse.

Quick start

A single Go binary. SQLite for storage. No cgo.

The reference implementation ships as msg — a CLI with a daemon mode (msg serve), an interactive inbox TUI (msg), and one-shot send for shell pipelines.

# install
go install code.bas.es/arne/msg/cmd/msg@latest

# first-run setup (URL, TLS mode, display name)
msg init

# start the daemon — auto-TLS via Let's Encrypt by default
msg serve

# open the inbox/compose TUI
msg

# one-shot send, for shell pipelines
echo '{"hello":"world"}' | msg send https://bob.example/inbox

What's in the repo

A wire protocol

Defined in SPEC.md. Implementable in any language with an HTTP client and Ed25519 — under 100 lines.

A reference Go binary

HTTPS daemon, bubbletea TUI, SQLite-backed inbox/outbox. Pure Go (no cgo) for clean cross-compilation.

Examples in three languages

Python, JavaScript, and Bash. CI runs each against every test vector to prove the protocol is portable.

Normative test vectors

Under testdata/vectors/. They are the contract for cross-language interop, not an afterthought.

Why this exists

If you want to receive a message at https://alice.example/inbox and know with cryptographic certainty that it really came from https://bob.example/inbox, this is the simplest thing that works.

No accounts. No shared API keys. No service in the middle.

Read further