Onboarding a Treasurer
This page gives the first-release operator flow for adding one treasurer and creating one or more treasurer-authorized Clear Mint Units (CMUs).
The authority rule is:
one active treasurer npub -> one or more CMUs
one CMU -> one active treasurer npub
The treasurer gives the mint operator an npub. The matching nsec stays with
the treasurer.
1. Confirm the Mint
Run privileged operator commands inside the Clear container:
docker compose exec clear-operator clear-root info
docker compose exec clear-operator clear-root cmu list
clear-root uses the loopback operator API and is not a remote treasurer
tool.
2. Get the Treasurer npub
The treasurer provides:
npub1...
Do not put the treasurer's nsec in .env, deployment notes, logs, database
exports, API requests, or clear-root commands.
For development only, the container includes a helper that prints a keypair and stores nothing:
docker compose exec clear-operator clear-root treasurer keygen
In separated custody, the treasurer should generate and keep their own nsec.
3. Add the Treasurer
docker compose exec clear-operator clear-root treasurer add npub1...
docker compose exec clear-operator clear-root treasurer list
Adding the treasurer records a public key. It does not create a CMU or issue Mint Notes.
4. Grant One CMU Creation
docker compose exec clear-operator clear-root treasurer grant npub1...
docker compose exec clear-operator clear-root treasurer grants
Copy the returned grant ID. A first-release grant is single-use and intended to produce one keyset and one CMU. After it is consumed, the same treasurer may receive another grant for another CMU.
5. Send the Grant Out of Band
Give the treasurer:
- the mint URL, for example
https://clear.safebox.dev; and - the grant ID.
Do not give the treasurer CLEAR_OPERATOR_TOKEN, CLEAR_MASTER_SECRET, the
mint database, or keyset secrets.
6. Treasurer Creates the CMU
The treasurer consumes the grant with their nsec:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
cmu create <grant-id> \
--name "Gym Guest Passes" \
--unit-alias "passes"
Or with the key in the environment:
export CLEAR_TREASURER_NSEC=nsec1...
clear-treasury --mint https://clear.safebox.dev \
cmu create <grant-id> \
--name "Gym Guest Passes" \
--unit-alias "passes"
At creation time, the treasurer may choose --name and --unit-alias as
display hints for the CMU they are authorized to create. Wallets still bind
balances to the mint URL, canonical cmu-<keyset-id> unit, and keyset ID.
After the CMU exists, display metadata changes are operator-mediated. The treasurer requests the change out of band, and the mint operator applies it:
docker compose exec clear-operator clear-root cmu label cmu-<keyset-id> \
--name "Food Share Credits" \
--unit-alias "shares"
The first argument can be the canonical CMU unit or the raw keyset ID. Label changes do not change the keyset, CMU, ledger, treasurer authority, existing Mint Notes, or holder balances.
The CLI signs the request. The mint verifies the treasurer key, mint URL, grant, and replay nonce before generating and encrypting the new keyset secret.
7. Treasurer Confirms the CMU
The treasurer can ask the mint which active CMU is bound to their key:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
cmu info \
--cmu-id <cmu-id>
Or, with CLEAR_TREASURER_NSEC already exported:
clear-treasury --mint https://clear.safebox.dev \
cmu info \
--cmu-id <cmu-id>
This is a signed read-only request. The mint returns the requested active CMU
only if that treasurer key controls the named unit. --keyset-id <keyset-id> is
also accepted when the treasurer wants to name the underlying keyset directly.
8. Choose Public Listing
By default, an active CMU appears on the mint home page. To hide it from the home page without disabling direct use by CMU id:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
cmu private \
--cmu-id <cmu-id>
To show it again:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
cmu publish \
--cmu-id <cmu-id>
The mint operator can also list or unlist any hosted CMU through clear-root.
A treasurer can only change visibility for CMUs controlled by their key.
9. Operator Verifies the CMU
The operator checks the consumed grant and the created CMU:
docker compose exec clear-operator clear-root treasurer grants
docker compose exec clear-operator clear-root cmu list
The CMU should also appear in public key discovery:
curl https://clear.safebox.dev/v1/keysets
curl https://clear.safebox.dev/v1/keys
Record the treasurer npub, grant ID, CMU name, cmu-<keyset-id> unit,
keyset ID, onboarding date, and out-of-band authorization reference.
Operator Fallback
For development or controlled local bootstrap, the operator can consume a grant from inside the container:
docker compose exec clear-operator clear-root cmu create <grant-id> \
--name "Gym Guest Passes" \
--unit-alias "passes"
Prefer the clear-treasury flow when the operator and treasurer are meant to
be separate. The signed flow proves that the treasurer controls the nsec.
Safety Checks
These failures are expected:
- adding an
nsecinstead of annpubis rejected; - consuming a grant with the wrong key is rejected;
- consuming a grant twice is rejected;
- signing for a different mint URL is rejected;
- creating another grant for the same active treasurer while an unused grant is still pending is rejected; and
clear-rootrefuses non-loopback operator API URLs.
9. Treasurer Issues Mint Notes
After onboarding, the treasurer can issue Mint Notes for their CMU:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
issue 25 \
--cmu-id <cmu-id> \
--memo "Workshop credits"
By default, issued proofs are stored in a local treasurer wallet. The wallet
path is derived from the mint URL and treasurer npub:
~/.clear/treasury-wallets/<mint-host>-<mint-hash>/<treasurer-npub>.json
Different nsecs and different mints therefore get different wallet files.
The path can be overridden with --wallet or CLEAR_TREASURY_WALLET.
Check the local wallet with:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
wallet balance
To issue directly to a token instead of the local wallet:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
issue 25 \
--cmu-id <cmu-id> \
--memo "Workshop credits" \
--to-token
The treasurer can send an exact amount from the local wallet to a compatible
NIP-05 address or npub:
clear-treasury --mint https://clear.safebox.dev \
--nsec nsec1... \
send 10 alice@example.com \
--cmu-id <cmu-id> \
--memo "Guest pass"
If exact proofs are not available, send refreshes a larger proof through
/v1/swap, delivers the requested amount, and stores the change back in the
same treasurer wallet.
Delivery uses an ephemeral Nostr sender key by default. The treasurer nsec
authorizes treasury actions and selects the local wallet; it is not reused as
the delivery sender key.
10. Check Recipient Receive Routes
Clear delivery uses the recipient's public receive route. It must not depend on a private or context-local home relay that remote senders cannot reach.
For an Acorn recipient, inspect the advertised route:
poetry run acorn set --show-public-relays
poetry run acorn inbox-relays --json
If the Acorn moved to a new context or the public relay changed, refresh the public route from the receiving wallet:
poetry run acorn inbox-relays wss://spurline.safebox.dev --json
poetry run acorn set --public-relays wss://spurline.safebox.dev
Then scan or accept the transfer:
poetry run acorn receive-clear --preview --json
poetry run acorn receive-clear --event-id <event-id> --json
poetry run acorn clear accept <event-id> --json
poetry run acorn clear balances --json
For Safebox Web, the NIP-05 external relay list should contain public WebSocket relay URLs. Do not advertise Docker service names, loopback URLs, VPN-only routes, or other context-local home relays as the recipient's public Clear delivery route.
Remote treasurer retirement is follow-on work.