This page is for plugin authors writing non-trivial agent halves. It walks the runtime path top to bottom: how the supervisor spawns your code, how IPC frames travel, how capabilities gate every privileged call, and how the supervisor decides when your plugin has misbehaved.Documentation Index
Fetch the complete documentation index at: https://docs.altnautica.com/llms.txt
Use this file to discover all available pages before exploring further.
Subprocess model
Every third-party agent plugin runs as its own process underados-supervisor. There is no in-process plugin tier for third
parties; isolation is mandatory.
| Variable | Purpose |
|---|---|
ADOS_PLUGIN_ID | Plugin id from the manifest. |
ADOS_PLUGIN_VERSION | Plugin version. |
ADOS_PLUGIN_DATA_DIR | /var/lib/ados/plugins/<id>/data/. Writable. |
ADOS_PLUGIN_CONFIG_PATH | Validated config JSON on disk. |
ADOS_PLUGIN_SOCKET | Unix domain socket path for IPC. |
ADOS_PLUGIN_GRANTED_CAPS | Comma-separated granted capability ids. |
ADOS_PLUGIN_DATA_DIR, with stdout and stderr piped to
the journal under the unit name.
IPC envelope
The supervisor opens the Unix socket before spawning the plugin and listens on it. The plugin’s SDK (ados-sdk for Python,
@altnautica/plugin-sdk for TypeScript on the GCS half) connects
on startup and speaks msgpack frames:
capability field on the wire.
It re-resolves the required capability from the method name on
every request, then checks the granted set. Forging the field
fails with permission_denied.
Lifecycle hooks
The SDK dispatches three async hooks against yourPlugin
subclass:
on_start runs once after the IPC handshake. on_config_change
runs every time the operator saves new config under the plugin’s
settings section. on_stop runs when the supervisor sends
SIGTERM; you have 10 seconds to drain in-flight work before
SIGKILL follows.
Capability tokens
Granted capabilities arrive inADOS_PLUGIN_GRANTED_CAPS and are
re-fetched from the host on the first IPC handshake (the env var
is the cold-start hint, the IPC value is authoritative). The SDK
keeps the set in ctx.capabilities and rejects undeclared calls
client-side before they hit the wire.
capabilities.changed event; the
SDK swaps the set atomically. In-flight requests for the now-revoked
capability return permission_denied.
cgroup limits
On Linux, every plugin runs inside a systemd scope under theados-plugins.slice. The supervisor populates the scope with
limits drawn from the manifest’s agent.resources block:
MemoryMax
the kernel OOM-kills the process and the supervisor records a
crashed lifecycle event. CPU breaches throttle rather than kill.
Tasks breaches refuse new threads / processes.
Read your live numbers with:
Supervisor restart policy
The supervisor runs each plugin underRestart=on-failure with a
back-off ladder:
| Restart # | Delay before retry |
|---|---|
| 1 | 1s |
| 2 | 5s |
| 3 | 15s |
| 4+ | circuit breaker trips |
crashed and the supervisor stops trying. The operator gets a
notification in the GCS event stream and decides whether to
disable, remove, or investigate.
A clean exit (return 0 from on_start) is treated as “the plugin
is done” and is not restarted. If your plugin is meant to run
indefinitely, do not return from on_start until on_stop is
called.
Service unit generation
Service units are not authored by hand. The agent host generates them from the manifest at install time and writes them to/etc/systemd/system/ados-plugin@<id>.service. A typical unit:
ados-plugin-runner is the SDK’s process entrypoint. It loads the
plugin’s manifest, checks the signature again, opens the IPC
socket, and dispatches into your Plugin subclass.
Watchdog
Type=notify plus WatchdogSec=30 means the SDK must ping
systemd every fifteen seconds (half the watchdog interval). The
SDK does this for you on a background task. If your event loop
stalls, the watchdog times out, the kernel signals SIGKILL,
and the supervisor records the crash.
If your plugin does long synchronous work in on_start, run it
on a worker thread (asyncio.to_thread) so the SDK keeps pinging.
Hot reload
Plugins are not hot-reloaded across version bumps. Updating from v1.0 to v1.1 stops the old subprocess, deletes the unit file, generates a new one, and starts fresh. State on disk underADOS_PLUGIN_DATA_DIR survives.
Debugging tips
journalctl -u ados-plugin@<id>.service -ffor live logs.systemctl cat ados-plugin@<id>.serviceto see the generated unit.ados plugin logs <id>is a thin wrapper overjournalctl.ados plugin info <id>prints granted caps, resource limits, and last lifecycle events.