diff --git a/README.md b/README.md
index de8925b..048ac57 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ For a server on a non-default port, start the bridge with `uv run bridge.py --al
- **Identity** (§2): one 32-byte seed; create or restore; the X25519 agreement keys are derived from the Ed25519 keypair. The seed lives in localStorage, mail stays sealed in IndexedDB — the browser profile is the trust boundary.
- **Addressing** (§3): short `user@host` and self-certifying `smol://` addresses, base32 fingerprints.
- **Trust** (§4, §8): server keys pinned on first trust, mismatch aborts the handshake; `RESOLVE` over an unpinned session is marked unverified; registration and fetching require a pinned server.
-- **Mail** (§5): sealed and signed envelopes, 1 KiB padding, frontmatter bodies, sent copies sealed to self, fetch verifies id and signature before acknowledging — anything unreadable stays on the server.
+- **Mail** (§5): sealed and signed envelopes, 1 KiB padding, frontmatter bodies, sent copies sealed to self, fetch verifies id and signature before acknowledging — anything unreadable stays on the server. Outgoing mail carries a signed `Reply-To` frontmatter field with the sender's full `smol://` address (an "anonymous" checkbox omits it), so a first-time recipient can name the sender and answer; on receipt such a claim binds only when its key matches the message's signer, and never over an address already pinned to a different key.
- **Rotation** (§7): rotate with a signed certificate; contacts accept the change from the chain; superseded seeds are kept, since mail sealed to them is readable with nothing else; key changes without a chain are surfaced, never applied silently.
## Layout
diff --git a/test/client.mjs b/test/client.mjs
index 890b9b0..c443a91 100644
--- a/test/client.mjs
+++ b/test/client.mjs
@@ -201,6 +201,9 @@ const commands = {
const fields = [["Subject", parseOption("--subject")], ["In-Reply-To", parseOption("--reply-to")]]
.filter(([, v]) => v);
+ if (state.account && !rest.includes("--anonymous"))
+ fields.push(["Reply-To", proto.parseAddress(`${state.account.user}@${state.account.host}` +
+ (state.account.port === proto.DEFAULT_PORT ? "" : `:${state.account.port}`)).uri(me.publicKey)]);
const body = utf8Bytes(proto.buildFrontmatter(fields, (parseOption("--body") || "") + "\n"));
const envelope = proto.seal(me, recipient, body);
const opened = await open(addr.host, addr.port, { requirePin: false });
diff --git a/test/e2e.py b/test/e2e.py
index ca2e7ff..f6000b7 100644
--- a/test/e2e.py
+++ b/test/e2e.py
@@ -107,6 +107,10 @@ def main() -> int:
alice_read = run(alice + ["read", reply_id[:8]])
check("alice reads bob's reply", "hello alice" in alice_read, alice_read)
check("reference client verifies the JS signature", "signature verified" in alice_read)
+ # bob's reply carries his signed smol:// address; a receiver that does
+ # not know the field must still preserve and display it (SPEC.md §5.5)
+ check("reference client preserves the signed reply address",
+ "reply-to:smol://bob@" in alice_read, alice_read)
# --- rotation: the JS client rotates, the reference client walks the chain
bob_rotated = run(bob + ["rotate"])
diff --git a/web/index.html b/web/index.html
index 43cc861..5367686 100644
--- a/web/index.html
+++ b/web/index.html
@@ -44,6 +44,7 @@