Overview
Autonome uses a hybrid data flow architecture combining oRPC procedures for request/response patterns and Server-Sent Events (SSE) for real-time updates. This ensures type-safe data fetching while maintaining instant UI reactivity.oRPC Request/Response Flow
Client → Server Data Fetching
The primary pattern for fetching data from the backend:Step-by-Step Breakdown
1. Component Query
- TanStack Query checks cache (15s staleTime for positions)
- If stale, triggers oRPC client request
- Suspends component until data arrives
2. oRPC Client Configuration
RouterClient<typeof router>infers full type signature from backendgetRpcUrl()usesVITE_API_URLenv var (or Vite proxy in dev)createTanstackQueryUtils()generates.queryOptions()for every procedure
3. API URL Resolution
4. Vite Proxy (Development Only)
- Browser requests
http://localhost:5173/api/rpc/trading.getPositions - Vite intercepts and forwards to
http://localhost:8081/api/rpc/trading.getPositions - Backend responds, Vite forwards back to browser
The proxy eliminates CORS issues in development. In production, the frontend directly calls the VPS API URL.
5. Hono API Server
- POST
/api/rpc/trading.getPositions→router.trading.getPositions - POST
/api/rpc/simulator.placeOrder→router.simulator.placeOrder - Wildcard matching for nested procedures
6. oRPC Procedure Handler
- Input validation: Zod parses and validates
input - Business logic: Calls feature module (e.g.,
fetchPositions) - Output validation: Zod validates return value matches schema
- Error handling: Zod validation errors auto-convert to 400 responses
- Observability: Sentry span tracks execution time
7. Feature Module (Business Logic)
- Drizzle query with join (
Orders+Models) - Filter for
OPENorders (positions, not trades) - Enrich with live price data (unrealized P&L)
- Return typed array
8. Response Flow
The response travels back through the stack:TanStack Query Caching Strategy
TanStack Query caches responses to reduce server load:
Stale time recommendations:
- High-frequency updates (positions, trades):
15s - Moderate updates (portfolio history):
30s - Rarely changing (models, variants):
5min
Server-Sent Events (SSE) Streaming
Why SSE?
SSE provides unidirectional real-time updates from server to client:
Autonome’s choice: SSE is simpler than WebSockets for one-way updates and automatically reconnects on network issues.
SSE Architecture
Backend: Event Broadcasting
EventEmitter Pattern
- In-memory cache: Stores latest event for immediate SSE hydration
- Max listeners: Increased from default (10) to support many SSE clients
- Cleanup function: Returned by
subscribefor proper unsubscription
SSE Endpoint Handler
- Cache hydration: Fetch latest positions from DB
- Initial data: Send current cache to new subscriber
- Subscription: Register listener for future events
- Heartbeat: Send ping every 15s to prevent proxy timeout
- Cleanup: Remove listener when client disconnects
- Keep-alive: Never-resolving promise keeps stream open
Event Emission from Schedulers
- Schedulers: Price tracker (10s), trade executor (60s)
- Simulator: Auto-close triggers (stop-loss, take-profit)
- Manual actions: User places/closes orders
Frontend: Event Consumption
EventSource Subscription
- Component mounts →
usePositionsStream()subscribes to SSE - Server emits position update → SSE pushes to client
onmessagehandler → updates TanStack Query cache- Cache update → React re-renders component with new data
- Component unmounts →
EventSource.close()called
SSE Endpoints Overview
State Management Layers
Autonome uses a multi-layer state architecture:1. Server State (TanStack Query)
For data fetched from the backend:Persistence: In-memory (cleared on refresh)
Synchronization: oRPC + SSE
2. URL State (TanStack Router)
For navigation and shareable state:Persistence: URL (shareable, bookmarkable)
Use cases: Filters, tabs, pagination
3. Local State (TanStack Store)
For ephemeral UI state:Persistence: In-memory (cleared on refresh)
Use cases: Sidebar expanded state, theme preference
4. Component State (React)
For isolated component logic:useState/useReducerPersistence: Component lifecycle only
Use cases: Form inputs, modals, tooltips
Optimistic Updates
For instant UI feedback on mutations:- User clicks “Close” → UI updates instantly (optimistic)
- Mutation sent to server → oRPC handler executes
- If success → SSE confirms update (cache already correct)
- If error → Rollback optimistic update (restore previous state)
onSettled→ Refetch to guarantee server truth
Data Consistency Guarantees
Single Source of Truth: Orders Table
The"Orders" table in PostgreSQL is the canonical source for positions:
- OPEN orders = active positions
- CLOSED orders = completed trades
Simulator State Restoration
On server restart, the simulator rehydrates from the database:- Simulator state matches DB on startup
- Auto-close triggers work after restart
- No position data loss during deployments
Cache Invalidation Strategy
Explicit invalidation after mutations:Performance Optimizations
1. Query Deduplication
TanStack Query deduplicates concurrent requests:2. Selective Cache Updates
SSE updates only changed data:3. Background Refetching
TanStack Query refetches stale data without blocking UI:4. Partial Hydration
SSE endpoints hydrate cache before sending initial data:Error Handling
oRPC Errors
SSE Reconnection
EventSource automatically reconnects on disconnect:Mutation Rollback
Optimistic updates rollback on error (see Optimistic Updates section).Summary
Autonome’s data flow architecture balances:- Type safety: oRPC procedures with Zod validation
- Performance: TanStack Query caching and background refetching
- Real-time: SSE for instant UI updates
- Consistency: Single source of truth (Orders table)
- Resilience: Automatic reconnection and error recovery
Next Steps
- Architecture Overview - System components and deployment
- Tech Stack - Detailed technology breakdown

