POST to send
Sending Alice a message is an HTTP POST to https://alice.example/inbox, carrying a signed JSON envelope in the body.
a messaging protocol
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.
Three things every URL agrees to do.
Sending Alice a message is an HTTP POST to https://alice.example/inbox, carrying a signed JSON envelope in the body.
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.
An Ed25519 signature over the raw POST body. No header canonicalization, no string-to-sign rules — read bytes, verify, parse.
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
Defined in SPEC.md. Implementable in any language with an HTTP client and Ed25519 — under 100 lines.
HTTPS daemon, bubbletea TUI, SQLite-backed inbox/outbox. Pure Go (no cgo) for clean cross-compilation.
Python, JavaScript, and Bash. CI runs each against every test vector to prove the protocol is portable.
Under testdata/vectors/. They are the contract for cross-language interop, not an afterthought.
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.