Perry is a distributed development environment orchestrator that creates isolated Docker-in-Docker containers accessible from multiple clients (CLI, Web UI, Mobile). It transforms development workflows by enabling remote workspace management with local-like performance.
Implemented Core Features:
- ✅ Agent daemon with HTTP/WebSocket API
- ✅ oRPC type-safe client-server communication
- ✅ CLI client with TUI
- ✅ React web UI with real-time terminal
- ✅ React Native mobile app
- ✅ Session management and tracking
- ✅ Worker binary pattern for container operations
- ✅ State persistence and locking
- Developer Experience: One command setup, zero friction workspace creation
- Network Simplicity: Works across any network, Tailscale-optimized but not required
- Real Testing: Integration tests with actual Docker containers
- Type Safety: oRPC provides compile-time API validation
$ perry agent run
# Starts agent on port 7391, creates default config if needed
Agent running at http://hostname:7391$ perry list # List workspaces
$ perry start myproject # Create and start workspace
$ perry shell myproject # Interactive terminal
$ perry config worker <host> # Set agent hostnameNavigate to http://agent-host:7391 for browser-based management with integrated terminal.
Install Perry mobile app, configure agent hostname for workspace management on the go.
┌─────────────────────────────────────────────────────────────┐
│ Network (Tailscale/Internet) │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Agent Machine │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ perry agent run (daemon) │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────────────┐ │ │ │
│ │ │ │ oRPC API │ │ WebSocket Terminal │ │ │ │
│ │ │ │ :7391 │ │ Session Manager │ │ │ │
│ │ │ └─────────────┘ └─────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │ │ Docker Engine │ │ │
│ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ │
│ │ │ │ │workspace│ │workspace│ ... │ │ │ │
│ │ │ │ │ -alpha │ │ -beta │ │ │ │ │
│ │ │ │ └─────────┘ └─────────┘ │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │ │ State & Config │ │ │ │
│ │ │ │ ~/.config/perry/state.json │ │ │ │
│ │ │ │ (file-locked, persistent) │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
│ ▲ │
│ │ HTTP/WebSocket/oRPC │
│ ┌─────────────────┼─────────────────┐ │
│ │ │ │ │
│ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │
│ │ Client │ │ Browser │ │ Mobile │ │
│ │perry CLI │ │ Web UI │ │ App │ │
│ │+ TUI │ │(React SPA)│ │(React NV) │ │
│ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────┘
Long-running process managing workspaces and serving clients:
- oRPC API server (port 7391, type-safe client/server communication)
- WebSocket terminal server with session management
- Docker lifecycle management (create, start, stop, delete workspaces)
- State persistence with file locking
- Web UI hosting (serves React SPA as static files)
perry agent run # Start agent (foreground)
perry agent install # Install systemd serviceType-safe commands via oRPC:
perry start myproject # Create and start workspace
perry stop myproject # Stop workspace
perry shell myproject # Interactive terminal via WebSocket
perry list # List all workspaces
perry delete myproject # Remove workspace and volumes
perry config worker <host> # Configure agent connectionReact SPA with real-time capabilities:
- Framework: React + Vite + shadcn/ui
- Terminal: xterm.js with WebSocket connection
- API: oRPC client with type safety
- Features: Full workspace lifecycle, real-time session tracking
React Native + Expo production app:
- Platform: iOS and Android
- Features: Workspace management, terminal access
- API: Same oRPC endpoints as web UI
- Real-time: Session updates via WebSocket
Location: ~/.config/perry/config.json
{
"port": 7391,
"workspacePrefix": "workspace-",
"imageTag": "workspace-base"
}Location: ~/.config/perry/client.json
{
"worker": "my-desktop.example.com:7391"
}Location: ~/.config/perry/state.json (agent machine)
{
"workspaces": {
"myproject": {
"name": "myproject",
"status": "running",
"containerId": "abc123def456...",
"created": "2025-01-10T15:30:00Z",
"image": "workspace-base",
"ports": {
"ssh": 22001,
"http": 22080
}
}
}
}State is managed with file locking (proper-lockfile) for safe concurrent access.
Perry uses oRPC for type-safe RPC communication. The API provides compile-time type safety between client and server.
http://<agent-host>:7391/rpc
Based on implementation in src/agent/router.ts:
{
workspaces: {
list: () => Workspace[]
get: ({ name: string }) => Workspace
create: ({ name: string }) => Workspace
delete: ({ name: string }) => { success: boolean }
start: ({ name: string }) => Workspace
stop: ({ name: string }) => Workspace
sync: ({ name: string }) => void
},
sessions: {
list: ({ workspaceName: string }) => SessionInfo[]
messages: ({ workspaceName: string, sessionId: string }) => Message[]
},
agent: {
info: () => AgentInfo
kill: () => void
},
config: {
get: () => Config
setWorker: ({ worker: string }) => Config
}
}interface Workspace {
name: string
status: 'running' | 'stopped' | 'creating' | 'error'
containerId?: string
created: string
ports: { ssh: number }
}
interface SessionInfo {
sessionId: string
title: string
agent: string
messageCount: number
lastActivity: string
}
interface AgentInfo {
version: string
hostname: string
uptime: number
workspacesCount: number
}Terminal Access:
GET /terminal/:workspaceName
Upgrade: WebSocket
Session Streaming:
GET /sessions/:workspaceName
Upgrade: WebSocket
Real-time session updates and terminal I/O via WebSocket with binary message protocol.
perry start myproject
1. Validate workspace name (unique, valid format)
2. Allocate SSH port from available range
3. Create Docker container:
- Image: workspace-base (pre-built with development tools)
- Volumes: Named volume for /home/workspace persistence
- Network: Custom bridge for container isolation
- Port mapping: SSH port for external access
4. Start container and wait for SSH availability
5. Copy perry worker binary for container-side operations
6. Update state.json with workspace metadata
7. Return workspace info via oRPC
Named Volumes per Workspace:
workspace-{name}-home: Persistent /home/workspace directoryworkspace-{name}-docker: Docker-in-Docker storage- Volumes persist across container restarts
- Deleted only when workspace is explicitly removed
Container Image:
- Pre-built
workspace-basewith development tools - Includes: Node.js, Python, Go, Docker-in-Docker setup, SSH server
- Workspace-specific setup via environment variables
Perry provides real-time tracking and management of development tool sessions (Claude, OpenCode, etc.) within workspaces.
Session Discovery:
perry workerbinary runs inside containers- Scans for session files/directories (e.g.,
/tmp/opencode-sessions/) - HTTP server provides session data to agent via internal API
Agent Aggregation:
- Agent polls container session endpoints
- Maintains centralized session index with WebSocket broadcasting
- Clients receive real-time session updates
Client Integration:
- Web UI shows live session list with message counts
- Mobile app displays active sessions per workspace
- CLI provides session inspection commands
- Container Side:
src/worker/- HTTP server with session scanning - Agent Side:
src/session-manager/- Aggregation and WebSocket broadcasting - Client Side: Real-time session updates via WebSocket connection
Perry's testing approach emphasizes real-world validation over test coverage metrics.
Unit Tests (test/unit/):
- Pure functions, config parsing, validation logic
- Fast execution, no external dependencies
Integration Tests (test/integration/):
- Agent + Docker interaction
- oRPC API functionality
- Workspace lifecycle operations
- Requires Docker daemon
E2E Tests (test/e2e/, web/e2e/):
- Full system workflows: CLI → Agent → Docker
- Web UI interactions (Playwright)
- Real network communication
bun run validate # Complete test suite + linting + buildsComponents:
bun run check- Linting and type checkingbun run build- CLI, worker binary, and web UI buildsbun run test- Unit and integration testsbun run test:web- Playwright e2e web tests
Tests must catch real bugs, not satisfy coverage metrics. Manual verification remains essential for validating user experience and complex integration scenarios.
Perry uses a pre-built Docker image (workspace-base) with development tools and Docker-in-Docker capability.
Pre-installed Tools:
- Node.js, Bun, Go, Python development environments
- Docker-in-Docker for nested container workflows
- SSH server for terminal access
- Development tools (git, curl, etc.)
Container Setup:
- User:
workspacewith sudo privileges - Persistent volumes for /home/workspace
- SSH port allocation for external access
- Internal Docker daemon for development workflows
The perry worker binary enables container-side operations:
Build Process:
bun build src/index.ts --compile --outfile dist/perry-worker --target=bunDeployment:
- Binary copied to containers during
perry sync - Enables session discovery and container management
- Self-contained, no npm dependencies in container
Usage:
# Inside container
perry worker sessions list # Discover active tool sessions
perry worker sessions messages # Get session message historyThis pattern allows rapid updates without rebuilding Docker images.
- Connect to multiple perry agents from single client
- Workspace distribution across machines
- Load balancing and failover capabilities
- Session persistence across workspace restarts
- Session sharing and collaboration features
- Advanced filtering and search capabilities
- Support for
.devcontainer.jsonconfiguration - VS Code DevContainer feature compatibility
- Custom container images per workspace
- Terminal latency improvements (local echo, compression)
- Session data compression and caching
- Offline capability for cached data
- Optional bearer token authentication
- Integration with Tailscale ACLs
- Role-based workspace access control
perry/
├── src/
│ ├── agent/ # oRPC server and HTTP endpoints
│ ├── workspace/ # Docker container lifecycle management
│ ├── session-manager/ # Real-time session tracking
│ ├── worker/ # Container-side HTTP server
│ ├── terminal/ # WebSocket terminal implementation
│ ├── docker/ # Docker CLI interface
│ ├── client/ # oRPC client utilities
│ └── index.ts # Main CLI entry point
├── web/ # React SPA served by agent
│ ├── src/components/ # shadcn/ui component library
│ ├── src/lib/ # oRPC client configuration
│ └── dist/ # Built assets served statically
├── mobile/ # React Native + Expo app
│ └── src/ # Mobile UI components and navigation
├── test/
│ ├── unit/ # Pure function tests
│ ├── integration/ # Agent + Docker tests
│ └── e2e/ # Full system tests
├── DESIGN.md # Architecture documentation
└── AGENTS.md # Implementation guidelines