Per-recipient pin providers
By default, every inbound mail body a server delivers is pinned to IPFS by
the operator’s provider — whatever the [ipfs] section of the server
config selects (see
[grpc] and [ipfs] — the chain pipeline).
That single pin is what the on-chain message references, and its
availability rests on that one operator continuing to pin.
A mailbox owner who wants an additional copy under their own control can
register their own pinning provider with the server. Once configured,
the delivery pipeline pins that recipient’s inbound sealed bodies to their
provider in addition to the operator’s default pin — automatically, at
delivery time, with no per-message action. It is the standing, server-side
counterpart to sithbit mail pin, which re-pins
already-delivered mail by hand.
Who it’s for: recipients who want their mail’s availability to outlive
the operator’s pin — without running their own mail server. Bring a
Pinata account, a
Filebase bucket, or your own
sithbit-ipfsd daemon.
How it fits into delivery
The recipient pin is deliberately best-effort and additive:
- The operator pin runs first and stays authoritative — it yields the CID the on-chain message account records. The recipient pin is a second copy of the same sealed bytes; because IPFS is content-addressed, both providers serve the same CID.
- A recipient-provider failure — provider outage, revoked credentials, a full bucket — is logged and dropped. It never delays, fails, or alters delivery or chain state. Your copy of the mail arrives either way; only the extra pin is missed.
- The
no_ipfsopt-out wins over everything: an opted-out mailbox gets no pin at all — not the operator’s, not yours. The opt-out means “my mail never touches public IPFS”, and a recipient-configured provider doesn’t override that.
Configuring a provider
The surface is three routes on the
account API, under the same
JWT wallet auth as the rest
of /v1/account — so a wallet holder manages their own provider, and only
theirs. The examples below assume $JWT holds a token from
wallet-challenge login.
Set (or replace) a provider — PUT /v1/account/pin-provider with a
kind-tagged JSON body, one of three kinds:
# Pinata: an API JWT plus your dedicated gateway
curl -X PUT http://127.0.0.1:8180/v1/account/pin-provider \
-H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
-d '{"kind":"pinata","jwt":"<pinata-api-jwt>","gateway":"https://example.mypinata.cloud"}'
# Filebase: S3 credentials plus the pinning bucket
# (endpoint is optional, default "https://s3.filebase.com")
curl -X PUT http://127.0.0.1:8180/v1/account/pin-provider \
-H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
-d '{"kind":"filebase","access_key":"<key>","secret_key":"<secret>","bucket":"my-mail"}'
# Remote: a sithbit-ipfsd pin daemon you operate
# (endpoint defaults to "http://127.0.0.1:8182"; auth_token is optional,
# matching the daemon's auth_token setting)
curl -X PUT http://127.0.0.1:8180/v1/account/pin-provider \
-H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
-d '{"kind":"remote","endpoint":"https://ipfsd.example.net:8182","auth_token":"<token>"}'
Success is 204 No Content. A missing or empty required credential field
is a 400 naming the field (e.g. filebase.secret_key must not be empty);
an unknown kind is a 422; a wallet with no account on this server is a
404. For the remote kind, point the endpoint at a
sithbit-ipfsd daemon — and since its pin API is a
write surface, expose it beyond loopback only with its auth_token set or
network isolation in front.
Check what’s configured — GET /v1/account/pin-provider:
curl -H "Authorization: Bearer $JWT" http://127.0.0.1:8180/v1/account/pin-provider
# → {"configured":true,"kind":"filebase"} (or {"configured":false,"kind":null})
The read surface is deliberately write-only for secrets: it reports
presence and the kind tag, never the credentials — not even redacted ones.
To rotate credentials, simply PUT the replacement.
Remove it — DELETE /v1/account/pin-provider:
curl -X DELETE -H "Authorization: Bearer $JWT" http://127.0.0.1:8180/v1/account/pin-provider
# → 204; future deliveries pin to the server default only
Deleting is idempotent — clearing an already-unconfigured provider still
answers 204.
What the server stores, and who can read it
The credentials are sealed (AES-256-GCM) under the operator’s server-wide
credential key and kept in the accounts store — the same mechanism as
stored mail passwords (see
credential_key_file).
Only the kind tag is stored in the clear, so the GET route (and the
operator) can answer which provider without opening the secret.
Be clear-eyed about the trust statement this implies: the operator’s
server can read these credentials — it has to, to pin to your provider
at delivery time. That is the design, and it is the opposite of
Lockbox-style end-to-end secrecy: sealing here
protects the credentials at rest (a stolen database dump without the
credential key reveals nothing), not from the operator. Use a scoped,
revocable credential — a Pinata JWT restricted to pinning, a Filebase key
for a dedicated bucket, an ipfsd auth_token you can rotate — never a
root account key.
Nothing about this setting is on-chain. Contrast the no_ipfs opt-out,
which is an on-chain mailbox flag because foreign senders must see it
before they pin; your pin provider only matters to the one server that
delivers your mail, so it stays operator-local.
Tradeoffs
- Your copy is only as good as your provider. The recipient pin
depends on your provider’s availability and your credentials staying
valid — an expired Pinata JWT or a deleted bucket silently costs you the
extra copies (watch the server’s logs, or spot-check with
GET /ipfs/<cid>against your own gateway). The operator pin remains the authoritative one either way. - No backfill. Configuring a provider affects mail delivered from
then on. To capture your existing history, run
sithbit mail pinonce against the same provider — it takes the identical Pinata/Filebase/remote credential shapes. - The operator holds your provider credentials (sealed at rest, but readable by the running server — see above). Scope and rotate them accordingly.
- You manage your provider’s lifecycle. The server only ever adds pins to your provider: settlement and deletion release the operator’s pin, never yours. A deleted message’s extra copy stays pinned on your provider until you remove it there yourself.
- Opt-out excludes you. A mailbox with the
no_ipfsopt-out never gets any pin, including this one — the two settings answer opposite wishes, and the opt-out wins.