Native daemon migration: Docker → systemd on .13, host pi, real FS paths, updated diagram/map/README

This commit is contained in:
sam
2026-09-16 11:02:58 +10:00
parent 55f82338e0
commit 3935fa0c8f
6 changed files with 181 additions and 190 deletions

127
README.md
View File

@@ -1,7 +1,7 @@
# Paseo — Agent Orchestration on the Home Lab
# Paseo — Agent Orchestration, Native on .13
**Multi-agent orchestration GUI (Paseo 0.7.2) self-hosted in Docker on `.13`, driving real pi
coding agents (0.82.1) through OmniRoute.**
**Multi-agent orchestration (Paseo 0.7.2) running as a native daemon on `.13` — driving the host's
pi (0.82.1) with full filesystem access, LLM via OmniRoute.**
> Maintained by Sam · Gitea — [sam/paseo](https://gitea.lab.audasmedia.com.au/sam/paseo) ·
> interactive map: [maps.lab → Paseo](https://maps.lab.audasmedia.com.au/paseo/docs/system-map.html)
@@ -11,14 +11,17 @@ coding agents (0.82.1) through OmniRoute.**
## What it is
[Paseo](https://github.com/getpaseo/paseo) is a client-server orchestration layer for AI coding
agents: one **daemon** manages agent processes and streams their output to desktop, CLI, mobile and
web clients. On this lab it runs **inside Docker on the always-on host `.13`**, with **pi** (the
coding agent) baked into the image and LLM traffic routed through the existing **OmniRoute** proxy
on `.13:20129`.
agents: one **daemon** manages agent processes and streams output to CLI, web and mobile clients.
- **Local-first** — agents run on our machines/dev environment; no telemetry.
- **Pi is a first-class provider** — the daemon spawns `pi --mode rpc` (pi 0.82.1, no Nix changes).
- **One console** — `paseo ls` / `attach` / `logs` from anywhere: `.27`, `.51`, mobile, web UI.
On this lab it runs **natively on the always-on NixOS host `.13`** (no container): the daemon spawns
the **host's own pi 0.82.1** (`pi --mode rpc`) as user `sam`, giving agents **direct access to the
real filesystem** — projects, the Obsidian vault, photos — with the user's real permissions. LLM
traffic routes through the existing **OmniRoute** proxy on `.13:20129`.
- **Local-first** — no telemetry, no cloud dependency (Tailscale/LAN only).
- **No sandbox** — this is deliberate: the daemon *is* `sam`. Guardrails come from pi's own
protections (filechanges, approvals), not a container boundary.
- **One console** — `paseo ls` / `attach` / `logs` from .27, .51, and mobile web UI.
## Architecture
@@ -27,27 +30,25 @@ flowchart LR
subgraph CLIENTS["Clients"]
D27[".27 desktop<br/>paseo CLI"]
D51[".51 laptop<br/>paseo CLI"]
M["Mobile / web UI<br/>browser"]
M["Mobile / web UI<br/>browser / Tailscale"]
end
subgraph S13["Paseo daemon (.13 — Docker, always-on)"]
D["Paseo daemon 0.7.2<br/>:6767 · auth"]
PI["pi agent 0.82.1<br/>pi --mode rpc<br/>@earendil pi + omni ext"]
CFG["pi config<br/>/home/paseo/.pi"]
W["workspaces<br/>/workspace/demo"]
PWD["PASEO_PASSWORD<br/>.env (600)"]
subgraph N13["Native daemon on .13 (NixOS, always-on)"]
D["Paseo daemon 0.7.2<br/>systemd user service<br/>0.0.0.0:6767"]
PI["pi 0.82.1 (Nix)<br/>pi --mode rpc<br/>host ~/.pi + ~/.agents"]
FS[("Host filesystem as `sam`<br/>projects: ~/paseo/projects/<br/>obsidian vault, photos, /mnt/*")]
FW["NixOS firewall<br/>port 6767 open"]
end
subgraph LLM["LLM routing"]
OR["OmniRoute<br/>.13:20129"]
OP["OpenCode-Go /<br/>OpenRouter / DeepSeek / Gemini"]
OR["OmniRoute .13:20129"]
OP["OpenRouter / DeepSeek /<br/>OpenCode-Go (deferred)"]
end
D27 -->|"PASEO_HOST + password"| D
D51 -->|"PASEO_HOST + password"| D
M -->|"https :6767"| D
D -->|"spawns / manages"| PI
PI -->|"reads settings + models"| CFG
PI -->|"works in"| W
D -->|"protects"| PWD
PI -->|"httpx /v1 (omni provider)"| OR
M -->|"Tailscale / LAN :6767"| D
D -->|"spawns / manages (no sandbox)"| PI
PI -->|"direct access, real permissions"| FS
D -.->|"listen allowed"| FW
PI -->|"omni provider /v1"| OR
OR -->|"combo routing"| OP
```
@@ -68,70 +69,64 @@ paseo/
## Quick start (client)
```bash
# .27 / .51 (one-time)
npm install -g @getpaseo/cli@0.7.2
npm install -g @getpaseo/cli@0.7.2 # one-time, per machine
export PASEO_HOST=192.168.20.13:6767
export PASEO_PASSWORD=<from .13: /home/sam/Docker/Containers/paseo/.env>
export PASEO_HOST=192.168.20.13:6767 # or in ~/.config/environment.d/10-secrets.conf
export PASEO_PASSWORD=<PASEO_PASSWORD> # on .13's secrets file
paseo status # daemon reachable?
paseo ls # running agents
paseo run --provider pi --model omni/openrouter/qwen/qwen3.7-flash \
--cwd /workspace/demo "describe this workspace"
paseo run --provider pi "your task" # defaults to combo model, cwd = your folder
paseo logs <agent-id> # full activity timeline
```
Web UI: `http://192.168.20.13:6767` (password). Mobile: browser over LAN/Tailscale.
Web UI: `http://192.168.20.13:6767` (LAN) · `http://100.114.62.46:6767` (Tailscale/mobile).
## Deployment (server, `.13`)
| Item | Value |
|---|---|
| Compose dir | `/home/sam/Docker/Containers/paseo/` |
| Image | `ghcr.io/getpaseo/paseo:latest` + custom stage |
| Daemon | 0.7.2 · `restart: always` · :6767 |
| pi | `@earendil-works/pi-coding-agent@0.82.1` (baked, `--mode rpc`) |
| Extensions | `omniroute-pi-ext-integration` (baked) |
| Config | `./paseo-home:/home/paseo` (persists pi + paseo config) |
| Workspace | `./workspace:/workspace` (agent project area) |
| Daemon | Paseo 0.7.2 · **native** · systemd user service `paseo.service` · `0.0.0.0:6767`, enabled |
| pi | Host Nix pi 0.82.1 (`/etc/profiles/per-user/sam/bin/pi`) — used directly via `--mode rpc` |
| Config | `~/.paseo/` (daemon) · `~/.pi/` + `~/.agents/` (host pi, Gitea `pi-config` clone) |
| Projects | `~/paseo/projects/<name>/` — real paths, direct FS access |
| LLM | pi → omni provider → `http://192.168.20.13:20129/v1` (OmniRoute) |
| Secrets | `.env` (chmod 600) — `PASEO_PASSWORD`, `OMNIROUTE_API_KEY` |
| Firewall | `networking.firewall.allowedTCPPorts` includes **6767** (NixOS configuration.nix) |
| Secrets | `PASEO_PASSWORD` in `.13` `~/.config/environment.d/10-secrets.conf` (chmod 600) |
**Build note:** base image runs as user `paseo` (uid 1000) with root-owned `/usr/local` — so pi and
the extension are installed at **build time** under `USER root` in the Dockerfile, then dropped back
to `USER paseo` for runtime.
**Start/stop/status:** `systemctl --user {start,stop,status} paseo` · logs: `journalctl --user -u paseo`.
## Verified end-to-end (2026-09-09)
## Verified end-to-end (2026-09-16)
```
$ paseo run --provider pi --model omni/openrouter/qwen/qwen3.7-flash \
--cwd /workspace/demo --title paseo-demo --background \
"List files, read HELLO.txt, write DEMO_SUMMARY.md"
AGENT ID 560ffca0-… STATUS running PROVIDER pi CWD /workspace/demo
↓
AGENT ID 560ffca0-… STATUS idle PROVIDER pi/omni/openrouter/qwen/qwen3.7-flash
↓ → wrote ./workspace/demo/DEMO_SUMMARY.md (correct content)
↓ → `paseo logs` shows full timeline (user → thought → shell → read)
↓ → web UI :6767 → HTTP 200
$ paseo run --provider pi "…" # bare run — model defaults to combo
Model omni/default-opencode-go-ds-flash # healthy combo (OpenRouter → DeepSeek → OpenCode-Go)
Cwd /home/sam/paseo/projects/obsidian_cleanup_tool
Status idle ✅ (agent replied NATIVE_OK / NEWPATH_OK)
```
## Known findings
- Clients: .27 CLI · .51 CLI · mobile web UI all reach the daemon (LAN + Tailscale). ✅
- Migration from the earlier Docker deployment completed; Docker removed (legacy backup in
`~/paseo/docker-legacy-backup-…`).
- **OmniRoute combo regression:** the default `default-opencode-go-ds-flash` combo currently returns
`400 missing x-opencode-session` (OpenCode-Go leg) — affects host pi too, not just the container.
Working legs verified: `openrouter/qwen/qwen3.7-flash`, `openrouter/openai/gpt-5.6-sol`,
`aug/claude-haiku-4.5`. Fix the combo/credentials on OmniRoute to restore the default path.
- Paseo is pre-1.0 (0.7.2) — pin the version; expect breaking changes between releases.
- CLI runtime install on `.27` carries 3 low/medium CVEs in transitive deps (`qs`, `uuid`,
`@ai-sdk/provider-utils`) — vet/npq scanned clean, author verified.
## Known notes
- **Model default:** the pi model catalog was reordered so `default-opencode-go-ds-flash` is first
(bare `paseo run` picks it instead of a flaky free leg). If the combo's OpenCode-Go leg is
sub-rate-limited, the combo still falls back OpenRouter → DeepSeek.
- **Skills:** the host pi (`~/.agents`) carries the full skill set (nixos-workflow, system-architect,
project-ops, obsidian-cli, etc.). Per-project `.pi/settings.json selects which are loaded.
- **Security model:** native = full user permissions. Do not expose the daemon publicly; keep
LAN/Tailscale + password (no public Caddy vhost; use Paseo relay only if internet access is ever
required).
## Why it matters
- **Proof the stack composes:** Nix pi binary stays untouched; the lab's OmniRoute routing powers a
third-party orchestrator; one always-on Docker host (.13) gives fleet control from any device.
- **A pattern for multi-agent work:** workspaces + `pi --mode rpc` + OmniRoute is a base for
coordinating several pi agents on real projects — exactly the coordination layer the lab needs.
- **Full-control autonomy: the daemon is `sam`, so agents manage real files (photos, vault,
projects) with no mount plumbing.
- One always-on control plane for pi across all devices — the "phone control plane" use case.
- n8n/Prefect email→agent workflows, scheduled runs, and browser tasks (pi-browser-harness) can be
layered on top.
---