Pipe

Backend

Pipe v0.50.5

The agent backend. It runs on the server (Docker, natively with systemd or in Kubernetes – deploy.md) and exposes a local Unix socket and TCP port 127.0.0.1:7379 for clients.

Requirements

Modules

ModuleRole
server.pyUnix socket + TCP, JSON lines frames, client commands, event subscription, starting the watcher
core/agent.pyLLM loop with tool calling (AGENT_MAX_ITERATIONS), confirmations, redacting and truncating results, history
core/handlers/One module per tool area; handle_<name> = the <name> tool
core/security.pyCommand classifier (fail-closed, token-based, quote-aware), workspace
core/runtime.pyRun mode (docker / native / kubernetes) and host path mapping
core/hostinfo.pyHost state from /proc: RAM, CPU, disks, sockets, without running commands
core/infra.pyInfrastructure discovery (Docker, nginx, Caddy, Traefik, systemd, git, Kubernetes) and the Mermaid map
core/diagram.pyMermaid → PNG + ASCII (mermaidx, offline)
core/targets.pyRemote targets: registry, validation, building ssh / docker exec / kubectl commands
core/workers.pyWorkers: sub-agents on the agent loop, reads only, in parallel
core/routines.pyRoutines: cron schedule, registry
core/watch.pyWatcher: checks, alerts, notifications (pub/sub), running routines and the loops below
core/metrics.pyMeasurement history (load, RAM, disks) and Mermaid xychart-beta charts
core/snapshots.pyHost state snapshots and diffs, "what changed"
core/checks.pyChecks without configuration: certificates, sites, DNS, backup freshness
core/digest.pyMorning report (no LLM)
core/usage.pyLLM token and cost counter, daily limits
core/posture.pyHost security audit (score, fixes), security change sentinel, SSH log
core/vulns.pyVulnerabilities without an LLM (OSV.dev, CISA KEV): host packages, container images, remote servers, applications; reports and patch steps
core/lockfiles.pyApplication dependencies from lockfiles (npm, yarn, pnpm, PyPI, uv, Go, crates.io, Packagist, RubyGems)
core/patching.py/patch: patching in the background without a question for every command, parts through the safety fuse, a fix layer inside the image, cleanup, result
core/fleet.pyRemote servers in the scan and patches: the package database over ssh, commands as root or through sudo -n
core/welcome.pyWelcome after installation: map, security score, what Pipe watches
core/feed.pyThe inbox: messages Pipe sent on its own, with actions; the notification decision (level, quiet hours, limit, muted kinds)
core/insights.pySuggestions without an LLM: disk full forecast, space to reclaim, restarts, a pending reboot, growing RAM
core/overview.pyServer status without an LLM (dashboard, /status): headline, gauges, the last day's trend
core/cleanup.pyFree up space: untagged images, build cache, downloaded packages (/cleanup, the suggestion button)
core/projects.pyThe workshop: cloning, recognising a project, the run plan, the sandbox (docker run without privileges)
core/toolsets.pyTools on demand: tool groups enabled by keywords or load_tools
skills_builtin/, skills_builtin_en/Built-in skills (Polish and English), installed into data/skills at startup according to PIPE_LANG
core/i18n.pyPipe's language (PIPE_LANG=en|pl): tr("polski", "english") next to the source text, prompt() picks prompts_en.py / tools_en.py
core/selfupdate.pyUpdating Pipe itself: a helper container (git pull, the release image from GHCR or docker-compose up -d --build), report after the restart
core/reminders.pyReminders: one-off messages and timed tasks (reminders.json), fired by the watcher
core/incidents.pyIncident memory: alert → findings from "Investigate" → what helped (journal)
core/webhooks.pyHTTP server for alerts from outside (Alertmanager, Grafana, Uptime Kuma, GitHub)
core/voice.pyVoice message transcription (an OpenAI-compatible Whisper endpoint or a Gemini model with audio input)
core/attachments.pyMessage attachments: limits, image / text / binary file by content, DATA_DIR/uploads/
tokens.pyClient tokens with admin/viewer roles (python3 -m backend.tokens)
core/mcp/MCP: server.py (Pipe as a server, both protocol eras, Streamable HTTP)
core/hostexec.pyDocker mode: a command on the host through a helper container (--privileged --pid=host, nsenter -t 1), after YES
core/approvals.pyAdmin approvals for operations of external agents
core/auth.pyToken authentication (JSON lines and MCP HTTP)
core/safety.pySafety fuse: the change plan (backups, check before, verification after) and guarded execution
core/journal.pyChange journal: file backups, git and crontab state, inverse commands, /undo
core/memory.pySERVER.md, DIRECTORY, skills, VIBE, secret detection and redaction
core/vibe.pyLearning the conversation style in the background
core/events.pyStream events: text, Attachment, Progress
core/executor.pyexecute (timeout, killing the process group), read_file, write_file
core/audit.pyAppend-only audit log
core/tools.pyTool schemas (OpenAI function calling)
config/settings.py (.env variables), providers.py (LLM presets), prompts.py
configure.pyProvider wizard (--check, --models, --providers, --from-env)

Configuration

Set up the LLM provider

From the repository root run the wizard (it creates backend/.env from .env.example by itself):

python3 -m backend.configure

The wizard:

  1. shows the list of providers and asks for the API key (with a link to where to get it),
  2. fetches the current list of models straight from the provider's API, so new models show up right away without updating Pipe,
  3. checks live whether the chosen model supports tool calling (without it the agent can't run any command),
  4. writes backend/.env (creating it from .env.example if it doesn't exist; permissions 600).

The wizard uses only Python's standard library, so there is nothing to install.

Useful modes:

python3 -m backend.configure --check      # test the current configuration (model list + tool calling)
python3 -m backend.configure --models     # current models of the current provider
python3 -m backend.configure --providers  # all available providers

Built-in providers

All through an API compatible with OpenAI Chat Completions. Addresses verified in September 2026.

LLM_PROVIDERProviderDefault modelNotes
geminiGoogle Geminigemini-3.8-flashDefault. Free tier for Flash models, but see below (privacy)
openaiOpenAIgpt-5.6-terraGPT-6 needs the Responses API for tool calling, use GPT-5.6
anthropicAnthropic Claudeclaude-sonnet-5Through the OpenAI SDK compatibility layer
openrouterOpenRoutergoogle/gemini-3.8-flashOne key, hundreds of models (DeepSeek, Qwen, GLM, Kimi...)
groqGroqopenai/gpt-oss-120bVery fast inference
deepseekDeepSeekdeepseek-flash
mistralMistral AImistral-large-latestEU provider
xaixAI Grokgrok-4.6
zaiZ.ai (GLM)glm-5.3
kimiMoonshot Kimikimi-k3
togetherTogether AImeta-llama/Llama-3.3-70B-Instruct-Turbo
cerebrasCerebrasgpt-oss-120b
fireworksFireworks AI-- (pick from the list)
ollamaOllama (local)-- (pick from the list)No key, see below

The default model is only a starting point. At startup the backend checks whether the configured model is still on the provider's list and prints a warning with suggestions if it was retired.

Privacy: Pipe sends the provider logs, configs and command output from the server (secrets are redacted). On the free tier of the Gemini API Google may use prompt content to improve its services and humans may read it. The wizard, pipe web and the CLI warn about this when you pick Gemini. On a production server enable billing in Google AI Studio or choose another provider, for example a local Ollama.

No model (LLM_PROVIDER=none)

Pipe can run without any model. Nothing leaves the server and nothing costs money, and these keep working: watching and alerts, snapshots and "what changed", the morning digest, certificate and site checks, the security audit, the journal and /undo, diagrams, webhooks (the alert arrives, nobody investigates it) and the MCP gateway for other agents (without ask_pipe). Chat, routines and alert investigations answer that there is no model. When you add a provider later in pipe web or /providers, chat turns on without a restart.

In the wizard it is the last item on the list; without questions: LLM_PROVIDER=none bash scripts/install-server.sh -y.

Configuring .env by hand

Instead of the wizard you can edit backend/.env by hand:

LLM_PROVIDER=openai
LLM_API_KEY=sk-...
LLM_MODEL=            # empty = the provider's default model

Instead of LLM_API_KEY you can set a provider-specific variable, for example OPENAI_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY; LLM_API_KEY takes precedence.

The provider from .env is the base provider, the one the backend starts with. More can be added without a restart in pipe web (the provider screen, /provider): the key goes to DATA_DIR/llm_keys.json (0600), and the switcher in the conversation changes the provider of the whole agent. Preset variables set next to LLM_API_KEY (for example OPENAI_API_KEY, GROQ_API_KEY) also count as ready providers in the switcher. WORKER_MODEL, LLM_REASONING_EFFORT and the LLM_PRICE_* prices apply only to the base provider.

Optionally:

LLM_BASE_URL=https://proxy.example.com/v1   # overrides the preset's address (e.g. a company proxy)
LLM_REASONING_EFFORT=low                     # for models that support this parameter
LLM_TIMEOUT=120                              # LLM response timeout in seconds

Old .env files (only LLM_BASE_URL + LLM_API_KEY + LLM_MODEL, without LLM_PROVIDER) keep working; the provider is recognised by its address.

Your own provider

You add any OpenAI-compatible endpoint (vLLM, LM Studio, LiteLLM, a company proxy...) with the wizard: pick Other and give the address. The provider is saved in backend/data/providers.json and from then on it is available like a built-in one (LLM_PROVIDER=<id>).

You can also edit this file by hand:

{
  "providers": [
    {
      "id": "my-vllm",
      "name": "My vLLM",
      "base_url": "http://192.168.1.10:8000/v1",
      "default_model": "qwen3",
      "requires_key": false
    }
  ]
}

An entry with the id of a built-in provider overrides it, which is how you fix an outdated address without waiting for a new Pipe version. The backend/data/ directory is mounted in the container, so changes don't need an image rebuild; docker-compose restart vps-agent is enough.

Ollama (local models)

The container sees the host at host.docker.internal (set in docker-compose.yml). Ollama listens only on 127.0.0.1 by default, so you have to start it with OLLAMA_HOST=0.0.0.0 for the container to see it. The model must support tool calling; the wizard checks that.

Protecting with a token

# Protection (optional, but strongly recommended)
# When set, every request, including confirming an operation, must contain
# this token. It protects the socket and the TCP interface from other processes on the server.
# Give the same token to the clients: to the CLI with --token (or the AGENT_TOKEN variable),
# to the Telegram bot with AGENT_TOKEN in clients/telegram/.env.
# Empty = no token required.
AGENT_TOKEN=your-secret-token

Agent memory: SERVER.md and skills

The agent has several kinds of persistent memory: SERVER.md, skills (below), and also DIRECTORY (a map of repositories and directories) and VIBE (conversation style), described in features.md. Everything lives on the host in backend/data/ (in the container /app/data, the DATA_DIR variable), survives a restart and an image rebuild, and doesn't go into git. targets.json, routines.json, reminders.json, watch_state.json and the audit log are there too.

SERVER.md

The agent's notes about the server, like AGENTS.md but for a server. The agent updates them by itself (the server_md tool) when it learns a lasting fact: services, containers, domains, ports, important paths, your decisions. The whole file is added to the system prompt of every conversation (up to 12,000 characters; the file itself can have up to 20,000).

To start you can ask: "explore the server and create SERVER.md". You can also edit the file by hand: backend/data/SERVER.md.

Skills

Saved reusable procedures, in the Agent Skills format:

backend/data/skills/<name>/SKILL.md
---
name: renew-certificate
description: Renewing the TLS certificate for nginx
---

1. `certbot renew`
2. `nginx -t && systemctl reload nginx`
3. Check the certificate's expiry date.

The system prompt has only the list of skills (name and description). The agent loads the full content with the skill_manage tool when a task matches the description, so many skills don't clog the context. The agent creates a skill after carrying out a multi-step procedure that will come up again, or when you ask.

Every skill also gets its own command on Telegram and in the CLI: dashes turn into _, and the name is shortened to 32 characters (Telegram's limit), for example renew-certificate → /renew_certificate. A skill whose name is taken by a built-in command (status, server, skills, ...) gets no command; run it through /skills or with plain text.

Memory security

Running

docker-compose up -d

Checking

docker logs vps-agent --tail 20

You should see:

[VPS Agent] Unix socket : /tmp/vps-agent.sock
[VPS Agent] TCP         : 0.0.0.0:7379 (localhost only — use an SSH tunnel)
[VPS Agent] Provider    : Google Gemini (https://generativelanguage.googleapis.com/v1beta/openai/)
[VPS Agent] Model       : gemini-3.8-flash
[VPS Agent] Runtime     : docker (host: /hostfs, proc: /hostproc)
[VPS Agent] Diagrams    : mermaidx
[VPS Agent] Watcher     : every 120 s
[VPS Agent] Server ready. Ctrl+C to stop.

Agent tools

ToolDescriptionRequires confirmation
execute_commandShell commands; in docker mode with host: true on the host (helper container)Depends on classification; host: always
read_fileReading files (secrets redacted)No
write_fileWriting files (creates missing directories)Yes; in the workshop (WORKSPACE_DIR): no
change_directoryWorking directoryNo
git_commandGit operationsModifying ones: yes
system_statsCPU/RAM/disks/processes from the host's /procNo
network_infoHost ports, connections, ping, curl, DNSNo
cron_manageHost cron (editing only in native mode)Modifying ones: yes
diagramInfrastructure map / your own Mermaid diagram as an imageNo
target_manageRemote target registryAdding: yes
remote_execA command on a remote targetDepends on classification
delegateWorkers (reads only)No, changes come back as proposals
routine_manageScheduled routinesAdding: yes
reminderA one-off reminder or a task at a given timeTask: yes; message: no
pipe_updateUpdating Pipe itself (check / apply)apply: yes
security_auditSecurity audit with a score and fix commandsNo (fixes: yes)
security_patchVulnerability patches like /patch: packages, images, remote servers, cleanupOnce for the whole patch (YOLO: no)
web_searchWeb search without a key (DuckDuckGo → Stack Exchange → Wikipedia)No
web_fetchReading a page; with question a separate model without tools reads itNo for addresses from results and from the user, others: yes
software_infoeol (endoflife.date), vulns (OSV.dev)No
journalJournal of approved changes and undoing themUndo: yes
server_historyWhat changed, load/RAM/disk charts, certificates/sites/DNS/backupsNo
server_md, directory, skill_manage, vibeAgent memory; skill_manage install – skills and plugins from a git repositoryNo; saving a skill and install: yes
projectThe workshop: clone, inspect, exec (isolated container), run, logs, stop/start, pull, removerun and remove: yes; the rest: no
mcp_manageExternal MCP servers: list, add (isolated container or HTTP), remove, reloadadd: yes
mcp__<server>__<tool>Tools of connected MCP servers (group mcp:<server>, loaded on demand)Reads (readOnlyHint): no; the rest: yes
load_toolsEnables a group of tools whose descriptions the model has not received yet (tools on demand)No

With TOOLS_MODE=lazy (default) the model starts with the basic tools only (commands, files, git, system state, Docker, network, server history, project, load_tools). The memory, security, schedule, fleet, web, diagram and update groups turn on by themselves when a message needs them, or through load_tools.

More .env settings

VariableDefaultDescription
PIPE_RUNTIMEautodocker / native / kubernetes
PIPE_LANGenAgent language: en or pl (prompts, tool descriptions, messages, alerts, reports, built-in skills, the wizard). The Telegram bot reads the same variable from its own .env, the CLI --lang / PIPE_LANG
HOST_ROOT, HOST_PROCby modeOverriding host paths
AGENT_MAX_ITERATIONS15Loop step limit per message
SESSION_KEEP_DAYS7How many days conversations stay in DATA_DIR/sessions (0600) and come back after a backend restart; 0 = not saved
CONFIRMED_COMMAND_TIMEOUT900Timeout (s) of commands approved by the user; reads have 30 s
REDACT_SECRETS1Secret redaction in tool results
WEB_SEARCHonInternet for the agent (web_search, web_fetch, software_info); off turns it off
WORKER_MODELthe agent's modelModel for workers and VIBE learning
WORKER_MAX_ITERATIONS, WORKER_TIMEOUT, MAX_WORKERS8, 240, 6Worker limits
VIBE_EVERY6How many messages between VIBE refreshes (0 = off)
WATCH_ENABLED, WATCH_INTERVAL1, 120Watcher
WATCH_DISK_PCT, WATCH_MEM_PCT, WATCH_LOAD_FACTOR90, 92, 2Alert thresholds
METRICS_KEEP_DAYS8How long to keep measurement history (charts)
SNAPSHOT_INTERVAL, SNAPSHOT_KEEP_DAYS3600, 30Host state snapshots ("what changed")
CHECKS_INTERVAL3600How often (s) to check certificates, sites, DNS and backups
VULN_INTERVAL21600How often (s) to ask OSV.dev about vulnerabilities in all packages (changed packages right away); 0 = off
WATCH_SITES10 turns off network checks (certificates, sites, DNS)
WATCH_CERT_DAYS, WATCH_BACKUP_HOURS14, 26Thresholds: days until a certificate expires, age of the newest backup
WATCH_SSH_FAILURES60Alert at this many failed SSH logins in 10 min (0 = off)
WATCH_SSH_LOGINS1Alert on an SSH login from a new address
WATCH_IGNORE--Domains and paths skipped by the checks (comma-separated)
DIGEST_TIME07:00Time of the morning report (server time); off turns it off
LLM_PRICE_IN, LLM_PRICE_OUT--Model prices in USD per million tokens, so /cost shows the cost
WORKER_PRICE_IN, WORKER_PRICE_OUTas LLMPrices of WORKER_MODEL
AGENT_VIEWER_TOKEN--Token of the viewer role (read only); needs AGENT_TOKEN
WEBHOOK_PORT, WEBHOOK_HOST, WEBHOOK_TOKEN0, 127.0.0.1, --Server for alerts from outside (0 = off; won't start without a token)
WEBHOOK_INVESTIGATE1A worker investigates a new webhook alert (reads only), findings with a Fix it button; limit once in 6 h per alert, 10/day
MCP_PORT, MCP_HOST, MCP_ALLOWED_ORIGINS0, 127.0.0.1, --Pipe as an MCP server over HTTP (/mcp, Pipe token as Bearer)
MCP_<NAME> (any)--Secrets for the MCP servers Pipe uses – referenced in the server configuration as ${MCP_<NAME>}
STT_BASE_URL, STT_API_KEY, STT_MODEL, STT_LANGUAGEby provider, Pipe's languageVoice transcription (gemini/openai/groq automatically; STT_* takes precedence)
SAFE_AUTO_ROLLBACK1Failed verification of a config file change → the backup is restored automatically
AUTONOMY1Routine changes without asking (through the safety fuse), Pipe asks about important things (docs/security.md, Autonomy); 0 = a question for every change
DAILY_TOKEN_LIMIT, DAILY_COST_LIMIT0Daily token / USD cost limit (0 = no limit)
TOOLS_MODElazylazy – tools on demand; all – the descriptions of all tools with every request
CONTEXT_KEEP_TURNS2Tool results older than this many turns go to the model trimmed (0 = always in full)
NOTIFY_LEVELimportantWhen to notify: all, important, critical (change it while running: /notifications)
QUIET_HOURS--Quiet hours, e.g. 22:00-07:00: non-critical messages wait until the morning
INSIGHTS_PER_DAY3At most this many suggestions a day with a notification (the rest in the inbox)
INSIGHTS_INTERVAL3600How often (s) to look for suggestions (0 = off)
AUTO_INVESTIGATE, AUTO_INVESTIGATE_DAILYcritical, 4Pipe investigates the cause of an alert itself (critical, all, off) and the daily limit
WORKSPACE_DIR/root/pipe-workspaceThe workshop: the host directory for projects and the agent's scripts
WORKSPACE_AUTONOMY1In the workshop, file writes and project exec without asking (0 = approval for every operation)
PROJECT_MEMORY, PROJECT_CPUS1g, 1Limits of project containers (sandbox and services)
PROJECT_EXEC_TIMEOUT, PROJECT_BUILD_TIMEOUT600, 1800Time limit (s) of a sandbox command and of building/starting
PROJECT_MAX_MB1024The largest size of a cloned repository

Stopping

docker-compose down          # docker mode
systemctl stop pipe          # native mode

Edit this page on GitHub