Authentication (SIWS)
The Order Manager’s HTTP API uses SIWS (Sign-In with Solana). There is no API key to provision and no server-side session: the token carries its own proof, and the server verifies it on every request.
How it works
Section titled “How it works”- Your wallet signs the literal message
Sign in with <aud>, whereaudis the API client id (CLOB_SIWS_AUD, defaultGoMarket API). - You build an unsecured JWT whose claims carry that proof.
- You send it as
Authorization: Bearer <token>. - The server verifies the ed25519 signature against the
subclaim, checksaud/iss/expand the token age, and usessubas your identity.
The JWT’s third segment is ignored — trust comes entirely from the embedded ed25519 signature, not from a JWT signing key.
Claims
Section titled “Claims”| Claim | Value |
|---|---|
sub | Your Solana address, base58 — this is your identity |
aud | The API client id; must match the server’s CLOB_SIWS_AUD |
iss | Issuer; checked only if the server configures CLOB_SIWS_ISS |
iat / exp | Issued-at and expiry, unix seconds |
jti | Token id |
signature | Base58 ed25519 signature of Sign in with <aud> |
Generating a token
Section titled “Generating a token”The get-token utility in the CLOB repo:
# From a Solana keypair fileexport CLOB_TOKEN=$(cargo run -p get-token --quiet -- --keypair ~/.config/solana/id.json)
# From a 12-word BIP39 seed phraseexport CLOB_SEED_PHRASE="word1 word2 … word12"export CLOB_TOKEN=$(cargo run -p get-token --quiet)Relevant environment:
| Variable | Default | Meaning |
|---|---|---|
CLOB_SIWS_AUD | GoMarket API | The aud claim; the wallet signs Sign in with <aud> |
CLOB_SIWS_ISS | (empty) | Expected issuer; empty means unchecked |
CLOB_TOKEN_TTL_SECONDS | 300 | Token lifetime (exp = iat + ttl) |
Key derivation from a seed phrase follows the Solana tooling exactly: BIP39
mnemonic → PBKDF2 seed → SLIP-0010 at m/44'/501'/0'.
Using it
Section titled “Using it”curl -s http://127.0.0.1:8080/v1/markets \ -H "Authorization: Bearer $CLOB_TOKEN" | jqGET /health is the only public route. Everything else under /v1/ requires
a token.
What the authenticated address controls
Section titled “What the authenticated address controls”Your sub is not advisory — it is the authorization boundary:
| Operation | Effect of sub |
|---|---|
| Insert order | Becomes the order’s owner. A client-supplied owner is accepted but ignored. |
| Cancel | You can only cancel orders you own (case-insensitive match). |
GET /v1/orders | Always scoped to you. |
GET /v1/orders/value-range | Always scoped to you. |
GET /v1/open-orders | Scoped to you. |
Any owner filter | Must equal sub, or the request is rejected. |
Other traders’ order hashes and owner addresses are never published. You see aggregate book depth, not participants.
Token lifetime
Section titled “Token lifetime”Tokens expire after CLOB_SIWS_MAX_AGE_SECONDS (default 300 seconds).
The server checks both exp and the absolute age, so a long-lived exp
won’t extend a token past the server’s limit.
Mint a fresh token per session. For a long-running bot, regenerate on a timer comfortably inside the window — signing is local and cheap.
Internal routes
Section titled “Internal routes”POST /v1/orders/size-update is not user-facing. The Executor reports
on-chain partial fills and cancels for any owner, so it authenticates with
a shared secret instead:
X-Internal-Token: <CLOB_INTERNAL_TOKEN>The comparison is constant-time and the route fails closed when no token is configured. Only the Executor holds that secret. If you’re building a client, you will never call this route.
The gRPC transport (clob.OrderManagerService) is an internal, in-cluster
surface. External clients use the HTTP API with SIWS.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause |
|---|---|
401 on every request | aud mismatch — the signed message must be Sign in with <aud> verbatim |
| Worked, then stopped | Token older than CLOB_SIWS_MAX_AGE_SECONDS; mint a new one |
| Order rejected on owner | An owner filter that isn’t sub |
| Empty order list | GET /v1/orders only ever returns your own orders |