Show HN: Selfhost pi agents API with Linux desktop sandbox A developer released Pi Managed Agents API, a self-hosted service built on the Pi coding agent SDK that lets applications define agents, give them workspaces, and run tasks with session state, code execution in Docker, and a Linux desktop sandbox, according to the project's Show HN post. The MVP, described as implemented as of 15 November 2026, deploys via Docker Compose on one Linux VPS and supports OpenAI, Anthropic, and OpenRouter model catalogs including DeepSeek, with desktop sessions running in a QEMU/KVM VM using Microsoft HCU for accessibility inspection and screenshots. Desktop networking is limited to Off, Internet, or domain-allowlist policies through an HTTP/HTTPS proxy, and Windows and macOS providers are not implemented. Give your apps agents that run code, keep context, and deliver files. Powered by Pi. Hosted by you. Get started get-started · Comparison how-it-compares · Connect your app connect-your-app · Built-in documentation documentation-in-your-workspace · Architecture https://github.com/taherchhabra/pi-managed-agents-api/blob/main/docs/architecture.md Useful agents need a place to work, a way to carry context into the next task, and a record of what happened. Pi Managed Agents brings those pieces together in a self-hosted API and browser console built around the Pi coding agent SDK . Define an agent, give it a workspace, and send it a task. Pi handles the model and tool loop; the service saves session state, runs code in Docker or operates a Linux desktop, records progress, and publishes the resulting files. Your application can call it from any server over HTTPS. A real DeepSeek run through OpenRouter: synthetic release metrics become a Python analysis and a downloadable report. Screenshots use a separate demo workspace. Choose Desktop assistant , select a vision model, and start a Linux desktop session. Pi uses Microsoft HCU https://github.com/microsoft/Hybrid-Computer-Use to inspect accessibility controls and screenshots, then operate real applications inside a QEMU/KVM VM. The console shows a live preview and saves the model’s observations alongside its tool calls. Apps and files stay with the session between runs. A desktop can run on a separate KVM server while your API stays on a VPS. Linux is supported first, with Off, Internet, or domain-allowlist networking for desktop sessions. Internet access uses an HTTP/HTTPS proxy; private networks and cloud metadata are blocked. The preview is read-only. Windows and macOS providers are not implemented. Open Docs → Linux desktops & HCU in the app for setup and the API guide. The same guide is in docs/desktop.md https://github.com/taherchhabra/pi-managed-agents-api/blob/main/docs/desktop.md . The agent created a note in Mousepad, saved it through the GUI, then resumed the same desktop and appended a second line. Screenshots and the saved file are available in the session. Choose a network policy for each new desktop session. The real DeepSeek browser test loaded an allowed website and verified that an unapproved domain was blocked. See the browser result https://github.com/taherchhabra/pi-managed-agents-api/blob/main/docs/images/desktop-browser-preview.jpg and in-app guide screenshot https://github.com/taherchhabra/pi-managed-agents-api/blob/main/docs/images/desktop-network-docs.jpg . Vendor documentation checked on 14 November 2026 ; the Pi column reflects the implemented MVP as of 15 November 2026 . Vendor columns describe their hosted services, with self-hosting options called out separately. Links in the table point to the relevant vendor documentation. | Capability | Pi Managed Agents API — current MVP | OpenAI Agents API | Claude Managed Agents API | |---|---|---|---| | Agent harness | Pi coding agent SDK | Managed Codex harness https://developers.openai.com/api/docs/guides/agents-api/architecture | Configurable Claude harness https://platform.claude.com/docs/en/managed-agents/overview | | Host the API, harness, and session storage yourself | Yes. Docker Compose deployment on one Linux VPS | OpenAI runs the service; self-hosted sandboxes move tool execution to your infrastructure https://developers.openai.com/api/docs/guides/agents-api/architecture | Hosted service, or self-hosted service and sandboxes https://platform.claude.com/docs/en/managed-agents/self-hosted | | Model choices | OpenAI, Anthropic, and OpenRouter catalogs; includes DeepSeek | OpenAI models supported by the API https://developers.openai.com/api/docs/guides/agents-api/configuration | Claude models https://platform.claude.com/docs/en/managed-agents/overview ; self-hosted service also supports a choice of inference backend https://platform.claude.com/docs/en/managed-agents/self-hosted | | Reusable agents | Immutable versions; sessions pin a version | Saved or inline configuration; sessions copy saved settings https://developers.openai.com/api/docs/guides/agents-api/configuration | Versioned agent definitions https://platform.claude.com/docs/en/managed-agents/agent-setup | | Sessions and follow-up messages | Yes. Persistent context, queued runs, and follow-ups; five-minute limit per run | Persistent sessions with asynchronous turns and follow-ups https://developers.openai.com/api/docs/guides/agents-api/sessions | Stateful sessions with asynchronous user events https://platform.claude.com/docs/en/managed-agents/overview | | Code execution and workspace | Docker workspaces or optional Linux desktops on QEMU/KVM with HCU; Bash, Python and persistent files; desktop HTTP/HTTPS with Off / Internet / domain allowlist, Docker networking off | Hosted, self-hosted, or no environment https://developers.openai.com/api/docs/guides/agents-api/architecture | Cloud sandboxes https://platform.claude.com/docs/en/managed-agents/environments or self-hosted execution https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes | | File inputs and outputs | Downloads of published outputs/ snapshots; no input-upload API yet | Uploads and published artifacts for hosted environments; self-hosted files use your infrastructure https://developers.openai.com/api/docs/guides/agents-api/environments/files | Upload, mount, and download session files https://platform.claude.com/docs/en/managed-agents/files | | Streaming and history | Saved lifecycle/tool/result events over resumable SSE; no token deltas yet | Live SSE with text deltas; saved items and turns for history https://developers.openai.com/api/docs/guides/agents-api/sessions/events | Live text previews plus persisted completed events https://platform.claude.com/docs/en/managed-agents/event-deltas | | Console and logs | Agent testing, session inspector, tool calls, log export, artifacts, and built-in docs | Dashboard session logs, turn traces, and usage https://developers.openai.com/api/docs/guides/agents-api/observability | Console session viewer, tool inspection, event export, and usage https://platform.claude.com/docs/en/managed-agents/session-observability | | MCP and custom application tools | Fixed Bash/file tools plus built-in HCU AX / GUI integration; no user-configurable MCP servers or custom tools yet | Function tools https://developers.openai.com/api/docs/guides/agents-api/tools/functions and MCP connections https://developers.openai.com/api/docs/guides/agents-api/tools/mcp | Custom tools and MCP https://platform.claude.com/docs/en/managed-agents/tools | | Steering and cancellation | Cancellation supported; new messages queue or reject while busy; no mid-run steering | Steer an active turn or cancel it https://developers.openai.com/api/docs/guides/agents-api/sessions | Steer or interrupt with user events https://platform.claude.com/docs/en/managed-agents/overview | | Subagents and orchestration | Not yet. One agent per session; one run at a time per worker | Parallel subagents with separate context https://developers.openai.com/api/docs/guides/agents-api/multi-agent | Subagents, threads, and dynamic workflows https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration | | Tool approvals | Not yet. Enabled tools execute within fixed sandbox restrictions | Automatic review for supported approval requests https://developers.openai.com/api/docs/guides/agents-api/sessions/approvals | Allow, ask, or automatic permission policies https://platform.claude.com/docs/en/managed-agents/permission-policies | | Scheduling and webhooks | Not yet. Your app submits runs and polls or streams results | Scheduled automations https://developers.openai.com/api/docs/guides/agents-api/sessions/automations and session webhooks https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks | Scheduled deployments https://platform.claude.com/docs/en/managed-agents/scheduled-deployments and webhooks https://platform.claude.com/docs/en/managed-agents/webhooks | | Failure recovery | Explicit waiting state; reconcile tool effects before retrying; no automatic tool replay | Inspect saved state and tool effects before retrying failed work https://developers.openai.com/api/docs/guides/agents-api/errors | Automatic rescheduling for transient errors; termination for unrecoverable errors https://platform.claude.com/docs/en/managed-agents/webhooks | | Client integration | Own HTTP API and runnable Node.js example; no OpenAI/Claude SDK compatibility | OpenAI SDK session APIs https://developers.openai.com/api/docs/guides/agents-api/sessions | Anthropic SDK and CLI; same API for its self-hosted service, with exceptions https://platform.claude.com/docs/en/managed-agents/self-hosted | Claude's self-hosted service currently omits the Files API, scheduled deployments, and webhooks, and limits delegation to predefined subagents; see its documented feature differences https://platform.claude.com/docs/en/managed-agents/self-hosted/differences . Hosting the whole service and hosting only a sandbox are separate options. Pi runs its service locally, while model requests go to the configured provider. Real inference has been tested through OpenRouter; direct OpenAI and Anthropic inference remains unverified. Pi currently suits a trusted operator who wants a Pi-based runtime, provider choice, and a console on their own VPS. It does not yet provide high availability, per-user roles, spending quotas, or a hardened public execution service. See the implemented contract and limits https://github.com/taherchhabra/pi-managed-agents-api/blob/main/docs/mvp.md for the exact scope. Start with instructions, a model, and the tools your agent needs. Use OpenAI, Anthropic, or OpenRouter , including DeepSeek V4.1 Flash 1017 through OpenRouter. The console shows the models available in the installed runtime. Save a version, select Test agent , and send a task. You can build a coding assistant that writes and tests Python, an analyst that turns supplied data into a report, or a writing partner that keeps context through revisions. Every saved agent version is immutable. Existing sessions keep their original configuration while you try new instructions or models in another session. A session connects an agent version to an environment. Send related tasks to the same session to continue the conversation and work with the files it already created. | You define | The service keeps | |---|---| | Agent | Versioned instructions, provider, model, and allowed tools | | Environment | A Docker workspace, a Linux desktop VM, or a conversation-only session | | Session | Conversation context, checkpoints, and persistent workspace files | | Run | One submitted task, its status, output, and reported usage | | Events and artifacts | Saved execution history and downloadable output snapshots | Managed workspaces provide Bash, Python, file reads, and file writes . Each attempt runs in a fresh container with the session's persistent volume. Files saved under outputs/ are published as artifacts after a successful run, ready for your app to download. Disconnecting your app or closing the console does not cancel a run. Reopen the session to inspect its result, or reconnect to the event stream from the last sequence you received. Interrupted worker execution follows an explicit recovery flow; uncertain tool effects are never automatically replayed. The console puts the conversation and execution history side by side. Inspect a run's tool calls, expand their arguments and results, search the event log, and preview or download its artifacts. Export the saved session history when you need to investigate a result. The API exposes that same history through a resumable server-sent event stream . These are saved lifecycle, tool, and result events; assistant responses appear when complete. Host Pi Agents on a VPS and call it from your application backend, wherever that backend runs. Your app needs an API URL and a workspace token get-your-workspace-token . Model provider credentials stay on the agents server. This complete Node.js example creates everything through the API: agent → environment → session → user message → result → user reply → next result → files . No console setup or existing session ID is required. Use Node.js 22.19+ and a running server with OpenRouter configured. Set these variables in your application's server environment, substituting your own URL and workspace token: export API URL='https://agents.example.com' export API TOKEN='your-workspace-token' export MODEL PROVIDER='openrouter' export MODEL='deepseek/deepseek-v4.1-flash-1017' For local development, use API URL=http://127.0.0.1:8080 . You can choose another configured provider/model pair from GET /v1/capabilities . Save the following as quickstart.mjs and run node quickstart.mjs . It uses Node's built-in HTTP client; no client package is required. Each execution creates a new agent and session and makes two real agent runs. js import { randomUUID } from 'node:crypto'; import { setTimeout as delay } from 'node:timers/promises'; const base = process.env.API URL ?? 'http://127.0.0.1:8080' .replace /\/$/, '' ; const token = process.env.API TOKEN; if token throw new Error 'Set API TOKEN to a workspace token from the server' ; const headers = { Authorization: Bearer ${token} }; // For a retry, pass the original requestId and the unchanged body. async function request path, body, requestId = randomUUID { const response = await fetch base + path, { method: body === undefined ? 'GET' : 'POST', headers: body === undefined ? headers : { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': requestId, }, body: body === undefined ? undefined : JSON.stringify body , signal: AbortSignal.timeout 15 000 , } ; if response.ok throw new Error HTTP ${response.status}: ${await response.text } ; return response.json ; } async function waitForRun sessionPath, runId { const runPath = ${sessionPath}/runs/${runId} ; const deadline = Date.now + 360 000; while Date.now < deadline { const run = await request runPath ; if run.status === 'completed' return run; if 'failed', 'cancelled', 'waiting' .includes run.status { throw new Error ${runPath}: ${run.status}; ${run.error?.message ?? 'inspect the run in the console'} ; } await delay 1 000 ; } throw new Error Timed out waiting for ${runPath}; the server may still be working ; } // 1. Create an agent and its first immutable version. const agent = await request '/v1/agents', { name: 'Python assistant', config: { provider: process.env.MODEL PROVIDER ?? 'openrouter', model: process.env.MODEL ?? 'deepseek/deepseek-v4.1-flash-1017', instructions: 'Use Python to verify calculations. Save deliverables under outputs/. Keep replies concise.', tools: 'bash', 'read file', 'write file' , }, } ; // 2. Give sessions a managed Docker workspace. const environment = await request '/v1/environment-templates', { name: 'Python workspace', type: 'managed', } ; // 3. Start a conversation pinned to this agent version. const session = await request '/v1/sessions', { agent version id: agent.latest version.id, environment template id: environment.id, } ; const sessionPath = /v1/sessions/${session.id} ; console.log 'Session:', session.id ; // Store this ID with the conversation in your app. // 4. Send the user's first message. Persist this key/body to retry safely. const firstRequestId = randomUUID ; const firstMessage = { input: process.env.PROMPT ?? 'Use Python to calculate the first ten Fibonacci numbers starting with 0, 1. Save the code to outputs/fibonacci.py and the numbers to outputs/fibonacci.txt.', when busy: 'queue', }; console.log 'User:', firstMessage.input ; const firstRun = await request ${sessionPath}/runs , firstMessage, firstRequestId ; console.log 'Run:', firstRun.id ; // HTTP 202 means accepted; model work is asynchronous. // 5. Wait for completion and return the assistant's response to your user. const firstResult = await waitForRun sessionPath, firstRun.id ; console.log 'Assistant:', firstResult.output text ; // 6. The user replies. Reuse the session to keep its conversation and files. const replyRequestId = randomUUID ; const replyMessage = { input: process.env.FOLLOWUP PROMPT ?? 'Read outputs/fibonacci.txt and tell me the sum of those numbers.', when busy: 'queue', }; console.log 'User:', replyMessage.input ; const nextRun = await request ${sessionPath}/runs , replyMessage, replyRequestId ; console.log 'Run:', nextRun.id ; const nextResult = await waitForRun sessionPath, nextRun.id ; console.log 'Assistant:', nextResult.output text ; // 7. Retrieve the latest run's output snapshots using the same authentication. const { data: files } = await request ${sessionPath}/artifacts ; for const file of files.filter file = file.run id === nextRun.id { const response = await fetch base + file.download url, { headers, signal: AbortSignal.timeout 15 000 , } ; if response.ok throw new Error Artifact download failed: HTTP ${response.status} ; const bytes = new Uint8Array await response.arrayBuffer ; console.log Downloaded ${file.path} ${bytes.length} bytes ; // Store these bytes, or return them to the authorized user from your backend. } The user's follow-up reads the saved file in the same session . A verified DeepSeek run printed these results session IDs and user messages omitted : Assistant: Done. - outputs/fibonacci.py — computes the sequence iteratively and writes the result. - outputs/fibonacci.txt — 0 1 1 2 3 5 8 13 21 34 Assistant: The sum is 88 . Downloaded fibonacci.txt 23 bytes Downloaded fibonacci.py 454 bytes Assistant wording and file sizes can vary by model. The downloaded bytes are available in memory for your app to save or return to its user. A 202 response means a message was accepted and saved. The example polls until the run completes; it stops with an error on failed , cancelled , or waiting . A waiting run needs an explicit recovery decision. Timing out in your client does not cancel the server's work. To follow saved progress events instead, use GET /v1/sessions/:session/events?stream=true . In your app, create an agent and environment once and reuse their IDs. Create a session for each new conversation, save its ID with the owning user or record, and send each subsequent user message as another run. Keep each POST's idempotency key and body in your job record so an uncertain response can be retried without duplicating work; generate a new key for a new message. The runnable example https://github.com/taherchhabra/pi-managed-agents-api/blob/main/examples/run.mjs contains this same code and is also shown in the console's Documentation → API quickstart . From a checkout, with the variables above set, run npm run example . Keep workspace tokens in your backend and authenticate your own app's users there. Keys mapped to one workspace share its data; separate workspaces isolate different apps. This project has its own HTTP contract; OpenAI and Claude SDK compatibility adapters are future work. The Pi coding agent SDK handles model interaction, tool selection, and context management. This service supplies API authentication, asynchronous work, persisted checkpoints, Docker or desktop execution, event history, and artifact storage around it. php flowchart LR App "Your app backend