An agent workspace that runs entirely on your machine — your keys, your data, your model.
0xCopilot takes on real, multi-step work across your apps and finishes it — on your machine, on your API key, on whatever model you pick. No cloud to trust, no seat to buy, no one holding your data but you.
- Local-first. The desktop app bundles its own Python runtime and PostgreSQL and runs the whole stack behind a strict-CSP Electron shell. Nothing leaves your machine except the model API calls you configure.
- BYOK. Bring your own OpenAI, Anthropic, or Google Gemini key. Keys are stored per-user, encrypted at rest, and never travel in request bodies.
- Open source. One monorepo, independently deployable services, self-hostable in one line.
Install the copilot CLI with npm or Bun — no DMG, no installer, no admin rights:
npm install -g @0x-copilot/cli # or: bun add -g @0x-copilot/cliThen, anywhere:
copilotThe first run stages a pinned, checksum-verified local runtime (Python + PostgreSQL + the app's services) and opens the app; every run after is instant. Because the runtime is fetched by your package manager rather than a browser, macOS Gatekeeper and Windows SmartScreen never flag it — no "unidentified developer" click-through — and on macOS the bundled binaries are ad-hoc signed at install time so they run on Apple Silicon without an Apple Developer certificate. macOS (Apple Silicon + Intel) and Windows x64. CLI internals: tools/cli.
- Install & run —
npm install -g @0x-copilot/cli, thencopilot. First run shows a boot screen while it stages and starts the embedded services. - Sign in — Connect a wallet (MetaMask, Rabby, or any EIP-6963 wallet; chains: Ethereum 1, Base 8453, Arbitrum One 42161, Robinhood Chain 4663) or Continue with Google.
- Add a model key — Settings → AI & data → Provider keys and paste your OpenAI, Anthropic, or Google Gemini key (encrypted at rest, used only for your runs).
Manage it: copilot doctor (diagnose) · copilot uninstall (remove runtime + data) · npm rm -g @0x-copilot/cli (remove the command). Prefer a signed DMG/installer? That's a future channel gated on signing certificates — see desktop-app.md §10.
If you'd rather run the web stack on your own host, one command brings up PostgreSQL 17, the four service images, and an nginx gateway:
curl -fsSL https://raw.githubusercontent.com/0x-copilot-dev/0x-copilot/main/deploy/self-host/install.sh | bashBy default the gateway is published on port 8090. You need Docker (with Compose) and a host or domain to serve from. The images are pulled from GitHub Container Registry (ghcr.io/0x-copilot-dev/0x-copilot-{backend,backend-facade,ai-backend,frontend}); if those packages are private for your org you'll need to docker login ghcr.io first.
Full configuration (env vars, TLS/domain, Google OAuth, wallet login) is in deploy/self-host/README.md. To turn on sign-in providers, see:
Questions, ideas, or want to help build? Join us on Discord.
Prerequisites: Node.js + npm, Python 3.13, and (for the Docker paths) Docker.
make setup # one .venv per Python service + node_modules
make setup-hooks # install pre-commit (ruff, ruff-format, prettier)make devStarts backend on :8100, ai-backend on :8000, backend-facade on :8200, and frontend on :5173. Open http://127.0.0.1:5173; the Vite dev server proxies /v1/* to the facade. Every app — browser, curl, Postman, native — must call the facade at :8200, never :8100/:8000 directly.
One-URL Docker dev stack (frontend + facade + backend + ai-backend behind a gateway at http://127.0.0.1:8080):
OPENAI_API_KEY=$OPENAI_API_KEY make docker-dev
make docker-dev-downPlain npm run dev --workspace @0x-copilot/desktop launches the Electron shell against a mock transport (or COPILOT_FACADE_URL if you point it at a running facade) — no bundled services.
To exercise the full packaged boot (embedded PostgreSQL + the three Python services under supervision), stage the self-contained runtime once, then launch against it:
# 1. Stage bundled CPython 3.13 + PostgreSQL 17 + the three services
# (output lands in apps/desktop/resources/runtime/, gitignored).
# Use your host's platform/arch — e.g. an Apple Silicon mac:
node tools/desktop-runtime/stage.mjs --platform darwin --arch arm64
# 2. Launch the Electron app against the staged runtime. Setting
# COPILOT_RUNTIME_DIR turns on the service supervisor.
COPILOT_RUNTIME_DIR="$PWD/apps/desktop/resources" \
npm run dev --workspace @0x-copilot/desktopSee apps/desktop/README.md for the supervisor boot contract and apps/desktop/SMOKE.md for the manual smoke checklist. Runtime-staging details are in tools/desktop-runtime/README.md.
make test runs a curated cross-service smoke subset. For a service's full suite or a single test, use that service's own .venv:
# Full suite for one service
cd services/ai-backend && .venv/bin/python -m pytest
# Single file / single test
cd services/ai-backend && .venv/bin/python -m pytest tests/unit/agent_runtime/agent/test_runtime_factory.py
cd services/ai-backend && .venv/bin/python -m pytest tests/unit/agent_runtime/agent/test_runtime_factory.py::TestName::test_methodFrontend / desktop / TS:
npm run typecheck --workspace @0x-copilot/frontend
npm run build --workspace @0x-copilot/frontend
npm run test --workspace @0x-copilot/desktopOne GitHub monorepo, microservice-style runtime: each service owns its API, Docker image, local dependency environment, tests, and deploy path. Service boundaries are hard — no deployable component imports another's src/. Cross-component integration is HTTP, generated contracts (packages/api-types), or constants-only (packages/service-contracts).
0x-copilot/
apps/
desktop/ # Electron shell — bundled runtime, supervised boot, strict CSP
frontend/ # Vite + React web surface
website/ # 0xcopilot.tech marketing site (Astro)
packages/
api-types/ # TypeScript contracts for app-facing payloads
design-system/ # React primitives + tokens
chat-surface/ # framework-agnostic chat UI surface
chat-transport/ # transport client for runs/events/streaming
surface-renderers/# renderers for agent output surfaces
audit-chain/ # tamper-evident audit chain primitives
service-contracts/# constants-only Python package shared via PYTHONPATH
services/
backend/ # MCP registration, OAuth state, token vault, skills, audit, identity
backend-facade/ # product-facing API — the ONLY surface apps may call
ai-backend/ # agent runtime (FastAPI + LangGraph + Deep Agents)
tools/
desktop-runtime/ # stages + boots the self-contained desktop runtime
deploy/ # self-host + tenant/rollout tooling
docs/ # architecture, deployment, roadmap, security
Request path: browser → Vite proxy (or nginx ingress in prod) → backend-facade:8200 → either backend:8100 (MCP / skills / OAuth / identity) or ai-backend:8000 (conversations, runs, events, approvals). The facade never exposes /internal/v1/*; the backend's /internal/v1/* is consumed only by ai-backend.
Published images: ghcr.io/0x-copilot-dev/0x-copilot-{backend,backend-facade,ai-backend,frontend}.
Deeper reading:
docs/architecture/workspace-topology.mddocs/architecture/service-boundaries.mdservices/ai-backend/README.mdandservices/ai-backend/docs/README.mddocs/dev-testing.md— curl, Postman, and the dev IdP for non-browser callers.
0xCopilot supports four ways in, plus bring-your-own model keys:
- Dev IdP (development only).
make devuses a signed bearer minted byPOST /v1/dev/identity/mint, registered only whenBACKEND_ENVIRONMENT=development. The bearer is signed withENTERPRISE_AUTH_SECRETand verified by the same path production uses — there is noDEV_AUTH_BYPASSshortcut. Mint one for curl withmake dev-bearer(see API testing). - Google OAuth. Set
GOOGLE_OAUTH_CLIENT_ID(andGOOGLE_OAUTH_CLIENT_SECRETfor web clients) and the login screen shows Continue with Google. Facade routes:GET /v1/auth/providers,GET /v1/auth/oidc/google/start,GET /v1/auth/oidc/callback. Step-by-step: Google OAuth setup. - Wallet login (SIWE). Sign-In-With-Ethereum via any EIP-6963 wallet. Facade routes:
POST /v1/auth/siwe/nonce,POST /v1/auth/siwe/verify. Chain allowlist defaults to Ethereum, Base, Arbitrum One, and Robinhood Chain (4663), tunable viaSIWE_ALLOWED_CHAIN_IDS. Details: Wallet login (SIWE). - BYOK provider keys. Per-user OpenAI / Anthropic / Google Gemini keys, encrypted at rest via
TokenVault, managed in Settings → AI & data → Provider keys (/v1/settings/provider-keys). Plaintext never appears in any response, log, or audit row — only akey_hint.
Service-to-service calls (facade ↔ backend, ai-backend ↔ backend) use ENTERPRISE_SERVICE_TOKEN plus x-enterprise-org-id / x-enterprise-user-id headers derived from the verified user bearer. Treat caller-supplied identity, role, scope, and tenant as untrusted unless derived from a verified session or token. Production fails closed without ENTERPRISE_AUTH_SECRET and ENTERPRISE_SERVICE_TOKEN.
Do not hardcode bearers, JWTs, or service tokens in source, Dockerfiles, README examples, or committed .env files. Mint a fresh bearer per session when you need one.
For non-browser callers, mint a dev bearer once and reuse it. All callers must hit the facade at :8200.
export TOKEN=$(make dev-bearer) # default: sarah_acme
export TOKEN=$(make dev-bearer PERSONA=marcus_admin) # admin variant
curl -sS http://127.0.0.1:8200/v1/me/profile \
-H "Authorization: Bearer $TOKEN" | jqFull recipes (conversations, runs, SSE streaming, MCP install, per-chat connector scope PATCH) and Postman setup live in docs/dev-testing.md.
After make dev is up, this should produce a streaming run:
TOKEN=$(make dev-bearer)
BASE=http://127.0.0.1:8200
CONV=$(curl -sS -X POST $BASE/v1/agent/conversations \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"title":"Local smoke"}' | jq -r .conversation_id)
curl -sS -X POST $BASE/v1/agent/runs \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d "{\"conversation_id\":\"$CONV\",\"user_input\":\"Hi\"}" | jq .stream_urlYou can also send Hi from the UI composer at http://127.0.0.1:5173.
The chat UI opens /v1/agent/runs/{run_id}/stream through the facade. The AI backend persists ordered RuntimeEventEnvelope records with a monotonic sequence_no per run before replaying or streaming them as runtime_event SSE frames. Clients track the highest sequence_no and reconnect with ?after_sequence=N so a paused stream resumes without replay. Replay-only is GET /v1/agent/runs/{run_id}/events. Use the backend's projected activity_kind / display_title / summary / status fields — do not derive activity types from event-name prefixes.
make prod validates required secrets and refuses to register the dev IdP routes when BACKEND_ENVIRONMENT != development:
ENTERPRISE_AUTH_SECRET=... \
ENTERPRISE_SERVICE_TOKEN=... \
MCP_TOKEN_VAULT_SECRET=... \
OPENAI_API_KEY=... \
make prodDeploy the built images with your production orchestrator and managed secret store. The backend production runtime requires a persistent MCP registry store and a managed token-vault adapter before it can serve production traffic.
- Service boundaries are hard. No deployable component imports another's
src/. Cross-component integration is HTTP, generated contracts (packages/api-types), or constants-only (packages/service-contracts). Never add a sibling service toPYTHONPATH, never reuse another service's.venv, never use relative imports across deployable boundaries. - Apps call the facade only — never
backendorai-backenddirectly. - Don't put AI orchestration in
backend-facade; don't put tenant auth, billing, or product persistence inai-backend. - Each deployable component owns its dependency environment and Dockerfile. Python services use a service-local
.venv+requirements.txt; the web/desktop apps use the npm workspace. - Don't create shared packages just to avoid small duplication — share only stable contracts and truly cross-cutting primitives.
- Don't commit secrets, real
.envfiles, tokens, certificates, or production credentials.
Path-scoped engineering rules live in hierarchical CLAUDE.md files that load when you touch that subtree (services/*/CLAUDE.md, apps/frontend/CLAUDE.md, packages/*/CLAUDE.md).
MIT © 0xCopilot