mail-grpc: the chain gateway
Standard port(s): gRPC 50051, health 8193 (both in-code defaults,
loopback-bound).
The gRPC gateway other servers use to reach Solana — it implements the
SolanaMail service’s full RPC surface. MX/ mail servers call this
instead of talking to Solana directly. See Route index below
for the complete method list.
Route index
Every RPC the SolanaMail service answers (mail_api/protos/sithbit.proto),
grouped by area. The surface carries no auth of its own — see the network
posture note above.
Alias directory
| RPC | Request → Response | Purpose |
|---|---|---|
ResolveAlias | AliasRequest → AliasResponse | resolve an email local-part alias to a wallet address |
ListAliases | ListAliasesRequest → ListAliasesResponse | list every alias local-part pointing at a wallet, off the alias indexer |
Mailbox and postage
| RPC | Request → Response | Purpose |
|---|---|---|
GetMailbox | MailboxRequest → MailboxResponse | read a mailbox’s mail count, default postage, and no_ipfs flag |
GetMailboxKey | MailboxKeyRequest → MailboxKeyResponse | fetch a wallet’s published encryption key (X25519/RSA/none) plus no_ipfs |
GetFrombox | FromboxRequest → FromboxResponse | look up a (from, to) frombox: required postage, stamp count, stamp fee terms |
Sending and receiving mail
| RPC | Request → Response | Purpose |
|---|---|---|
SendMail | SendMailRequest → SendMailResponse | submit a mail send on-chain, with an optional reply-bounty escrow |
DeleteMail | DeleteMailRequest → DeleteMailResponse | delete a received message on-chain |
RefundMail | RefundMailRequest → RefundMailResponse | refund a received message back to its sender |
FindMessage | FindMessageRequest → FindMessageResponse | locate a landed message by IPFS CID — a dedupe check after a lost SendMail response |
Reply bounties
| RPC | Request → Response | Purpose |
|---|---|---|
ClaimBounty | ClaimBountyRequest → ClaimBountyResponse | claim an escrowed reply bounty using a reply message as evidence |
RefundBounty | RefundBountyRequest → RefundBountyResponse | reclaim an expired, unclaimed reply bounty |
Transactions
| RPC | Request → Response | Purpose |
|---|---|---|
GetTransactionStatus | TransactionStatusRequest → TransactionStatusResponse | poll the commitment status of a previously submitted transaction |
Domains
| RPC | Request → Response | Purpose |
|---|---|---|
GetMailDomain | MailDomainRequest → MailDomainResponse | check a MailDomain account: exists, active, authority, gateway_is_authority |
ListAuthoritativeDomains | ListAuthoritativeDomainsRequest → ListAuthoritativeDomainsResponse | list active domains whose on-chain authority is this gateway’s own signing key |
GetSenderAttestation | SenderAttestationRequest → SenderAttestationResponse | check whether a domain has attested a sender wallet as legitimate |
Marketplace
| RPC | Request → Response | Purpose |
|---|---|---|
BrowseListings | BrowseListingsRequest → BrowseListingsResponse | browse open marketplace listings (aliases and domains for sale) |
ListSales | ListSalesRequest → ListSalesResponse | read the marketplace sales ledger (by wallet, name+kind, or newest overall) |
ListParticipants | ListParticipantsRequest → ListParticipantsResponse | search on-chain participant opt-in beacons by tag bitmap filter |
Reputation and pinning
| RPC | Request → Response | Purpose |
|---|---|---|
GetSenderReputation | SenderReputationRequest → SenderReputationResponse | look up a wallet’s cumulative stamp-spend reputation and resulting price-rate bps |
GetPinLease | PinLeaseRequest → PinLeaseResponse | look up IPFS pinning-lease deposits/count on a CID |
When you need it: always, in any deployment where mail should actually
reach the chain — without it, sithbitd runs with its chain pipeline
disabled.
Network posture: the gRPC surface carries no TLS or authentication — anyone who can reach the port can sign with the gateway’s keypair. Bind it to loopback or a private interface only, never a public address. Why the gateway is a separate private service at all (and what would change that) is recorded in the topology design note.
Quickstart:
cargo run -p mail-grpc
Run it as an OS service with mail-grpc service install — the shared
subcommand is documented in
Running as an OS service.
Config file mail_grpc.toml, or point MAIL_GRPC_CONFIG at an alternate
path — the same layered TOML/env mechanism as every other SithBit binary
(in-code default → TOML → ./.env → ./.env.$APP_ENV → environment;
MAIL_GRPC_* variables override individual settings, e.g.
MAIL_GRPC_BIND_ADDR or MAIL_GRPC_ALIAS_INDEX__DATABASE). An empty
or missing config file is a runnable dev gateway: it binds
127.0.0.1:50051 — the private-network posture above is the default the
code enforces now, not a convention — and resolves the chain endpoint and
the signing keypair from the operator’s Solana CLI config
(~/.config/solana/cli/config.yml; a missing file means the stock CLI
defaults, i.e. mainnet-beta), exactly like the sithbit CLI, so
solana config set governs a zero-config gateway. As with the CLI, a
bare JSON_RPC_URL environment variable overrides the endpoint (it wins
over a configured json_rpc_url), which is handy for pointing a gateway
at a local validator without editing config. The annotated
mail_grpc/mail_grpc.example.toml documents every key with its default;
the Configuration reference has the
key/default table.
Migration (clean break):
mail-grpcno longer reads its legacy environment-only configuration.GRPC_SERVER_ADDRESS,DEFAULT_KEYPAIR(andDEFAULT_KEYPAIR_VAULT_URI/DEFAULT_KEYPAIR_SECRET_NAME),ALIAS_INDEX_DB,ALIAS_INDEX_POLL_SECONDS,ALIAS_CACHE_SECONDS, andHEALTH_BINDare all ignored. The one legacy name still honored isJSON_RPC_URL(bare, un-prefixed), for parity with thesithbitCLI and the standard Solana convention — it overrides the configuredjson_rpc_url. In particular, there is no keypair-content-in-an-environment-variable shape anymore:DEFAULT_KEYPAIRused to hold the raw JSON keypair array itself, while the TOMLkeypairnames a key source — a keypair file path, or a cloud secret-manager secret. The nearest env-var equivalent isMAIL_GRPC_KEYPAIR=/path/to/id.json.
Signing keypair: a file or a cloud secret manager
keypair is a
key source like
the workspace’s other file-loaded secrets. A bare string is a Solana
keypair file; the table form fetches the keypair from a cloud secret
manager instead — the secret’s value holds exactly what the file would,
the raw JSON keypair array. Authentication is each cloud’s ambient
chain (Azure managed identity, the AWS credential chain, Google ADC):
no new auth to configure, and no key material in the config file — only
the secret’s coordinates.
keypair = "signer.json"
keypair = { kind = "akv", vault_uri = "https://<vault>.vault.azure.net/",
secret_name = "signer" }
keypair = { kind = "asm", secret_id = "sithbit/signer" }
keypair = { kind = "gsm", project = "my-project", secret = "signer" }
Unset (the default), the keypair comes from the Solana CLI config’s
keypair_path — ~/.config/solana/id.json unless solana config set
moved it.
mail-grpc also runs the alias indexer that backs Aliases’s
ListAliases lookups ([alias_index]; an explicitly empty database
disables it). See the
Configuration reference for the full
key/default table.