Proxy transport: request recipient-bound acceptance signatures ("payslip") — ack.post is an unauthenticated boolean today
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 54
- Forks
- 50
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 4
Description
Context
We build Locktime, a non-custodial RGB venue. Our browser wallet embeds @utexo/rgb-lib-wasm 1.0.0-beta.3 (RGB 0.11.1 schema line), and our coordinating agent runs an RGB JSON-RPC proxy transport for consignment delivery and ACKs.
While testing two-wallet flows we found that the proxy transport's ACK channel is an unauthenticated boolean, and that sender-side settlement depends on it. We implemented an app-layer mitigation on our own transport (described below), but the capability gap is inside rgb-lib, so we're requesting it upstream.
Summary
The reference RGB CLI flow documents a signed acceptance exchange: Bob delivers the consignment, Alice runs rgb accept, which returns a signature over the consignment (the "payslip"), and Bob runs rgb check <sig> before broadcasting — so the sender knows the exact transfer was accepted by the intended recipient before its transaction is committed.
rgb-lib's proxy transport replaced that exchange with ack.post, an unauthenticated boolean:
src/api/proxy.rs(master, verified 2026-09-19):PostAckParams { recipient_id: String, ack: bool }— no signature, key, or proof material.- The
recipient_idis not a secret: it travels inside every invoice. Anyone who learns a registeredrecipient_idcan answerack.get/ack.poston the recipient's behalf. - The sender then settles its transfer — and unlocks its change allocation — based on a signal with no cryptographic link to the recipient's wallet. We demonstrated this live: a sender's transfer sits in
WaitingCounterpartywith its change excluded fromspendableuntilack.getanswers. - A bare
falseis parsed by rgb-lib as a NACK and can fail a whole multi-recipient batch transfer.
This is not a regression introduced in some refactor — the signed-acceptance exchange of the reference CLI was simply never carried into the proxy transport. We're asking for it as a new capability.
The request
Any of these would let wallets close the gap, in order of preference:
- Acceptance signature API (payslip) — after a successful consignment import, return (or let the wallet compute) a BIP-340 signature over the accepted transfer's identity — minimally
(recipient_id | consignment_sha256 | txid | vout), ideally with a versioned domain tag. Senders verify before settling, exactly likergb checkin the reference flow. wvoutkey disclosure — expose the per-invoice witness output public key (or a derivation proof for it), so an app-layer verifier can bind an external signature to the exact transfer output. We probed this: the invoice embeds a P2TR x-onlywvoutkey that is not derivable from any documented path of the receiving wallet's mnemonic, so today an app cannot prove the signer controls the output key the invoice names.- Transport auth hook — a signed-payload envelope for
ack.post/consignment.postwhose key is derived inside the wallet, so proxies can require proof-of-possession per call.
We deliberately kept the request version-neutral: an additive, versioned acceptance-signature primitive is adoptable by any rgb-lib line without touching transfer semantics.
What we implemented meanwhile (app-layer)
Our proxy verifies a BIP-340 signature from a wallet-seed-derived identity key, bound to the recipient at invoice-creation time, over a single-use TTL-bounded challenge that pins the ACK value — before any public ACK changes persisted state. This closes the anonymous forgery vector for our deployments.
What it honestly cannot prove: that the signing key controls the specific witness output (wvout) named in the invoice — that key is wallet-internal randomness rgb-lib does not disclose. So our proof is "signed by the wallet that created this invoice", not "signed by the output key of this exact transfer". That residual gap is only closable inside rgb-lib, which is why we're filing. We also record best-effort txid/vout import evidence with each ACK, but only when the pairing is unambiguous — a guessed transfer identity would be manufactured provenance.
Compatibility constraints we'd need preserved
ack.getmust keep returningnullwhen no ack exists — a barefalsefails whole batch transfers downstream (rgb-lib parses it as a NACK).- The ack boolean is recipient-level; senders of later transfers to the same recipient must not be stranded by a first-writer ack (we keep a capped evidence list app-side for exactly this reason).
- Any new signed field must be additive: older rgb-lib senders that never verify it must keep working.
References
- rgb.tech power-user guide — reference flow with
rgb acceptpayslip andrgb check <sig>. src/api/proxy.rs—post_ack/get_ack/PostAckParams(unauthenticated boolean channel; verified current on master 2026-09-19).
Happy to provide the two-wallet test transcript or a PR sketch for any of the three options.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/api/proxy.rs, especially post_ack, get_ack, and PostAckParams, then compare the proxy ACK flow with the referenced rgb accept/check exchange. Determine which of the proposed acceptance-signature, wvout disclosure, or transport-auth options fits the existing API without breaking null ACK behavior or older senders. Done means an agreed, additive and versioned capability with sender-verifiable recipient acceptance.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design, cryptography, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100