Skip to content

Latest commit

 

History

History
961 lines (771 loc) · 30.1 KB

File metadata and controls

961 lines (771 loc) · 30.1 KB

Arquitetura Técnica - Virtual Office

Status: Aprovado Data: 2026-02-07 Autor: Aria (Architect Agent) Modo: YOLO - Decisões autônomas com rationale claro


Executive Summary

Virtual Office é um SaaS greenfield que visualiza agentes AIOS trabalhando em tempo real. Esta arquitetura prioriza MVP speed com fundação sólida para escala futura.

Decisões Chave:

  • Frontend: React 18 + Vite (ecossistema maduro, time real expertise)
  • Rendering: Canvas 2D isométrico (MVP velocity, 3D em roadmap)
  • WebSocket: Socket.io (auto-reconnect, broadcasting, dev experience)
  • State: Zustand (simplicidade, performance, real-time friendly)
  • License: HMAC-SHA256 (pragmático, seguro suficiente para MVP)
  • Database: SQLite (local-first, zero config, migração fácil)
  • Testing: Vitest + Playwright (Vite ecosystem, velocidade)

Timeline: 8-12 semanas para MVP completo (4 epics, 17 stories)


1. Tech Stack Decisions

1.1 Frontend Framework: React 18

Decisão: React 18 com TypeScript + Vite

Rationale:

  1. Ecosystem Maturo: Maior biblioteca de componentes (Shadcn/ui, Radix), state managers, ferramentas
  2. Real-Time Support: Concurrent features do React 18 otimizam atualizações frequentes (WebSocket streams)
  3. Time Expertise: Equipe já tem experiência com React (menor curva de aprendizado)
  4. Hiring Pool: Facilita contratação futura se necessário
  5. Vite: Build ultra-rápido (< 1s HMR), TypeScript out-of-box

Alternativas Consideradas:

  • Vue 3: Excelente, mas ecossistema menor para enterprise features
  • Svelte: Mais rápido, mas ecossistema imaturo para SaaS complexo

Implicações:

  • Bundle size: ~150KB gzipped (React + ReactDOM + Router)
  • Dev velocity: Alta (componentes prontos, debugging maduro)
  • Performance: 30+ FPS garantido com otimizações corretas

ADR: docs/architecture/adrs/adr-001-frontend-framework.md


1.2 Rendering Approach: Canvas 2D Isométrico

Decisão: Canvas 2D com projeção isométrica para MVP, migração 3D em v2.0

Rationale:

  1. MVP Velocity: Canvas 2D é 3-5x mais rápido de implementar que Three.js
  2. Performance Garantida: 60 FPS em hardware modesto (sem GPU requirements)
  3. Baixa Complexidade: Menos bugs, debugging mais simples
  4. Visual Suficiente: Isométrico entrega "office aesthetic" sem overhead 3D
  5. Migração Path: Arquitetura permite swap para Three.js sem refactor total

Implementação:

// Camada de abstração para rendering
interface Renderer {
  drawAgent(agent: Agent, position: Position): void;
  drawMonitor(monitor: Monitor, content: Activity[]): void;
  animate(deltaTime: number): void;
}

class Canvas2DRenderer implements Renderer {
  // Isometric projection: x' = (x - y), y' = (x + y) / 2
  projectIsometric(x: number, y: number): [number, number] {
    return [x - y, (x + y) / 2];
  }
}

// Future: class WebGLRenderer implements Renderer

Assets:

  • Sprites: Desks, monitors, chairs em PNG (com alpha channel)
  • Agents: Avatar isométrico por role (dev=blue, qa=green, etc.)
  • Animations: Sprite sheets para online/offline transitions

Alternativa 3D (v2.0):

  • Three.js com modelos low-poly
  • Mantém interface Renderer para compatibilidade

ADR: docs/architecture/adrs/adr-002-rendering-approach.md


1.3 WebSocket Stack: Socket.io

Decisão: Socket.io para comunicação real-time

Rationale:

  1. Auto-Reconnection: Lida com network drops automaticamente (crítico para desktop apps)
  2. Broadcasting: Facilita multi-client sync para Enterprise features futuras
  3. Rooms: Isola activity por projeto (Performance optimization)
  4. Fallback: HTTP long-polling se WebSocket falhar (rare mas útil)
  5. Dev Experience: Debug tools, logging estruturado, middlewares

Arquitetura:

// Backend: Socket.io Server
import { Server } from 'socket.io';

const io = new Server(httpServer, {
  cors: { origin: process.env.FRONTEND_URL },
  pingTimeout: 60000,
  pingInterval: 25000
});

io.on('connection', (socket) => {
  socket.on('subscribe_project', ({ projectId }) => {
    socket.join(`project:${projectId}`);
    // Enviar estado inicial
    socket.emit('initial_state', getProjectState(projectId));
  });
});

// AIOS Connector → Socket.io bridge
aiosConnector.on('agent_activity', (activity) => {
  io.to(`project:${activity.projectId}`).emit('agent_update', activity);
});

Protocol:

// Client → Server
{
  type: 'subscribe_project',
  projectId: string
}

// Server → Client
{
  type: 'agent_update',
  agent: string,
  status: 'online' | 'offline',
  activity?: ActivityItem
}

Performance:

  • Latência: < 100ms (local network)
  • Throughput: 100+ msgs/sec por projeto
  • Memory: ~10KB per socket connection

Alternativa Native WS: Requer implementar reconnection manual, rooms, broadcasting (3-4 dias extra)

ADR: docs/architecture/adrs/adr-003-websocket-stack.md


1.4 State Management: Zustand

Decisão: Zustand para global state, React Query para server state

Rationale:

  1. Simplicidade: Zero boilerplate vs Redux (5x menos código)
  2. Performance: Subscription granular (re-renders mínimos)
  3. Real-Time Friendly: Update state fora de React (WebSocket handlers)
  4. DevTools: Redux DevTools compatibility
  5. TypeScript: Type inference excelente

Arquitetura:

// Store: Agent Status
interface AgentState {
  agents: Record<string, Agent>;
  updateAgent: (id: string, update: Partial<Agent>) => void;
  setAgents: (agents: Agent[]) => void;
}

export const useAgentStore = create<AgentState>((set) => ({
  agents: {},
  updateAgent: (id, update) =>
    set((state) => ({
      agents: {
        ...state.agents,
        [id]: { ...state.agents[id], ...update }
      }
    })),
  setAgents: (agents) =>
    set({ agents: Object.fromEntries(agents.map(a => [a.id, a])) })
}));

// React Query: Activity Logs (server cache)
export function useActivityLog(projectId: string) {
  return useQuery({
    queryKey: ['activity', projectId],
    queryFn: () => fetchActivityLog(projectId),
    staleTime: Infinity, // WebSocket atualiza, não polling
    refetchOnWindowFocus: false
  });
}

// WebSocket Integration
socket.on('agent_update', (data) => {
  useAgentStore.getState().updateAgent(data.agentId, data);
});

State Separation:

  • Zustand: UI state (active project, filters, sidebar open)
  • React Query: Server cache (activity logs, project list)
  • WebSocket: Real-time push updates

Alternativas:

  • Redux: Over-engineered para MVP (toolkit ainda verbose)
  • Jotai: Excelente mas menos maduro (debugging inferior)
  • Context: Não escala com 10+ agentes updating (performance)

ADR: docs/architecture/adrs/adr-004-state-management.md


1.5 License Crypto: HMAC-SHA256

Decisão: HMAC-SHA256 com secret key server-side

Rationale:

  1. Pragmatismo: Suficientemente seguro para MVP, simples de implementar
  2. Velocidade: 1-2 dias vs 5-7 dias para RSA key infrastructure
  3. Reversibilidade: Fácil migrar para RSA em v2.0 se necessário
  4. Tamper-Proof: License key assinado, adulteração detectável
  5. No PKI: Não requer public/private key management

Estrutura de License Key:

VIRTUAL-OFFICE-ENT-{userId}-{expiryDate}-{hmac}

Exemplo:
VIRTUAL-OFFICE-ENT-user123-20271231-a3f2d8e9b1c4...

Algoritmo:

import crypto from 'crypto';

const SECRET_KEY = process.env.LICENSE_SECRET_KEY; // 32+ bytes random

function generateLicenseKey(userId: string, expiryDate: string): string {
  const payload = `${userId}-${expiryDate}`;
  const hmac = crypto
    .createHmac('sha256', SECRET_KEY)
    .update(payload)
    .digest('hex');

  return `VIRTUAL-OFFICE-ENT-${payload}-${hmac}`;
}

function validateLicenseKey(key: string): boolean {
  const parts = key.split('-');
  if (parts[0] !== 'VIRTUAL' || parts[1] !== 'OFFICE') return false;

  const userId = parts[3];
  const expiryDate = parts[4];
  const providedHmac = parts[5];

  const payload = `${userId}-${expiryDate}`;
  const expectedHmac = crypto
    .createHmac('sha256', SECRET_KEY)
    .update(payload)
    .digest('hex');

  if (providedHmac !== expectedHmac) return false;
  if (new Date(expiryDate) < new Date()) return false;

  return true;
}

Storage:

// Local storage (encrypted)
const encryptedKey = AES.encrypt(licenseKey, deviceId).toString();
localStorage.setItem('license', encryptedKey);

Security:

  • Secret Key: Environment variable, nunca commitado
  • Rate Limiting: Max 5 validation attempts/hora
  • Audit Log: Todas tentativas logadas

Upgrade Path (v2.0):

  • Migrar para RSA-2048 se bypass se tornar problema
  • Adicionar online activation/deactivation

Alternativas:

  • RSA: Mais seguro mas 5x mais complexo (overkill para MVP)
  • JWT: Requer token server (adiciona infra complexity)

ADR: docs/architecture/adrs/adr-005-license-crypto.md


1.6 Database: SQLite

Decisão: SQLite para local storage, migração para PostgreSQL em team server

Rationale:

  1. Zero Config: Arquivo local, sem servidor externo
  2. Portabilidade: Funciona em macOS, Windows, Linux
  3. Performance: 100k+ inserts/sec (suficiente para activity logs)
  4. Simplicidade: SQL familiar, sem ORM overhead
  5. Migração Path: Fácil export para PostgreSQL quando team features chegarem

Schema:

-- Projetos
CREATE TABLE projects (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  path TEXT NOT NULL,
  aios_version TEXT,
  last_active_at DATETIME,
  archived BOOLEAN DEFAULT 0,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Activity Logs
CREATE TABLE activities (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  project_id TEXT NOT NULL,
  agent_id TEXT NOT NULL,
  type TEXT NOT NULL, -- 'code' | 'command' | 'file_op' | 'tool_call'
  content TEXT,
  file_path TEXT,
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (project_id) REFERENCES projects(id)
);

CREATE INDEX idx_activities_project_timestamp ON activities(project_id, timestamp DESC);
CREATE INDEX idx_activities_agent ON activities(agent_id);

-- License Data
CREATE TABLE license (
  key TEXT PRIMARY KEY,
  tier TEXT NOT NULL, -- 'open_source' | 'enterprise'
  validated_at DATETIME,
  expires_at DATE
);

ORM: Drizzle ORM (TypeScript-first, zero runtime overhead)

import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';

const sqlite = new Database('virtual-office.db');
const db = drizzle(sqlite);

// Type-safe queries
const recentActivities = await db
  .select()
  .from(activities)
  .where(eq(activities.projectId, projectId))
  .orderBy(desc(activities.timestamp))
  .limit(50);

Performance:

  • Activity log: 1 milhão de registros sem degradação
  • Cleanup: Auto-delete activities > 30 dias (CRON)

Backup:

// Auto-backup diário
setInterval(() => {
  fs.copyFileSync('virtual-office.db', `backups/vo-${Date.now()}.db`);
}, 24 * 60 * 60 * 1000);

Alternativas:

  • JSON Files: Performance degrada com 10k+ activities
  • IndexedDB: Browser-only (não funciona em Electron)
  • PostgreSQL: Over-engineered para single-user MVP

ADR: docs/architecture/adrs/adr-006-database-choice.md


1.7 Testing Strategy: Vitest + Playwright

Decisão: Vitest (unit/integration) + Playwright (E2E)

Rationale:

  1. Vitest: Native Vite integration (10x mais rápido que Jest)
  2. Playwright: Multi-browser (Chromium, Firefox, WebKit), stable API
  3. TypeScript: Ambos têm suporte first-class
  4. Paralelização: Vitest workers, Playwright sharding
  5. Dev Experience: Hot reload (Vitest), UI mode (ambos)

Arquitetura de Testes:

tests/
├── unit/              # Vitest: Utils, stores, logic
│   ├── stores/
│   ├── utils/
│   └── components/
├── integration/       # Vitest: API, WebSocket, DB
│   ├── api/
│   ├── websocket/
│   └── database/
└── e2e/              # Playwright: User flows
    ├── onboarding.spec.ts
    ├── agent-monitoring.spec.ts
    └── project-switching.spec.ts

Unit Tests (Vitest):

import { describe, it, expect } from 'vitest';
import { useAgentStore } from '@/stores/agent';

describe('Agent Store', () => {
  it('updates agent status', () => {
    const { updateAgent, agents } = useAgentStore.getState();

    updateAgent('dev-1', { status: 'online' });

    expect(agents['dev-1'].status).toBe('online');
  });
});

E2E Tests (Playwright):

import { test, expect } from '@playwright/test';

test('monitor agent activity', async ({ page }) => {
  await page.goto('http://localhost:3000');

  // Conectar projeto
  await page.click('[data-testid="add-project"]');
  await page.fill('[data-testid="project-path"]', '/path/to/aios');
  await page.click('[data-testid="connect"]');

  // Verificar agente online
  await expect(page.locator('[data-agent="dev"]')).toHaveClass(/online/);

  // Clicar agente, ver activity log
  await page.click('[data-agent="dev"]');
  await expect(page.locator('[data-testid="activity-panel"]')).toBeVisible();
});

CI Pipeline (GitHub Actions):

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run test:unit      # Vitest
      - run: npm run test:e2e        # Playwright
      - uses: codecov/codecov-action@v3

Coverage Targets:

  • Backend: 80% (business logic crítico)
  • Frontend: 60% (UI testing é custoso)
  • E2E: User flows críticos (onboarding, monitoring, switching)

Alternativas:

  • Jest: Mais lento que Vitest (sem Vite integration)
  • Cypress: Bom mas Playwright tem melhor API e multi-browser

ADR: docs/architecture/adrs/adr-007-testing-strategy.md


2. System Architecture

2.1 High-Level Component Diagram

┌─────────────────────────────────────────────────────────┐
│                    Virtual Office UI                    │
│                   (React 18 + Vite)                     │
│                                                         │
│  ┌─────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │   Canvas    │  │  Agent       │  │  Activity    │  │
│  │  Renderer   │  │  Status      │  │  Detail      │  │
│  │  (2D Iso)   │  │  Grid        │  │  Panel       │  │
│  └─────────────┘  └──────────────┘  └──────────────┘  │
│                                                         │
│  ┌─────────────────────────────────────────────────┐  │
│  │         State Management (Zustand)              │  │
│  └─────────────────────────────────────────────────┘  │
└────────────────────┬────────────────────────────────────┘
                     │ WebSocket (Socket.io Client)
                     │
┌────────────────────┴────────────────────────────────────┐
│                  Backend API Server                     │
│                  (Node.js + Express)                    │
│                                                         │
│  ┌─────────────────────────────────────────────────┐  │
│  │         Socket.io Server                        │  │
│  │   (Rooms per project, Broadcasting)             │  │
│  └──────────────┬──────────────────────────────────┘  │
│                 │                                       │
│  ┌──────────────┴──────────────┐  ┌─────────────────┐ │
│  │    AIOS Connector           │  │   SQLite DB     │ │
│  │ (IPC/WebSocket to AIOS)     │  │  (Projects,     │ │
│  │                             │  │   Activities)   │ │
│  └──────────────┬──────────────┘  └─────────────────┘ │
└─────────────────┴─────────────────────────────────────┘
                  │
        ┌─────────┴─────────┐
        │   AIOS Framework  │
        │  (Local Instance) │
        └───────────────────┘

2.2 Sequence Diagram: Real-Time Activity Update

User     Frontend      Backend       AIOS         SQLite
 │           │            │            │             │
 │  Open App │            │            │             │
 ├──────────>│            │            │             │
 │           │ Connect WS │            │             │
 │           ├───────────>│            │             │
 │           │            │ Subscribe  │             │
 │           │            ├───────────>│             │
 │           │            │            │             │
 │           │            │ Agent Start│             │
 │           │            │<───────────┤             │
 │           │            │ Store Log  │             │
 │           │            ├────────────┼────────────>│
 │           │ Update     │            │             │
 │           │<───────────┤            │             │
 │  Visual   │            │            │             │
 │  Update   │            │            │             │
 │<──────────┤            │            │             │
 │           │            │            │             │
 │  Click    │            │            │             │
 │  Agent    │            │            │             │
 ├──────────>│            │            │             │
 │           │ Fetch Log  │            │             │
 │           ├───────────>│ Query DB   │             │
 │           │            ├────────────┼────────────>│
 │           │            │<───────────┼─────────────┤
 │           │ Activities │            │             │
 │           │<───────────┤            │             │
 │  Display  │            │            │             │
 │  Detail   │            │            │             │
 │<──────────┤            │            │             │

2.3 AIOS Integration Protocol

Connection Methods:

  1. WebSocket (Preferred): AIOS expõe porta WS para external monitoring
  2. IPC (Fallback): Process communication se AIOS no mesmo host
  3. File Watching (Last Resort): Monitor .aios/logs/ directory

Event Schema:

interface AIOSEvent {
  type: 'agent_activated' | 'agent_deactivated' | 'task_started' | 'task_completed' | 'activity';
  projectId: string;
  agentId: string;
  timestamp: string;
  payload?: {
    activityType?: 'code' | 'command' | 'file_op' | 'tool_call';
    content?: string;
    filePath?: string;
  };
}

Backend Connector:

import WebSocket from 'ws';

class AIOSConnector {
  private ws: WebSocket;

  connect(aiosUrl: string) {
    this.ws = new WebSocket(aiosUrl);

    this.ws.on('message', (data) => {
      const event: AIOSEvent = JSON.parse(data.toString());
      this.handleEvent(event);
    });
  }

  handleEvent(event: AIOSEvent) {
    // Store in DB
    db.insert(activities).values({
      projectId: event.projectId,
      agentId: event.agentId,
      type: event.payload?.activityType || 'unknown',
      content: event.payload?.content,
      filePath: event.payload?.filePath,
      timestamp: new Date(event.timestamp)
    });

    // Broadcast to frontend
    io.to(`project:${event.projectId}`).emit('agent_update', event);
  }
}

3. Security Architecture

3.1 License Validation Flow

┌──────────────┐
│  User Enters │
│  License Key │
└──────┬───────┘
       │
       ▼
┌──────────────────┐
│ Parse Key Parts  │
│ (user, expiry,   │
│  hmac)           │
└──────┬───────────┘
       │
       ▼
┌──────────────────┐
│ Compute Expected │
│ HMAC with Secret │
└──────┬───────────┘
       │
       ▼
┌──────────────────┐    Valid    ┌──────────────┐
│ Compare HMACs    ├────────────>│ Unlock       │
│ & Check Expiry   │             │ Enterprise   │
└──────┬───────────┘             └──────────────┘
       │
       │ Invalid
       ▼
┌──────────────────┐
│ Show Error +     │
│ Purchase Link    │
└──────────────────┘

3.2 Data Encryption

Sensitive Data:

  • License key: AES-256-GCM encrypted em localStorage
  • Project paths: Plain (não sensível)
  • Activity logs: Plain (local database, sem transmissão)

Network Security:

  • WebSocket: TLS (wss://) em production
  • CORS: Whitelist frontend origin apenas
  • Rate Limiting: Express rate-limit middleware

3.3 Threat Model

Threat Mitigation
License key bypass HMAC validation, rate limiting, audit log
Activity log tampering SQLite integrity check, file permissions
WebSocket hijacking Origin validation, session tokens
XSS React auto-escaping, CSP headers
CSRF SameSite cookies, CORS

4. Performance Optimization

4.1 Frontend Optimizations

Canvas Rendering:

class OptimizedRenderer {
  private offscreenCanvas: OffscreenCanvas;
  private dirtyRects: Set<Rect> = new Set();

  // Only redraw changed areas
  render() {
    if (this.dirtyRects.size === 0) return;

    for (const rect of this.dirtyRects) {
      this.renderRect(rect);
    }

    this.dirtyRects.clear();
  }

  // Use requestAnimationFrame
  animate = () => {
    this.render();
    requestAnimationFrame(this.animate);
  }
}

Virtual Scrolling (Activity Panel):

import { useVirtualizer } from '@tanstack/react-virtual';

function ActivityPanel({ activities }: Props) {
  const virtualizer = useVirtualizer({
    count: activities.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 60, // 60px per row
    overscan: 5
  });

  // Only render visible rows
  return virtualizer.getVirtualItems().map(item => (
    <ActivityRow key={item.key} activity={activities[item.index]} />
  ));
}

4.2 Backend Optimizations

WebSocket Throttling:

import throttle from 'lodash/throttle';

// Max 10 updates/sec per agent
const throttledEmit = throttle((projectId, data) => {
  io.to(`project:${projectId}`).emit('agent_update', data);
}, 100);

Database Indexing:

  • idx_activities_project_timestamp: Fast recent activities query
  • idx_activities_agent: Fast per-agent filtering

Memory Management:

// Cleanup old activities (30+ days) daily
setInterval(() => {
  db.delete(activities)
    .where(lt(activities.timestamp, new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)))
    .execute();
}, 24 * 60 * 60 * 1000);

5. Deployment Architecture

5.1 MVP Deployment

Open Source:

  • Frontend: Static build deployed to GitHub Pages
  • Backend: User runs locally (npm run backend)
  • Database: SQLite em ~/.virtual-office/

Enterprise (Cloud):

  • Frontend: Vercel (CDN, auto-scaling)
  • Backend: Railway (Node.js, auto-deploy)
  • Database: SQLite (migrate to PostgreSQL para team server)

5.2 Build Pipeline

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm ci
      - run: npm run test
      - run: npm run lint

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
      - uses: actions/upload-artifact@v3
        with:
          name: dist
          path: dist/

  deploy-frontend:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: vercel/vercel-action@v2
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}

  deploy-backend:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: railway-deploy@v1
        with:
          api-key: ${{ secrets.RAILWAY_API_KEY }}

5.3 Monitoring

Error Tracking: Sentry

import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: process.env.VITE_SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 0.1 // 10% transaction sampling
});

Analytics: PostHog (privacy-friendly)

import posthog from 'posthog-js';

posthog.init(process.env.VITE_POSTHOG_KEY, {
  api_host: 'https://app.posthog.com',
  autocapture: false, // Manual events only
  disable_session_recording: true
});

// Track key events
posthog.capture('project_connected', { projectId });
posthog.capture('agent_activity_viewed', { agentId });

6. Migration Path & Future Proofing

6.1 Rendering Upgrade (v2.0)

Current (MVP): Canvas 2D Isométrico Future: Three.js 3D

Migration Strategy:

  1. Manter interface Renderer abstrata
  2. Implementar WebGLRenderer em paralelo
  3. Feature flag: ENABLE_3D_RENDERING
  4. A/B test com 10% users
  5. Gradual rollout baseado em GPU detection

6.2 Database Scaling (Team Server)

Current: SQLite (local) Future: PostgreSQL (cloud)

Migration:

-- Export SQLite
sqlite3 virtual-office.db .dump > dump.sql

-- Import PostgreSQL
psql -U postgres -d virtual_office < dump.sql

Code Changes: Drizzle ORM abstrai DB (zero code change)

6.3 Team Collaboration (Enterprise v2.0)

Architecture:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  User A UI  │     │  User B UI  │     │  User C UI  │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                   │                   │
       │    WebSocket      │    WebSocket      │
       └───────────┬───────┴───────────────────┘
                   │
          ┌────────▼────────┐
          │  Team Server    │
          │  (PostgreSQL)   │
          └─────────────────┘

7. ADRs Summary

ADR Decisão Rationale Chave
001 React 18 Ecosystem maduro, real-time support, time expertise
002 Canvas 2D MVP velocity (3-5x faster), performance garantida
003 Socket.io Auto-reconnect, broadcasting, dev experience
004 Zustand Simplicidade, performance, real-time friendly
005 HMAC Pragmático para MVP, upgrade path para RSA
006 SQLite Zero config, portabilidade, migração fácil
007 Vitest + Playwright Vite integration, multi-browser, TypeScript

8. Implementation Roadmap

Sprint 1: Foundation (Semanas 1-3)

  • Story 1.1: Project setup, monorepo, CI/CD
  • Story 1.2: Backend WebSocket + AIOS connector
  • Story 1.3: Frontend shell + agent grid
  • Story 1.4: Agent online/offline status

Deliverable: Prototype funcional - conecta AIOS, mostra status

Sprint 2: Activity Visualization (Semanas 4-6)

  • Story 2.1: Monitor activity feed
  • Story 2.2: Detail panel
  • Story 2.3: Filtering
  • Story 2.4: Activity export

Deliverable: Real-time monitoring completo

Sprint 3: Multi-Project (Semanas 7-9)

  • Story 3.1: Project discovery
  • Story 3.2: Project switcher
  • Story 3.3: Project management
  • Story 3.4: Activity isolation

Deliverable: Multi-project workspace funcional

Sprint 4: Enterprise (Semanas 10-12)

  • Story 4.1: License system
  • Story 4.2: Upgrade prompts
  • Story 4.3: Team collaboration MVP
  • Story 4.4: Documentation + Launch

Deliverable: MVP completo pronto para lançamento


9. Conclusão

Esta arquitetura prioriza MVP speed sem comprometer fundação sólida:

Decisões Pragmáticas: Canvas 2D, HMAC, SQLite (implementação rápida) ✅ Tech Stack Maduro: React, Socket.io, Zustand (low risk, high velocity) ✅ Future-Proof: Abstrações permitem upgrades (3D, RSA, PostgreSQL) ✅ Performance Garantida: < 3s load, 30+ FPS, < 100ms latency ✅ Timeline Realista: 8-12 semanas para 17 stories

Próximos Passos:

  1. @ux-design-expert: Criar design system e mockups
  2. @dev: Iniciar Story 1.1 (Project Setup)
  3. @devops: Configurar CI/CD pipeline

Aprovação: Arquitetura pronta para implementação Data: 2026-02-07 Arquiteto: Aria (YOLO Mode)