mirror of
https://github.com/kennethnym/aris.git
synced 2026-02-02 05:01:17 +00:00
- Add apps/aris-backend package with Hono server - Add /health endpoint - Add backend-spec.md with design decisions Co-authored-by: Ona <no-reply@ona.com>
6.6 KiB
6.6 KiB
ARIS Backend Specification
Problem Statement
ARIS needs a backend service that manages per-user FeedEngine instances and delivers real-time feed updates to clients. The backend must handle authentication, maintain WebSocket connections for live updates, and accept context updates (like location) that trigger feed recalculations.
Requirements
Authentication
- Email/password authentication using BetterAuth
- PostgreSQL for session and user storage
- Session tokens validated via
Authorization: Bearer <token>header - Auth endpoints exposed via BetterAuth's built-in routes
FeedEngine Management
- Each authenticated user gets their own FeedEngine instance
- Instances are cached in memory with a 30-minute TTL
- TTL resets on any activity (WebSocket message, location update)
- Default sources registered for each user:
LocationSource,WeatherSource,TflSource - Source configuration is hardcoded initially (customization deferred)
WebSocket Connection
- Single endpoint:
GET /ws(upgrades to WebSocket) - Authentication via
Authorization: Bearer <token>header on upgrade request - Rejected before upgrade if token is invalid
- Multiple connections per user allowed (e.g., multiple devices)
- All connections for a user receive the same feed updates
- On connect: immediately send current feed state
JSON-RPC Protocol
All WebSocket communication uses JSON-RPC 2.0.
Client → Server (Requests):
{ "jsonrpc": "2.0", "method": "location.update", "params": { "lat": 51.5, "lng": -0.1, "accuracy": 10, "timestamp": "2025-01-01T12:00:00Z" }, "id": 1 }
{ "jsonrpc": "2.0", "method": "feed.refresh", "params": {}, "id": 2 }
Server → Client (Responses):
{ "jsonrpc": "2.0", "result": { "ok": true }, "id": 1 }
Server → Client (Notifications - no id):
{ "jsonrpc": "2.0", "method": "feed.update", "params": { "items": [...], "errors": [...] } }
JSON-RPC Methods
| Method | Params | Description |
|---|---|---|
location.update |
{ lat, lng, accuracy, timestamp } |
Push location update, triggers feed refresh |
feed.refresh |
{} |
Force manual feed refresh |
Server Notifications
| Method | Params | Description |
|---|---|---|
feed.update |
{ context, items, errors } |
Feed state changed |
error |
{ code, message, data? } |
Source or system error |
Error Handling
- Source failures during refresh are reported via
errornotification - Format:
{ "jsonrpc": "2.0", "method": "error", "params": { "code": -32000, "message": "...", "data": { "sourceId": "weather" } } }
Acceptance Criteria
-
Auth Flow
- User can sign up with email/password via
POST /api/auth/sign-up - User can sign in via
POST /api/auth/sign-inand receive session token - Invalid credentials return 401
- User can sign up with email/password via
-
WebSocket Connection
GET /wswith validAuthorizationheader upgrades to WebSocketGET /wswithout valid token returns 401 (no upgrade)- On successful connect, client receives
feed.updatenotification with current state - Multiple connections from same user all receive updates
-
FeedEngine Lifecycle
- First connection for a user creates FeedEngine with default sources
- Subsequent connections reuse the same FeedEngine
- FeedEngine is destroyed after 30 minutes of inactivity
- Activity (any WebSocket message) resets the TTL
-
JSON-RPC Methods
location.updateupdates LocationSource and triggers feed refreshfeed.refreshtriggers manual refresh- Both return
{ "ok": true }on success - Invalid method returns JSON-RPC error
-
Feed Updates
- FeedEngine subscription pushes updates to all user's WebSocket connections
- Updates include
context,items, anderrors
Implementation Approach
Phase 1: Project Setup
- Create
apps/aris-backendwith Hono - Configure TypeScript, add dependencies (hono, better-auth, postgres driver)
- Set up database connection and BetterAuth
Phase 2: Authentication
- Configure BetterAuth with email/password provider
- Mount BetterAuth routes at
/api/auth/* - Create session validation helper for extracting user from token
Phase 3: FeedEngine Manager
- Create
FeedEngineManagerclass:getOrCreate(userId): FeedEngine- returns cached or creates newtouch(userId)- resets TTLdestroy(userId)- manual cleanup- Internal TTL cleanup loop
- Factory function to create FeedEngine with default sources
Phase 4: WebSocket Handler
- Create WebSocket upgrade endpoint at
/ws - Validate
Authorizationheader before upgrade - On connect: register connection, send initial feed state
- On disconnect: unregister connection
Phase 5: JSON-RPC Handler
- Create JSON-RPC message parser and dispatcher
- Implement
location.updatemethod - Implement
feed.refreshmethod - Wire FeedEngine subscription to broadcast
feed.updateto all user connections
Phase 6: Connection Manager
- Create
ConnectionManagerto track WebSocket connections per user - Broadcast helper to send to all connections for a user
Phase 7: Integration & Testing
- Integration test: auth → connect → location update → receive feed
- Test multiple connections receive same updates
- Test TTL cleanup
Package Structure
apps/aris-backend/
├── package.json
├── src/
│ ├── index.ts # Entry point, Hono app
│ ├── auth.ts # BetterAuth configuration
│ ├── db.ts # Database connection
│ ├── ws/
│ │ ├── handler.ts # WebSocket upgrade & message handling
│ │ ├── jsonrpc.ts # JSON-RPC parser & types
│ │ └── methods.ts # Method implementations
│ ├── feed/
│ │ ├── manager.ts # FeedEngineManager (TTL cache)
│ │ ├── factory.ts # Creates FeedEngine with default sources
│ │ └── connections.ts # ConnectionManager (user → WebSocket[])
│ └── types.ts # Shared types
Dependencies
{
"dependencies": {
"hono": "^4",
"better-auth": "^1",
"postgres": "^3",
"@aris/core": "workspace:*",
"@aris/source-location": "workspace:*",
"@aris/source-weatherkit": "workspace:*",
"@aris/data-source-tfl": "workspace:*"
}
}
Open Questions (Deferred)
- User source configuration storage (database schema)
- Rate limiting on WebSocket methods
- Reconnection handling (client-side concern)
- Horizontal scaling (would need Redis for shared state)