waylog
Architecture

Server Architecture

The design and philosophy behind the Waylog API Server (Hono).

The Waylog API is powered by Hono (Node.js), migrated in place from a Fastify implementation (decision record: notes/adr/0004-server-hono-migration.md). Domain logic lives in transport-agnostic services (packages/api/src/services/) consumed by both the tRPC routers and the REST routes, keeping routing, authentication, and business logic isolated and testable.

Design

Single Hono app assembled in apps/server/src/index.ts:

apps/server/src/
├── index.ts           # App assembly: logger → CORS → ip → identity → rate limits → WS → Better Auth → tRPC → REST → 404 envelope
├── identity.ts        # resolveIdentity — wlk_ API key / wl_ native token / browser session
├── env.ts             # AppEnv type + resolveIp (trusted-proxy allowlist via TRUST_PROXY)
├── envelope.ts        # success() / fail() BigInt-safe envelope helpers
├── background.ts      # cleanupExpiredRows, job reconciliation, graceful shutdown
├── trpc-context.ts    # tRPC context with signed-cookie helpers
├── ws.ts              # Live WebSocket routes + ConnectionManager
├── lib/               # Pure utilities (error mapping, http helpers, rate limiting, logger, signed cookies, OG utils)
└── routes/            # REST route files (health, client, native-auth, tmp, me, jobs, users, vtcs, api-keys, leaderboards, webhooks, steam, og)

Security & Access Control

Unified Identity Middleware

resolveIdentity runs on /api/v1/* and /webhooks/* and resolves the actor for every request: VTC API key (Bearer wlk_..., Phase 6 — scoped, per-VTC), native token (Bearer wl_...), or Better Auth browser session. Route handlers then enforce the required session / permission / scope via requireSession, requireApiKey, and checkVtcActorAccess from @waylog/api.

Trusted Proxy IPs

resolveIp honors x-real-ip / x-forwarded-for only when the socket peer is in the TRUST_PROXY allowlist (exact IPs or CIDR via net.BlockList). Without a trusted proxy, the socket address is used, so clients cannot spoof their IP for rate limiting or keying purposes.

Tiered Rate Limiting

The Hono server applies per-route sliding-window limits keyed by VTC API key ID, user ID, or client IP (one token per request from the most specific bucket):

  • REST (/api/v1/*): 200 requests per minute per user / API key / IP.
  • Auth (/api/auth/*): 20 requests per minute per user / IP (Better Auth adds its own database-backed limits).
  • Health (/api/v1/health): 60 requests per minute.
  • Telemetry client (/api/v1/client/*): 60 requests per minute per user (/client/latest capped at 30).
  • Webhooks (/webhooks/*): 500 requests per minute per IP.
  • Steam auth (/auth/*): 10 requests per minute; OG images (/api/v1/og/*): 30 per route.

Validation & Consistency

All HTTP routes validate request parameters, query strings, and bodies against Zod schemas before the route handler is invoked, guaranteeing type safety and eliminating malformed data.

Furthermore, every response adheres to a strict envelope:

// Success
{ "ok": true, "data": { ... } }

// Error
{ "ok": false, "error": { "code": "ERR_CODE", "message": "Description" } }

On this page