Policy-driven audit and guard system for safe operations.
Watchtower intercepts system operations (SSH, kubectl, filesystem, search), evaluates them against declarative policies, executes approved actions in restricted subprocesses, and records everything in a tamper-evident ledger.
- Policy Engine: Declarative JSON policies with a safe expression language—no more hardcoded bash rules
- Tamper-Evident Ledger: SHA-256 hash chain across append-only JSON Lines files
- Structured Logging: Every action is a queryable record, not a text line
- Safe Command Wrappers: Real binaries that parse arguments, evaluate policy, and execute with restrictions
- No External Dependencies: Core runs on Python 3.8+ standard library only
- Cross-Platform: Linux, macOS, and BSD support with graceful degradation
- Python 3.8+
- Bash or Zsh (for shell integration)
Optional (for enhanced sandboxing on Linux):
python-seccompfor syscall filtering- Linux 5.13+ for Landlock LSM support
# Clone
git clone https://github.com/awdemos/opencode-watchtower.git
cd opencode-watchtower
# Install package (puts all commands on PATH)
./install.sh
source ~/.bashrc # or ~/.zshrcOr manually with pip:
pip install -e .
# On systems with PEP 668 (macOS Homebrew, Debian 12+):
pip install -e . --break-system-packages# Initialize
wt_init
# Check status
wt_status
# View recent activity
wt_tail
# Verify ledger integrity
wt_verify
# Export for analysis
wt_export ./audit-report.json| Command | Purpose |
|---|---|
wt_init |
Create ledger directory and default policy |
wt_clean |
Clear all ledger files |
wt_stats |
Show usage statistics by domain and verdict |
wt_watch |
Live dashboard (updates every 2s) |
wt_tail |
Show recent ledger entries |
wt_alert |
Alert on filesystem mutations |
wt_gtfo |
GTFOBins monitoring and alerting |
wt_export |
Export ledger as JSON |
wt_report |
Usage frequency report |
wt_recent |
Show last N entries |
wt_status |
Check initialization status |
wt_verify |
Verify ledger cryptographic integrity |
All safe commands route through the Watchtower Guard:
safe-rm,safe-cp,safe-mvsafe-mkdir,safe-touchsafe-chown,safe-chmod,safe-ln
ssh-ls,ssh-cat,ssh-ps
kubectl-exec-read,kubectl-get-yaml,kubectl-logs
rg-search,jq-query
After pip install -e ., all safe commands are available on PATH. Add Watchtower to your opencode UTCP config:
{
"imports": ["/path/to/opencode-watchtower/watchtower.json"]
}The watchtower.json exposes 16 safe tools:
- SSH:
ssh_ls,ssh_cat,ssh_ps - Kubernetes:
kubectl_exec_read,kubectl_get_yaml,kubectl_logs - Search:
rg_search,jq_query - Filesystem:
safe_rm,safe_mkdir,safe_touch,safe_mv,safe_cp,safe_chown,safe_chmod,safe_ln
All commands automatically:
- Evaluate policy before execution
- Log to the tamper-evident ledger
- Scan for GTFOBins and escalate if critical
- Run in restricted subprocesses
Policies are stored in ~/.watchtower/policy.json. Example:
{
"version": "2.0.0",
"policies": [
{
"name": "allow-tmp-read",
"priority": 100,
"match": {
"domain": "filesystem",
"operation": "read"
},
"condition": "target.path.startswith('/tmp/')",
"action": "allow",
"audit": "standard",
"risk_level": "low"
}
]
}| Action | Behavior |
|---|---|
allow |
Execute and log |
deny |
Block and log |
escalate |
Queue for approval |
shadow |
Execute with heavy logging |
A safe subset of Python expressions:
target.path.startswith('/home/') and '..' not in target.path
identity.capabilities.count('fs:read') > 0
any(target.path.startswith(p) for p in ('/etc', '/usr'))Stored in ~/.watchtower/ledger-*.jsonl:
{
"seq": 1,
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"identity": {"principal": "user:alice", "capabilities": ["fs:read"]},
"intent": {"domain": "filesystem", "operation": "read", "target": {"path": "/tmp/test.txt"}},
"decision": {"verdict": "allow", "matched_rules": ["allow-tmp-read"], "risk_score": 0.15}
},
"prev_hash": "0000...0000",
"hash": "a3f2...b8c1"
}Each entry chains to the previous via SHA-256. Tampering breaks the chain and is detected by wt_verify.
Watchtower 2.0 includes the complete GTFOBins database (458 binaries) and actively monitors for GTFOBins invocations at runtime.
Every executed command is analyzed against the full GTFOBins database:
- Binary matching — detects if a GTFOBin is invoked
- Flag analysis — identifies dangerous flag combinations (
-c,-exec,-p, etc.) - Context awareness — detects sudo/suid/capabilities contexts
- Dynamic scoring — critical binaries score 1.0, high-risk score 0.85+
GTFOBins detections automatically influence policy decisions:
- Critical GTFOBins → auto-escalate to require approval
- High-risk GTFOBins → shadow mode (execute with heavy logging)
- All GTFOBins → annotated in the tamper-evident ledger
# List all GTFOBins in the database
wt_gtfo --list
# Scan ledger for GTFOBins usage
wt_gtfo
# Real-time GTFOBins monitoring
wt_gtfo --monitorWhen a GTFOBin is detected, the ledger entry includes:
{
"annotations": {
"gtfo_alerts": [
{
"binary": "bash",
"risk": "critical",
"score": 1.0,
"flags": ["shell:-c"]
}
]
}
}The complete database is sourced from GTFOBins.github.io and parsed directly from the official repository.
The original static gtfo.json has been replaced with the full 458-binary database. Risk levels in policies (critical, high, medium, low) are dynamically computed from the GTFOBins functions (shell, command, file-read, file-write, suid, sudo, capabilities, etc.).
| Variable | Default | Purpose |
|---|---|---|
WATCHTOWER_DIR |
~/.watchtower |
Ledger and policy directory |
WT_PYTHON |
python3 |
Python interpreter path |
See ARCHITECTURE.md for the full design evolution, trade-off analysis, and forward trajectory.
MIT