-
-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
ovx-labs edited this page Sep 5, 2026
·
2 revisions
+---------------------+ inotifywait +------------------+
| /etc/agentic-route/|<---------------------| intent.json |
| (directory watch) | (survives vim) | (user intent) |
+----------+----------+ +------------------+
| ^
| events | edit
v |
+---------------------+ ip monitor |
| FIFO multiplexer |<--------------------------+
| (exec 3<> "$FIFO") | kernel Netlink
+----------+----------+
| 200ms debounce
v
+---------------------+
| reconcile binary |
| (idempotent) |
+----------+----------+
| surgical ip rule/route
v
+---------------------+
| Kernel routing |
| tables + rules |
+---------------------+
User-editable declaration of routing intent. See Intent-File.
Pure Bash library, zero dependencies. Functions:
-
ar_rule_get— normalize liveip rule show -
ar_spec_rule— extract rule from spec by priority -
ar_reconcile_rules— add missing, delete forbidden -
ar_reconcile_routes— replace missing pinned routes -
ar_apply_once— single pass, returns drift count
Idempotent one-shot:
- Discovers live kernel state
- Builds effective spec from intent + discovered
- Calls
ar_apply_once - Updates
/run/agentic-route/state.json
Event-driven FIFO multiplexer:
-
Stream 1:
inotifywait -m -q -e close_write,moved_to --format '%f' /etc/agentic-route/ -
Stream 2:
ip monitor rule route link -
FIFO:
exec 3<> "$FIFO"holds pipe open permanently -
Loop:
read -u 3+read -t 0.2 -u 3(200ms debounce) -
Action: calls
agentic-route-reconcile
Daemon-written, read-only for humans:
{
"discovered_rules": [...],
"discovered_routes": [...],
"last_reconcile": "2026-09-04T14:29:22Z",
"drift_corrected": 0
}Observed (kernel) + Intent (/etc/.../intent.json)
-> Compute Delta
-> Apply (surgical ip rule/route)
-> Emit Status (/run/.../state.json)
| Bug | Fix |
|---|---|
| Subshell scope isolation | Single consumer loop in main process |
| Netlink echo loop | Idempotent reconcile + debounce |
| Burst storm (50 events/100ms) |
read -t 0.2 drains burst |
inotify inode trap (vim rename) |
Directory watch, not file watch |
| FIFO EOF death (writer restart) |
exec 3<> "$FIFO" holds pipe open |
The reconciler is designed for drop-in extensibility — adding a new VPN, pinned route, or forbidden rule takes one JSON edit and zero code changes.
# Edit the intent file
vim /etc/agentic-route/intent.jsonAdd any rule type:
{
"forbidden_rules": [
{"priority": 31580, "comment": "ProtonVPN catch-all"},
{"priority": 31581, "comment": "ProtonVPN split-tunnel re-add"}
],
"pinned_routes": [
{"priority": 480, "table": 52, "dest": "100.64.0.0/10", "comment": "Tailscale"},
{"priority": 32765, "table": 205, "dest": "0.0.0.0/0", "comment": "NordVPN egress"},
{"priority": 100, "table": 100, "dest": "10.2.0.0/24", "via": "10.2.0.1", "comment": "ProtonVPN DNS"}
]
}Daemon auto-applies within 200ms — no restart needed.
| Type | Purpose | Example |
|---|---|---|
forbidden_rules |
Delete rules matching priority (anti-clobber) | ProtonVPN re-add, Tailscale hijack |
pinned_routes |
Ensure route exists in table (restore if deleted) | VPN egress, Tailscale, DNS |
| Custom tables | Any table ID 1-252 |
table: 100 for custom VPN |
- Start VPN (WireGuard, OpenVPN, Tailscale, etc.)
-
Find its table/priority —
ip rule show | grep <vpn-iface> - Add to intent.json — one pinned_route + optional forbidden_rules
{
"pinned_routes": [
{"priority": 500, "table": 500, "dest": "0.0.0.0/0", "via": "10.8.0.1", "comment": "WireGuard egress"}
],
"forbidden_rules": [
{"priority": 501, "comment": "WireGuard auto-readd"}
]
}| Step | Action |
|---|---|
| 1 | Daemon detects intent.json change (inotify) |
| 2 | 200ms debounce coalesces rapid edits |
| 3 | Reconcile runs: discovers current kernel state |
| 4 | Computes delta: missing pinned routes, forbidden rules present |
| 5 | Applies surgical ip rule add / ip route replace / ip rule del
|
| 6 | Updates /run/agentic-route/state.json
|
| 7 | Exposes via agentic-route status / REST API / MCP |
| Tool | How to Add |
|---|---|
| Custom script | Call agentic-route-reconcile after VPN up |
| systemd unit | ExecStartPost=/usr/local/bin/agentic-route-reconcile |
| NetworkManager dispatcher | Script in /etc/NetworkManager/dispatcher.d/
|
| Ansible/Terraform | Template intent.json, daemon picks up change |
No daemon restart, no config templates, no code changes. The engine reconciles whatever is declared.