Skip to content

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

The response includes the grant id and public mint_url, ready to share with the treasurer. Both fields are also returned by treasurer grants. 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

To replace an unused grant, first revoke it as the operator:

docker compose exec clear-operator clear-root treasurer revoke-grant <grant-id>
docker compose exec clear-operator clear-root treasurer grant npub1...

Only pending grants can be revoked. The grant remains in treasurer grants with status revoked and an audit record; it can no longer create a CMU. Consumed grants cannot be revoked. Existing CMUs and notes are unaffected.

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

For an interactive handoff, save the single grant response as grant.json and run:

poetry run clear-treasury cmu create --grant-file grant.json

The file is read without modifying or deleting it. The grant is consumed at the mint only after successful CMU creation. Piped JSON is also supported:

cat grant.json | poetry run clear-treasury cmu create --grant-stdin

The CLI reads mint_url, id, and npub from the grant. It displays the mint and treasurer, prompts for the nsec with hidden input (unless supplied through --nsec or CLEAR_TREASURER_NSEC), then asks for the CMU name and unit label. The secret stays local; only a signed request is sent to the mint. The key must match the grant's npub. Prompts use the controlling terminal, so stdin remains available for the piped JSON. A terminal is required for this interactive mode. Optional --name and --unit-alias values skip their respective prompts.

For a local operator-to-treasurer pipeline, disable the Docker TTY:

docker compose exec -T clear-operator clear-root treasurer grant "$TREASURER_NPUB" \
  | poetry run clear-treasury cmu create --grant-stdin

Cancelling during the prompts leaves the grant pending; it can be reused or revoked. For automation without an interactive terminal, use the explicit command below.

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 Home Page 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 unlist \
  --cmu-id <cmu-id>

To show it again:

clear-treasury --mint https://clear.safebox.dev \
  --nsec nsec1... \
  cmu list \
  --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.

Both listed and unlisted CMUs are accessible at /cmus/<cmu-id>; existing /cmus/<keyset-id> links also work. Unlisting does not restrict access to the description, supply, or treasurer details.

For operators, clear-root cmu list <cmu-id> lists the CMU on the home page; clear-root cmu list without an ID still returns all CMU records. The previous private and publish commands remain compatibility aliases.

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 nsec instead of an npub is 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-root refuses 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 Safebox wallet address or explicit wallet npub / hex public key:

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.

Describe a CMU

After creating a CMU, its owning treasurer can set the public description beneath the title on the individual CMU page. Describe its purpose, where it is accepted, and what holders can expect. Use the usual configured treasurer credentials:

poetry run clear-treasury --mint https://clear.safebox.dev cmu describe \
  --cmu-id cmu-YOUR-ID \
  --description "Community meal credits, accepted at participating kitchens."

For longer text, replace --description with --description-file description.txt. Files are UTF-8. Plain text and paragraph breaks are supported, up to 10,000 characters; HTML and Markdown are not rendered. Use --clear to remove the text. Treasury commands require an explicit --cmu-id or --keyset-id.

The operator can independently update any hosted CMU:

docker compose exec clear-operator clear-root cmu describe cmu-YOUR-ID \
  --description "Community meal credits, accepted at participating kitchens."

When using --description-file inside Docker, the file must be available inside the container. Both update paths are audited. Existing CMUs start with an empty description after the automatic database migration. Description changes do not alter identity, supply, or listing visibility. An unlisted CMU's description is still visible on its direct page; do not put secrets in it.

Wallet address discovery

clear-treasury send resolves name@domain only through https://domain/.well-known/safebox.json?name=name. The version-1 document must match the requested address, provide its Acorn wallet key and public WSS relays, and advertise clear-token-transfer with nip59, kind 7379, and the selected unit (or omit units to support any Clear unit).

There is no NIP-05 or LNURL fallback, even on 404 or network errors. A separate social npub cannot become the wallet destination. Older receivers must publish the wallet directory, or the treasurer can supply a verified wallet public key and explicit --relay. Do not substitute the recipient's social npub.

Mint acceptance, treasury accounting, internal-mint guards and Stroma delivery are unchanged. Discovery does not add a mint allowlist. Delivery errors retain the existing uncertain-outcome semantics; do not blindly repeat a send.

Hidden key prompt

For treasury commands such as issue and send, omit --nsec (or pass --nsec "") to use hidden terminal input when CLEAR_TREASURER_NSEC is unset. An explicit nonempty key takes precedence over that environment variable. The prompted key is kept only for the current invocation. Commands without a terminal must supply a key explicitly or through the environment; they never fall back to visible secret input.

clear-treasury wallet balance includes friendly_alias and friendly_unit_alias from each mint's public keyset metadata, while retaining mint, unit, and amount. If the mint is unavailable or has no label, the CMU unit identifier is shown instead. This lookup does not change local proofs or validate whether they remain unspent.