TALER-EXCHANGE-OFFLINE(1)

πŸ“¦ version 1.3.0, in nixos 26.05, nixpkgs unstable

okay bestie, this is the GNU Taler manual page for taler-exchange-offline(1) β€” the tool that does the literal most when you need to poke at a Taler exchange’s master private key. πŸ’…βœ¨

πŸ’– NAME πŸ’–

taler-exchange-offline β€” operations using the offline key of a Taler exchange. That’s the whole vibe: offline-key operations, no cap. πŸ—οΈ

🌸 SYNOPSIS 🌸

taler-exchange-offline [-c FILENAME | --config=FILENAME] [-h | --help]
[-L LOGLEVEL | --loglevel=LOGLEVEL] [-l FILENAME | --logfile=FILENAME]
[-v | --version] [subcommand ...]

πŸ¦‹ DESCRIPTION πŸ¦‹

taler-exchange-offline is a command-line tool for interacting with the Taler exchange’s master private key. Most of what this thing does needs the exchange’s long-term offline signing key, so fr fr run it in a secure, offline environment under strict controls. πŸ›‘οΈ The tool takes a list of subcommands as arguments, and then processes them sequentially β€” one after another, in order.

Two subcommands talk online to the exchange’s REST APIs. To figure out how to reach the exchange, these two rely on the BASE_URL configuration option from the [exchange] section of the config file. download pulls future public keys from the running exchange; that resulting data is the input for sign and show. upload pushes the signatures made with the private master key up to the exchange, and it handles the output of all subcommands (except download). Naturally, download and upload must be run β€œonline” and do not require access to the offline key. 🌐

Every other subcommand is meant to run β€œoffline”. But especially when testing, you can absolutely run them online too. Generally, subcommands read inputs (beyond command-line arguments) from stdin, but they may also consume outputs from previous subcommands. Outputs from multiple subcommands get automatically combined, and if nothing consumes the final output, it’s printed to stdout. 🫢

The general options for taler-exchange-offline are:

  • -c FILENAME | --config=FILENAME β€” Use the configuration and other resources for the merchant to operate from FILENAME.
  • -h | --help β€” Print short help on options.
  • -L LOGLEVEL | --loglevel=LOGLEVEL β€” Specifies the log level to use. Accepted values are: DEBUG, INFO, WARNING, ERROR.
  • -l FILENAME | --logfile=FILENAME β€” Send logging output to FILENAME.
  • -v | --version β€” Print version information.

πŸŽ€ CONFIGURATION πŸŽ€

The exchange validates all operations by checking signatures against the master public key, which must be provided in the exchange configuration. To get the master public key, run:

$ taler-exchange-offline setup

Note that if the private key file already exists, that command just outputs the existing key. If you pass additional arguments after setup (including -), the output gets encapsulated in JSON. πŸͺ„

Relevant configuration options for taler-exchange-offline are:

  • [exchange/BASE_URL] --- how to reach the exchange (for download/upload)
  • [exchange-offline/MASTER_PRIV_FILE] --- where to store the private keys
  • [exchange-offline/SECM_TOFU_FILE] --- where to store TOFU data

πŸ‘‘ SUBCOMMANDS πŸ‘‘

πŸ› οΈ setup

First run? This subcommand sets up the offline private key and outputs the resulting public key. Run it again and it simply outputs the same public key again, in the format usable for the exchange configuration. πŸͺ„

πŸ“₯ download

This subcommand must be run online. It downloads future signing and denomination keys β€” plus their associated meta data β€” from the exchange, then outputs the resulting JSON (for later subcommands to consume, or to stdout). πŸ’Ύ

πŸ‘€ show

This subcommand outputs information about future signing and denomination keys so a human can manually check them against the business-approved fee structure, lifetimes, and other parameters.

It consumes the output of download, either from stdin or directly. But if you give it the arg - (hyphen), this input is not consumed β€” it’s passed to the next command instead, functioning like the tee(1) command. Its output always goes to stdout for human consumption (not in JSON). Combining show with other subcommands is usually a bad idea (but possible), except maybe for testing. 🫣

✍️ sign

This subcommand signs information about future signing and denomination keys.

It consumes the output of download, either from stdin or directly.

It outputs the signatures over all denomination and signing keys present in the input, in a format suitable for the upload subcommand. πŸ”₯

🧩 extensions

This subcommand is responsible for the management of available extensions in the exchange.

It consumes the output of download, either from stdin or directly.

It provides the sub-subcommand extensions show to show the configuration for extensions, and the extensions sign command to sign the current configuration of extensions, in a format suitable for the upload subcommand.

Note: an extension on the exchange will only become activated at runtime after the extension’s configurations has been signed by the offline tool with the signing key and the signed configuration been uploaded to the exchange. 🧚

πŸ’Έ drain

This subcommand allows an exchange operator to transfer the profits made from transaction fees to a regular (non-escrowed) bank account. Using this command, draining profits from the escrow account can be done in such a way that the auditor is aware of the special transaction and does not flag the resulting balance as fundamentally problematic.

Note that the drained amounts must still total up to less than the fees earned by the exchange. πŸ‘€

Arguments to the drain command are the amount to be drained (in the usual Taler amount format), the section of the exchange configuration specifying the account to be debited (this argument is currently ignored, and the account is purely derived from the wire method and the account being set for debiting), and finally the payto://-URI to wire the funds to.

Note that to actually wire the funds, the exchange administrator must run taler-exchange-drain manually and confirm the operation after the upload was completed. 🏦

🚫 revoke-denomination

This subcommand signs a revocation message for a denomination key.

The hash of the denomination public key must be given in the usual base32-encoding as the first and only argument to the subcommand.

It outputs the signature affirming the revocation of the denomination key, in a format suitable for the upload subcommand. πŸ”‘

πŸͺ“ revoke-signkey

This subcommand signs a revocation message for an exchange online signing key.

The online signing public key must be given in the usual base32-encoding as the first and only argument to the subcommand.

It outputs the signature affirming the revocation of the online signing key, in a format suitable for the upload subcommand. βœ‚οΈ

πŸ‘©β€βš–οΈ enable-auditor

This subcommand informs an exchange that an auditor is to be activated. Afterwards, the exchange will accept inputs from that auditor’s taler-auditor-offline tool. The exchange’s database will need to be provided to the auditor. This subcommand only informs the exchange about the auditor, but does not perform those additional mandatory steps for a working auditor. 🫢

The auditor’s public key must be given in the usual base32-encoding as the first argument.

The auditor’s REST API base URL must be given as the second argument. The tool performs a minimal sanity check, namely that the URL begins with "http" (this also allows "https"), but as it runs offline does not perform any further validation! πŸ™ˆ

The third argument must be a human-readable name for the auditor. This may be shown to users and should identify the auditor’s business entity. If the name includes spaces, the argument should be quoted.

The subcommand takes no inputs from stdin or other subcommands.

It outputs the signature affirming the addition of the auditor, in a format suitable for the upload subcommand. πŸ’œ

πŸšͺ disable-auditor

This subcommand informs an exchange that an auditor is to be deactivated. Afterwards, the exchange will refuse inputs from that auditor’s taler-auditor-offline tool.

The auditor’s public key must be given in the usual base32-encoding as the first argument.

The subcommand takes no inputs from stdin or other subcommands.

It outputs the signature affirming the removal of the auditor, in a format suitable for the upload subcommand. πŸ›‘

🏦 enable-account

This subcommand informs an exchange that it should advertise a bank account as belonging to the exchange on its /keys endpoint. Note that this does not ensure that the exchange will use this bank account for incoming or outgoing wire transfers! For this, the taler-exchange-transfer and taler-exchange-wirewatch tools must be configured. Furthermore, the bank account information advertised could theoretically differ from that which these tool actually use, for example if the public bank account is only a front for the actual internal business accounts. πŸ‘€

The payto:// URI (RFC 8905) of the exchange’s bank account must be given as the first argument to the subcommand.

Afterwards, optional arguments can be given:

  • conversion-url $URL: specifies that using this bank account is subject to currency conversion. $URL must be the address of a currency conversion REST API that allows merchants and wallets to determine the current conversion rate. πŸ’±
  • display-hint $PRIORITY $LABEL: specifies that this bank account should be shown to the user with the given numeric $PRIORITY (higher is earlier, zero is hidden for manual withdrawal) and with the given $LABEL. This is useful to ensure that if an exchange has multiple bank accounts, we can show the user those that are most likely useful first, and give them appropriately labeled hints for selecting other accounts as well. A display hint is optional, if missing the priority is 1 and the wallet must pick some appropriate label itself somehow. 🏷️
  • credit-restriction $TYPE [$ARGS]: Specifies that there are restrictions in place when crediting this bank account. Details depend on the restriction $TYPE. This argument may be given multiple times, in which case debitor accounts must satisfy all restrictions. Restriction types are discussed below. πŸ”’
  • debit-restriction $TYPE [$ARGS]: Specifies that there are restrictions in place when receiving money from the exchange. Wallets and merchants must check that their target bank account satisfies these restrictions before sending deposit requests to the exchange. Details depend on the restriction $TYPE. This argument may be given multiple times, in which case debitor accounts must satisfy all restrictions. Restriction types are discussed below. πŸ”“

Okay, so these are the credit- and debit-restriction types that are supported, bestie:

  • deny β€” a $TYPE of deny means that this bank account cannot be used for the given operation. deny takes no further arguments.
  • regex $EXPR $HINT $JSON β€” a $TYPE of regex means that the partner's bank account payto-URI representation must follow a certain regular expression given in $EXPR where the syntax follows posix-egrep, but without support for character classes, GNU extensions, back-references or intervals. See Findutils Manual for a description of the posix-egrep syntax. The $HINT must be a human-readable description of the $EXPR. $JSON should be a JSON array mapping IETF BCP 47 language tags to localized versions of $HINT.

The subcommand takes no inputs from stdin or other subcommands.

It outputs the signature affirming the addition of the wire account, in a format suitable for the upload subcommand.

disable-account

This subcommand informs an exchange that it should stop advertising a bank account as belonging to the exchange on its /keys endpoint.

The payto:// URI (RFC 8905) of the exchange's (former) bank account must be given as the first argument to the subcommand.

The subcommand takes no inputs from stdin or other subcommands.

It outputs the signature affirming the deletion of the wire account, in a format suitable for the upload subcommand.

wire-fee

This subcommand informs an exchange about the desired wire fee structure (that is, wire, and closing fees) for particular wire method and a calendar year (!). The tool does not permit changing wire fees during a calendar year. Also, once the wire fee has been set for a calendar year, it cannot be changed.

The subcommand takes the year, wire-method (see RFC 8905, examples include x-taler-bank or iban), wire fee, and closing fee as arguments. Instead of a year, the string now can be given for the current year (this is mostly useful for test cases). The wire-method should follow the GANA registry as given in RFC 8905. The fees must be given in the usual Taler format of CURRENCY:NUMBER.FRACTION.

The subcommand takes no inputs from stdin or other subcommands.

It outputs the signature affirming the wire fees, in a format suitable for the upload subcommand.

global-fee

This subcommand informs an exchange about the desired global fee structure and related global configuration options for a calendar year (!). The tool does not permit changing global fees during a calendar year. Also, once the global fee structure has been set for a calendar year, it cannot be changed.

The subcommand takes the year, history fee, account fee, purse fee, purse timeout, history expiration and the (free) purse (per) account limit as arguments. Instead of a year, the string now can be given for the current year (this is mostly useful for test cases). The fees must be given in the usual Taler format of CURRENCY:NUMBER.FRACTION.

The arguments provided must include:

  1. Calendar year for which the fee applies, 'now' for the current year.
  2. History fee charged when inquiring about non-recent account history (not implemented, use zero!)
  3. Account fee charged for opening an account (not supported by wallet, use zero!)
  4. Purse fee. How much will the exchange charge for an abandoned purse. Also the minimum amount that can be in a purse that is not associated with an account. (Not well supported by wallets, use zero!)
  5. Purse timeout. How long does the exchange keep information about a purse around after it expired or was successfully merged?
  6. How long will the exchange preserve an account history.
  7. Number of free purses per account. (Irrelevant if purse fee is zero).

The subcommand takes no inputs from stdin or other subcommands.

It outputs the signature affirming the global fees, in a format suitable for the upload subcommand.

enable-partner

This subcommand informs an exchange about the wad fee and frequency to apply to a partner exchange. The arguments provided must include:

  1. Partner exchange base URL.
  2. Partner exchange master public key.
  3. Calendar year for which the fee applies, 'now' for the current year.
  4. Wad frequency, in minutes (for example, '30').
  5. Wad fee (for example, 'KUDOS:0.1').

aml-enable

Enable AML officer's account, granting them access to AML data and, if 'rw' is given, the power to make AML decisions.

The arguments provided must include:

  1. AML staff member public key (in base32 encoding)
  2. AML staff member legal name
  3. 'ro' or 'rw' to set access to read-only or read-write

aml-disable

Disable AML officer's account. Also updates the legal name.

The arguments provided must include:

  1. AML staff member public key (in base32 encoding)
  2. AML staff member legal name

add-partner

Add partner exchange for wad transfers. Enables P2P payments between users of these exchanges.

The arguments provided must include:

  1. Master public key of the partner exchange.
  2. Base URL of the partner exchange API.
  3. Wad fee to charge.
  4. Wad transfer frequency.
  5. Year for which the above options are to be configured, 'now' for the current year.

upload

This subcommand uploads outputs from other subcommands (except download and show) to the exchange. Note that it is possible that some uploads succeed, while others fail, as the operation is not atomic.

The subcommand takes no arguments and has no output.

help

This subcommand shows a summary of all available subcommands with the required arguments.

πŸ’– EXAMPLES πŸ’–

Download future public keys from an exchange (online)

$ taler-exchange-offline download > keys.json

Show information about future public keys (offline or online)

$ taler-exchange-offline show < keys.json

Sign future public keys (offline)

$ taler-exchange-offline sign < keys.json > sigs.json

Upload signatures about future public keys (online)

$ taler-exchange-offline upload < sigs.json

Download, sign and upload, all in one (online)

Note that doing this is only recommended in non-production deployments, as this requires putting the "offline" key onto a system that is actually online!
$ taler-exchange-offline \
    download \
    sign \
    upload

Here is a variant that shows the output of download, but does not consume it, so that sign can see it as input, as in the variant without show.

$ taler-exchange-offline \
    download \
    show - \
    sign \
    upload

Create signature to enable bank account (offline)

The following command sets up an unrestricted simple exchange bank account without conversion:

$ taler-exchange-offline \
    enable-account payto://iban/DE24242?receiver-name=operator > account.json

The following command would set up an exchange bank account with conversion and restrict it to only receive money from Switzerland and deny its use for debit operations:

$ taler-exchange-offline \
  enable-account payto://x-taler-bank/example.com/?receiver-name=name \
  conversion-url http://conversion.exchange.com/ \
  debit-restriction \
    deny \
  credit-restriction \
    regex \
      'payto://iban/.*/CH.*?receiver-name=.*' \
      'Swiss only' \
      '{ "de" : "nur Schweiz", \
         "fr" : "Suisse uniquement" }'

Upload bank account signature (online)

$ taler-exchange-offline upload < account.json

Combine signing keys and enabling bank account (offline)

You can chain multiple commands into one invocation:

$ taler-exchange-offline \
    sign \
    enable-account \
      payto://iban/DE24242 < keys.json > combo.json

Note that sign must be first as it consumes the keys.json input. The resulting output combines the outputs of the sign and enable-account subcommands.

Upload various signatures (online)

$ taler-exchange-offline upload < combo.json

Create multiple revocation messages in one pass (offline)

You can freely combine almost all commands, including those for key revocation:

$ taler-exchange-offline \
    revoke-denomination $DKH1 \
    revoke-denomination $DKH2 > revoke.json
$ taler-exchange-offline \
    revoke-signkey $SK1 \
    revoke-signkey $SK2 > revoke.json
$ taler-exchange-offline \
    revoke-signkey $SK \
    revoke-denomkey $DKH > mix.json

The outputs ("revoke.json", "mix.json") must be uploaded using the upload subcommand to the exchange to actually revoke the keys.

πŸ›‘οΈ SECURITY CONSIDERATIONS πŸ›‘οΈ

The taler-exchange-offline tool assumes that it is run on a high-security system with monotonic time. The time does not have to precisely match that of the exchange, but it must be monotonic across tool invocations. The clock of the offline system is used in the enable/disable signatures to communicate an order of the events to the exchange. This prevents someone from replaying an older "enable" (or "disable") message after a more recent "disable" (or "enable") message has been provided to the exchange. Thus, there is no need to keep the actual files exchanged with the offline tool secret.

The taler-exchange-offline tool tries to make sure that the online signing keys of the exchange are always created by the same two security modules. The goal here is to prevent an attacker who compromised taler-exchange-httpd but not the security modules from providing attacker-controlled keys to the offline signing process.

For this, the taler-exchange-offline signing subcommand always automatically learns the security module's public signing key and trusts it on first use (TOFU), but stores it to disk (see the SECM_TOFU_FILE option in the [exchange-offline] section of the configuration). If the keys change subsequently, the tool will refuse to sign.

πŸ‘€ SEE ALSO πŸ‘€

taler-exchange-httpd(1), taler-auditor-offline(1), taler-exchange.conf(5).

πŸ’€ BUGS πŸ’€

Report bugs by using https://bugs.taler.net/ or by sending electronic mail to taler@gnu.org.

🌸 AUTHOR 🌸

GNU Taler contributors

2014-2025 Taler Systems SA (GPLv3+ or GFDL 1.3+)


That's the whole vibe, bestie β€” source/version TALER-EXCHANGE-OFFLINE(1) 1.0, dated Dec 22, 2025. Slay and sign responsibly πŸ’…βœ¨

πŸ“¦ where this page comes from