Why a relayer
If a user’s wallet created a record,$creator would name the wallet forever. So a relayer creates
every record and, in the same transaction, transfers ownership to the record’s one-time owner
S. On chain, every record looks the same: $creator is a relayer shared by everyone, $owner is
an address used once.
The relayer also pays the gas, so users need no tokens or GLM to write.
One transaction per batch
Arkiv’sexecuteBatch applies creates before ownership changes inside one transaction. The relayer:
- predicts the keys its next creates will receive (
predictEntityKeys), - sends one transaction: all creates, then each predicted key transferred to its owner,
- if a prediction went stale, predicts again; if anything else fails, splits the batch in halves and retries each, so one bad record cannot sink everyone else’s.
Hiding in the batch
Timing is the obvious leak: “a request arrived, two seconds later a record appeared”. Batching removes it:- the relayer waits until k records are queued (default 5) or the oldest has waited max wait (default 60 s), and never writes more often than every 5 blocks;
- it shuffles the batch, so position means nothing;
- the client waits a random 0–20 s before sending;
- each client keeps one write in flight, so a user’s own records never share a batch, even across browser tabs.
Small batches
In a quiet period, the max wait can expire with only one or two records queued. By default the relayer writes anyway and tells the client the batch size. A client that would rather wait can setminAnonymity: the relayer then holds the record until a batch of at least that size forms, up to
maxHoldMs, and fails it rather than write it in a smaller batch.
Trusting nothing the relayer says
A relayer could edit a record, keep it, or hand it to its own address. The client checks:- it reads the whole batch back from Arkiv (never just its own key),
- it verifies that the record’s envelope signature recovers to its one-time owner,
- on any mismatch it marks the relayer bad and retries through the next one.
Discovery
Each relayer keeps an expiring heartbeat entity on Arkiv (arkognito.relayer, 150 blocks, renewed
every 2 minutes) with its URL, batch size, whether it requires tokens, and its OHTTP gateway key.
A relayer that dies drops out on its own. Clients trust only each heartbeat’s $creator as that
relayer’s address.
Latency
Measured: about 25–30 s per write on a busy relayer, up to about 90 s on a quiet one. Operators can
shorten the wait (
MAX_WAIT_MS) at the cost of smaller anonymity sets.