Operator Control Plane
Persisted AOIs, sub-feeds, source settings, and regions — the state behind the operator UI.
The operator control plane is the durable state the Farsight UI reads on load and mutates as the operator works: areas of interest, named sub-feeds, per-source settings, and the predefined region catalog. All of it is exposed as plain JSON over HTTP.
Overview
| Surface | Purpose |
|---|---|
| AOIs | Operator-drawn shapes (bbox, polygon, circle) reused as spatial filters. |
| Sub-feeds | Named, addressable filtered views over a layer — combine AOIs, message types, and property filters into one URL. |
| Sources | Per-upstream settings: enabled, poll quota, default AOIs, credentials presence (booleans only). |
| Regions | Read-only catalog of named regions (continents, basins, NAIs) usable as ready-made AOIs. |
| Snapshot | One document with everything above — what the UI fetches on load. |
State is persisted to a JSON file (default
<catalog_dir>/config/operator.json; override with OPERATOR_CONFIG_PATH).
Routes
Snapshot
| Method | Path | Purpose |
|---|---|---|
GET | /operator/config | Whole operator state in one document (AOIs, sub-feeds, sources, overrides). |
Areas of interest
| Method | Path | Purpose |
|---|---|---|
GET | /aois | List all persisted AOIs. |
POST | /aois | Create an AOI (server assigns id if omitted). |
PUT | /aois/{id} | Replace one AOI. |
DELETE | /aois/{id} | Remove an AOI. |
GET | /regions | Predefined regions (read-only). |
An AOI body looks like:
{
"name": "Eastern Med",
"shape": { "bbox": [22, 30, 36, 38] }
}shape accepts {bbox: [...]}, {center: [lon, lat], radiusKm: n}, or a
GeoJSON Polygon/MultiPolygon.
Sub-feeds
| Method | Path | Purpose |
|---|---|---|
GET | /subfeeds | List sub-feed definitions. |
POST | /subfeeds | Create a sub-feed. |
GET | /subfeeds/{id} | Read one definition. |
PUT | /subfeeds/{id} | Replace one definition. |
DELETE | /subfeeds/{id} | Remove a sub-feed. |
GET | /subfeeds/{id}/features | Resolved GeoJSON applying the sub-feed's filters. |
A sub-feed body looks like:
{
"name": "Tankers — Hormuz",
"layer": "ais",
"aoi_ids": ["hormuz"],
"message_types": ["1", "2", "3", "5"],
"properties": { "vesselType": "tanker" },
"enabled": true
}Sources
| Method | Path | Purpose |
|---|---|---|
GET | /sources | All per-source settings (config, credential presence, quota, notes). |
PUT | /sources/{id} | Partial update — enabled, poll_max_per_window, poll_window_minutes, default AOIs, notes. |
PUT /sources/{id} is partial — only fields you send are changed. Credentials
themselves are write-only: the response carries booleans confirming presence,
never the secret.
How it fits together
- The operator draws an AOI on the map →
POST /aois. - They define a sub-feed pinned to that AOI + a layer + message-type filter
→
POST /subfeeds. - A capability URL like
/subfeeds/{id}/featuresis then a stable, shareable addressable subset of the live layer — the same one MQTT routing and per-source default-AOI logic can reference by id. GET /operator/configreturns the union for the UI to hydrate from.
Conventions
- All paths return JSON unless noted.
- IDs are strings; supply your own on create, or accept the server-assigned one.
- Mutating responses return the updated record (
201for create,200for update,204for delete).