PRD: v1 reference implementation #1
Labels
No labels
needs-triage
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
arne/msg#1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem Statement
The wire protocol and design rationale are documented in
SPEC.md,CONTEXT.md,README.md, andCLAUDE.md, but no implementation exists. Users cannot send or receive messages; the protocol is paper, not running software. Anyone wanting to integrate URL-to-URL messaging with cryptographic origin authentication has nothing to install, run, or import. There are also no test vectors yet, so the spec's normative conformance contract — declared inSPEC.md§14 — is not enforceable.Solution
Build the v1 reference implementation as a Go single binary with a public package for embedding (
pkg/msg), an HTTPS daemon (msg serve), a bubbletea TUI (msg) sharing SQLite state with the daemon, and a normative test-vector suite undertestdata/vectors/. Ship example clients in Python, JavaScript, and Bash to prove the spec is implementable in <100 lines elsewhere.After this PRD is delivered, a user can:
msg initto set up a URL identity and TLS configuration.msg serveto start receiving at their URL.msgto read inbox and compose messages from a TUI.msg send <url>for shell automation.SPEC.mdand the test vectors.pkg/msg.User Stories
Operator / first-time setup
msg init, so I don't have to manually generate keys, write a config, or pick a TLS mode.Sender
bad-signature,wrong-recipient,stale-timestamp,unknown-key,duplicate-id,malformed-envelope,unsupported-version), so I can debug.echo … | msg send <url>), so I can automate notifications from scripts.inReplyToset automatically, so threads work without ceremony.Receiver
SPEC.md§9, so senders can debug.keyIdarrives, so key rotation works transparently.--acl=contacts), so I can run a stricter policy.(sender, id)duplicate to be rejected asduplicate-id, so replays are cleanly visible in error responses.TUI
namefrom a peer's actor doc as a label alongside their URL, never as a substitute for the URL, so I can't be fooled by a spoofed display name.Identity and key management
0600in my config dir, so they're protected by filesystem permissions.msg keys rotate), so I can recover from suspected compromise.msg keys prune), so my actor doc stays clean.Embedder / protocol implementer
pkg/msg) for embedding sign / verify / actor logic, so I can integrate URL-to-URL messaging into my Go service without running a sidecar.Maintainer
go test ./..., so a regression in protocol behavior fails CI.Operations
Implementation Decisions
Architecture
code.bas.es/arne/msg. Public package atpkg/msg; daemon, TUI, and store internals underinternal/.modernc.org/sqlite(pure Go) — keeps the binary cgo-free for clean cross-compilation.msg serve(daemon) andmsg(TUI). No IPC, no Unix sockets, no localhost HTTP between them.Deep modules
These encapsulate behavior behind a small, stable interface and carry the protocol's logic. They are individually unit-testable and composable.
envelope— envelope and actor-doc schema, JSON encode/decode, structural validation. Pure.signing— Ed25519 sign and verify over arbitrary byte sequences, plus key encoding. Pure crypto.verifier— composes envelope + signing + actor cache + clock + dedup-set into the fullSPEC.md§7 verification procedure. Returns a structured outcome with stableSPEC.md§9 error codes. Single entry point: takes raw body bytes, signature header, claimed recipient URL, and dependencies. This is the protocol's central rejection-logic module — every vector tests it.sender— symmetric: builds an envelope, signs the raw body bytes, POSTs to the recipient, parses the response (status, error code, optional receipt). Receipt verification reuses the same verifier path.actor_cache— TTL-based cache of actor docs keyed by URL, encapsulating theSPEC.md§7.3 "refetch once on unknown keyId" rule. Hides fetch + cache + refetch policy.keys— load / savekeys.json, generate, rotate (add new + retire old with retain window), prune. File-mode 0600 enforcement.outbox— send-loop state machine: pending → sending → delivered | failed-permanent | failed-pending-user. Backoff schedule 5s, 30s, 5m, 30m. Hard cap: 4 attempts or 24h elapsed.Shallow / glue modules
store— SQLite migrations and typed DAO methods over the schema (unifiedmessagestable withdirection, plusactor_cache,contacts,receipts, FTS5 virtual tablemessages_fts).http—net/httphandler wiring the verifier into request handling; serves the actor doc onGET <url>; handles per-sender rate limiting and the--acl=contactsmode.tls— config helper for the three TLS modes (auto via certmagic / autocert; cert+key files; HTTP loopback for reverse-proxy fronting).config— TOML loading at${XDG_CONFIG_HOME:-~/.config}/msg/config.toml, with override stack flag > env (MSG_*) > config > default.tui— bubbletea views, lipgloss styles, key bindings; address-book add flow;rfor retry;/for FTS search;?for help.cli— cobra command tree forserve,init,send,inbox list,keys init|rotate|prune,version.examples— Python, JavaScript, and Bash reference clients exercising the same vectors against the Go binary.Wire protocol behavior
testdata/vectors/. Vectors are normative: a behavior change updates the vector first, the code second.Msg-Receipt: required. Receivers may decline (return 204); senders tolerate the absence.algorithmfield on each key in the actor doc is the source of truth for what verifies a givenkeyId.Msg-Signaturecarries plain padded-base64 (RFC 4648 §4), no algorithm prefix.bad-signature,stale-timestamp,unknown-key,duplicate-id,wrong-recipient,malformed-envelope,unsupported-version) are stable. Renaming is breaking.Defaults and policy
:443when TLS mode isautoorfiles;127.0.0.1:8088whennone.Storage schema (high-level)
messages— unified inbox + outbox.directioncolumn. Outbound rows additionally carrysend_status,attempts,next_attempt_at,error_code. Raw signed envelope bytes stored on every row for re-verification, audit-grade export, and migration.actor_cache— URL → cached actor doc +fetched_at.contacts— URL → user-defineddisplay_name, pinned status.receipts— signed delivery acks for outbound messages, FK tomessages.rowid.messages_fts— FTS5 virtual table overpayloadandpeer_urlfor TUI search.Logging
slogJSON output by default. Levels: error, warn, info, debug.Testing Decisions
A good test asserts external behavior at the public boundary, not implementation details. For msg, this means:
verifierandsender. Every file undertestdata/vectors/is a black-box case (inputs + expected status code, error code, or expected signature bytes). Vectors are the conformance contract; a vector change is a behavior change.envelope,signing,actor_cache,keys,outbox. Each has a small, pure-ish interface that is comfortable to test in isolation.tui,cli,tls,config. Covered transitively by the integration test or are too presentation-heavy to test in isolation cheaply.Prior art: this is a greenfield Go repo; no in-repo prior art exists. Reference patterns are the Go standard library's
*_test.gotable-driven test convention, thetestdata/directory convention, and black-box testing viapkg_testpackages.Out of Scope
The following are explicitly out of scope for v1, per
CONTEXT.md:keys.jsonbut is not designed-for).0600; threat model documented inCONTEXT.md.--notifyin v1).Further Notes
Recommended order of work
The order matters for keeping spec-first discipline honest:
envelope+signing+ actor-doc parsing (pure modules, zero I/O).testdata/vectors/— start with sign-and-verify, tampered-body, and recipient-mismatch cases.verifier+sender(compose the above; vectors run green).actor_cache,store,http,outbox(the daemon parts).examples/python— do this before the TUI. If a Python client takes more than ~100 lines, the spec is too complex; fix the spec before building UI on top of it.tls+config+cli(operator surface).tui(presentation).examples/javascript+examples/bash(round out cross-language coverage).License
Deferred to maintainer; README placeholder.