Skip to content
LogoLogo

Platform services: what the best platforms do, and what we build

2026-10-18 (PRODUCTION_PLAN Phase 3).

The node (arrekta-node, Rust; Python reference) owns consensus. Everything else is the platform layer: indexing, issuer tooling, wallets and developer APIs. This note looks at how established platforms build that layer and defines the four core platform services.

1. What established platforms do

NeedWho does it wellTheir approachLicence / reuse
Explorer / indexer APIBlockstream Esplora (electrs fork); mempool.space; Trezor BlockbookA separate indexer follows the node and serves a REST API: address history and UTXOs, block and tx lookup, fee estimates. Esplora's API is a de-facto standard that many wallets speakEsplora front-end and backend: MIT (API design reusable). mempool.space and Blockbook: AGPL, so their code is never copied
Regulated asset issuanceBlockstream AMP (Liquid)Transfer-restricted assets: holders are registered before they may receive. Assignments (an off-chain allocation) are separate from distributions (the on-chain transfer, signed by the issuer's own wallet). An optional issuer authorisation endpoint approves each transferProprietary service; we reuse the model
Permissioned token rulesERC-3643 / T-REX (Tokeny)Every transfer is checked by an Identity Registry (holder eligibility, identity separate from wallet so keys can rotate) and Modular Compliance (transfer rules). Agents can freeze (full or partial), force-transfer, recover lost wallets and pause. Audits concentrate on those privileged rolesOpen standard; we reuse the model
Wallet platform / custodyBitGoWallets with multisig or MPC. A policy engine: velocity limits, allowlists, initiator rules, and webhook policies where your server approves or denies a transactionProprietary; we reuse the model
Developer notificationsAlchemy NotifyPer-webhook signed HTTP POSTs (X-Alchemy-Signature), retries with exponential backoff. Consumers must dedupe and be reorg-awareProprietary; we reuse the model

The common pattern is that the node stays minimal, and each platform capability is a separate service that follows the chain, keeps its own read model and exposes a focused API.

2. What we build: four services (Rust, sharing arrekta-core)

                    ┌──────────── arrekta-gateway ────────────┐  API keys, quotas, OAuth,
   clients ───────▶ │ REST /v1 · SSE/WebSocket · webhooks   │  PQ-signed webhooks
                    └──┬──────────────┬──────────────┬──────┘
                       │              │              │
              arrekta-indexer     arrekta-issuer     arrekta-wallet
              (Esplora-compat   (AMP + ERC-3643  (BitGo-style policies,
               + assets)         model, enforced  never holds keys,
                                 by consensus)    PST multi-party signing)
                       │              │              │
                       └──────── node events (P2P/SSE) ──── arrekta-node (consensus)
                                Postgres per service · chain events streamed from the node (SSE)

2.1 arrekta-indexer

  • Follows the node over the node's event stream and REST API. On reorg it rolls back by height. It is a pure read model and can be rebuilt from genesis at any time.
  • Esplora-compatible routes (/block/:hash, /tx/:txid, /address/:addr/txs, /address/:addr/utxo, /blocks/tip/height, /fee-estimates, …) so existing explorer front-ends and wallet libraries can integrate with little change.
  • ARRK extensions: /asset/:id (supply timeline: issued, minted, burned), /asset/:id/holders (cap table at any height), /asset/:id/frozen, per-address per-asset history.
  • Stronger than Esplora: cap tables at a historical height (needed for dividends and audits), and asset-aware balances.

2.2 arrekta-issuer (built: ISSUER_SERVICE.md)

The AMP issuer workflow plus ERC-3643's identity/compliance model, but the controls are enforced by Arrekta consensus (FREEZABLE/CLAWBACK, control and authority tokens), not by contract code that can be buggy.

  • Investor registry: holders identified by an identity that owns one or more addresses, so a lost key means rotating the address, not re-verifying KYC (ERC-3643 idea). Claims (KYC, accreditation, jurisdiction) come from trusted claim issuers.
  • Assignments → distributions (AMP idea): allocate off-chain, then distribute on-chain in batches signed by the issuer's wallet (arrekta-wallet produces PSTs, the issuer signs).
  • Corporate actions: dividends and coupons from an indexer cap-table snapshot at a record height; burns and redemptions.
  • Controls with governance: freeze, unfreeze and clawback require M-of-N approval inside the service, and the control token lives in an on-chain multisig lock. Every action carries a reason code and an audit record (the privileged-role risk auditors flag in ERC-3643).
  • Transparency: hosts ARRK-AMS-1 metadata documents and reserve attestations, verifies them against metadata_hash, and publishes the controls policy that holders see before receiving.

What we improved after researching existing designs (implemented):

Known weakness or proven patternSourceWhat arrekta-issuer does
Transfer restrictions in contract code are bypassable when one transfer path skips the checkZealynx ERC-1400 reviewRestricted assets live only in managed 2-of-2 addresses; the authorizer co-signature is the single path (clawback aside), with no "second transfer function"
AMP enforces restrictions with a co-signing server and an issuer authorization endpointBlockstream AMPSame model on standard Arrekta multisig + PST; a per-asset authorizer key that can never move coins alone
Claims come from trusted claim issuers, scoped and expiringERC-3643 ONCHAINID, PolymeshClaims must be ML-DSA-signed by a trusted claim issuer, carry an expiry, and can be revoked; registry operators can't invent them
Rules as OR of AND-groups with jurisdiction conditionsPolymesh asset complianceSame rule shape, plus max holders, max balance and lock-up
A single privileged controller key is a backdoor; use multisig, time lock and a court-order hashSecureum, ZealynxControls need an officer quorum; clawbacks wait out a time lock; every action carries a reason code and evidence hash; execution also needs the treasury wallet's co-signers
Assignments are separate from on-chain distributionsBlockstream AMPAssignments (with vesting height) → batched treasury transfers settled by a worker
Snapshot dividends need a record date; rounding must be exactSettleMint, Taurus CMTATRecord height must be buried under N confirmations; largest-remainder allocation sums exactly; the table is hashed and signed
Reserve reports are point-in-time attestationsCircle USDC attestationsAttestations record the live circulating supply and chain height next to the report hash
Holders can't see how privileged roles were usedERC-3643 audit checklistA per-asset signed hash-chained public log of registrations, rule changes, executed controls, dividends, distributions and attestations

2.3 arrekta-wallet (built: WALLET_SERVICE.md)

  • Never holds private keys.
  • Watch-only wallets: PQ schemes have no xpub, so clients register batches of locks or public keys (an address pool) generated on the client from their seed.
  • PST, the partially-signed transaction format (Arrekta's analogue of Bitcoin PSBT): unsigned tx, spent outputs, required signers, then signatures merged from several parties. Supports multisig custody and hardware or air-gapped signers.
  • Policy engine (BitGo model): per-wallet spend limits, velocity windows, address allowlists, and approval quorums. Webhook policies call the customer's server to approve or deny before co-signing.

What we improved after researching existing designs (implemented):

Known weakness or proven patternSourceWhat arrekta-wallet / PST does
A PSBT change output can be swapped for a lock the attacker co-controls; multisig change is the weakest pointBitBox Trezor write-up, Coldcard, bitcoindev listPST carries key origins per output. A signer counts an output as change only if an origin derives from its own seed to a key in redeem data that hashes to the lock (§16.3)
A coordinator can substitute co-signer keys in a multisig walletbitcoindev list; BIP 388 wallet-policy registrationEach co-signer's key batches are signed by its identity key. The service builds policies only from attested keys
PSBT v0 can't add inputs or outputs after creationBIP 370PST modifiable flags with a constructor role (e.g. atomic asset-for-ARRK swaps), closed by the first signature
A tampered PSBT can exploit signer parsers or hide outputsbitcoindev list (signing PSBTs proposed)Every PST the service hands out carries an ML-DSA attestation; the decoder is canonical and fuzzed
Queued transactions re-pick the same inputsBitGo unspent selection, reservationsInputs of live transfers are reserved; manual holds for coin control
Approvals must come before signing; stale requests should lapseFireblocks TAP (two-tier, 2 h timeout)pending_approval refuses signatures; transfers expire (default 2 h, 5 min–24 h)
Policy changes need an admin quorum; policies can be lockedFireblocks admin quorum (7-day expiry), BitGo policy lockPolicy changes need the approver quorum within 7 days; lock makes the policy immutable until a date
New withdrawal addresses wait out a cooldownExchange allowlist practice (24–48 h)Allowlist entries count only after allowlist_cooldown_secs (default 24 h)
Webhook policy approvalsBitGo webhook policyRequests are ML-DSA-signed; anything but an explicit approve fails closed; public-only DNS resolver, no redirects

Not adopted yet: Liana-style timelocked recovery paths need a relative-timelock lock kind, which is a consensus change and a candidate for a future soft fork.

2.4 arrekta-gateway

  • API keys, scopes (read, write, webhooks), per-key quotas and rate limits, idempotency keys on writes.
  • One public surface: proxies /v1 to node, indexer, issuer and wallet. Problem+json errors. ETag and If-None-Match pass through, so unchanged reads answer 304 Not Modified.

Three ways to authenticate (implemented):

WhoHowDetails
Servers and scriptsAPI key: X-API-Key: arrk_…Created by the admin (POST /gateway/v1/keys), shown once, stored as a SHA3 hash
PartnersAPI key plus a signature on every requestThe key carries a post-quantum public key (signing_public_key on create, or PUT /gateway/v1/keys/{id}/signing-key). Each request sends X-ARRK-Timestamp, X-ARRK-Nonce and X-ARRK-Signature: v1,sig=<base64> over METHOD \n path?query \n timestamp \n nonce \n hex(SHA3-256(body)) \n key prefix (context ARRK-REQUEST-v1). Refused: timestamps more than 300 s off (stale-request), a nonce used before (replayed-request), a bad signature (bad-signature), and unsigned requests when the key has require_signature (signature-required). A leaked API key alone is then useless. The SDKs sign automatically (signingKey / signing_key)
End users of your appOAuth 2.0 / OpenID Connect: Authorization: Bearer <JWT>The gateway is a resource server for your identity provider (Auth0, Keycloak, Okta, Entra ID, Google…). It checks the token's signature against the provider's keys (JWKS, discovered and cached), the issuer, audience and expiry, and maps read / write / webhooks (or arrekta:…) scopes. The user becomes principal oidc:<sub> with its own rate limit, usage, webhooks and streams. Only asymmetric algorithms (RS*, PS*, ES256/384, EdDSA) are accepted. Settings: ARREKTA_GATEWAY_OIDC_ISSUER, _AUDIENCE, _JWKS_URL, _SCOPES_CLAIM, _DEFAULT_SCOPES, _RATE_PER_MIN

The gateway does not run a login page or issue tokens itself: sign-in stays with the identity provider, which already handles passwords, MFA and account recovery.

  • Webhooks done right:
    • Events for address activity, asset issue/mint/burn/freeze/clawback, and tx confirmations at chosen depths.
    • Reorg-safe: explicit confirmed and reorged (retraction) events; every delivery carries an event_id for dedupe; retries with backoff; auto-suspend after repeated failures.
    • PQ-signed deliveries: each payload is signed with the platform's ML-DSA key, and the public key is published, so receivers can verify authenticity even against a future quantum attacker. Alchemy and BitGo use classical HMAC/ECDSA signatures.
  • Streaming: SSE and WebSocket subscriptions with resume-from-event_id (built: STREAMS.md).

Streams: what we adopted after research (implemented):

Pattern or pitfallSourceWhat the gateway does
EventSource resends Last-Event-ID; servers must define replay semantics and retentionWHATWG SSE (via SSE test recipes), AblyIds are store seq; resume is strictly-after; history kept 7 days; older cursors get an explicit stream.reset with a REST resync hint, never a partial story
Monotonic ids, dedupe, at-least-onceAbly / Svix guidesInserts serialised so seq commits in order (no skipped events on resume); event ids are stable for dedupe
Reorgs: resend removed logs flagged removedEthereum eth_subscribe (Infura docs)event.retracted for every event of an orphaned block, carrying the original's addresses/assets so filtered streams get it
Bounded per-client queues; transaction feeds must replay, not dropWebSocket gateway design guidesPull-from-store by cursor: slow consumers fall behind with bounded memory and replay on reconnect
Browsers can't set WebSocket/EventSource headers; tokens in URLs leak to logsWebSocket auth guidanceShort-lived, stream-only st_ tokens (revoked with the key) instead of API keys in URLs
Heartbeats and half-open detectionWebSocket API design guidesSSE keepalive comments every 15 s; WS pings every 20 s, close after 60 s silence; app-level ping/pong
Address tracking subscriptions (batched, with limits)mempool.space WebSocket APIAddress/asset/subject filters per subscription; 10 subscriptions per socket, 20 streams per key
Events emitted only for committed changesTransactional outbox patternWallet/issuer audit rows (same transaction as the change) relayed to the gateway; gap-aware cursor

3. Why this is stronger and more distinctive

  1. Post-quantum end to end: keys, transactions, P2P transport and webhook signatures.
  2. Compliance enforced by consensus, not smart contracts: no contract-upgrade or re-entrancy class of bugs, and holders can see an asset's controls before receiving it.
  3. Ecosystem compatibility: Esplora-style API for explorers and wallets, PST for multi-party signing.
  4. Reorg-aware by design: every read model and webhook handles retractions explicitly.
  5. No custody risk by default: the wallet service holds no keys; issuers sign with their own wallets.

4. Build order

StepDeliverable
1 ✅arrekta-indexer + Esplora-compatible API + asset extensions, reorg rollback, rebuild-from-genesis
2 ✅arrekta-gateway: keys, quotas, proxy, PQ-signed reorg-safe webhooks
3 ✅arrekta-wallet: address pools, PST format (spec + vectors in PROTOCOL.md), policy engine
4 ✅arrekta-issuer: registry, assignments/distributions, corporate actions, governed controls

Each step ships with tests, a schema job (a one-shot job owns the database schema), a compose service and an end-to-end check in all_svc/.

Sources