Plenum
User Stories and Use Cases
(UI, MCP, and REST - end customers and escrow vendors)
Internal working document · June 2026 · companion to plenum_idea.md and architecture_idea.md
This catalogues what people and programs actually do with Plenum, exhaustively, across the three surfaces (the desktop GUI, the MCP API, the REST API) and the two personas (an end customer running shared custody, and a vendor who has automated the wallet as a signer in a multisig escrow).
Two principles from the architecture frame everything below and are not repeated in each entry:
- One command set. The GUI, MCP, and REST are co-equal frontends over the same
WalletCommand/WalletEventcontract (architecture_idea W8, D3). Any action one surface can trigger, the others can too. In non-headless mode the GUI mirrors what an MCP agent or REST caller does, live, and the human can intervene. - The threshold is the hard bound. A client holds exactly one FROST share. Nothing any surface does can exceed the group's m-of-n threshold. "The agent may do everything" means everything a single member may do, never more. Per-spend human gating is a configurable policy layered on top, not a property of the threshold.
Each use case lists its Actor, the Surfaces it applies to, Preconditions, the Flow, Variations (errors and branches), and the Postcondition. Command names refer to architecture_idea D3.
Contents¶
- 1. Actors and surfaces
- 2. End-customer use cases
- 3. Vendor and escrow-automation use cases
- 4. Surface ergonomics
- 5. Cross-cutting behaviours
1. Actors and surfaces¶↑
Actors.
- Member - one of the n signers in a group. Every participant is a member; there is no admin role after setup. The master is a member who happened to initiate a group (a bootstrap role only, no lasting power).
- End customer - a human member running shared custody for themselves: a couple's savings, a small treasury, a club, two partners holding escrow between themselves. Drives Plenum mainly through the GUI.
- Vendor - an operator who has automated their own member client to act as a signer in escrows they offer to others (a marketplace arbiter, an OTC desk, a service that holds funds between a buyer and seller). Drives Plenum through MCP or REST, usually headless, often at scale.
- AI agent - a program acting for a member through MCP, under a policy, with the GUI watching when attached.
- Verifier - an outsider who is not a member and runs no group client. They consume what an auditable group publishes (a signed statement, a proof of reserves) and check it against the group's published identity key. Verification is local, needs no interaction with the group, and reveals nothing to it (2.9.6, 2.9.7).
- Observer / adversary - not a user, but present in every flow; see the whitepaper threat model. Listed here only where a use case changes what an observer learns.
Surfaces.
- GUI - the desktop wallet. Human-facing, visual, shows approval prompts and the live event stream.
- MCP - tools for an AI agent. Same commands, plus the agent sees the same events the GUI does.
- REST - typed HTTP/RPC for scripts and remote operators, keypair-authenticated. Same commands.
A single deployment can expose all three at once: a vendor might run REST for its order backend, MCP for an AI agent that judges disputes, and a GUI for a human operator to watch and override.
2. End-customer use cases¶↑
2.1 Onboarding and identity¶↑
UC-2.1.1 First run, create wallet root.
Actor: end customer. Surfaces: GUI or local provisioning only (the seed is generated and shown on the device; it is never created or transported over the REST/MCP network API). A headless instance is seeded out of band locally, not via the API. Precondition: fresh install, no wallet.
Flow: the client generates a seed (Polyseed by default), shows the words for the user to record, and derives the backup-encryption key from it. No MLS credential exists yet: a fresh, per-group credential is minted when the user creates or joins a group (2.2), so identities are never shared across groups. Command: CreateIdentity (a sub-step of first launch).
Variations: the seed is the backup root, not a Monero spend seed and not a standing network identity; it derives the backup-encryption key only. The per-group MLS credential and the FROST share are generated at group setup and live inside the backup blob. The user may set an optional local app-lock password (may be empty; it is not part of backup or recovery).
Postcondition: a backup root exists; no group, no credential, and nothing on-chain yet.
UC-2.1.2 Restore from seed.
Actor: end customer on a new device. Surfaces: GUI or local provisioning only (the seed is entered on the device, never over the network API). Precondition: the user has their seed.
Flow: enter the seed; the client re-derives the backup key, fetches a surviving backup copy, and decrypts it to recover the per-group credential, the FROST share, and state (UC-2.7.1). The seed alone, with no surviving backup, recovers nothing usable, since the credential and share live only in the blob. Command: Restore { source: Seed }.
Variations: a wrong or mistyped seed derives the wrong backup key and decrypts nothing; the client warns rather than silently producing an empty wallet.
Postcondition: identity and group membership reconstituted if a backup copy survives.
UC-2.1.3 Set or change the app-lock. Actor: end customer. Surfaces: GUI. Precondition: identity exists. Flow: set, change, or clear the local screen-lock password. Postcondition: the running app is gated locally; losing this password costs nothing while the seed survives.
2.2 Forming or joining a group¶↑
UC-2.2.1 Initiate a group as master.
Actor: end customer (initiator). Surfaces: all. Precondition: identity exists; the initiator knows who the members are and how to reach them out of band.
Flow: choose the threshold (m-of-n) and the member set; the client creates the MLS group, derives the rendezvous, and produces one out-of-band invite per member carrying only a ceremony id and rendezvous coordinates. Command: CreateGroup { config }.
Variations: the initiator can cancel before the ceremony completes; no funds exist yet.
Postcondition: an MLS group exists awaiting members; the DKG has not run.
UC-2.2.2 Distribute invites out of band. Actor: master. Surfaces: all (the client renders the invite; the human or script delivers it over a channel the members already trust). Precondition: a group is being formed. Flow: the master sends each invite over an existing trusted channel (in person, an existing secure messenger). The invite is not a credential and grants nothing on its own. Postcondition: members can reach the ceremony rendezvous.
UC-2.2.3 Join a group as an invited member.
Actor: end customer (invitee). Surfaces: all. Precondition: received a valid invite.
Flow: the client generates its credential and KeyPackage locally and returns the KeyPackage to the master, who applies the MLS Add and Welcome. Command: Join { invite }.
Postcondition: the member is in the MLS group, ready for the DKG.
UC-2.2.4 Run the key-generation ceremony. Actor: all members + master as sequencer. Surfaces: all (usually unattended once started; the GUI shows progress). Precondition: all members joined. Flow: the two-round FROST DKG runs (commit, then share), the master relaying each barrier; every member ends with a FROST share and the identical group key. Variations: a slow member is reminded and re-invited; a permanent dropout aborts and restarts the (cheap, two-round) DKG with the replacement set. Postcondition: a group key exists; the Monero address is derived.
UC-2.2.5 Verify members out of band. Actor: end customer. Surfaces: GUI (shows fingerprints), all. Precondition: ceremony in progress or just completed. Flow: members compare their MLS credential fingerprints over the same trusted channel used for the invite, binding each key to a real person. Postcondition: the member set is human-verified.
UC-2.2.6 Confirm the final address (all-N gate).
Actor: all members. Surfaces: all. Precondition: DKG completed.
Flow: every member independently derives the address and reports it; the group is valid only when all n report the identical address. A mismatch (a substituted key, a buggy or malicious master) aborts the ceremony. Command/event: SetupAddressConfirm, GroupReady.
Postcondition: the wallet is live and trusted, or it failed closed.
2.3 Receiving funds¶↑
UC-2.3.1 Show a receive address. Actor: end customer. Surfaces: all. Precondition: group ready. Flow: the client shows the group's Monero address (or a subaddress slot) to hand out. Postcondition: a payer can send.
UC-2.3.2 Detect incoming funds and show balance.
Actor: every member independently. Surfaces: all. Precondition: group ready; a node is reachable.
Flow: the client scans the chain with the shared view key; incoming outputs are detected by each member independently. The correct spendable balance additionally needs the per-output key-image addenda synced over the channel (to know what the group has already spent) plus the channel history, so a member returning from a long offline period catches up on both before its balance is authoritative. Command/event: ScanTick, Scanned.
Variations: a reorg rolls back affected outputs; a member offline for a long time catches up on reconnect.
Postcondition: balance reflects confirmed receipts.
UC-2.3.3 Label addresses and entries. Actor: end customer. Surfaces: GUI (primary). Precondition: wallet in use. Flow: attach human labels to addresses and transactions; labels live in the encrypted backup blob, not on-chain. Postcondition: personal bookkeeping, private to the member.
2.4 Spending and approval¶↑
This is the core threshold flow. It is identical underneath for all surfaces; only the prompts differ.
UC-2.4.1 Propose a spend.
Actor: any member. Surfaces: all. Precondition: spendable balance.
Flow: the proposer specifies payments (destination, amount) and an optional note; the client builds the deterministic unsigned transaction and broadcasts a proposal to the group. Command/event: ProposeSpend, SpendProposed.
Variations: insufficient funds, or a fee that pushes the change negative, are rejected at build time before any proposal goes out.
Postcondition: a pending proposal visible to all members.
UC-2.4.2 Review a pending proposal.
Actor: every other member. Surfaces: all. Precondition: a proposal exists.
Flow: the client first rebuilds the unsigned transaction from the proposal's parameters and confirms it matches the received unsigned_tx, then shows the destination, amount, and fee parsed from that verified transaction (never the self-declared fields), plus who proposed it and the note. The GUI renders it as an approval card; MCP/REST return the verified structured object. A rebuild mismatch is flagged and the proposal cannot be approved (fail closed).
Postcondition: the member can approve or reject.
UC-2.4.3 Approve (contribute a signature).
Actor: a member. Surfaces: all. Precondition: a proposal under review.
Flow: on approval the client joins the two-round FROST signing for that proposal (nonce commitment, then partial signature). Command/event: Approve, SpendApproved { have, need }.
Variations: the member may approve unattended under a policy (UC-3.7); the human may be prompted per spend.
Postcondition: one more share toward the threshold; progress reported as have/need.
UC-2.4.4 Reject or veto a proposal.
Actor: a member. Surfaces: all. Precondition: a proposal under review.
Flow: the member declines to contribute; the proposal cannot reach threshold without enough approvers. Command: Reject.
Postcondition: the proposal stalls or is abandoned; no funds move. There is no on-chain trace of a rejected proposal.
UC-2.4.5 Threshold reached, finalize and broadcast.
Actor: the group (automatic once m approve). Surfaces: all observe. Precondition: m valid shares collected.
Flow: the proposer coordinates its own proposal (a transient role, no standing power), with the first m members to approve forming the signing set; their shares aggregate into a valid transaction, which the client broadcasts and tracks via the eventuality. Event: SpendBroadcast { txid }, then SpendFinalized.
Variations: a faulty share is attributed to its signer and the round can be re-attempted without it; a signer dropping mid-round aborts that attempt, which restarts with fresh nonces (never replayed); two proposals racing for the same inputs are resolved by locking the inputs over the channel before any broadcast, so they do not both reach the mempool, with first-broadcast-wins as the last-resort tiebreaker; a crash between sign and broadcast is recovered by checking whether the transaction already landed.
Postcondition: funds moved; all members see the txid.
UC-2.4.6 Track confirmation. Actor: any member. Surfaces: all. Flow: the client watches the chain until the spend confirms. Postcondition: the spend is final.
2.5 Coordination and the group channel¶↑
UC-2.5.1 Group chat.
Actor: members. Surfaces: GUI (primary), REST/MCP (text in, events out). Precondition: group exists.
Flow: members exchange ordinary messages in the group's social room, the same encrypted channel that carries coordination. A live chat keeps the group reachable, which keeps the backup network healthy. Command: ChatMessage.
Postcondition: a record private to the group.
UC-2.5.2 Be notified that a signature is needed. Actor: a member. Surfaces: all. Flow: when a proposal needs this member, the client surfaces a notification (GUI badge, MCP/REST event). Postcondition: the member knows to act.
UC-2.5.3 Receive while offline (store and forward). Actor: an offline member. Surfaces: all. Flow: the others hold messages destined for an offline member; on reconnect the member receives the queue and catches up. Postcondition: nobody must be online at the same time.
2.6 Backup¶↑
UC-2.6.1 Automatic backup.
Actor: every member's client. Surfaces: all (transparent). Precondition: group state changed.
Flow: the client assembles the encrypted blob (FROST share, group key, roster, scan state, labels), encrypts it under the seed-derived key, and replicates it to the configured destinations. Command/event: BackupNow (also automatic), BackupReplicated.
Postcondition: a current, seed-locked backup exists in several places.
UC-2.6.2 Configure backup destinations. Actor: end customer. Surfaces: GUI (primary), REST. Precondition: identity exists. Flow: enable or disable destinations: local (always on), one or more S3-compatible endpoints (ciphertext only, untrusted buckets fine), and the other members over the channel. Postcondition: chosen redundancy.
UC-2.6.3 Check backup health. Actor: end customer. Surfaces: all. Flow: the client reports which destinations hold a current blob version. Postcondition: the user knows their recovery surface.
2.7 Recovery and restore¶↑
UC-2.7.1 Lost device, seed survives (the normal case).
Actor: end customer. Surfaces: all. Precondition: the user has their seed and at least one backup copy survives.
Flow: re-enter the seed, fetch the blob from any surviving copy, decrypt, deserialize the FROST share and state, rejoin the MLS group by an MLS external commit using the group's current public GroupInfo (the member re-adds itself and re-keys in one step). Non-interactive: no other member and no master acts; the member needs only the current public GroupInfo, which each client mirrors into its own backup on every epoch change, so the normal restore is genuinely self-contained; only a stale backed-up copy makes a reachable member necessary. Command: Restore.
Postcondition: full membership restored unilaterally.
UC-2.7.2 Lost seed, member share gone. Actor: the affected member plus the surviving group. Surfaces: all. Precondition: the seed is lost, so no copy of that member's blob can be opened. Flow: that member's material is unrecoverable; funds remain safe while the threshold of other members survives. To restore redundancy the survivors run a fresh DKG (a new group, UC-2.8.1) dropping or replacing the lost member, and sweep funds across. Postcondition: a new group holds the funds; the old member is dropped or replaced.
UC-2.7.3 Catastrophic loss beyond threshold. Actor: the group. Precondition: more members lost their share than the threshold tolerates. Flow: funds cannot be moved, because sweeping itself needs the old threshold. This is the hard limit of m-of-n; the three-destination redundancy exists to make it unlikely. Postcondition: funds frozen; stated plainly, not worked around.
2.8 Membership change¶↑
UC-2.8.1 Replace or add or remove a member. Actor: the group. Surfaces: all. Precondition: the current threshold still holds. Flow: changing who signs is a brand-new group: a fresh DKG, new keys, new address, new backups, with no protocol link to the old one. Funds are moved across by an ordinary spend (UC-2.4) under the old threshold while it still holds. The old group and its backups become defunct and each member deletes their own copies when they choose. Variations: there is no in-place reshare (the only available primitive is a fresh DKG); see architecture_idea C4. Postcondition: a new independent group; the old one emptied and abandoned.
2.9 Auditable mode¶↑
A group is private by default. Auditable mode (whitepaper 8, architecture_idea W9) is an opt-in disclosure layer with compartmentalized keys; it never weakens private operation. Going auditable takes n-of-n consensus; the attestations afterward run at the normal m-of-n.
UC-2.9.1 Switch to auditable mode.
Actor: the group (n-of-n). Surfaces: all. Precondition: group in private mode.
Flow: a member proposes the switch and the group's public alias; every member must approve (n-of-n); the client derives the separate identity key for the alias and the group becomes auditable. Command: SwitchMode { Auditable, alias }.
Variations: any single member can veto going auditable (n-of-n); switching back to private is m-of-n and stops new attestations but cannot un-publish what is already out.
Postcondition: the group can publish an identity, statements, and reserves.
UC-2.9.2 Publish the group identity.
Actor: the group. Surfaces: all. Precondition: auditable mode.
Flow: publish the dedicated identity key as a handle (fingerprint, profile, .well-known). It is separate from the wallet spend key, the rendezvous, and member identities.
Postcondition: existence announced without exposing the private layer.
UC-2.9.3 Sign a public statement m-of-n.
Actor: the group. Surfaces: all. Precondition: auditable mode.
Flow: propose a statement; m signers contribute FROST shares (plain Schnorr); the output is one standard signature anyone can verify against the identity key. Command: SignStatement.
Postcondition: a publicly verifiable group statement, no on-chain action.
UC-2.9.4 Prove reserves m-of-n.
Actor: the group. Surfaces: all. Precondition: auditable mode.
Flow: the group produces, m-of-n, a zero-knowledge proof of reserves attesting that it controls at least a stated amount. The proof reveals only that minimum balance, not which outputs back it nor their origin. Command: ProveReserves.
Variations: this needs a ZK proof of reserves, not Monero's native reserve proof (which would reveal the outputs) nor a published view key (which would reveal all incoming). Maturity: this is the project's heaviest, research-grade crypto piece, not an off-the-shelf primitive and not yet available; it is expected to mature with the post-FCMP++ full-chain machinery (architecture_idea W9). Statement signing (2.9.3), by contrast, is build-now.
Postcondition: a third party can verify the group holds at least the claimed amount, learning nothing about which funds back it, without custody.
UC-2.9.5 Carry identity across a membership change. Actor: the group. Surfaces: all. Precondition: an auditable group changing members (a new group, UC-2.8.1). Flow: the old group signs a succession certificate endorsing the new group's identity key at the full n-of-n (endorsing a successor is as consequential as going public) and publishes it to a public append-only log; verifiers follow the chain, and a covert hijack by any subset is both impossible and immediately visible. Postcondition: the persistent public identity survives the new-group model without resharing.
The counterpart to producing attestations is a third party consuming them. The verifier is an outsider, not a group member.
UC-2.9.6 Verify a signed statement. Actor: verifier (outsider). Surfaces: none required: any standard Ed25519/Schnorr verifier works, a Plenum client is not needed. Precondition: the group is auditable and has published its identity key under its alias. Flow: the verifier takes the published statement and its signature and checks it against the group's identity key with a standard signature verification. If the group changed members since the verifier last saw the key, the verifier follows the succession-certificate chain (UC-2.9.5) to confirm the current identity key legitimately descends from the one they trust. No interaction with the group, nothing disclosed to it, and the group need not be online. Postcondition: the verifier knows the statement genuinely came from the group (an m-of-n of it), or rejects it.
UC-2.9.7 Verify a proof of reserves. Actor: verifier (outsider). Surfaces: none required: a zero-knowledge proof-of-reserves verifier plus the signature check from 2.9.6. Precondition: the group published a proof of reserves signed under its identity key. Flow: the verifier checks the signature binds the proof to the group's identity key (as in 2.9.6), then verifies the zero-knowledge proof of reserves against the chain: it confirms the group controls unspent outputs summing to at least the claimed minimum, without learning which outputs they are. Variations: the proof reveals only the minimum balance; it discloses neither the backing outputs nor their origin. Maturity: this verification depends on the threshold ZK proof of reserves, which is research-grade and not yet available (2.9.4); the signed-statement verification (2.9.6) works today. Postcondition: the verifier independently confirms the group holds at least the claimed balance, learning nothing about which funds back it, without trusting the group and without any custody relationship.
3. Vendor and escrow-automation use cases¶↑
A vendor runs their own Plenum member, automated, as one signer in escrows offered to other people. The vendor is never a custodian: they hold one share of an m-of-n, exactly like any member, and cannot move funds alone.
3.1 The escrow model¶↑
UC-3.1.1 The 2-of-3 escrow. Actor: vendor (arbiter), buyer, seller. Surfaces: vendor on MCP/REST, the two parties on GUI (or their own clients). Precondition: a trade to escrow. Flow: a group is formed with three members - buyer, seller, vendor - at threshold 2-of-3. Normal completion needs only buyer + seller (two signatures); the vendor's signature is needed only to break a dispute (vendor + one party = two). The vendor automating their signer means the arbiter is a program that evaluates release conditions and dispute outcomes under a policy. Postcondition: a funded escrow whose outcome any two of three can settle. Note: other shapes are possible (n-of-m with multiple arbiters, 2-of-2 between two principals with no arbiter), but 2-of-3 buyer/seller/vendor is the canonical case below.
3.2 Provisioning escrow groups¶↑
UC-3.2.1 Create an escrow group programmatically.
Actor: vendor. Surfaces: REST (order backend), MCP (agent). Precondition: a buyer and seller identified, each with a Plenum identity (or onboarded via the vendor's flow).
Flow: the vendor's backend calls CreateGroup { config: 2-of-3 [buyer, seller, vendor] }, generates and delivers the two invites to buyer and seller out of band (email, the marketplace messaging, a link), and the DKG runs once both join. Before the DKG completes, each invited party confirms a short safety-number in their client against the one the vendor displays, binding their MLS credential to the right person; the master accepts only the KeyPackage matching that committed fingerprint. This identity gate is mandatory: the all-N address gate proves the group computed one consistent address, not that the right people are in it, so without the safety-number an intercepted invite could substitute an impostor as a signer. Command: CreateGroup, then the members Join.
Variations: a party never joins -> the vendor aborts the setup; no funds exist pre-DKG, so there is nothing to refund. An intercepted invite without the matching safety-number is rejected at the fingerprint gate, so a MITM on the invite channel cannot become a signer.
Postcondition: a live escrow address per trade.
UC-3.2.2 Onboard a party who has no wallet. Actor: vendor. Surfaces: REST/MCP drive it; the party uses a GUI or a guided web flow. Precondition: a party without Plenum. Flow: the vendor's flow walks the party through creating a seed and identity (UC-2.1.1) and joining the escrow group. The party holds their own share; the vendor never sees it. Postcondition: a self-custodial party in the escrow.
3.3 Funding and watching¶↑
UC-3.3.1 Watch for escrow funding.
Actor: vendor (and the parties). Surfaces: REST/MCP (events), GUI for humans. Precondition: escrow address known.
Flow: the buyer sends funds to the escrow address; the vendor's client scans and emits Scanned when the expected amount confirms, signalling the order backend to release goods or start the clock.
Variations: underpayment, overpayment, or late payment are surfaced as events for the backend to handle by policy.
Postcondition: the trade is funded and the backend knows.
3.4 Conditional release (happy path)¶↑
UC-3.4.1 Buyer confirms, funds release to seller. Actor: buyer + seller (vendor not required). Surfaces: parties on GUI/REST; vendor observes. Precondition: funded escrow, goods delivered. Flow: the seller proposes a spend to their own address; the buyer approves on receipt of goods. Two of three signatures complete the spend without the vendor. The vendor's automation simply records the outcome. Postcondition: seller paid; escrow emptied; the vendor never signed.
UC-3.4.2 Vendor auto-approves a release on a verifiable condition. Actor: vendor (arbiter). Surfaces: MCP (agent evaluates) or REST (backend rule). Precondition: a release condition the vendor can verify (a delivery oracle, a signed receipt, a timeout). Flow: when the seller proposes and the buyer is unresponsive but the condition is met, the vendor's policy contributes the vendor's signature, so seller + vendor reach 2-of-3. The action is bounded by the policy (UC-3.7) and by the threshold; the GUI, if attached, shows the agent approving live and a human can veto. Postcondition: a conditional release without the buyer, only when the policy's condition holds.
3.5 Dispute resolution¶↑
UC-3.5.1 Buyer and seller disagree, vendor adjudicates.
Actor: vendor (arbiter). Surfaces: MCP (an agent judges from evidence), REST (a human-driven backend), GUI (a human operator). Precondition: a dispute; buyer and seller will not both sign.
Flow: each party submits its case over the channel or the vendor's intake; the vendor decides which party is right and contributes the vendor's signature to that party's proposal, reaching 2-of-3 in their favour. Command: Approve on the chosen proposal.
Variations: the AI agent (MCP) drafts a decision but the vendor's policy requires a human to confirm above a value cap; the human approves or overrides in the GUI.
Postcondition: the disputed funds go to one party; the vendor's share was the deciding vote, but never unilateral.
UC-3.5.2 Vendor refuses to adjudicate. Actor: vendor. Flow: if the vendor will not or cannot decide, the funds stay locked until buyer and seller agree (2-of-3 by the two principals). The vendor cannot seize funds; the worst case is a frozen escrow, not theft. Postcondition: stalemate, resolvable only by the two principals.
3.6 Refund and timeout¶↑
UC-3.6.1 Refund to buyer. Actor: seller + buyer, or vendor + buyer. Surfaces: all. Flow: the buyer proposes a spend back to themselves; the seller approves (goods not shipped) or the vendor approves under a refund condition. Two signatures release the refund. Postcondition: buyer refunded.
UC-3.6.2 Timeout policy. Actor: vendor. Surfaces: REST/MCP. Precondition: a configured deadline. Flow: at a trade deadline with no completion (a policy deadline on the spend, distinct from the setup ceremony, which has no hard timeout, plenum 5.1), the vendor's policy contributes a live signature toward a default outcome (refund to buyer, or release to seller, per the trade terms agreed at setup). This is a policy on the vendor's signer, not a protocol feature, and it cannot settle alone: the threshold still requires a second live signer (the buyer or the seller). There is no pre-signed or pre-arranged counter-signature; FROST partials are bound to a specific transaction and nonce round, so every signature in the default outcome is produced live at timeout. Postcondition: the escrow resolves to the agreed default.
3.7 The vendor policy engine¶↑
UC-3.7.1 Configure auto-approval policy. Actor: vendor. Surfaces: REST/MCP config; GUI to review. Precondition: vendor signer running. Flow: the vendor defines when its signer contributes a signature without a human: value caps (auto below X, human above), rolling budgets (at most Y per 24h), allowed destinations, required conditions or oracles, and which proposals demand human confirmation. These mirror the kind of controls a human-in-the-loop wrapper offers, but they gate only the vendor's own share. Postcondition: a documented, enforced policy on the vendor's signing.
UC-3.7.2 Human override of the agent. Actor: vendor's human operator. Surfaces: GUI (watching the MCP agent live). Precondition: an agent is acting. Flow: because the GUI mirrors the agent over the same command stream, a human sees each action and can veto or take over before a signature is contributed. Postcondition: human-in-the-loop without a separate tool.
UC-3.7.3 The threshold bounds unilateral action, not blast radius. Actor: n/a (invariant, and a deployment caution). A compromised client holding one FROST share can never move funds alone: that floor holds against any compromise, including a fully hijacked agent. But "cannot act alone" is not "cannot cause loss." A share can still be contributed toward an attacker-favourable outcome whenever a second signature is obtainable, and in the canonical 2-of-3 buyer/seller/vendor escrow the vendor's share is structurally the swing vote in any dispute. A compromised vendor signer therefore needs only one cooperating principal (colluding, deceived, or simply the party that benefits) to settle a disputed escrow wrongly. The honest bound on a single compromised vendor share is thus not zero: it is the disputed amount of every escrow that signer arbitrates within the window before a budget cap or a human stops it. This is the number a vendor deployment must design against.
Prompt injection is in scope, by design, and bounded rather than prevented. Plenum is built to let autonomous agents operate a member client (W8, 4.2). An agent that adjudicates disputes ingests counterparty-supplied evidence over the channel (3.5.1); that evidence is untrusted input, and steering an LLM through the very material it judges is a known, currently-unsolved attack class. Plenum does not claim to prevent it. What the architecture provides is containment of its blast radius:
- The cryptographic floor. Even a fully injected agent is bounded to one share. It can never exceed the group threshold, never touch a group it is not a member of, never reach the seed or the share itself over the API (W8), and never move the whole pool. The worst case is bounded loss within the groups that one member arbitrates, not custody-scale theft.
- Policy is defence-in-depth, not a containment boundary. Value caps, rolling budgets, destination allowlists, and per-spend human gates (3.7) raise the cost and cap the rate of a wrong signature. But policy executes in the same trust domain as the agent: an attacker who owns the daemon owns the policy. Treat caps as the economic bound on a single compromise (loss is at most the cap times the concurrent escrows in the budget window), not as a barrier that a compromised daemon respects.
- The real containment boundary is authority outside the agent's trust domain. The live GUI mirror (3.7.2) is one such boundary, but only while a human is watching, which headless operation removes (W8). Where containment must survive full daemon compromise, the deciding authority has to sit in a different trust domain than the agent's reasoning.
Deployment shapes carry genuinely different risk. For an end customer in self-custody with per-spend human approval, the threshold floor is the meaningful bound and agent-injection risk is low. For a headless vendor arbiter running auto-approval at scale, a single compromised share is materially consequential, policy is not containment, and the deployment must add an out-of-agent boundary. These are not the same risk and are not described with the same sentence.
Engineered bounds that actually reduce vendor blast radius (recommended for any automated-arbiter deployment):
- Split the swing vote across independent trust domains. Replace the single vendor signer with k independent arbiter operators (an n-of-m with multiple arbiters, a supported shape in 3.1.1). Compromising one agent then no longer decides disputes, which removes the "one compromised share is the swing vote" property at the root.
- Put high-value dispute decisions behind a second-domain attestation. Above a configured value, require the decision to be co-signed by a key the agent does not hold and cannot reach (a human approval key, an HSM, or a quorum of independent agents), so the injection surface and the authorizing surface are not the same machine.
- Size caps to the worst case you accept. Because policy is co-located with the agent, a cap is the security parameter for a single compromise, not a convenience setting. Set per-escrow and rolling caps to the loss a full daemon compromise may cause before a human or a budget halts it, and treat that product (cap times concurrent escrows in the window) as the stated risk number, not a residual footnote.
- Separate evidence intake from signing authority where the deployment allows, so the component that parses adversarial counterparty evidence is not the component that holds the share; an injection of the evidence-parsing surface then cannot directly reach the signing key. Postcondition: the threshold is a hard floor on unilateral action; the blast radius of one compromised arbitrating share is bounded economically by policy and structurally by an out-of-agent boundary, and a serious automated-arbiter deployment uses both.
3.8 Bulk and concurrent escrows¶↑
UC-3.8.1 Run many escrows at once. Actor: vendor. Surfaces: REST/MCP. Precondition: a marketplace with many trades. Flow: the vendor's signer participates in many groups concurrently, each a separate MLS group and FROST key. The daemon multiplexes ceremonies, scans, and signings across groups. Proposals arrive as events keyed by group and proposal id. Variations: per-group isolation means one stuck escrow does not block others. Postcondition: a portfolio of independent escrows served by one operator.
UC-3.8.2 Event-driven automation.
Actor: vendor backend. Surfaces: REST (webhooks or an event stream), MCP. Flow: the backend subscribes to WalletEvents (funded, proposed, approved, broadcast) and drives its own order logic, calling back into Plenum to approve or propose under policy.
Postcondition: escrow wired into the vendor's existing systems.
3.9 Audit and observability¶↑
UC-3.9.1 Per-escrow audit trail. Actor: vendor (and, for their own group, each party). Surfaces: REST/MCP query, GUI view. Flow: the client exposes the local history of a group: proposals, who approved, broadcasts, txids, and the policy decision that led to each vendor signature. This is local and private to each member; there is no shared on-chain log of who approved. Postcondition: the vendor can reconstruct and defend each decision.
UC-3.9.2 Agent action log. Actor: vendor. Surfaces: MCP/REST. Flow: every command an agent issued and every event it saw is logged for review, doubling as the record of what the automated arbiter did and why. Postcondition: an accountable automation.
3.10 Public attestations and proof of reserves¶↑
These use auditable mode (2.9) for the vendor persona: an operator who wants counterparties to trust it without becoming a custodian.
UC-3.10.1 Prove reserves to customers.
Actor: vendor (escrow operator or public treasury). Surfaces: REST/MCP drive it; anyone verifies. Precondition: the vendor's group is auditable.
Flow: the group periodically publishes a threshold reserve proof plus a signed statement of its terms, so counterparties can check the operator controls at least a stated balance at a chain-anchored time T and the published policy is authentic. The proof attests control over a minimum balance at T, not accounting solvency (assets minus liabilities); repeated, randomly-timed proofs guard against snapshot-gaming with briefly-borrowed funds. Maturity: the threshold ZK proof of reserves is research-grade and not yet available (expected with the post-FCMP++ full-chain machinery, 2.9.4); the signed statement of terms works today, so a vendor can publish authentic terms now and add reserve proofs once the primitive lands. Commands: ProveReserves, SignStatement.
Postcondition: verifiable control over a minimum balance at a known time, plus authentic terms, with no trusted third party and no custody.
UC-3.10.2 Signed rulings and receipts. Actor: vendor arbiter. Surfaces: REST/MCP. Precondition: auditable mode; a resolved dispute (3.5). Flow: after deciding a dispute, the arbiter group publishes an m-of-n signed statement of the outcome as a verifiable receipt that any party can check against the published identity key. Postcondition: an accountable, publicly verifiable arbitration record.
UC-3.10.3 Persistent public brand. Actor: vendor. Surfaces: all. Precondition: auditable mode. Flow: the vendor publishes the identity key as its long-lived handle and carries it across membership changes by succession certificate (UC-2.9.5), so customers track one stable identity even as the operator's signer set changes. Postcondition: a durable public identity decoupled from the wallet and the operator's private operation.
4. Surface ergonomics¶↑
The same commands, three presentations. This section only notes where a surface differs.
4.1 GUI¶↑
The human client. Renders proposals as approval cards, shows balances and confirmations, prompts per spend by default, and runs the setup ceremony with visible progress and fingerprint comparison. Critically, it subscribes to the full WalletEvent stream, so when an MCP agent or REST caller acts on the same member, the GUI shows it live and the human can step in. Best for end customers and for a vendor's human operator supervising the automation.
4.2 MCP¶↑
Tools for an AI agent acting for a member. The agent can do anything a member can (propose, approve, reject, query, back up), bounded by the policy (UC-3.7) and always by the threshold. Because MCP and the GUI share the command path, the agent can also drive the real GUI end to end, which doubles MCP as an automated UI test harness (architecture_idea W8). Best for dispute-judging arbiters and condition evaluators.
4.3 REST¶↑
Typed HTTP/RPC for scripts and remote operators, keypair-authenticated and bound to a member. Stateless request/response plus an event subscription. No reduced command set. Best for a vendor's order backend, batch operations, and webhook-driven flows. Never exposes the seed or the FROST share.
5. Cross-cutting behaviours¶↑
These hold across every use case above.
- Offline tolerance. Any member can be offline; store and forward holds their messages, and they catch up on reconnect (UC-2.5.3). Setup and signing wait for the needed members rather than timing out.
- No silent fund loss on bad input. A wrong seed derives the wrong backup key and simply fails to decrypt any backup, with a warning, rather than a usable-looking empty wallet (UC-2.1.2).
- Fail closed on tampering. A substituted member key or a malicious master changes the group address and trips the all-N gate (UC-2.2.6); a faulty signing share is attributed and dropped (UC-2.4.5). A spend proposal is verified by each approver rebuilding the transaction and matching it against the displayed destination and amount, so a proposer cannot show one thing and sign another (UC-2.4.2).
- The threshold bounds unilateral action, not blast radius. From any surface, by any persona, under any policy, a single client moves nothing alone. That floor is absolute, but it is not zero loss: a compromised share can still be contributed toward a wrong outcome whenever a second signature is obtainable, so the honest bound on a compromised arbitrating signer is the disputed amount it can swing before a cap or an out-of-agent authority stops it. Prompt injection of an agent is in scope and contained, not prevented, and policy is defence-in-depth rather than a containment boundary (UC-3.7.3).
- Privacy of coordination. Rejected proposals, who approved, group membership, and amounts in coordination stay off-chain and inside the encrypted channel; an on-chain observer sees only the Monero transaction that results, subject to the whitepaper threat model.
- Recovery rarely needs anyone else. The common loss (a device, with the seed intact) is a unilateral solo restore; only rebuilding the group after a lost share re-invokes the ceremony (UC-2.7, UC-2.8).
- Auditable is opt-in and compartmentalized. Going public (2.9) takes unanimous (n-of-n) consent, so no subset can take the others public; the attestations afterward run at the normal m-of-n. It only ever exposes what the group chose, through a separate identity key, never the spend key, the rendezvous, or member identities, and the private default keeps running underneath.