GNU Taler primer
Orientation for readers who know agriculture but not Taler, and for readers who know Taler but want to see which specific mechanisms we lean on. Authoritative documentation lives at docs.taler.net.
| What | Electronic payment system with blind-signed digital coins |
|---|---|
| Model | Payer-anonymous, merchant-accountable |
| Parties | Exchange, merchant backend, wallet, auditor |
| Signatures | EdDSA |
| Contract hash | SHA-512 over contract terms |
| Blockchain | None |
| Licence | Free software (GNU project) |
Contents
1. The three parts
GNU Taler splits a payment into three roles that matter to us:
- Exchange — holds the settlement float and issues digital coins as blind signatures. Because the signature is blind, the exchange cannot link a coin it later redeems to the withdrawal that produced it. This is what makes the payer anonymous.
- Merchant backend — the seller's server. It creates orders, produces contract terms, and signs them. Each merchant runs instances, and each instance can hold its own signing keys.[1] That detail carries a lot of weight in our design.
- Wallet — the payer's app. It receives contract terms, verifies the merchant's signature, shows the user what they are agreeing to, and signs the deposit.
The asymmetry is deliberate and it is the reason Taler is here: the buyer is anonymous, the seller is not. Every sale is bound to an identified merchant key and an auditable settlement account. For a certification scheme this is exactly the right way round — we want the producer accountable for the claim and the shopper unsurveilled for having bought.
2. Contract terms are the payload
A merchant creates an order by POSTing to
/private/orders (or
/instances/$INSTANCE/private/orders) on its backend. The backend
fills in the missing details; the completed object is the contract
terms.[1]
Fields we rely on:
| Field | Meaning | Our use |
|---|---|---|
order_id | Unique within the merchant instance | Ties the sale to a producer's own numbering |
summary, summary_i18n |
Human-readable description, localisable | Must state the claim in Spanish and, where relevant, Quechua |
products[] | Line items with product detail | One entry per lot, carrying weight and unit price |
merchant_pub | EdDSA public key of the instance | The producer's key. The anchor of the whole scheme |
merchant_sig | EdDSA signature over the contract | The producer signing the certified sale |
h_contract_terms | SHA-512 hash of the contract JSON | Stable identifier for the sale; what the attestation binds against |
extra |
Merchant-defined data, interpreted by the merchant frontend, carried on the contract without needing separate storage | Where the attestation block lives. See spec |
fulfillment_url | Where the payer is sent after payment | Receipt page with the full verification trace |
timestamp, pay_deadline | Contract timing | Bounds replay of a signed lot claim |
The wallet's verification is straightforward: compute the hash of the contract
JSON, check merchant_sig against the instance's public key, and
authenticate later requests with the contract
hash.[1] Everything we add rides inside a
structure that is already signed and already hashed; we are not inventing
a signature envelope.
3. Contract format v1: choices, inputs, outputs
Taler's Contract Format v1 (design document 46) generalises a contract into
a set of alternatives. Alongside the metadata fields above, a v1 contract carries
a choices array; each choice has an amount, a
max_fee, an inputs array of things the payer must
supply, and an outputs array of things the merchant
promises.[2]
Inputs may be coins or tokens (type: "token", with a
token_family_slug, a valid_after timestamp and a
number). Outputs may be coins, tokens, or a tax receipt. When the
wallet pays, it commits to an h_outputs hash over the blinded
outputs and signs over the selected choice_index as part of the
deposit request.[2]
This is more machinery than a simple payment needs, and it is precisely the machinery a certification scheme needs, because it lets a contract say: this sale consumes N units of a bounded, authority-issued thing.
4. Token families
A contract's token_families map is keyed by slug, and each entry
carries a name, a description, a set of issuing public
keys, a details object describing the class (subscription or
discount, with trusted or expected domains), and a critical
flag.[2]
Tokens are issued by blind signature, like coins. That gives them three properties we want:
- Bounded issuance. An authority can only sign as many as it decides to sign. A token cannot be forged and cannot be duplicated.
- Unlinkability. The issuing authority cannot tell which of its tokens was spent where. A certifier learns that its quota was consumed, not who bought.
- Spend-once. Redemption is checked against a double-spend database at the exchange, so the same unit cannot be sold twice.
Taler introduced this for subscriptions and discounts. We use it for a
different thing entirely: a token family standing for
"one kilogram certified under standard X, season Y". See
kilo-tokens and mass balance. The
critical flag matters — a wallet that does not understand a
critical family must refuse rather than silently ignore it, which is the
behaviour a certification claim requires.
Taler's design document frames details.class as
subscription or discount. A certification quota is
neither. Whether we can express this within the existing classes, or need to
propose a third, is open question OQ-1 and the
first thing to raise with upstream. We do not want a fork.
5. Why this stack fits the problem
| What the problem needs | What Taler already provides |
|---|---|
| The seller must be cryptographically identified | Every contract is signed with merchant_sig under an instance's
own key[1] |
| The buyer must not be surveilled for buying organic | Blind-signed coins make the payer unlinkable by construction |
| The claim must be attached to the payment, not to a label | Contract terms are the object being signed and paid against |
| Arbitrary structured data must ride along | extra, plus products[] detail |
| Certified volume must be bounded | Token families: blind-signed, bounded issuance, spend-once |
| Verification must work with bad connectivity | Signature verification is local; only settlement needs the network |
| It has to be legal to operate | Settlement is conventional banking, auditable, with a defined auditor role |
| Nobody should have to buy a speculative asset to sell potatoes | Taler is denominated in ordinary currency — soles are soles |
6. Why not a blockchain
The obvious objection: agricultural traceability on a distributed ledger is a well-populated field. We are not doing that, for reasons that are practical rather than ideological.
- Consensus solves a problem we do not have. There is no dispute about who the certifying authority is — SENASA publishes the list. We need signatures under a known root, not agreement among strangers.
- Public ledgers are the wrong privacy default. A permanent public record of every certified purchase is a surveillance artefact. Blind signatures give bounded issuance without a public transaction graph.
- Volatility is not acceptable at a market stall. A vendor pricing papa nativa cannot absorb settlement-asset price movement.
- Fees and latency. Per-kilo attestations at market volumes need to cost approximately nothing and confirm immediately.
- Garbage in. The hardest part of produce traceability is the first assertion — that this sack really came from that parcel. No ledger fixes that. An SGP peer audit is a better answer to it than a consensus algorithm, which is why the institutional design here matters more than the cryptographic one.
7. Limits and things we still owe
- Taler adoption in Peru is approximately zero. There is no deployed exchange, no wallet installed base, no bank integration. This is the largest risk in the project and no amount of protocol elegance touches it. See pilot risks.
- Token families are not designed for this. See the note in § 4.
- Offline payment is a different problem from offline verification. We get the second for free; the first is genuinely hard, and market stalls have patchy signal. OQ-2.
- Key custody for producers is unsolved in a way that respects the reality of shared phones and lost devices. Discussed in the spec, not resolved.
See also: Protocol overview · Data model & spec
References
- GNU Taler, Merchant Backend RESTful API. docs.taler.net
- GNU Taler, DD 46: Contract Format v1. docs.taler.net
- GNU Taler, Merchant Point of Sale App Manual. docs.taler.net