The Postmaster
The postoffice is a singleton on-chain account that administers the whole SithBit deployment. Since the delegation cutover its admin surface is split in two:
- The standing delegate — a wallet address recorded on the postoffice. It holds the operational powers: authorizing and deactivating domains, tuning the protocol fees, publishing the root KSK fingerprint, and fee-free bulk alias reservation. This is the key an operator uses day to day, and the one services like domain-sithbit keep hot.
- The postmaster — the owner. It is not a pubkey on-chain: the postoffice stores only a 32-byte Merkle commitment root over a hidden set of keys derived in an offline key ceremony. Nobody can read the postmaster’s keys off the chain, because they are never written there. The ownership powers — sweeping the postoffice revenue, rotating the delegate, and handing ownership itself over — are exercised by revealing one ceremony key with a Merkle membership proof.
Every ownership operation is rotate-on-use: the revealed key spends the entire committed generation, and the same instruction installs the next generation’s root. A revealed key is never accepted twice.
Note: almost everything in this topic is an operator/administrator action, not something an everyday mailbox owner does. If you’re just sending and receiving mail, you can skip this page — it’s here because understanding who administers your domain is part of understanding the system.
For how to hold the ceremony seeds safely — and what survives losing one — see the postmaster custody runbook.
Checking status
Anyone can check the current fees and the postoffice without signing
anything — the fee getters are public, read-only commands under
postoffice (the delegate only sets fees; reading them is ungated):
sithbit postoffice fee stamp # per-stamp protocol fee
sithbit postoffice fee domain # domain-authorization fee
sithbit postoffice fee alias # alias claim / transfer fees + the premium short-name schedule
sithbit postoffice fee settlement # settlement basis-point rates (operator share, stamp-purchase rate)
sithbit postoffice fee attestation # one-time verified-sender attestation fee
sithbit postoffice fee pin-lease # one-time pinning-lease creation fee + minimum deposit
sithbit postoffice postmaster # postoffice address, delegate, commitment status
sithbit postoffice postmaster reports the postoffice address, the standing
delegate’s wallet, and whether an ownership commitment root is installed
(the root itself is opaque — it reveals nothing about the keys behind it).
It is read-only and ships in the default CLI build; the state-changing
subcommands below are not (see the note at the end of this page). The same
query is also reachable as sithbit postmaster get in an admin build (see
the note below), since it lives on the postmaster command tree too.
Initializing the postoffice
sithbit postmaster init \
--seed <seed0.json> --seed <seed1.json> [--seed …] \
[--keys-per-seed <K>] \
[--keypair <delegate keypair>] \
[--skip-preflight]
A one-time step run once per network deployment, right after the on-chain
programs are first deployed. The signing keypair (--keypair) pays,
becomes the standing delegate, and the ceremony seed files (at least
two, in a fixed order) derive the genesis commitment root that ships in the
instruction — no postmaster pubkey is ever recorded. --keys-per-seed is a
ceremony parameter: every later ownership operation must present the same
value, so record it alongside the seeds.
A fresh init also claims the global postmaster alias
for the delegate, atomically in the same transaction: instruction 1
creates the postoffice (recording the delegate), so instruction 2’s alias
claim fee is waived by the delegate fee waiver by construction — the
claim costs only the alias account’s rent. Mail addressed to postmaster
resolves to the operator from the deployment’s first block. The claim has
three outcomes:
- Unclaimed (the normal fresh-deployment case) — registered to the delegate in the init transaction.
- Already held by the delegate — an idempotent skip: init proceeds and notes the alias already belongs to it.
- Held by anyone else — a loud refusal naming the holder, and
nothing is submitted, not even the postoffice init: the deployment
must never come up without its
postmastername. Recover by acquiring the alias (a transfer from the holder) or having the holder close it (sithbit alias close), then re-run init.
A reinit attempt (the postoffice account already exists) sends the init
instruction alone, so the chain’s authoritative refusal surfaces instead
of an alias complaint. One standing limitation to know: the alias program
has no reserved words, so the atomic claim narrows the squat window
to the gap between program deploy and postmaster init — it cannot close
it. Run init promptly after deploying the programs.
The delegate’s operational powers
The delegate signs the operational admin instructions; a non-delegate
signer is refused with on-chain error 66 (NotDelegate):
- All domain operations —
domain create, the two-step deactivation (request, cancel, finalize),domain close, anddomain transfer. These land on the domain program, which checks the delegate against the postoffice cross-program — the postoffice itself stays a mail-program account. - Fee tuning —
postmaster fee stamp,fee domain,fee alias,fee alias-tiers(the four premium claim fees for 1–4 character names, set together in lamports for 1/2/3/4 characters),fee settlement(the two settlement basis-point rates set together: the operator share of settled value and the stamp-purchase fee rate — the bps arm of the hybrid per-stamp fee; a zero rate resets to its protocol default),fee attestation(the one-time verified-sender attestation fee; zero likewise resets to the default),fee reputation-floor(the floor of reputation-scaled first-contact pricing, in basis points of a mailbox’s default postage; zero likewise resets to the default), andfee pin-lease(the one-time pinning-lease creation fee; zero likewise resets to the default), each value bounded by a hardcoded on-chain cap (see Economics), so even a compromised delegate cannot price the protocol out of reach. - The root KSK fingerprint —
postmaster ksk set(and the read-onlypostmaster ksk get), the DNSSEC trust anchor for authorize-by-proof. - Fee-free bulk alias reservation — the per-alias claim fee is waived
when the delegate signs a multi-alias
alias create, premium 1–4 character names included: reserve short names for rent alone and resell them on the marketplace.
The delegate is deliberately a hot-capable key: it can run inside domain-sithbit for self-service domain onboarding, because nothing it signs can move postoffice funds or touch the ownership commitment.
Ownership operations
The three ownership instructions share one CLI shape: the ceremony seed
files re-derive everything — the signing chain key, its membership proof
against the current on-chain root, and the next generation’s root to
install. The CLI is stateless; the current generation is recovered by
scanning candidate roots against the chain, so there is no counter file to
keep. A stale or invalid proof is refused with on-chain error 67
(InvalidCommitmentProof).
--seed <seed0.json> --seed <seed1.json> [--seed …] # every seed, ceremony order
[--keys-per-seed <K>] # must match the value init used (default 2)
[--key-index <INDEX>] # which committed key signs (default 0)
[--generation <G>] # force a generation (tests; normally scanned)
Installing a successor commitment (ownership handover)
sithbit postmaster commitment \
--seed <seed0.json> --seed <seed1.json> \
[--keypair <fee payer>] [--skip-preflight]
This replaces the old postmaster transfer (it rides the same
instruction slot on-chain): instead of pointing the postoffice at a new
owner pubkey, it installs a successor commitment root. To hand the
deployment to a new owner, the new owner runs their own ceremony and the
current owner installs the resulting root — from then on only the new
owner’s seeds can prove ownership. The --keypair here only pays the
transaction fee; it needs no admin standing.
Withdrawing protocol fees
sithbit postmaster withdraw [destination] \
--seed <seed0.json> --seed <seed1.json> \
[--keypair <fee payer>] [--skip-preflight]
Sweeps the postoffice balance above its rent-exempt minimum — the
accumulated domain, alias, and stamp protocol fees — to destination
(defaults to the fee payer’s address). Ownership-gated: the delegate
collects fees into the postoffice but can never take them out.
Rotating the delegate
sithbit postmaster delegate <new_delegate> \
--seed <seed0.json> --seed <seed1.json> \
[--keypair <fee payer>] [--skip-preflight]
Repoints the operational powers at a new wallet — the recovery path for a lost or compromised delegate key, and the scheduled-rotation path for a healthy one. The all-zero address is refused (it is the on-chain “no delegate” sentinel).
Because every one of these three operations spends the revealed generation and installs the next one, running any of them advances the generation for all of them — the next ownership operation, whichever it is, proves against the new root. The CLI recovers the new generation from the chain automatically.
postmaster reclaim — wipe and start fresh
Sometimes you want to throw a whole deployment away and start over — most
often on devnet or a local validator, where the on-chain state is fixture
data you’re done with. reclaim is the tool for that: in one pass it drains
every rent-empty account all three programs own and hands the rent back.
Accounts still holding value are deliberately out of its reach — see
It refuses accounts that still hold value.
sithbit postmaster reclaim \
[--rent-recipient <address>] \
[--keypair <delegate keypair>] \
[-y] \
[--skip-preflight]
It enumerates every program-owned account for all three program IDs (a
getProgramAccounts scan of the mail, alias, and domain programs), then
batch-closes them with each program’s own delegate-signed
AdminCloseAccount instruction (a program can only drain accounts it owns,
so each of the three carries its own reclaim twin) — mailboxes,
fromboxes, domains, aliases, published-key accounts, the
lot. The postoffice singleton is closed last, deliberately: every other
close authenticates against it, so it has to outlive them. Each closed
account’s rent is refunded to --rent-recipient, which defaults to the
signing keypair’s own address.
It refuses accounts that still hold value
AdminCloseAccount closes an account only when its balance is at or below
its rent-exempt minimum. A target holding anything above that is refused
on-chain with custom error 106 (AdminCloseEscrowPresent), in all three
programs.
That guard is what keeps a wipe tool from being a drain: a frombox holds a
sender’s prepaid postage, a message account holds escrowed postage and any
reply bounty, an alias-bid account holds a live auction bid. Without the
check, the standing delegate could sweep all of it into
--rent-recipient — and on a real deployment that is other people’s money,
not fixture data.
The practical consequence on devnet is that a reclaim pass over live fixture
state will skip funded accounts rather than reporting a clean sweep. To
retire those, settle or close them through their normal paths first
(mail delete to settle messages,
frombox close or
frombox reclaim
to empty fromboxes, alias bid --cancel to return bids), then run reclaim
to collect the rent-empty remainder.
It is the standing delegate that signs
AdminCloseAccount is gated on a standing-delegate signature checked
against the mail postoffice — the same delegate that runs the operational
commands above. Since the delegation cutover there is no
postmaster pubkey on-chain, so for the purposes of this instruction the
“postmaster” simply is the standing delegate. A signer that isn’t the
recorded delegate is refused on-chain with AdminCloseUnauthorized; the
--keypair you pass must therefore be the delegate key. Being the delegate
is necessary but not sufficient — the value guard above applies to the
delegate too.
Why the launch build doesn’t have this command
reclaim is a compile-time footgun, and it is fenced off as one. The
AdminCloseAccount instruction lets the standing delegate destroy any
account any of the three programs owns — anyone’s mailbox, any domain, the
postoffice itself — and pocket that account’s rent. That is enormous power
to leave sitting in a live deployment’s admin key.
So the command is gated behind a second Cargo feature, reclaim, layered
on top of postmaster (the code is compiled only under
all(postmaster, reclaim)). The intended lifecycle is:
- Pre-launch, build the CLI and the on-chain programs reclaim-capable, so you can reset devnet/testnet freely while you iterate.
- At mainnet launch, cut a build with the
reclaimfeature off. The command disappears from the CLI and — because the on-chainAdminCloseAccounthandler is itself feature-gated out of the shipped bytecode — the whole close-anything path leaves the programs entirely. A reclaim-free launch build simply has no instruction that can delete a user’s account out from under them.
Because it is destructive, reclaim will not run silently:
- On an unknown or mainnet cluster it demands a typed confirmation
phrase — you must type
reclaim mainnetexactly. This guardrail is never bypassable by-y; there is no unattended way to wipe mainnet. - On devnet or localnet it takes a light
y/Nprompt instead, and there-ydoes skip it, so scripted test resets stay ergonomic.
Data accounts vs. the program account
Reclaim exists because closing the program and draining its data are two different jobs, and a fresh start needs both:
solana program close <id>+ redeploy resets the program’s bytecode and reclaims the program account’s own rent. It does not touch the data PDAs — the mailboxes, aliases, domains, and fromboxes the program owns. Those live at addresses derived from the program ID, so a redeploy at the same vanity ID re-reads all the old state as if nothing happened.reclaimdoes the other half: it drains those data PDAs (refunding their rent) but leaves the program bytecode in place.
A genuine clean slate therefore requires both: run reclaim to empty the
data accounts, then close and redeploy the programs. Either step alone
leaves the deployment half-reset.
It prints the program-close steps — it does not run them
After the data reclaim finishes, reclaim prints the remaining
steps — the solana program close <id> commands and the cargo build-sbf /
solana program deploy redeploy sequence for all three programs — and stops.
It never runs them. Closing a program account is irreversible and, at the
same vanity ID, hands the deployer a one-way choice; the CLI deliberately
leaves that final, unrecoverable action to a human who has read what it
printed and is ready to paste the commands.
Note: the entire
sithbit postmastercommand tree — including the read-onlyget— is gated behind the CLI’spostmasterfeature, which is not part of the default feature set. Build the CLI withcargo build -p mail-client --features postmaster(or--all-features) to get it. The default build still exposes the read-only query, assithbit postoffice postmaster— the same underlying lookup aspostmaster get.