A lightweight Linux operations agent built on the pi coding agent SDK. It reuses pi's agent loop, tools, sessions, skills, TUI and providers, and adds a security-first ops layer: a three-tier safety policy, a tamper-evident audit chain, ops tools/skills, and a pluggable monitoring & alerting system.
- Lightweight: single Bun process + embedded SQLite, no Redis/Mongo/Milvus. Runs on a 1c1g server.
- Safety first: destructive ops are blocked by default; writes require confirmation; everything is audited.
- Interactive: pi-style TUI dialog; monitoring is defined conversationally, no manual YAML.
- Pluggable: custom collectors/notifiers as TypeScript files, hot-reloaded.
Detailed design: design.md · monitor_design.md · coding_desc.md
OpAgent is purpose-built for lightweight Linux operations: a single Bun process with embedded SQLite that runs comfortably on a 1c1g server and assists with daily ops — inspect, monitor, execute, alert, script generation, and security review. The defining constraint is safety: the agent must never delete files or database records on its own initiative, and every action must be auditable.
All model-proposed actions pass through a three-tier defense before anything executes. Interception happens inside pi's tool_call hook (before execution), so the model cannot bypass it.
| Tier | What it does | Code |
|---|---|---|
1. Pattern (PolicyGuard) |
Fast deterministic block of destructive commands (rm -rf, mkfs, find -delete, | sh, eval, base64|sh, interpreter deletion), destructive SQL (DROP/TRUNCATE/DELETE without WHERE), and protected paths (/etc/shadow, ~/.ssh, /proc, /sys, /dev, /boot) |
src/safety/policy.ts · src/safety/patterns.ts |
2. LLM semantic (LlmAuditor) |
Audits writes/scripts for variable indirection, obfuscation, exfiltration, privilege escalation. Merged strict — LLM can only escalate, never downgrade. Fail-safe: on error, escalates to human confirm. | src/audit/llm.ts |
| 3. Confirm gate + audit | Writes/destructive require interactive y/N; no UI (print mode) → fail-closed block. Every decision and result goes to the hash-chained audit log. |
src/safety/extension.ts · src/audit/store.ts |
| Default (Blocked) | With --allow-write (Confirm) |
|---|---|
![]() |
![]() |
Guarantees:
- Deleting tools (
controlled_delete,db_mutate) are not registered by default — only with--allow-destructive, and still require confirmation + reason (src/tools/destructive.ts). write/edittools are off by default — need--allow-write+ per-action confirm.- Collectors that run commands/SQL route through
PolicyGuardtoo (defense in depth) — src/monitor/builtin/collectors/file_sql_cmd.ts. - Generated scripts go through
run_script:bash -nsyntax check →dry_runpreview → policy + LLM audit → confirm → execute (src/tools/script.ts).
Every tool-call decision, LLM audit verdict, and execution result is appended to a hash-chained SQLite table (hash = sha256(prev_hash || fields)). Any after-the-fact tampering breaks the chain and is detectable.
- Append/verify/list: src/audit/store.ts
- Slash commands:
/audit list [n],/audit verify(src/audit/extension.ts) - DB:
~/.op_agent/audit.db
| Ops need | How | Code |
|---|---|---|
| Inspect (disk/mem/cpu/net/service/proc/logs) | Read-only inspect_* tools, auto-execute, no confirm |
src/tools/inspect.ts |
| Monitor (OS metrics, logs, DB, commands) | Scheduled daemon + pluggable collectors | src/monitor/ |
| Alert (Feishu/DingTalk/webhook/email…) | Pluggable notifiers with signing | src/monitor/builtin/notifiers/ |
| Execute (commands/scripts) | bash (guarded + pipefail/timeout rewrite) and run_script (lint + dry-run) |
src/safety/extension.ts · src/tools/script.ts |
| Script (generate/bash/python) | run_script with syntax check + dry-run preview |
src/tools/script.ts |
| Security review | Read-only inspection + --llm_audit semantic review of any write |
src/safety/ · src/audit/llm.ts |
| Recover | Non-destructive playbooks via skills; destructive only via confirmed controlled_delete |
skills/ · src/tools/destructive.ts |
- Single Bun process, embedded
bun:sqlite— no Redis/Mongo/Milvus. - Read-only inspections skip the LLM audit tier (cost/latency only on writes/scripts).
- Monitoring daemon runs headless, no LLM cost for routine checks.
bun build --compile→ single static binary for target servers.
- Bun (runtime) — also loads
.envautomatically - A DeepSeek API key (default model) or any pi-supported provider key
# Install globally via npm
npm install -g @xianzongwendao/op-agent
# Or via bun
bun add -g @xianzongwendao/op-agent
# Or run directly without installing
npx @xianzongwendao/op-agentgit clone https://github.com/liveljack/op_agent.git
cd op_agent
bun install# 1. Configure your API key (see "LLM configuration" below)
mkdir -p ~/.op_agent
echo 'DEEPSEEK_API_KEY=sk-xxxxxxxx' > ~/.op_agent/.env
chmod 600 ~/.op_agent/.env
# 2. Self-test (offline, no LLM call)
opagent --self-test
# 3. Start the interactive TUI
opagent- CLI flags —
--model,--allow-write,--llm_audit, ... process.env— real env vars + Bun auto-loaded<cwd>/.env~/.op_agent/.env— global config file; only fills keys not already set
~/.op_agent/.env is the recommended place for secrets (global, 0600, never committed):
DEEPSEEK_API_KEY=sk-xxxxxxxx
# optional flags
# OPAGENT_ALLOW_WRITE=1
# OPAGENT_LLM_AUDIT=1| Variable | Purpose |
|---|---|
DEEPSEEK_API_KEY |
DeepSeek API key (default model) |
OPAGENT_API_KEY |
Generic fallback key |
- Main model: pi's built-in DeepSeek provider uses a fixed endpoint — no URL config needed, only the key.
- Custom / compatible endpoint (proxy, self-hosted, OpenAI-compatible service): define a custom provider in
~/.op_agent/models.json(pi mechanism), then setOPAGENT_MODEL=<provider>/<model>. Examplemodels.json:Then:{ "my-openai": { "baseUrl": "https://your-endpoint/v1", "apiKey": "$YOUR_API_KEY", "api": "openai-completions", "models": [ { "id": "gpt-4o", "name": "GPT-4o", "reasoning": false, "input": ["text"], "contextWindow": 128000, "maxTokens": 4096, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } } ] } }OPAGENT_MODEL=my-openai/gpt-4o opagent - LLM auditor endpoint (only with
--llm_audit):OPAGENT_AUDIT_BASE_URL(defaulthttps://api.deepseek.com),OPAGENT_AUDIT_MODEL(defaultdeepseek-chat),OPAGENT_AUDIT_API_KEY(default =DEEPSEEK_API_KEY).
# Override per run
opagent --model deepseek/deepseek-v4-flash
opagent --model anthropic/claude-sonnet-4-5 # needs ANTHROPIC_API_KEY
opagent --model openai/gpt-4o # needs OPENAI_API_KEY
# Or persist in ~/.op_agent/.env
OPAGENT_MODEL=deepseek/deepseek-v4-proTo list available models for a provider, set the key and run pi --list-models (or use /model inside the TUI).
Do not put secrets in
.vscode/settings.json— it is editor config, not app config, and is often shared/committed. Use~/.op_agent/.envinstead. (VSCode terminal env injection viaterminal.integrated.env.osxworks but is not portable.)
opagent # interactive TUI (default)
opagent --allow-write # enable writes (still per-action confirm)
opagent --allow-destructive # enable destructive channel (still confirm + reason)
opagent --llm_audit # LLM semantic audit of writes/scripts
opagent -p "check disk usage" # headless one-shot
opagent --self-test # offline self-check
opagent monitor # start monitoring daemon
opagent monitor new-collector <name> # scaffold a custom collector
opagent monitor new-notifier <name> # scaffold a custom notifier| Flag | Env | Default | Description |
|---|---|---|---|
--model |
OPAGENT_MODEL |
deepseek/deepseek-v4-flash |
Model provider/model |
--allow-write |
OPAGENT_ALLOW_WRITE=1 |
off | Enable writes (confirmed) |
--allow-destructive |
OPAGENT_ALLOW_DESTRUCTIVE=1 |
off | Enable destructive ops (confirmed + reason) |
--llm_audit |
OPAGENT_LLM_AUDIT=1 |
off | LLM semantic audit |
--cwd |
— | process.cwd() |
Working directory |
-p, --print |
— | — | Headless one-shot |
| — | OPAGENT_DIR |
~/.op_agent |
Config directory |
| — | OPAGENT_WRITE_PATHS |
<cwd>/workspace |
Write allowlist (colon-separated) |
| — | OPAGENT_AUDIT_DB |
~/.op_agent/audit.db |
Audit DB path |
- Pattern layer (
PolicyGuard): fast, deterministic. Blocksrm -rf,mkfs,find -delete,| sh,eval,base64|sh, interpreter deletion (python os.remove,perl unlink, ...), destructive SQL (DROP/TRUNCATE/DELETEwithoutWHERE), and protected paths (/etc/shadow,~/.ssh,/proc,/sys,/dev,/boot). - LLM layer (
LlmAuditor,--llm_audit): semantic audit of writes/scripts — catches variable indirection, obfuscation, exfiltration, privilege escalation. Merged strict (LLM can only escalate, never downgrade). Fail-safe: on error, escalates to human confirm. - Confirm gate + audit: writes/destructive require interactive
y/N; every decision and execution result goes to the hash-chained audit log.
Safety level binds to flags: --allow-write / --allow-destructive define what the LLM auditor may permit. The /audit list and /audit verify slash commands query and verify the chain.
Deleting tools (controlled_delete, db_mutate) are not registered by default — only with --allow-destructive, and still require confirmation + reason.
Two pluggable extension systems, plus a daemon and conversational setup.
system.cpu · system.mem · system.disk · system.net · file.tail · sql · command.read
(prometheus/grafana/http/journald planned)
log · webhook · feishu · dingtalk (email/slack/telegram planned)
opagent monitor # scheduled collection → condition eval → alert → notify
# SIGHUP to hot-reload config & pluginsConfig files (auto-generated by the TUI tools, or hand-written):
~/.op_agent/monitors.yaml~/.op_agent/notifiers.yaml
Example:
# notifiers.yaml
notifiers:
- id: feishu-ops
type: feishu
params: { webhook_url: 'https://open.feishu.cn/...', secret: '${FEISHU_SECRET}' }
# monitors.yaml
monitors:
- id: disk-root
collector: system.disk
params: { mount: '/' }
when: { field: usage_percent, op: '>', value: 85 }
for: 2m
severity: warn
interval: 60s
notifiers: [feishu-ops]
cooldown: 5mIn the TUI, just describe what you want — the agent reads each plugin's paramsSchema, asks for params, runs a test collection, sends a test notification, and writes the config:
"monitor disk, alert feishu above 85%"
The agent uses monitor_* tools (monitor_list_collectors, notifier_add, monitor_add, monitor_test, ...). Config writes require --allow-write.
opagent monitor new-collector my-monitor # → ~/.op_agent/monitor/my-monitor.ts
opagent monitor new-notifier my-channel # → ~/.op_agent/notification/my-channel.tsEdit the generated template, then kill -HUP <daemon-pid> (or restart) to load. Declare paramsSchema and the conversational setup picks it up automatically. Mark sensitive fields with { secret: true } for redaction.
src/
├── index.ts # CLI entry (TUI / print / monitor subcommand)
├── config.ts # env config (3-tier priority + ~/.op_agent/.env loader)
├── prompt.ts # ops system prompt
├── safety/ # PolicyGuard + safety extension + patterns
├── audit/ # hash-chained audit store + LLM auditor
├── tools/ # inspect / run_script / destructive tools
├── skills/ # builtin skill loader
└── monitor/ # monitoring daemon + collectors/notifiers/tools
skills/ # builtin SKILL.md
test/ # bun tests
design.md · monitor_design.md · coding_desc.md
bun test # run tests
bun run typecheck # tsc --noEmit
bun run dev # hot-reload dev
