Skip to main content
This document explains how market makers can authenticate and integrate with the Ekiden Gateway HTTP API and WebSocket feeds exposed by the Axum service. It mirrors what is generated by utoipa and available in Swagger UI.
Swagger UI
OpenAPI JSON

1. Service Overview

  • Protocol: HTTP/1.1 + WebSocket
  • Default Port: 3010
  • Base REST Path: /api/v1
  • CORS: Open (all origins/headers/methods allowed)
  • Auth: JWT Bearer (obtained via an Ed25519 signature challenge)
  • Modules exposed: orders, fills, user, deposits, withdraws (all are nested under /api/v1)
  • WebSocket: GET /ws (top‑level; not under /api/v1)
The service depends on external gRPC backends:
  • SEQUENCER_GRPC_URL → Sequencer
  • REFEREE_GRPC_URL → Referee

2. Authentication Flow

Authentication is performed via a one-shot signature with anti‑replay protection. Clients sign the message AUTHORIZE|{timestamp_ms}|{nonce} using their Ed25519 private key and send the signature, public key, timestamp_ms, and nonce to /api/v1/authorize. On success, the API returns a JWT bearer token.

Endpoint

POST /api/v1/authorize
Summary: Verify signature over AUTHORIZE|timestamp_ms|nonce and issue a JWT. Tags: Auth Sec: no auth required for this call.

Request Body –

AuthorizeParams

{
  "public_key": "<ed25519-public-key>",
  "signature": "<sig-over-AUTHORIZE|timestamp_ms|nonce>",
  "timestamp_ms": 1736962800000,
  "nonce": "zHjQm0gkz9a2iV7xJ2y3-w"
}
  • public_key: Client’s Ed25519 public key (encoding per your SDK; commonly base64 or hex).
  • signature: Signature over the UTF‑8 string AUTHORIZE|{timestamp_ms}|{nonce}.
  • timestamp_ms: Unix time in milliseconds; must be within ±120s of server.
  • nonce: Base64URL string (8–64 chars); must be unique per request.
The server verifies with signature::verify_msg(b”AUTHORIZE||”, signature, public_key), enforces the time window and nonce rules, and rejects replays. It also computes a user address from the public key. If the user does not exist, it is created.

Responses

  • 200 OK – AuthorizeResponse
{ "token": "<jwt-bearer-token>" }
  • 400 Bad Request – invalid signature or public key
  • 500 Internal Server Error

Example – cURL

curl -X POST "http://<host>:3010/api/v1/authorize" \
  -H "Content-Type: application/json" \
  -d '{
    "public_key": "<YOUR_ED25519_PUBKEY>",
    "signature": "<SIG_OVER_AUTHORIZE|timestamp_ms|nonce>",
    "timestamp_ms": <NOW_MS>,
    "nonce": "<BASE64URL_NONCE>"
  }'
On success, save the returned token and include it in the Authorization header:
Authorization: Bearer <token>

3. Using the JWT

All authenticated REST endpoints require the standard HTTP Bearer token.
Authorization: Bearer <token>
The OpenAPI spec declares a bearer_auth security scheme, and all protected routes are annotated to use it.

4. REST API Surface

All resource routers are mounted under /api/v1:
  • GET/POST/… /api/v1/orders – Order management (exact routes in Swagger)
  • GET/… /api/v1/fills – Fill information
  • GET/… /api/v1/user – User/account endpoints
  • GET/POST/… /api/v1/deposits – Deposit endpoints
  • GET/POST/… /api/v1/withdraws – Withdraw endpoints
Authoritative contract:

Example – Authenticated request

curl -X GET "http://<host>:3010/api/v1/fills?limit=100" \\
  -H "Authorization: Bearer $TOKEN"
Tip: If you see 401 Unauthorized, refresh your token via /api/v1/authorize.

5. WebSocket Streaming

  • Endpoint: GET ws://<host>:3010/ws
  • Purpose: Real-time event feed (emitted via an internal broadcast channel)
  • Auth: Implementation-specific. If private streams are offered, you may be required to present the JWT (e.g., as the first message or via subprotocol). Consult the deployed config/team for the expected pattern.

Example – JavaScript

const ws = new WebSocket("ws://<host>:3010/ws");
ws.onopen = () => {
  // If auth is required, send your token here (example only):
  // ws.send(JSON.stringify({ op: "auth", token: YOUR_JWT }));
};
ws.onmessage = (e) => console.log("event", e.data);
ws.onclose = () => console.log("closed");
Event payloads are defined by the server’s ws::EventWithChannel. Use your environment’s Swagger or internal docs for the exact schema of each channel.

6. Local Testing

  1. Export backend endpoints:
export SEQUENCER_GRPC_URL=grpc://<sequencer-host>:<port>
export REFEREE_GRPC_URL=grpc://<referee-host>:<port>
  1. Run the API (binds 0.0.0.0:3010):
cargo run -p <your-api-crate>
  1. Open Swagger UI:
<http://localhost:3010/swagger-ui>

7. Error Handling

  • 400 – Invalid signature or public key on /authorize.
  • 401 – Missing/expired/invalid Authorization: Bearer token on protected routes.
  • 5xx – Internal server errors or dependency failures (database, gRPC backends).
Errors are logged with tracing and the service emits structured JSON logs in non‑debug builds.

8. Security Notes

  • Use HTTPS in production (behind a reverse proxy / LB) and store tokens securely.
  • JWT validity/claims are generated by AuthClaims::generate_with_token.
  • Rate limits are not enforced by this service; deploy behind an API gateway if needed.

9. FAQ

Q: What encodings are expected for public_key, signature, and nonce? A: The server treats them as strings and passes to the Ed25519 verifier. Use the same encoding your SDK produces (commonly base64 or hex). Confirm with your Ekiden contact if unsure. Nonce must be Base64URL (A‑Z, a‑z, 0‑9, -, _), 8–64 chars. Q: Is /ws namespaced under /api/v1? A: No. It is mounted at the root /ws. Q: Where can I find the exact schemas for Orders/Fills/etc.? A: Open Swagger UI at /swagger-ui or fetch /api-docs/openapi.json. Those are generated from the code and are the source of truth.

10. Changelog

  • v1.1 – Added id field to WebSocket aggregated trades (trade/{market_addr}) events. Deterministic per aggregated price/side in an execution: hash(market_addr, price, side, seq, timestamp). Use (market_addr, id) as a stable key for upserts / dedupe.
  • v1.0 – Initial draft based on Axum service wiring and utoipa annotations provided in source.