OpenCode Server Protocol - Phase 0 Findings
Status: Initial Discovery Complete
OpenCode Version: 1.1.53
Discovery Date: 2026-02-15
Environment: codev pod (code-server namespace)
Executive Summary
OpenCode 1.1.53 provides two protocols for different use cases:
1. ACP (Agent Client Protocol) - JSON-RPC over stdio
Use case: Editor integrations (VSCode, Cursor, etc.)
Per https://opencode.ai/docs/acp/:
To use OpenCode via ACP, configure your editor to run the opencode acp command. The command starts OpenCode as an ACP-compatible subprocess that communicates with your editor over JSON-RPC via stdio.
- Protocol: JSON-RPC over stdin/stdout
- Command:
opencode acp - Target: Editor extensions
- Not suitable for: OpenClaw integration (we’re not an editor)
2. HTTP REST API + SSE - For programmatic access
Use case: Web UI, automation, external integrations (like OpenClaw!)
- Protocol: HTTP REST + Server-Sent Events
- Command:
opencode weboropencode serve - Target: Browser UI, programmatic clients
- ✅ Perfect for: OpenClaw Gateway integration
Key Findings:
- ✅ HTTP REST API fully documented and tested
- ✅ Server-Sent Events (SSE) for real-time updates
- ✅ Session creation/messaging/listing all working
- ✅ Session storage on filesystem
- ✅ Phase 0 Complete
Running Server in codev
CRITICAL DISCOVERY: Server is workspace-specific!
The OpenCode web server serves sessions from the workspace where it was started. This means:
- Starting from
/home/coder/src→ serves sessions from project58c042e67dd312114ee098f01f12b2db0226abca - Starting from
/home/coder→ serves sessions from projectglobal - Starting from
/home/coder/src/repos/k8s-lab→ serves sessions from project74f4e11b32dded159775570fcf47b20dd2a9ff39
Current Deployment:
# Must start from the correct workspace directory!
cd /home/coder/src
opencode web --hostname 0.0.0.0 --port 5400 &Accessible at:
- Internal:
http://localhost:5400(from within pod) - Service:
codev.code-server:5400(from other pods) - Port-forward:
kubectl port-forward -n code-server deployment/codev 5400:5400
Multi-Workspace Strategy: For OpenClaw integration with multiple builder workspaces, we have options:
- Single server per workspace - Run separate OpenCode servers on different ports
- Switch context - Restart server when switching workspaces (not ideal)
- Project-scoped API calls - Check if
/project/:id/sessionendpoints exist (needs testing)
HTTP REST API - Complete Documentation
Base URL
http://localhost:5400 (codev pod)
Project & Workspace Management
List All Projects
GET /project
List all OpenCode projects (workspaces) registered in the system.
curl http://localhost:5400/project | jq '.'Response (200 OK):
[
{
"id": "58c042e67dd312114ee098f01f12b2db0226abca",
"worktree": "/home/coder/src",
"vcs": "git",
"sandboxes": [],
"time": {
"created": 1770323322243,
"updated": 1771248366416
},
"icon": {
"color": "pink"
}
},
{
"id": "74f4e11b32dded159775570fcf47b20dd2a9ff39",
"worktree": "/home/coder/src/repos/k8s-lab",
"vcs": "git",
"sandboxes": [],
"time": {
"created": 1770375621736,
"updated": 1770375673994
}
},
{
"id": "global",
"worktree": "/",
"sandboxes": [],
"time": {
"created": 1770323336398,
"updated": 1771247827349
}
}
]Key Fields:
id- Unique project hash (SHA-1 of worktree path, or “global”)worktree- Absolute path to workspace directoryvcs- Version control system (“git” or omitted)sandboxes- Array of sandbox environments (usually empty)time- Creation/update timestampsicon- UI display properties
Project ID Calculation:
// Project ID is SHA-1 hash of worktree path
// Example: SHA1("/home/coder/src") = "58c042e67dd312114ee098f01f12b2db0226abca"
// Special case: "global" project has id "global"Workspace-Specific Behavior
CRITICAL: The OpenCode HTTP server is workspace-scoped:
GET /sessionreturns sessions only from the workspace where the server startedPOST /sessioncreates sessions in the current workspacePOST /session/:id/messageonly works for sessions in the current workspace
To access sessions from different workspaces:
- Start OpenCode server from that workspace’s directory
- Or run multiple OpenCode servers (one per workspace) on different ports
Session Management
Create Session
POST /session
Create a new OpenCode session.
curl -X POST http://localhost:5400/session \
-H 'Content-Type: application/json' \
-d '{
"directory": "/home/coder/src/.builders/my-builder",
"title": "My Session Title",
"agent": "default"
}'Request Body:
{
"directory": "/home/coder/src/.builders/my-builder", // Working directory
"title": "My Session Title", // Human-readable title
"agent": "default" // Agent type
}Response (200 OK):
{
"id": "ses_39a240ec5ffe9TtVT3xbBgKfTl",
"slug": "glowing-circuit",
"version": "1.1.48",
"projectID": "global",
"directory": "/home/coder",
"title": "Test API Session",
"time": {
"created": 1771235438906,
"updated": 1771235438906
}
}List All Sessions
GET /session
List all sessions in the workspace where the server was started.
⚠️ CRITICAL: This endpoint is workspace-specific! The server only returns sessions from the project/workspace where opencode web was launched.
curl http://localhost:5400/sessionResponse (200 OK):
[
{
"id": "ses_39a240ec5ffe9TtVT3xbBgKfTl",
"slug": "glowing-circuit",
"version": "1.1.48",
"projectID": "global",
"directory": "/home/coder",
"title": "Test API Session",
"time": {
"created": 1771235438906,
"updated": 1771235463633
},
"summary": {
"additions": 0,
"deletions": 0,
"files": 0
}
}
]Get Session Details
GET /session/:id
Get details for a specific session.
curl http://localhost:5400/session/ses_39a240ec5ffe9TtVT3xbBgKfTlResponse (200 OK):
{
"id": "ses_39a240ec5ffe9TtVT3xbBgKfTl",
"slug": "glowing-circuit",
"version": "1.1.48",
"projectID": "global",
"directory": "/home/coder",
"title": "Test API Session",
"time": {
"created": 1771235438906,
"updated": 1771235463633
},
"summary": {
"additions": 0,
"deletions": 0,
"files": 0
}
}Send Message to Session
POST /session/:id/message
Send a message to an existing session.
curl -X POST http://localhost:5400/session/ses_39a240ec5ffe9TtVT3xbBgKfTl/message \
-H 'Content-Type: application/json' \
-d '{
"parts": [
{
"type": "text",
"text": "What files are in this directory?"
}
]
}'Request Body:
{
"parts": [
{
"type": "text",
"text": "Your message here"
}
],
"model": {
"providerID": "anthropic",
"modelID": "claude-sonnet-4-5-20250929"
}
}Note: The model field is optional. If omitted, uses session’s default model.
Response Behavior:
⚠️ IMPORTANT: This endpoint streams the response and may take a long time (2+ minutes) to complete. The HTTP connection remains open while Claude processes the message.
The response includes the full assistant message once complete:
{
"info": {
"id": "msg_...",
"sessionID": "ses_...",
"role": "assistant",
"time": {...},
"modelID": "...",
"providerID": "...",
"mode": "build",
"agent": "build",
...
},
"parts": [...]
}For real-time updates: Use GET /global/event (SSE) to receive streaming updates instead of waiting for POST to complete.
Get Messages from Session
GET /session/:id/message
Retrieve all messages from a session.
curl http://localhost:5400/session/ses_3ae187259ffeMdIpeZbzA5R4ZU/message | jq '.'Response (200 OK):
[
{
"info": {
"id": "msg_...",
"sessionID": "ses_3ae187259ffeMdIpeZbzA5R4ZU",
"role": "user",
"time": {
"created": 1771248461234,
"updated": 1771248461234
}
},
"parts": [
{
"type": "text",
"text": "Test from HTTP API - please respond with OK"
}
]
},
{
"info": {
"id": "msg_...",
"sessionID": "ses_3ae187259ffeMdIpeZbzA5R4ZU",
"role": "assistant",
"time": {...},
"modelID": "claude-sonnet-4-5-20250929",
"providerID": "anthropic"
},
"parts": [
{
"type": "text",
"text": "OK"
}
]
}
]Use cases:
- Poll for new messages after sending via
POST /session/:id/message - Retrieve conversation history
- Check if assistant has responded
Error Response (400 Bad Request):
{
"data": {...},
"error": [
{
"expected": "array",
"code": "invalid_type",
"path": ["parts"],
"message": "Invalid input: expected array, received undefined"
}
],
"success": false
}Health & Status Endpoints
GET /global/health
Server health check
curl http://localhost:5400/global/healthResponse:
{
"healthy": true,
"version": "1.1.48"
}GET /project/current
Current project information
curl http://localhost:5400/project/currentResponse:
{
"id": "global",
"worktree": "/",
"sandboxes": [],
"time": {
"created": 1770323336398,
"updated": 1770492969629
}
}GET /session/status
Current session status (context-specific)
curl http://localhost:5400/session/statusResponse:
{}(Empty when no active session in request context)
Event Streaming (Server-Sent Events)
GET /global/event
Real-time event stream
curl http://localhost:5400/global/eventProtocol: Server-Sent Events (SSE)
Event Format:
data: {"payload":{"type":"server.connected","properties":{}}}
data: {"directory":"/home/coder/src","payload":{"type":"message.part.updated","properties":{...}}}
data: {"directory":"/home/coder/src","payload":{"type":"file.edited","properties":{...}}}
Event Types Observed:
server.connected- Initial connection establishedmessage.part.updated- Streaming message updatesfile.edited- File modification eventscommand.executed- Command execution eventssession.updated- Session state changessession.diff- Session diffs
Additional Endpoints (From JavaScript Analysis)
Discovered from bundled JavaScript (/assets/index-CtVOE8w2.js):
Configuration:
/config/providers- Provider configuration/global/config- Global configuration
File Operations:
/file/content- File content access/file/status- File status/find/file- File search/find/symbol- Symbol search
Session & Lifecycle:
/session/status- Session status/global/dispose- Global cleanup/instance/dispose- Instance cleanup
Experimental:
/experimental/resource- Resource access/experimental/tool- Tool access/experimental/worktree- Worktree management
Auth:
/provider/auth- Provider authentication
TUI:
/tui/publish- Terminal UI publishing
(These endpoints need further testing to document request/response formats)
Session Storage
Filesystem Location
Sessions are persisted to filesystem:
~/.local/share/opencode/storage/session/<project-id>/
Session File Format
Each session is stored as JSON:
File: ses_<session-id>.json
Example:
{
"id": "ses_3a7955e53ffe71fy4GJWD2yQyy",
"slug": "playful-star",
"version": "1.1.48",
"projectID": "58c042e67dd312114ee098f01f12b2db0226abca",
"directory": "/home/coder/src",
"parentID": "ses_3ae187259ffeMdIpeZbzA5R4ZU",
"title": "review changes [commit|branch|pr], defaults to uncommitted (@build subagent)",
"permission": [
{
"permission": "todowrite",
"action": "deny",
"pattern": "*"
},
{
"permission": "todoread",
"action": "deny",
"pattern": "*"
},
{
"permission": "task",
"action": "deny",
"pattern": "*"
}
],
"time": {
"created": 1771009909164,
"updated": 1771009996086
},
"summary": {
"additions": 0,
"deletions": 0,
"files": 0
}
}Key Fields:
id- Unique session ID (format:ses_<hash>)slug- Human-readable slugprojectID- Project hash IDdirectory- Working directoryparentID- Parent session (for sub-agents)permission- Permission configuration arraytime- Creation/update timestampssummary- File change summary
Client Attachment
Interactive TUI
opencode attach http://localhost:5400 [options]Options:
--session <id>- Continue specific session--dir <path>- Working directory--password <pw>- Basic auth password
Note: This opens an interactive Terminal UI, not programmatic access.
Authentication
Default (Unsecured)
Server runs without authentication if OPENCODE_SERVER_PASSWORD is not set.
Warning shown on startup:
Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.
Secured Mode
Set environment variable before starting:
export OPENCODE_SERVER_PASSWORD="your-secure-password"
opencode web --port 5400Then authenticate via:
opencode attach --password <pw>(TUI)- HTTP Basic Auth header (programmatic)
Commands Comparison
opencode web
- Starts server with web UI
- Serves HTML/JS application on root
/ - Provides REST API endpoints
- SSE event streaming
- Use for: Browser access + programmatic API
opencode serve
- Headless server (no UI focus)
- Same API endpoints as
web - Use for: Programmatic access only
opencode acp
- Unclear - Did not bind to HTTP port as expected
- May be deprecated or different protocol
- Needs further investigation
- Recommendation: Use
opencode web/serveinstead
opencode attach
- Client that connects to running server
- Opens interactive TUI
- Not for programmatic access
Architecture Implications
For OpenClaw Integration
Discovery Impact:
- ✅ HTTP REST API exists - Can make programmatic API calls
- ✅ SSE for events - Real-time streaming without WebSocket
- ⚠️ Session creation unclear - Need to test POST endpoints
- ⚠️ Message sending unclear - Need to test messaging API
- ✅ Session storage known - Can query filesystem if needed
Recommended Approach:
- Gateway → OpenCode REST API (HTTP POST/GET)
- Gateway ← OpenCode SSE stream (Server-Sent Events)
- Event filtering in Gateway (hardcoded rules, no AI)
Questions Answered ✅
Critical (Formerly Blocking) - ALL RESOLVED
-
How to create session programmatically?
- ✅
POST /sessionwith JSON body - ✅ Request format:
{directory, title, agent}
- ✅
-
How to send message to session?
- ✅
POST /session/<id>/message - ✅ Request format:
{parts: [{type: "text", text: "..."}]}
- ✅
-
How to list sessions?
- ✅
GET /sessionreturns array of all sessions
- ✅
Remaining Questions (Non-blocking)
Important
- How to terminate session programmatically?
- Try:
DELETE /session/:id?
- Try:
- Can multiple clients attach to same session?
- Test: Multiple SSE connections to
/global/event
- Test: Multiple SSE connections to
- Session persistence across server restarts?
- Test: Restart OpenCode, check if sessions survive
- Rate limiting / throttling?
- Check response headers
- CORS configuration?
- Test cross-origin requests
Nice-to-have
- Bulk operations?
- Session export/import?
- WebHook support?
- Pagination for session lists?
Phase 0 Status: COMPLETE ✅
Critical Path Completed
✅ HTTP REST API fully documented and tested:
- ✅ Project listing:
GET /project - ✅ Session creation:
POST /session - ✅ Session listing:
GET /session(workspace-specific!) - ✅ Session details:
GET /session/:id - ✅ Message sending:
POST /session/:id/message(tested end-to-end) - ✅ Message retrieval:
GET /session/:id/message - ✅ Event streaming:
GET /global/event(SSE) - ✅ Health check:
GET /global/health
✅ Architecture decision made:
- Use HTTP REST API (not ACP/JSON-RPC)
- OpenClaw Gateway → OpenCode HTTP API
- Server-Sent Events for real-time updates
✅ Critical discoveries:
- ✅ Workspace isolation: Server is workspace-specific (must start from correct directory)
- ✅ Project management:
/projectendpoint lists all workspaces - ✅ End-to-end tested: Successfully sent message via API and received response
- ✅ Message polling works: Can retrieve messages via
GET /session/:id/message
✅ Blocking questions answered:
- ✅ How to create sessions →
POST /session - ✅ How to send messages →
POST /session/:id/messagewith{parts, model} - ✅ How to retrieve messages →
GET /session/:id/message - ✅ How to list sessions →
GET /session(workspace-scoped) - ✅ How projects/workspaces work →
GET /project+ workspace isolation - ✅ Session storage format → Documented in filesystem section
Ready for Phase 1 Implementation
Can now proceed with:
- ✅ Build TypeScript/Node.js client library for OpenCode HTTP API
- ✅ Implement Gateway integration
- ✅ Handle workspace isolation (one server per builder workspace)
- ✅ Test event filtering (hardcoded rules, no AI)
- ✅ Validate with real builder workspaces
Multi-Workspace Strategy for OpenClaw
Decision needed: How to handle multiple builder workspaces?
Option 1: Multiple servers (Recommended)
- Run one
opencode webper builder workspace - Each on a different port: 5400, 5401, 5402, etc.
- Gateway maintains mapping: workspace → port
- Pros: Clean isolation, no context switching
- Cons: More processes, more ports
Option 2: Dynamic server restart
- Stop server,
cdto new workspace, restart - Only works for single active builder at a time
- Pros: Single process
- Cons: Destroys active sessions, slow switching
Recommendation: Option 1 (multiple servers)
Optional (Non-blocking)
- Test session termination:
DELETE /session/:id - Test multi-client SSE connections
- Document remaining endpoints (
/file/*,/find/*, etc.) - Session persistence testing
- Test project-scoped endpoints (e.g.,
/project/:id/sessionif they exist)
References
- OpenCode CLI:
opencode --help - Running Instance:
http://localhost:5400(codev pod) - Session Storage:
~/.local/share/opencode/storage/session/ - Phase 0 Research Plan:
phase-0-research.md - Initial Findings:
phase-0-findings.md
Version History
- 2026-02-16: Phase 0 Complete!
- Discovered workspace isolation (server is workspace-specific)
- Documented
/projectendpoint for workspace management - End-to-end tested message sending via HTTP API
- Documented message retrieval via
GET /session/:id/message - All critical questions answered
- Ready for Phase 1 implementation
- 2026-02-15: Initial discovery (HTTP REST + SSE documented)