Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Attest a verified sender

See Verified-sender attestation for what an attestation is and why a sending organization buys one. This page is the technical reference: the attest, lookup, and revoke commands, the postmaster fee setting, and the on-chain mechanics they ride.

sithbit domain attest-sender <MAIL_DOMAIN> [WALLET] \
  ( --witness-file <path> | --witness-hex <hex> ) \
  [--payer-keypair <path>] \
  [--skip-preflight]

This is the third member of the proof-carrying family, alongside domain authorize and domain reclaim. It submits the same DNSSEC witness — the RRSIG chain proving the domain’s _solana.authority delegation, which the on-chain program verifies for itself — but instead of authorizing the domain, a verified proof records that the domain vouches for a wallet as a legitimate sender. Like its siblings it is permissionless: anyone may submit a proof and pay for it, because the proof, not the signer, is the authority.

What a verified proof mints is the SenderAttestation account for the (domain, wallet) pair, stamped with the chain clock. Three shape differences from domain authorize are worth knowing:

  • No domain account is involved. Attesting neither reads nor creates a MailDomain: a domain that has never been registered as a mail domain can attest senders just the same. The attestation is a freestanding record, and it confers no serving rights over the domain.
  • The attested wallet rides the instruction payload. It defaults to the payer’s own address, but the proving claimant may attest any wallet — control of the domain’s DNS is the sole authorization, and the authority key the leaf TXT publishes is not required to match the attested wallet (the domain vouches for whoever it names).
  • One record per (domain, wallet) pair, any number of pairs. A domain may attest as many wallets as it likes; each attestation is minted — and later revoked — independently.

The witness, its two input sources (--witness-file / --witness-hex), the client-side size bound, gathering the witness from live DNS, the automatic buffer staging + ComputeBudget bump + precompile emission for non-RSA chain links, and the automatic buffer close on success are exactly as domain authorize describes — an attest reuses that whole pipeline, including the prerequisite root KSK. The staged buffer is even the same (payer, domain) PDA, so either command can reclaim it.

# attest your own wallet (the payer's address is the default)
sithbit domain attest-sender acme.com --witness-file ./proof.bin

# attest a different sending wallet
sithbit domain attest-sender acme.com mAiLiLdgjgGdWoCZkpW3cj7JLAC56qb4NErFyQFWNJg \
  --witness-file ./proof.bin

The attestation fee

A verified proof pays a one-time flat fee to the postoffice — DEFAULT_SENDER_ATTESTATION_FEE_LAMPORTS, 0.01 SOL by default — debited from the payer once, at the attest step, after the chain walk succeeds. A failed proof charges nothing beyond transaction fees, and the staged buffer’s rent stays reclaimable. The CLI quotes the currently tuned fee in its success output, and anyone can read it ahead of time — no signature, ships in every build:

sithbit postoffice fee attestation

The delegate tunes the fee with:

sithbit postmaster fee attestation <LAMPORTS> \
  [--keypair <delegate keypair>] \
  [--skip-preflight]

The value is hard-capped on-chain at MAX_SENDER_ATTESTATION_FEE_LAMPORTS (0.1 SOL, 10× the default) — an over-cap value refuses with custom error 101 (SenderAttestationFeeAboveCap) — so even a compromised delegate key cannot price attestation out of reach. Zero resets to the protocol default: like the settlement rates, a zero stores the “unset” sentinel, so the fee cannot be tuned to literal zero. A postoffice account that predates the fee field reads back the default, and the setter grows the legacy account in place (to the 184-byte layout) on its first run. See the tunable-constants table.

Looking up an attestation

sithbit domain attestation <MAIL_DOMAIN> <WALLET>

Read-only, and — like domain get — it ships in every build. It prints the attested wallet, the attestation account, and the attested-at timestamp, or a not-found notice when the domain has not attested that wallet (a missing record is the common negative answer, not an error):

sithbit domain attestation acme.com mAiLiLdgjgGdWoCZkpW3cj7JLAC56qb4NErFyQFWNJg

Servers ask the same question over the mail-grpc gateway: the GetSenderAttestation call takes {domain, wallet} and answers {attested, attested_at}. A clean on-chain absence answers attested = false; a failed chain read is an UNAVAILABLE status, never a false — absent and unknown stay distinguishable. The read is finalized-commitment and uncached, so a fresh attestation (or revocation) is visible on the next call.

Revoking an attestation

sithbit domain revoke-attestation <MAIL_DOMAIN> \
  [--keypair <path>] \
  [--skip-preflight]

Closes the (domain, wallet) attestation for the signing wallet and refunds the record’s rent to it. Only the attested wallet itself can revoke: the attestation’s address is re-derived from the signer, so any other key — the domain’s included — derives a different address and never reaches the record (the same derivation binding CloseProofWitness uses for its payer). The command refuses client-side when no attestation exists for the signer under that domain.

sithbit domain revoke-attestation acme.com -k wallet.json

Reclaiming an abandoned witness buffer

sithbit domain attest-sender <MAIL_DOMAIN> --close-witness \
  [--payer-keypair <path>] \
  [--skip-preflight]

A successful attest reclaims the staged witness buffer’s rent automatically; a failed or abandoned attempt leaves the buffer behind for a retry or inspection, exactly as domain authorize does. When you are done with it, --close-witness closes the buffer and refunds its full rent to the same payer keypair that staged it — the buffer PDA is seeded on the payer, so it is the only key that can. Because the buffer is the shared (payer, domain) staging PDA, domain authorize --close-witness reclaims the identical buffer.

Measured compute cost

Measured through the deployed .so on a real cluster, like every row in the compute-unit table: the attest transaction consumed 322,474 CU (fenced at 345,000) — the DNSSEC chain walk dominates, which is why the CLI prepends the same ComputeBudget limit domain authorize sizes to the proof’s zone depth. RevokeSenderAttestation measured 11,224 CU (fenced 34,000) and SetSenderAttestationFee 6,546 CU (fenced 30,000), both comfortably on the 200k default budget.