31 KiB
Officer.dev Backend Architecture
Executive Summary
Officer is a full-stack AI-assisted personal backend and frontend for life management. The backend is a Bun-based monorepo using:
- Hono framework for REST API (port 5000)
- PostgreSQL with Drizzle ORM for persistent data
- WebSockets for real-time terminal and AI chat functionality
- Multi-workspace shared libraries for code reuse
- Node.js PTY sidecars for terminal emulation
- Pi coding agent integration for AI-powered development assistance
This document covers the backend architecture, data flow, and technical patterns.
Table of Contents
- Architecture Overview
- Technology Stack
- Directory Structure
- Core Server
- API Architecture
- Database Design
- WebSocket Services
- Shared Workspaces
- Error Handling
- Development & Deployment
- Key Features
Architecture Overview
High-Level Architecture
┌─────────────────────────────────────────────────────────────┐
│ Web Browser (Frontend) │
└──────────────┬──────────────────────────────────────────────┘
│
├─ HTTP REST API (port 5000)
├─ WebSocket: Terminal (ws://api:5000/api/terminal/ws)
├─ WebSocket: Pi Chat (ws://api:5000/api/pi/chat/ws)
└─ Dev Server Proxy (ws://api:5000/api/dev-server-proxy/*)
│
┌──────────────▼──────────────────────────────────────────────┐
│ Bun Server (src/server.tsx + Hono Router) │
├─────────────────────────────────────────────────────────────┤
│ Middleware: │
│ - CORS handler │
│ - User authentication (JWT + token blacklist) │
│ - Request body parser │
│ │
│ Routes: │
│ - /api/auth → Authentication (login, signup) │
│ - /api/plans → Plans management │
│ - /api/skills → Skills (Pi integration) │
│ - /api/tasks → Tasks & processes │
│ - /api/upload → File uploads │
│ - /api/workspaces → Workspace management │
│ - /api/settings → User settings │
│ - /api/dev-server → Local dev server routing │
│ - WebSocket handlers → Real-time communication │
└──────────────┬──────────────────────────────────────────────┘
│
┌──────┴──────────────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ PostgreSQL DB │ │ Node.js PTY Sidecar │
│ (officer_db) │ │ (Terminal Emulation)│
│ │ │ │
│ Users, Plans, │ │ xterm-js │
│ Skills, Tasks, │ │ node-pty │
│ Sessions, etc │ │ Docker container │
└──────────────────┘ └──────────────────────┘
Request/Response Flow
Client Request
↓
Server Router (Bun)
↓
Authentication Middleware
↓
Protected/Public Handler
↓
Database Query (Drizzle ORM) OR External Service
↓
Response (JSON/WebSocket)
↓
Client
Technology Stack
Core Runtime & Framework
- Bun (v1.30+) - Fast JavaScript runtime with built-in tooling
- TypeScript (v5.9+) - Strict mode enabled
- Hono (v4.11+) - Lightweight, type-safe web framework
- Node.js - For PTY sidecar processes
Database & ORM
- PostgreSQL - Primary persistent data store
- Drizzle ORM (v0.45+) - Type-safe SQL query builder
- Drizzle Kit (v0.31+) - Schema management and migrations
Real-Time Communication
- WebSockets (native Bun) - Bidirectional communication
- Redis (optional) - Session/cache storage
Authentication & Security
- JWT - Token-based authentication
- Argon2 - Password hashing (bcrypt alternative)
- SimpleWebAuthn - WebAuthn/FIDO2 passkey support
- Token Blacklist - Revocation tracking
AI & Automation
- @anthropic-ai/claude-agent-sdk - Claude integration
- Pi Coding Agent - CLI-based AI development assistant
- Nodemailer - Email notifications
- Googleapis - Google integration for services
Utilities
- node-pty - Cross-platform pseudoterminal support
- Cron (v4.3+) - Scheduled task execution
- Date-fns (v4.1+) - Date manipulation
- Zod (v4.2+) - Schema validation
Directory Structure
Complete Backend Layout
monorepo/
├── src/
│ ├── server.tsx # Main Bun server entry point
│ │
│ ├── servers/ # Backend core
│ │ ├── bootstrap.ts # Server initialization
│ │ ├── hono.ts # Hono app & router setup
│ │ ├── jwt.ts # JWT token handling
│ │ ├── custom-errors.ts # Custom error classes
│ │ ├── create-router.ts # Router factory with context
│ │ ├── data-path.ts # Path management
│ │ │
│ │ ├── _middlewares/ # Shared middleware
│ │ │ ├── user-middleware.ts # Auth verification
│ │ │ └── body-parser.ts # Request parsing
│ │ │
│ │ └── api/ # API routes
│ │ ├── auth/ # Authentication routes
│ │ │ ├── signin.ts # Login endpoint
│ │ │ ├── signup.ts # Registration endpoint
│ │ │ ├── signout.ts # Logout endpoint
│ │ │ ├── verify.ts # Email verification
│ │ │ ├── passkey-router.ts # WebAuthn endpoints
│ │ │ ├── reset-password.ts # Password reset
│ │ │ └── auth.ts # Core auth logic
│ │ │
│ │ ├── terminal/ # Terminal WebSocket
│ │ │ ├── websocket.ts # WS handler
│ │ │ └── pty-sidecar.mjs # PTY subprocess
│ │ │
│ │ ├── pi/ # Pi coding agent integration
│ │ │ ├── websocket.ts # Chat WS handler
│ │ │ ├── rest.ts # REST endpoints
│ │ │ ├── pi-bridge.ts # Pi SDK bridge
│ │ │ ├── storage.ts # Chat session storage
│ │ │ └── session-manager.ts # Session management
│ │ │
│ │ ├── dev-server/ # Local dev server routing
│ │ │ └── router.ts # Dev server proxy
│ │ │
│ │ ├── plans/ # Plans management
│ │ ├── skills/ # Skills routes
│ │ ├── tasks/ # Tasks & processes
│ │ ├── workspaces/ # Workspace management
│ │ ├── settings/ # User settings
│ │ ├── upload/ # File upload
│ │ ├── file-browser/ # File browser API
│ │ ├── scrape/ # Web scraping
│ │ ├── sessions/ # Chat sessions
│ │ ├── task-logs/ # Task execution logs
│ │ ├── server-settings/ # System configuration
│ │ └── users/ # User management
│ │
│ ├── databases/ # Data layer
│ │ └── officer_db/ # Main database package
│ │ ├── drizzle.config.ts # Drizzle configuration
│ │ ├── migrations/ # Database migrations
│ │ ├── src/
│ │ │ ├── schema.ts # Table definitions
│ │ │ ├── types.ts # Inferred types
│ │ │ └── index.ts # Exports
│ │ └── package.json # Workspace manifest
│ │
│ └── workspaces/ # Shared libraries
│ ├── components/ # React components
│ ├── hooks/ # React hooks
│ ├── helpers/ # Utility functions
│ ├── types/ # Shared types
│ ├── state/ # State management
│ ├── config/ # Configuration
│ ├── definitions/ # Constants
│ ├── emailer/ # Email service
│ ├── injector/ # Dependency injection
│ └── i18n/ # Internationalization
│
├── package.json # Root workspace config
├── bunfig.toml # Bun configuration
├── tsconfig.json # TypeScript config
└── .env # Environment variables
Core Server
Entry Point: src/server.tsx
The main server file initializes the Bun HTTP server and handles:
- Route registration (static files, API, WebSockets)
- WebSocket upgrade logic and authentication
- Dev server proxy for HMR and local dev environments
- PI coding agent installation/verification
Key Responsibilities
// 1. Static file serving
'/static/*' → public/ directory files
// 2. HTML fallback for SPA routing
'/' and '/*' → officer-web app
// 3. API routing
'/api/*' → honoServer (REST endpoints)
// 4. WebSocket routing with auth
'/api/terminal/ws' → Terminal WebSocket
'/api/pi/chat/ws' → Pi Chat WebSocket
// 5. Dev server proxy
'/api/dev-server-proxy/*' → Proxied WS for local dev servers
WebSocket Authentication Pattern
async function upgradeWs(req: Request, server: any, provider: 'terminal' | 'pi') {
// 1. Extract JWT token from query params
const token = new URL(req.url).searchParams.get('token');
// 2. Verify token and extract user
const user = await verify(token);
// 3. Check token blacklist (for revoked tokens)
const blacklisted = await officerdb.query.TokenBlacklist.findFirst({...});
// 4. Extract WS-specific parameters
const sessionId = url.searchParams.get('sessionId');
const cwd = url.searchParams.get('cwd');
const cols = url.searchParams.get('cols'); // terminal dimensions
// 5. Upgrade connection with authenticated data
server.upgrade(req, { data: { userId, email, role, provider, ... } });
}
Hono Server: src/servers/hono.ts
Hono is a lightweight web framework perfect for edge computing and Bun.
Server Initialization
const honoServer = new Hono<{ Variables: HonoVariables }>();
honoServer.use(cors({ origin: '*', allowMethods: [...] }));
// Public routes
honoServer.get('/api', (ctx) => ctx.json({ officerAPI: 'ok' }));
honoServer.route('/api/auth', authRouter);
honoServer.route('/api/server-settings', serverSettingsRouter);
// Protected routes
const protectedRouter = createRouter();
protectedRouter.use(bodyParser());
protectedRouter.use(userMiddleware);
protectedRouter.route('/plans', plansRouter);
protectedRouter.route('/tasks', tasksRouter);
// ... other routes
honoServer.route('/api', protectedRouter);
// Error handling
honoServer.onError((error, ctx) => {
if (error instanceof CustomError) {
return ctx.json(error.returnValue, error.statusCode);
}
return ctx.text('Internal Server Error', 500);
});
Hono Variables Context
type HonoVariables = {
userId: number;
email: string;
role: 'admin' | 'user';
// Accessible in all routes via ctx.get('userId')
};
API Architecture
Route Organization
The API is organized by feature domain:
/api/
├── auth/ # User authentication (public)
│ ├── POST /signin
│ ├── POST /signup
│ ├── POST /signout
│ ├── GET /verify
│ ├── POST /passkeys (WebAuthn)
│ └── ...
│
├── plans/ # Plans management (protected)
├── skills/ # Skills endpoints
├── tasks/ # Task operations
├── workspaces/ # Workspace operations
├── settings/ # User preferences
├── upload/ # File uploads
├── file-browser/ # File system access
├── terminal/ws # Terminal emulation (WS)
├── pi/ # AI assistant integration
│ ├── /ws # Chat (WebSocket)
│ └── /rest # REST endpoints
│
└── dev-server/ # Local dev server routing
Router Creation Pattern: src/servers/create-router.ts
// Factory function for creating contextualized routers
export function createRouter() {
return new Hono<{ Variables: HonoVariables }>();
}
// Usage in route handlers:
const router = createRouter();
router.get('/endpoint', (ctx) => {
const userId = ctx.get('userId');
// Route logic
});
Error Handling: src/servers/custom-errors.ts
class CustomError extends Error {
constructor(
public statusCode: number,
message: string,
public returnValue?: Record<string, any> | string,
) {
super(message);
}
}
// Usage patterns
throw new CustomError(400, 'Bad request', { error: 'Invalid input' });
throw new CustomError(401, 'Unauthorized');
throw new CustomError(500, 'Internal error', { error: 'Database failed' });
Database Design
Primary Database: officer_db
Located in src/databases/officer_db/, this is the main persistent data store.
Drizzle Configuration
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema.ts',
out: './migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
Schema Structure
The schema defines all tables and their relationships:
// src/databases/officer_db/src/schema.ts
// Core tables
export const Users = pgTable('users', {
id: serial('id').primaryKey(),
email: text('email').unique().notNull(),
password: text('password'),
role: text('role').$type<'admin' | 'user'>().default('user'),
verified: boolean('verified').default(false),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});
export const Plans = pgTable('plans', {
id: serial('id').primaryKey(),
userId: integer('user_id').references(() => Users.id),
title: text('title').notNull(),
description: text('description'),
// ... other fields
});
export const Skills = pgTable('skills', {
id: serial('id').primaryKey(),
userId: integer('user_id').references(() => Users.id),
name: text('name').notNull(),
content: text('content'),
// ... other fields
});
// ... many more tables for tasks, sessions, workspaces, etc.
// Relations
export const usersRelations = relations(Users, ({ many }) => ({
plans: many(Plans),
skills: many(Skills),
}));
Type Generation
Types are automatically inferred from schema:
// src/databases/officer_db/src/types.ts
// Simple table types
export type User = typeof Users.$inferSelect;
export type UserInsert = typeof Users.$inferInsert;
// Extended types for API responses
export type UserWithRelations = User & {
plans: Plan[];
skills: Skill[];
};
Database Operations in Routes
import { officerdb, eq, Users } from 'officerdb';
// Simple query
const user = await officerdb.query.Users.findFirst({
where: eq(Users.id, userId),
});
// Query with relations
const user = await officerdb.query.Users.findFirst({
where: eq(Users.id, userId),
with: {
plans: true,
skills: true,
},
});
// Insert with returning
const [newUser] = await officerdb.insert(Users).values({
email: 'user@example.com',
password: hashedPassword,
}).returning();
// Update
await officerdb.update(Users).set({
verified: true,
}).where(eq(Users.id, userId));
Token Blacklist Table
export const TokenBlacklist = pgTable('token_blacklist', {
id: serial('id').primaryKey(),
jti: text('jti').unique().notNull(), // JWT ID
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at').defaultNow(),
});
// Used for token revocation (logout, password change, etc.)
Database Migrations
Migrations are generated by Drizzle and stored in src/databases/officer_db/migrations/:
# Generate new migration
bun run db:gen
# Apply migrations
bun run db:push
# Open Drizzle Studio for visual management
bun run db:studio
WebSocket Services
1. Terminal WebSocket: src/servers/api/terminal/
Provides an interactive terminal emulator in the browser using xterm.js.
Architecture
Browser (xterm.js)
↓ WebSocket
Officer Server (websocket.ts)
↓ IPC/Stdio
PTY Sidecar (pty-sidecar.mjs)
↓ Shell Process
System Shell (bash/zsh)
Handler: websocket.ts
export const terminalWebsocket = {
// Connection established
open(ws: ServerWebSocket<WSData>) {
const { userId, cwd, cols, rows, sandboxed } = ws.data;
// 1. Spawn or reuse PTY sidecar
// 2. Set terminal dimensions
// 3. Send initial prompt
},
// Client sends data (typing, etc.)
message(ws: ServerWebSocket<WSData>, raw: string | Buffer) {
// 1. Parse message type (input, resize, etc.)
// 2. Forward to PTY process
},
// Connection closed
close(ws: ServerWebSocket<WSData>) {
// 1. Kill PTY process
// 2. Cleanup resources
},
};
PTY Sidecar: pty-sidecar.mjs
A Node.js subprocess that manages the actual pseudoterminal:
// Uses node-pty for cross-platform terminal support
const pty = require('node-pty');
const term = pty.spawn('bash', [], {
name: 'xterm-color',
cols: 120,
rows: 40,
cwd: process.env.CWD,
});
// stdout → send to client
term.on('data', (data) => {
process.stdout.write(JSON.stringify({ type: 'output', data }));
});
// stdin from client → write to terminal
process.stdin.on('data', (chunk) => {
const msg = JSON.parse(chunk);
if (msg.type === 'input') term.write(msg.data);
if (msg.type === 'resize') term.resize(msg.cols, msg.rows);
});
Sandboxed vs. Real Terminals
- Sandboxed: Limited to specific directories, no system access
- Real: Full system access from user's working directory
2. Pi Coding Agent WebSocket: src/servers/api/pi/
Integrates the Pi coding agent for AI-assisted development.
Architecture
Browser (Chat UI)
↓ WebSocket
Officer Server (pi/websocket.ts)
↓ SDK
Pi Coding Agent (pi-bridge.ts)
↓ RPC/Tools
LLM API (Claude, GPT, etc.)
Components
websocket.ts: WebSocket message handler
- Routes incoming chat messages to Pi
- Streams responses back to client
- Manages session state
pi-bridge.ts: SDK integration layer
export async function createPiSession(
cwd: string,
userEmail: string,
sessionId?: string,
) {
// 1. Initialize Pi SDK
const { session } = await createAgentSession({
cwd,
model: selectedModel,
tools: createCodingTools(cwd),
});
// 2. Subscribe to events
session.subscribe((event) => {
// Stream events back to client via WS
});
// 3. Return session for messaging
return session;
}
rest.ts: REST endpoints for Pi operations
- Create new sessions
- List sessions
- Get session details
- Export conversations
storage.ts: Chat session persistence
// Store conversation history in database
interface PiSession {
id: string;
userId: number;
cwd: string;
model: string;
createdAt: timestamp;
messages: PiMessage[];
}
// Retrieve from database when resuming
session-manager.ts: Session lifecycle management
Message Flow
Client: "ls -la"
→ WebSocket to Officer
→ Pi process stdin
→ Pi executes bash tool
→ Tool output
→ Stream back to client
Integration with Tools
Pi can execute:
read- Read filesbash- Run shell commandsedit- Modify fileswrite- Create files- Custom tools via extensions
Shared Workspaces
Shared code is organized in src/workspaces/ as Bun workspace packages:
Key Workspaces
types/ - TypeScript type definitions
- Centralized, exported via
'types'alias - Shared by frontend and backend
- Database types imported and re-exported
helpers/ - Utility functions
- Formatters, validators, converters
- Pure functions with no side effects
hooks/ - React hooks
- Frontend-only
useAuth,useQuery,useMutation, etc.
state/ - State management
- Global state stores
- Context providers
- Zustand/React Context usage
components/ - React components
- UI components (buttons, inputs, etc.)
- Complex feature components
- Reusable across apps
config/ - Configuration
- Constants
- Environment-specific settings
definitions/ - Enum and constant definitions
- User roles
- Status values
- Feature flags
emailer/ - Email service
// Nodemailer-based email sending
export async function sendEmail(to: string, subject: string, html: string) {
const transporter = nodemailer.createTransport({...});
return transporter.sendMail({ to, subject, html });
}
injector/ - Dependency injection
- Service locator pattern
- Configuration management
i18n/ - Internationalization
- Multi-language support
- Locale management
sounds/ - Audio assets
- Notification sounds
- UI feedback audio
widgets/ - Complex UI widgets
- Feature-rich components
- Composed from basic components
Workspace Configuration
Each workspace has a package.json:
{
"name": "components",
"version": "0.1.0",
"type": "module",
"exports": {
".": "./index.ts"
},
"main": "./index.ts"
}
Import Pattern in Code
// In any app (frontend/backend)
import { User, Plan } from 'types';
import { formatDate } from 'helpers';
import { useAuth } from 'hooks';
import { useGlobalState } from 'state';
import { Button } from 'components';
Error Handling
Error Classification
// Custom error hierarchy
class CustomError extends Error {
constructor(
public statusCode: number,
message: string,
public returnValue?: any,
) {}
}
// Usage patterns
throw new CustomError(400, 'Bad request', { field: 'email', error: 'Invalid' });
throw new CustomError(401, 'Unauthorized');
throw new CustomError(403, 'Forbidden');
throw new CustomError(404, 'Not found', { resource: 'Plan' });
throw new CustomError(409, 'Conflict', { error: 'Email already exists' });
throw new CustomError(500, 'Internal server error');
Middleware Error Handling
honoServer.onError((error, ctx) => {
// CustomError → formatted response
if (error instanceof CustomError) {
return ctx.json(error.returnValue || error.message, error.statusCode);
}
// Unexpected error → 500
console.error('Unexpected error:', error.message);
return ctx.text('Internal Server Error', 500);
});
Try/Catch Pattern
// In route handlers
router.post('/endpoint', async (ctx) => {
try {
const body = await ctx.req.json();
// Validation
if (!body.email) {
throw new CustomError(400, 'Email required', { field: 'email' });
}
// Database operation
const user = await officerdb.insert(Users).values({...}).returning();
if (!user) throw new CustomError(500, 'Failed to create user');
return ctx.json({ success: true, user });
} catch (error) {
// Re-throw CustomErrors, let middleware handle
if (error instanceof CustomError) throw error;
// Unexpected error
console.error('Route error:', error);
throw new CustomError(500, 'Internal server error');
}
});
Authentication System
JWT Flow
User Login
↓
Verify credentials (email + password)
↓
Generate JWT token
{
"sub": userId,
"email": email,
"role": "user",
"iat": timestamp,
"exp": timestamp,
"jti": unique-token-id
}
↓
Return token to client
↓
Client stores in localStorage
↓
Client sends in Authorization header
↓
Server verifies signature & expiration
↓
Grant access
Token Verification: src/servers/jwt.ts
export async function verify(token: string) {
try {
// 1. Verify signature and expiration
const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload;
// 2. Check token blacklist (for revoked tokens)
if (decoded.jti) {
const blacklisted = await officerdb.query.TokenBlacklist.findFirst({
where: eq(TokenBlacklist.jti, decoded.jti),
});
if (blacklisted) return null; // Token revoked
}
return { id: decoded.sub, email: decoded.email, role: decoded.role };
} catch {
return null; // Invalid token
}
}
Logout with Token Blacklist
router.post('/signout', async (ctx) => {
const token = ctx.req.header('Authorization')?.replace('Bearer ', '');
const decoded = jwt.decode(token) as JWTPayload;
// Add to blacklist
await officerdb.insert(TokenBlacklist).values({
jti: decoded.jti,
expiresAt: new Date(decoded.exp * 1000),
});
return ctx.json({ success: true });
});
WebAuthn/Passkey Support
// Registration
router.post('/passkeys/register/options', async (ctx) => {
const user = ctx.get('userId');
const options = await generateRegistrationOptions({
rpID: 'officer.dev',
rpName: 'Officer',
userID: Buffer.from(String(user.id)),
userName: user.email,
});
return ctx.json(options);
});
// Verification
router.post('/passkeys/register/verify', async (ctx) => {
const credential = await ctx.req.json();
const verification = await verifyRegistrationResponse({
credential,
expectedOrigin: 'https://officer.dev',
expectedRPID: 'officer.dev',
});
if (verification.verified) {
// Store passkey
await officerdb.insert(Passkeys).values({...});
}
return ctx.json({ verified: verification.verified });
});
Development & Deployment
Development Server
# Start with hot reload
bun dev
# Server runs on http://localhost:5000
# Frontend available at http://localhost:5000/
Database Operations
# Generate migration from schema changes
bun run db:gen
# Apply pending migrations
bun run db:push
# Open Drizzle Studio (UI for database)
bun run db:studio
Build & Production
# Full build
bun run build
# Production server
NODE_ENV=production bun src/server.tsx
# Runs on port specified by PORT env var (default 5000)
Environment Variables
# .env
DATABASE_URL=postgresql://user:pass@localhost:5432/officer
JWT_SECRET=your-secret-key
NODE_ENV=development
PORT=5000
Key Features
1. Real-Time Terminal
- Interactive shell in browser
- File operations
- Code execution
- Multi-session support
- Sandboxed mode option
2. AI-Powered Development with Pi
- Chat interface with Claude
- File reading/writing capabilities
- Shell command execution
- Session persistence
- Model selection
3. Task Management
- Create and track tasks
- Task dependencies
- Scheduled execution (cron)
- Task logs and history
- Process management
4. Workspace Management
- Multi-workspace support
- Project organization
- Settings per workspace
- Resource management
5. File Management
- File browser with preview
- Upload support (50GB max)
- Web scraping
- Export/import
6. Skills System
- Pi agent skills
- Custom task templates
- Reusable automation
7. Settings & Configuration
- User preferences
- System configuration
- Integration settings
- Resource limits
Performance Considerations
Connection Pooling
PostgreSQL connections are managed by Drizzle ORM with configurable pool size.
Caching
- Redis support for session data (optional)
- Database query result caching
- Static asset caching
Rate Limiting
- Per-user API limits (todo)
- WebSocket message throttling
- File upload size limits (50GB max)
Database Optimization
- Proper indexing on foreign keys
- Query optimization with relations
- Pagination for large datasets
Security
CORS
honoServer.use(cors({
origin: '*',
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
}));
JWT Security
- Secret stored in env variables
- Token expiration enforced
- Token blacklist for revocation
- jti (JWT ID) for tracking
Password Security
- Argon2 hashing (modern, secure)
- No plain-text storage
- Email verification required
- Password reset via email
WebAuthn/FIDO2
- Hardware key support
- Phishing-resistant
- No passwords stored for passkeys
Monitoring & Logging
Current Logging
- Console.log for debugging
- Error stack traces printed to console
Future Monitoring (Signoz Integration)
- Distributed tracing
- Performance monitoring
- Error tracking
- Log aggregation
Testing
Current Test Setup
@testing-library/reactfor component tests@playwright/testfor E2E testshappy-domfor DOM testing
Test Command
# Not yet fully configured
bun test
Related Documentation
- Frontend Architecture: See
OFFICERDEV_FRONTEND.md - CONVENTIONS.md: Detailed code patterns and style guide
- CLAUDE.md: Project overview and patterns
- Each folder CLAUDE.md: Feature-specific documentation
Summary
Officer's backend is a modern, type-safe Bun monorepo with:
- RESTful API using Hono framework
- Real-time capabilities via WebSockets
- PostgreSQL persistence with Drizzle ORM
- AI integration through Pi coding agent
- Modular architecture with shared workspaces
- Strong TypeScript support throughout
- Robust authentication with JWT and WebAuthn
The architecture prioritizes developer experience, type safety, and maintainability while providing powerful real-time and AI-powered features to users.