diff --git a/docs/discord-references.md b/docs/discord-references.md new file mode 100644 index 0000000..b317b37 --- /dev/null +++ b/docs/discord-references.md @@ -0,0 +1,98 @@ +# references to DOs in Discord messages + +*In Smalltalk, JavaScript, and Unix sockets, a message can carry +references the receiver can then send messages on. Discord messages +can refer to messages, threads, channels, and servers, but not to our +Durable Objects. This note works out how they could, after +[@endo/chat](https://github.com/endojs/endo-but-for-bots/tree/llm/packages/chat). +Written 2026-10-08.* + +## webkeys in markdown: works, but discloses + +The cheapest option is a [webkey](webkeys.md) as a markdown link, +`[cookie store](https://webapp.example/store/#)`. It gets the +basics right: following it in a browser works, and the key sits in the +fragment, so Discord's link-preview fetcher never sends it anywhere. + +But the message body itself is the leak. Every reader of the channel +now holds the capability, and so does Discord: its servers, its logs, +its search index, its moderation tooling, and whoever reads those. +That is fine for capabilities meant to be public (a public cache page) +and wrong for anything like webapp's store id, where holding it means +choosing whose session the cache uses. + +## what @endo/chat does instead + +In @endo/chat a message is not a string with secrets in it. Its parser +(`message-parse.js`) splits `Here is @my-file for you` into + +- `strings`: the text between references, +- `edgeNames`: the names the *sender* put in the text (`my-file`), +- `petNames`: the sender's own names for those references, + +and the daemon delivers the references themselves beside the text, +over CapTP. The receiver sees the edge name and **adopts** the +reference under a petname of their own. Three things follow: + +- **References never appear in text.** Only names do, and names are + safe to show anyone. +- **Names are per-person.** My `cookie-store` and your `alice's store` + can be the same object, and neither name has authority by itself. +- **Adopting is an act.** Receiving a message gives you the option to + hold a reference, not the reference itself, until you choose a name + for it. + +## the endo-shaped version for Discord + +A capsite bot (a Worker serving Discord's HTTP interactions endpoint) +plays the daemon's part: + +- **Petstores.** Each Discord user has a petstore DO: petname → + DO reference. Minting files results there directly: webapp's mint + could file the store id under `diigo cookie store` in Alice's + petstore instead of handing it to her password manager. +- **Sending.** A slash command, e.g. + `/send to:@bob text:"here is @cookie-store"`. The bot parses the text + like `parseMessage`, looks up each `@name` in the sender's petstore, + posts the text (names only) to the channel, and files + `{ discord message id → recipients, edgeName → reference }` in a + mailbox DO. +- **Adopting.** The posted message carries an **Adopt** button. On a + click, Discord tells the bot who clicked (in a signed interaction); + if they are a recipient, the bot answers with an *ephemeral* message, + shown only to them, asking for a petname, then files the reference + in *their* petstore. +- **Using.** References are exercised through the bot by petname + (`/call cookie-store set cookie …`, or a link to a page the bot + serves). The reference stays inside our Workers. + +### what this does and doesn't keep from Discord + +Ephemeral replies hide content from other channel readers, but **not +from Discord**: they still pass through its servers. So the design +above never sends a reference through Discord at all, ephemeral or +not; only petnames, edge names, and message ids cross it. Handing over +a webkey in an ephemeral reply would be a weaker variant: hidden from +the channel, still disclosed to the infrastructure. + +What Discord *does* hold is identity: the bot believes Discord about +who clicked **Adopt**. A compromised Discord account, or Discord +itself, could therefore adopt references meant for that user. That is +the same trust users already put in Discord for their messages, but it +should be stated where references matter more than chat does. + +## open questions + +- **Durable references across RPC.** A petstore entry is a DO id plus + enough to say what kind of thing it is (webapp instance, header + store, cache). Getting from that back to a live RpcTarget is the + marshalling-layer question from the cookie-rotation work + (`docs/cookie-rotation-wiring.html` on the `webapp-cookie-rotation` + branch). +- **Petstore ids.** `idFromName(discordUserId)` is convenient but + ambient: any worker with the namespace binding can recompute it. + Acceptable if only the bot has the binding; otherwise mint with + `newUniqueId` and keep the mapping in one place. +- **Leaving Discord.** The same mailbox and petstore could back a web + page or @endo/chat itself; Discord would then be one front end of + several.