RGB-Tools / RGB-Tools/rgb-lib

Proxy transport: request recipient-bound acceptance signatures ("payslip") — ack.post is an unauthenticated boolean today

Open
#116 0 comments 0 reactions 0 assignees View on GitHub

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_id is not a secret: it travels inside every invoice. Anyone who learns a registered recipient_id can answer ack.get/ack.post on 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 WaitingCounterparty with its change excluded from spendable until ack.get answers.
  • A bare false is 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:

  1. 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 like rgb check in the reference flow.
  2. wvout key 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-only wvout key 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.
  3. Transport auth hook — a signed-payload envelope for ack.post/consignment.post whose 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.get must keep returning null when no ack exists — a bare false fails 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 accept payslip and rgb check <sig>.
  • src/api/proxy.rspost_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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.