Skip to content

Architecture

ovx-labs edited this page Sep 5, 2026 · 2 revisions

Architecture

High-Level Flow

+---------------------+     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     |
+---------------------+

Components

1. Intent File (/etc/agentic-route/intent.json)

User-editable declaration of routing intent. See Intent-File.

2. Reconciliation Engine (/usr/local/lib/agentic-route/core.sh)

Pure Bash library, zero dependencies. Functions:

  • ar_rule_get — normalize live ip 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

3. Reconcile Binary (/usr/local/bin/agentic-route-reconcile)

Idempotent one-shot:

  1. Discovers live kernel state
  2. Builds effective spec from intent + discovered
  3. Calls ar_apply_once
  4. Updates /run/agentic-route/state.json

4. Daemon (/usr/local/bin/agentic-route-daemon)

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

5. State File (/run/agentic-route/state.json)

Daemon-written, read-only for humans:

{
  "discovered_rules": [...],
  "discovered_routes": [...],
  "last_reconcile": "2026-09-04T14:29:22Z",
  "drift_corrected": 0
}

Kubernetes Controller Pattern

Observed (kernel) + Intent (/etc/.../intent.json)
  -> Compute Delta
  -> Apply (surgical ip rule/route)
  -> Emit Status (/run/.../state.json)

Hardened Against 5 Bash Daemon Bugs

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

Adding New Routing Rules/VPNs (Trivial)

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.

Via Intent File (Instant)

# Edit the intent file
vim /etc/agentic-route/intent.json

Add 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.

Supported Rule Types

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

Adding a New VPN (3 Steps)

  1. Start VPN (WireGuard, OpenVPN, Tailscale, etc.)
  2. Find its table/priorityip rule show | grep <vpn-iface>
  3. 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"}
  ]
}

What Happens Automatically

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

Integration with External Tools

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.

Clone this wiki locally