Open Personal Adaptive Node-based Distributed Assistant
Any device, any compute, one command. A personal task-orchestration assistant that runs across your heterogeneous devices as a peer-to-peer network of nodes.
English · 简体中文 · 日本語 · Español · Deutsch
- What is OpenPanda?
- Key features
- Architecture
- Installing
- Getting started
- Usage
- CLI reference
- Configuration
- Documentation
- Testing
- Deployment
- Tech stack
- Roadmap
- Contributing
- License
- Acknowledgements
OpenPanda is not another agent CLI — it is the layer above them: the household manager for every device and every tool you own.
Claude Code, Codex, OpenCode, Grok Build, DeepSeek Harness, OpenClaw, Hermes … each is a powerful agent on one machine. OpenPanda doesn't compete with them — it hires them. Whatever device you speak to becomes the commander; it answers directly when it can, and when it can't, it routes the task over your network to whichever node can actually do the work — handing it to that node's own agents (Claude Code, Codex, …), or straight to the hardware when a plain device action is all that's needed (a servo doesn't need an LLM).
sub-agents (single machine) agent teams (single machine) OpenPanda (many machines)
┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐
│ Claude Code │ │ multi-agent │ │ heterogeneous fleet │
│ Codex … │ │ orchestration│ │ + their agents │
│ │ │ │ │ + bare hardware │
└──────────────┘ └──────────────┘ └──────────────────────┘
upstream of all of them: OpenPanda delegates, they execute
In practice: you ask once, from any device, and OpenPanda delegates the task to the node best suited to run it, streams back the result, and remembers what it learned for next time — while keeping project work strictly isolated from personal memory, so your codebase never drifts because "the assistant knows you prefer dark themes".
It is built from the ground up as a personal system: no cloud dependency, your memory stays on your devices, and every node speaks to its peers over direct WebSocket links you control.
- Heterogeneous node network — nodes advertise their real capabilities (CPU class, shell, agent adapters) via a capability card; the network routes each task to the node that can actually do it. Built for laptops, SBCs, desktops, and every platform tier in between.
- Unified entry model — one prompt in, three intents out:
answer(pure LLM reply),tool_call(your tools),task(delegated to a node). Automatic intent classification with graceful fallback. - Three-tier capability execution —
native(direct shell execution),agent(adapter-backed agent, e.g. Claude Code via an Anthropic-compatible endpoint), andmanual(queued for you to approve/run by hand). - Superior task review — after an agent runs, the entry model judges the result against the task's success criteria; incomplete work is re-delegated to the agent chain (with what remains and the next step) until it is accepted or the round budget runs out. Completed reversible tasks finish into done, while irreversible side effects (pushes, deletes, …) and never-satisfied tasks land in review for your explicit approval.
- P2P delegation protocol — idempotent
task_idkeys and per-attemptattempt_ids over WebSocket + JSON, so crashed retries never double-execute. - Self-evolving skills — procedural memory in
SKILL.mdfiles: a skill declares when it applies, how it runs, and can be refined after each use. - Daily-assistant tools — the agent can read the system clock, fetch live
weather, and set scheduled reminders (
reminder.set): stored in SQLite, fired by a scanner, delivered as Web Push notifications and live SSE updates to any open console.panda reminder list/add/rmmanages them from the CLI. - MCP integration — one stdio Model Context Protocol server configurable
in
config.yaml(mcp.command) or the console's settings page; its tools join the agent's toolset live, no daemon restart. - Two-layer memory — per-user and per-project memory (
USER.md/MEMORY.mdstyle) kept behind an isolation wall, plus a background Dreaming engine that consolidates daily logs into long-term memory while the node is idle. - Voice entry — optional sidecar pipeline (wake word → STT → LLM → TTS), hardware-gated and ready for embedded microphones.
- Interactive REPL + embedded web console —
panda replis the operator's seat: bare input goes to the ask engine, slash commands drive the panel surfaces (/tasks,/approve,/projects,/nodes,/lang…), and/webboots the embedded console in one click. The console's task queue is a kanban board (to do / in progress / in review / finished) with inline approvals, plus chat sessions, reminders, an editable memory page (USER / MEMORY / DREAMS), and a settings page (model endpoint — Anthropic or OpenAI compatible — and MCP server).panda webis the one-command path: loopback bind + ephemeral token by default, browser opens already logged in (no config, no token paste). Five UI languages: English, 简体中文, 日本語, Español, Deutsch. - Self-update —
panda web(and/web) checks the release channel in the background; the console downloads and verifies an available update, then installs it in one click once the task queue is idle. Discard a downloaded update and nothing is left behind. - Defense & safety layers — permission tiers, a circuit breaker, scope-drift and infinite-loop detection, plus execution-side hardening: sandboxing, network allow-lists, secret redaction, and audit logging.
- Slim by design — steady-state RSS ≈ 13–20 MB, built to live on resource-constrained single-board computers.
- Cross-compiles cleanly — one static binary per platform, no CGO needed
(pure-Go SQLite via
modernc.org/sqlite).
┌───────────────────────────┐
│ You: CLI / web / voice │
└─────────────┬─────────────┘
│
┌────────────────────▼────────────────────┐
│ entry · panda ask │
│ classify: answer | tool_call | task │
└────────────────────┬────────────────────┘
│ delegate over WebSocket + JSON
┌──────────────┴───────────────┐
│ │
┌────────────▼────────────┐ ┌────────────▼────────────┐
│ Worker node │ │ Worker node │
│ e.g. Laptop (Standard) │ │ e.g. SBC (Micro) │
└─────────────────────────┘ └─────────────────────────┘
Inside each node:
┌─────────────────────────────────────────────────────────────┐
│ cmd/panda daemon + CLI (ask / status / queue / task …) │
├─────────────────────────────────────────────────────────────┤
│ entry unified entry model (answer · tool_call · task)│
│ scheduler delegation & routing decisions │
│ commander 3-tier execution: native · agent · manual │
│ defense permission tiers · circuit · drift · loops │
│ security sandbox · allow-lists · redaction · audit │
│ memory USER/MEMORY stores + Dreaming engine │
│ skills SKILL.md procedural memory │
├─────────────────────────────────────────────────────────────┤
│ bus WebSocket transport + message envelope │
│ ledger capability directory (cards, heartbeat) │
│ storage SQLite (WAL) + migrations │
│ log / util structured JSON logs, UUIDv7 │
└─────────────────────────────────────────────────────────────┘
Get a release binary in one line — macOS, Linux, or Windows; consistent
experience, no root required. The installer downloads the matching release
archive, verifies its SHA-256, unpacks the binary plus its agent adapters
(adapters/*.py) into a per-user prefix, and links panda onto your PATH.
| Platform | Command |
|---|---|
| macOS / Linux | curl -fsSL https://raw.githubusercontent.com/Xustalis/OpenPanda/main/scripts/install.sh | sh |
| macOS (Homebrew) | brew tap Xustalis/openpanda && brew install openpanda |
| Windows (PowerShell) | Set-ExecutionPolicy -Scope Process Bypass, then irm https://raw.githubusercontent.com/Xustalis/OpenPanda/main/scripts/install.ps1 | iex |
Override the defaults with flags:
sh scripts/install.sh --version 0.0.4 # pin a version (default: latest)
sh scripts/install.sh --prefix /opt/openpanda # custom install directory
sh scripts/install.sh --yes # also register auto-start, no prompt
sh scripts/install.sh --no-service # skip auto-start entirelyOn macOS / Linux files land under a per-user prefix following the XDG convention:
${XDG_DATA_HOME:-~/.local/share}/openpanda/
├── bin/panda # the real binary
├── adapters/*.py # agent adapters (the daemon needs these to delegate)
├── config.example.yaml
└── capabilities.example-*.yaml
~/.local/bin/panda is a symlink to that binary (already on PATH); if your
shell doesn't include ~/.local/bin, the script prints the export PATH line to
add. On Windows the files go to %LOCALAPPDATA%\OpenPanda\ and its bin is added
to your user PATH. The installer can also register an auto-start service
(panda daemon at login) — say yes only after panda init, since the daemon
won't start without a config.
After installing, bootstrap and run:
panda init # interactive config.yaml + capabilities card
panda doctor # self-check: binary / PATH / config / adapters / agents
panda repl # drop into the interactive REPL
panda web # open the embedded web console (loopback, auto-login)To remove everything:
- macOS / Linux:
rm -rf ~/.local/share/openpanda ~/.local/bin/panda(and stop any auto-start service first). - Windows: delete
%LOCALAPPDATA%\OpenPandaand remove itsbinfrom the userPATH.
Full guide and troubleshooting: docs/install.md.
| Tool | Version |
|---|---|
| Go | 1.26.5+ |
| Python | 3.10+ (agent adapters / voice sidecar) |
| make | any recent version |
make build # native binary → bin/panda (release, stripped)
make web # web console into the binary (needs node/npm; skip = console shows a hint page)
make test # run the full test suite
make vet # static analysisCross-compile for the devices you actually run:
make build-linux-arm64 # → bin/panda-linux-arm64 (SBCs, embedded boards)
make build-linux-amd64 # → bin/panda-linux-amd64
make build-darwin-arm64 # → bin/panda-darwin-arm64
make build-windows-amd64 # → bin/panda-windows-amd64.exeBootstrap a node interactively — the model endpoint, node name, and a capabilities card are generated from one prompt:
./bin/panda initOr copy the example config and edit it for each node:
cp config.example.yaml /etc/openpanda/config.yaml # or keep it local and use --configThe config is small and self-explanatory. The two things that matter most:
network:
listen_addr: ":7836" # WebSocket listener
shared_secret: "..." # HMAC auth between nodes — all nodes must share the same value
peers: # other nodes in your network
- "worker-1.your-tailnet.ts.net:7836"
model:
base_url: "https://api.deepseek.com/anthropic" # any /v1/messages-compatible endpoint
model: "deepseek-chat"
# api_key: "" # prefer the OPENPANDA_MODEL_API_KEY env varNode identity rules are enforced at daemon startup. A physical host may run
only one physical node, regardless of its display name. A virtual machine
may run as an additional vm node; set a stable node.identity for each VM.
The OS lock is keyed by physical-host identity or VM identity, so an ordinary
second process on the same device is rejected while host plus VM is allowed.
Secrets (model API keys) are read from OPENPANDA_MODEL_API_KEY rather than the
config file whenever possible.
The fastest way to see the whole system is the one-command web console: loopback bind, ephemeral token, browser opens already logged in — no config editing, no token pasting:
./bin/panda webThe console also manages the model endpoint (Anthropic- or OpenAI-compatible) from its settings page if you haven't configured one yet.
For a resident multi-node setup, run the daemon itself:
./bin/panda daemon --config config.yaml --card config/capabilities.example-desktop.yamlEach node that can execute work should be started with its capability card. A node without a card still participates in heartbeats, but it won't be routed work.
Ask anything — the entry model decides whether to answer, call a tool, or delegate:
./bin/panda ask --card config/capabilities.example-desktop.yaml "summarize the git log for the last week"
--cardpoints at this node's capability card; answer/tool_call work without it, but a delegated-task output is refused without it.
Inspect the network and queue:
./bin/panda status
./bin/panda queueDrill into or cancel a specific task:
./bin/panda task <task-id>
./bin/panda cancel <task-id>
./bin/panda logs <task-id>Manage skills:
./bin/panda skill| Command | Description |
|---|---|
panda (no args) |
Open the interactive REPL (same as panda repl); the daemon now runs via the panda daemon subcommand |
panda daemon [--config PATH] [--card PATH] |
Run the daemon: register node, heartbeat, WS server, peer reconnect |
panda ask [--config PATH] [--card PATH] [--authorize] "<question>" |
Unified entry: classify into answer / tool_call / task and execute |
panda repl [--config PATH] [--card PATH] |
Interactive shell: slash commands (tasks/approve/projects/nodes/lang), bare input goes to the ask engine, /web boots the embedded console |
panda web [--config PATH] [--card PATH] [--no-browser] |
One-command web console: loopback + ephemeral token by default, opens the browser already logged in |
panda init |
Interactive first-run setup: generates config.yaml + capabilities.yaml (model endpoint, node name, hardware-scan defaults) |
panda install [--dir PATH] [--no-path] |
Register panda globally on PATH (persistent across reboots) and self-verify the installed copy |
panda uninstall [--config PATH] [--yes] [--no-backup] [--dry-run] |
Safe removal: full plan first, confirm required, whitelist-only deletion, user assets (projects/memory/skills) always kept, zip backup + report |
panda doctor [--config PATH] |
Self-check: installed copy runs, PATH resolves, persistence survives reboot, config/database usable |
panda status |
Node & task status |
panda queue |
List the task queue |
panda task [--config PATH] <task-id> |
Task details |
panda cancel [--config PATH] <task-id> |
Cancel a task (cascades to the executing node) |
panda approve [--config PATH] <task-id> |
Approve a reviewed task (review → done) |
panda reject [--config PATH] [--reason s] <task-id> |
Reject a reviewed task |
panda logs [--config PATH] <task-id> |
Task execution logs |
panda skill |
Skill store management |
panda reminder list | add | rm |
Scheduled reminders: list / add (--after 10m or --at "2006-01-02 15:04") / remove |
panda detect [-o PATH] |
Scan this machine's hardware (CPU/RAM/GPU/agent CLIs) into a capabilities.yaml draft |
panda agents [list] |
Probe the agent CLIs on PATH (Codex, Claude Code, OpenCode, Grok Build, DeepSeek Harness, OpenClaw, Hermes); test <name> checks one, `install |
panda metrics [--csv] |
Export delegation metrics |
panda audit [--task <id>] |
Verify the prev_hash chain of the audit log or one task's events |
panda version / panda help |
Print version / command overview |
| Section | Key | Meaning |
|---|---|---|
node |
name |
Unique node ID (used across the network) |
node |
resource_class |
Micro | Standard | Full → scheduler tier |
node |
kind |
physical (default) or vm; controls the local singleton rule |
node |
identity |
Stable VM identity; physical nodes use the host fingerprint and ignore this override |
network |
listen_addr |
WebSocket listener address |
network |
shared_secret |
HMAC secret authenticating node-to-node hellos; the WS listener refuses to start without it (all nodes share one value) |
network |
max_connections |
Global concurrent WS connection limit (0 = unlimited) |
network |
max_connections_per_ip |
Per-remote-IP concurrent WS connection limit (0 = unlimited) |
network |
panel_addr |
Web console HTTP address (panda web / /web); default 127.0.0.1:7840 |
network |
panel_token |
Bearer token guarding the console's /api/* (loopback auto-generates an ephemeral one; prefer OPENPANDA_PANEL_TOKEN) |
network |
peers |
Manual peer addresses to dial |
Use panda nodes (or panda status) to inspect node kind, local/remote
placement and running state. panda status --running shows only nodes with a
fresh runtime heartbeat; the Web Nodes page exposes the same data through
/api/self and /api/nodes.
| storage | db_path | SQLite database path |
| storage | context_path | Context snapshot store |
| storage | memory_path | Personal memory root |
| storage | projects_path | Per-project memory root |
| storage | skills_path | Procedural-memory root |
| storage | work_path | Where agents execute; scope drift is measured here |
| log | level | debug | info | warn | error |
| model | base_url | Anthropic- or OpenAI-compatible API base URL |
| model | model | Model id (e.g. deepseek-chat, deepseek-reasoner) |
| model | api_type | anthropic | openai (default anthropic) |
| model | api_key | Secret — prefer OPENPANDA_MODEL_API_KEY |
| model | max_tokens | Completion cap (default 4096) |
| mcp | command | stdio MCP server argv (empty = disabled); tools hot-load into the agent toolset |
| push | enabled | Serve /api/push/* and send Web Push (embedded console + webui sidecar) |
| push | vapid_subject | VAPID subject (e.g. mailto: address) |
| push | vapid_key_path | VAPID key path (auto-generated on first boot) |
Config load order: --config flag > environment > default /etc/openpanda/config.yaml.
Full documentation lives in the docs/ directory:
- Documentation index — entry point for the public docs.
- Contributing guide — toolchain, engineering gates,
code conventions, and the PR checklist (translations available in
CONTRIBUTING.zh-CN.md/CONTRIBUTING.ja.md/CONTRIBUTING.es.md/CONTRIBUTING.de.md). - Desktop & packaging roadmap — staged plan toward a native desktop client, signed installers, notarization, and auto-updates.
make test # full suite
make vet # go vetKey protocol invariants are covered by real two-node WebSocket tests (no Tailscale required):
go test ./internal/core/ -run 'TestTwoNodeProtocol|TestDelegateIdempotent|TestCancelPropagates' -vOpenPanda nodes speak plain WebSocket (ws://) by default. Plain WebSocket must
only be used over a trusted private path:
- Loopback / same-host links (e.g.
127.0.0.1,localhost). - A private overlay network you control, such as Tailscale or a VPN.
- A physically isolated LAN where every device is trusted.
If any OpenPanda peer crosses the public Internet, terminate TLS in front of the
WebSocket listener (e.g. nginx, Caddy, Traefik) and configure peers with the
wss:// URL. The shared_secret authenticates node-to-node hellos, but it is
not a substitute for transport encryption — do not expose a plain ws://
listener on the public Internet.
The panel_addr web console serves plain HTTP and carries a Bearer token (an
ephemeral one is auto-generated on loopback). Keep it on loopback, or put it
behind the same TLS reverse proxy.
OpenPanda targets low-power devices. Verify your steady-state memory before shipping
to hardware — a single ps sample is unreliable due to GC noise; take several:
make build
for i in 1 2 3 4 5; do
./bin/panda daemon --config testdata/node-a.yaml >/dev/null 2>&1 &
PID=$!; sleep 3
ps -o rss= -p $PID | awk '{printf "%d MB\n", $1/1024}'
kill -TERM $PID; wait $PID 2>/dev/null
done| Layer | Choice |
|---|---|
| Core daemon | Go (modernc.org/sqlite — pure Go, no CGO) |
| Glue / adapters | Python 3.10+ |
| Transport | WebSocket + JSON envelopes |
| State | SQLite in WAL mode |
| Frontend | Web console (Vite + Preact, go:embed single binary) via panda repl → /web, or the standalone webui/ sidecar |
| LLM access | Anthropic-compatible /v1/messages or OpenAI-compatible endpoints (e.g. DeepSeek) |
Phases 0–3 (entry model · P2P delegation · memory + voice + execution hardening · kernel/console/REPL rebuild + live two-node verification) are complete. Phase 4 (desktop client + signed installer pipeline + auto-update mechanism + release channels) is planned in detail in the desktop & packaging roadmap.
Contributions are welcome. To keep the codebase consistent, please follow the engineering gates before opening a pull request:
make vet && make testmust pass.gofmt -l internal/ cmd/ adapters/must be empty.- Keep core-module test coverage above ~60% where practical.
See CONTRIBUTING.md for the full conventions: error
wrapping (%w / errors.Is), complexity limits, no dead code, concurrency
rules, i18n rules, and the commit style. Translated editions are available in
CONTRIBUTING.zh-CN.md,
CONTRIBUTING.ja.md,
CONTRIBUTING.es.md, and
CONTRIBUTING.de.md.
Released under the MIT License.
Inspired by distributed multi-agent scheduling theory and by the memory patterns of Hermes and OpenClaw. Built by Xenith.