PortOS is a monorepo application with a React frontend and Express.js backend, managed by PM2.
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ PortOS │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────┐ ┌──────────────────────────────────────┐ │
│ │ React Client │ │ Express Server │ │
│ │ (port 5554) │ HTTP │ (port 5555) │ │
│ │ │ <---------> │ │ │
│ │ ┌──────────────┐ │ │ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Pages │ │ │ │ Routes │──│ Services │ │ │
│ │ └──────────────┘ │ │ └──────────┘ └──────────────────┘ │ │
│ │ | │ Socket.IO │ | | │ │
│ │ ┌──────────────┐ │ <---------- │ | ┌────v─────────┐ │ │
│ │ │ Components │ │ │ | │ PM2 API │ │ │
│ │ └──────────────┘ │ │ | └──────────────┘ │ │
│ │ | │ │ | | │ │
│ │ ┌──────────────┐ │ │ | ┌────v─────────┐ │ │
│ │ │ api.js │ │ │ | │ JSON Files │ │ │
│ │ │ socket.js │ │ │ | │ (data/) │ │ │
│ │ └──────────────┘ │ │ | └──────────────┘ │ │
│ └────────────────────┘ └──────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────────┐ │
│ │ PM2-Managed Satellite Services │ │
│ │ │ │
│ │ ┌─────────────────────┐ ┌────────────────────┐ ┌────────────────────────┐ │ │
│ │ │ Chief of Staff │ │ portos-browser │ │ portos-autofixer │ │ │
│ │ │ portos-cos :5558 │ │ CDP :5556 │ │ daemon :5559 │ │ │
│ │ │ │ │ health :5557 │ │ UI :5560 │ │ │
│ │ │ Task Watcher │ │ │ │ │ │ │
│ │ │ CoS Evaluation │ │ Persistent │ │ PM2 crash monitor │ │ │
│ │ │ Sub-Agent Spawner │ │ Chromium instance │ │ (polls every 15m) │ │ │
│ │ │ (Claude CLI) -------|->│ CDP WebSocket for │ │ Claude CLI auto-fix │ │ │
│ │ │ │ │ web automation │ │ Reads apps.json │ │ │
│ │ └─────────────────────┘ └────────────────────┘ │ Session history │ │ │
│ │ └────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Communication paths:
Client <--HTTP/Socket.IO--> Server --PM2 API--> all satellite processes
Server --browserService--> portos-browser (CDP :5556, health :5557)
Server --apps.json--> portos-autofixer reads registered apps to monitor
CoS agents --CDP WebSocket--> portos-browser for web automation tasks
portos-autofixer --pm2 jlist--> detects crashed processes --Claude CLI--> auto-fix
portos-autofixer-ui --reads--> data/autofixer/sessions/ for fix history
PortOS/
├── client/ # React + Vite frontend
│ └── src/
│ ├── components/ # Reusable UI components
│ │ ├── cos/ # Chief of Staff components
│ │ └── Layout.jsx # Main app layout
│ ├── hooks/ # Custom React hooks
│ ├── pages/ # Route-based page components
│ │ └── Browser.jsx # Browser management dashboard
│ └── services/ # API client (api.js, socket.js)
│
├── server/ # Express.js backend
│ ├── routes/ # HTTP endpoint handlers
│ │ └── browser.js # /api/browser/* endpoints
│ ├── services/ # Business logic
│ │ ├── cos.js # Chief of Staff core
│ │ ├── subAgentSpawner.js # Claude CLI integration
│ │ ├── pm2.js # PM2 process management
│ │ ├── runner.js # AI execution engine
│ │ ├── memory.js # Memory system
│ │ └── browserService.js # Browser CDP/health/PM2 control
│ ├── lib/ # Shared utilities
│ │ ├── errorHandler.js # Error normalization
│ │ ├── validation.js # Zod schemas
│ │ └── taskParser.js # TASKS.md parser
│ └── cos-runner/ # Isolated agent runner
│ └── index.js # Standalone Express server
│
├── browser/ # portos-browser service
│ ├── server.js # Launches Chromium with CDP, runs health server
│ └── package.json # Playwright dependency
│
├── autofixer/ # portos-autofixer service
│ ├── server.js # Crash detection daemon (polls PM2 every 15min)
│ └── ui.js # Standalone Express UI with SSE log streaming
│
├── data/ # Runtime data (gitignored)
│ ├── apps.json # Registered apps (read by autofixer)
│ ├── providers.json # AI provider configs
│ ├── history.json # Action history
│ ├── browser-config.json # Browser CDP/health configuration
│ ├── TASKS.md # User task file
│ ├── COS-TASKS.md # System task file
│ ├── COS-GOALS.md # Mission and goals
│ ├── cos/ # CoS state and agents
│ │ ├── state.json # Daemon state
│ │ ├── agents/ # Agent outputs
│ │ └── memory/ # Memory storage
│ ├── autofixer/ # Autofixer session history
│ │ ├── index.json # Fix session index (max 100 entries)
│ │ └── sessions/ # Per-session prompt, output, metadata
│ ├── brain/ # Brain second-brain data
│ │ ├── meta.json # Settings
│ │ ├── inbox_log.jsonl # Captured thoughts
│ │ ├── people.jsonl # People records
│ │ ├── projects.jsonl # Project records
│ │ ├── ideas.jsonl # Ideas
│ │ ├── admin.jsonl # Admin tasks
│ │ ├── links.json # Saved links
│ │ └── digests.jsonl # Daily/weekly digests
│ ├── digital-twin/ # Digital twin identity documents
│ │ ├── meta.json # Settings and state
│ │ └── documents/ # Markdown identity documents
│ ├── uploads/ # Generic file uploads
│ ├── repos/ # Cloned GitHub repositories
│ └── agent-personalities/ # Agent personality configs
│
├── docs/ # Documentation
├── .github/workflows/ # CI/CD
└── ecosystem.config.cjs # PM2 configuration
Browser → React Page → api.js → Express Route → Service → Response
│
├── Zod Validation
├── Service Logic
└── JSON File / PM2 API
Server Event → Socket.IO → socket.js → React Component State Update
│
└── Real-time: logs, CoS status, errors, memory changes
1. Task Watcher monitors TASKS.md for changes
2. CoS Service evaluates tasks on interval
3. For each pending task:
a. Select appropriate AI model based on task complexity
b. Build prompt with context and memory injection
c. Spawn Claude CLI via Sub-Agent Spawner
4. Agent executes task, output captured
5. On completion:
a. Mark task as completed
b. Extract memories from output
c. Update usage metrics
1. portos-browser launches persistent Chromium with CDP on :5556
2. Health server on :5557 reports connection status
3. Express Server proxies browser management via browserService.js:
- Client UI (Browser.jsx) → /api/browser/* → browserService → CDP/PM2
4. CoS agents connect directly to CDP WebSocket for web automation
5. Configuration persisted to data/browser-config.json
1. portos-autofixer daemon starts, reads registered apps from data/apps.json
2. Every 15 minutes, polls PM2 (pm2 jlist) for crashed processes
3. For each errored process (with 30min cooldown):
a. Fetch last 100 lines of error logs + 50 lines of output logs
b. Build prompt with crash context and app info
c. Spawn Claude CLI in app's repo directory to diagnose and fix
d. Save session (prompt.txt, output.txt, metadata.json) to data/autofixer/sessions/
4. portos-autofixer-ui (:5560) serves standalone dashboard:
- SSE endpoint for real-time log streaming
- Fix history viewer with success/failure status
- Process status indicators
- CRUD operations for registered apps
- Persists to
data/apps.json
- Start/stop/restart processes
- Status monitoring
- Log retrieval
- AI provider execution
- CLI and API-based providers
- Output streaming and capture
- Task evaluation and prioritization
- Agent orchestration
- Health monitoring
- Self-improvement task generation
- Claude CLI process spawning
- Model selection based on task complexity
- MCP server integration
- Usage tracking
- Semantic memory storage
- Vector embeddings via LM Studio
- Memory retrieval for context injection
- Completion tracking and success rates
- Duration estimates by task type
- Model tier effectiveness analysis
- Actionable recommendations
- Cron-based script scheduling
- Command allowlist enforcement
- Agent trigger integration
- Run history tracking
- Thought capture and AI classification
- CRUD for People, Projects, Ideas, Admin
- Daily digest and weekly review generation
- Classification correction workflow
- Link capture with GitHub auto-clone
- Identity scaffold document management
- Personality trait extraction (Big Five, values hierarchy)
- Behavioral test generation and execution
- External data import (Goodreads, Spotify, Letterboxd, iCal)
- Confidence scoring and gap recommendations
- Agent personality CRUD and AI generation
- Custom communication styles, tones, and quirks
- Manages portos-browser lifecycle via PM2 (launch/stop/restart)
- Proxies CDP queries (open pages, version info) via HTTP to :5556
- Health checks against :5557
- Configuration CRUD persisted to
data/browser-config.json - CDP host restricted to localhost to prevent SSRF
- Daemon (:5559): Polls PM2 every 15 minutes for errored processes
- Reads
data/apps.jsonto know which processes to monitor - 30-minute cooldown per process to prevent fix loops
- Spawns Claude CLI with crash context (error logs + app info) to auto-repair
- Stores fix sessions in
data/autofixer/sessions/(prompt, output, metadata) - UI (:5560): Standalone Express server with SSE real-time log streaming
- Fix history viewer, process status dashboard
- PTY-based web terminal via node-pty
- Session management with WebSocket I/O
- Terminal resize handling
All routes use asyncHandler wrapper from server/lib/errorHandler.js:
// Routes automatically catch errors and:
// 1. Log to console with emoji prefix
// 2. Emit Socket.IO event for UI notification
// 3. Return structured JSON error responseError severity levels:
- warning: Non-critical, logged only
- error: Server error, shown to user
- critical: System-threatening, triggers auto-fix
- Network Security: Relies on Tailscale for access control
- Command Allowlist: Shell execution restricted to approved commands
- No Shell Interpolation: Uses
spawn()with argument arrays - Zod Validation: All API inputs validated
- Path Traversal Prevention: Filename sanitization on uploads
| Process | Port | Script | Purpose |
|---|---|---|---|
| portos-client | 5554 | client/ (Vite) |
React frontend dev server |
| portos-server | 5555 | server/index.js |
Main Express API server |
| portos-browser | 5556 (CDP), 5557 (health) | browser/server.js |
Persistent Chromium with CDP for web automation |
| portos-cos | 5558 | server/cos-runner/index.js |
Isolated CoS agent runner |
| portos-autofixer | 5559 | autofixer/server.js |
Autonomous crash detection and Claude CLI repair |
| portos-autofixer-ui | 5560 | autofixer/ui.js |
Standalone fix history dashboard with SSE logs |
- Create component in
client/src/pages/ - Add route in
client/src/App.jsx - Add navigation link in
client/src/components/Layout.jsx
- Create route file in
server/routes/ - Register in
server/index.js - Add Zod schema if needed in
server/lib/validation.js
- Create service file in
server/services/ - Export functions (not classes)
- Import in routes as needed
- Update
SELF_IMPROVEMENT_TYPESinserver/services/cos.js - Add prompt template in
generateSelfImprovementTask()