Configuration
Everything the Settings screens write, they write through these routes. Zones and regions shape the ground; rules and doctrine decide what the system may do on it.
Zones
A zone is a named, categorised, shaped area. Its category is what the system acts
on: boundary is the geofence, keep_out is a solver hard constraint, jam and
gps_denied degrade comms in simulation, free_fire is doctrine the rules read,
isr_priority is a coverage objective, no_go and corridor steer navigation.
curl -X POST https://<your-backend-host>/api/zones \
-H "Content-Type: application/json" \
-d '{
"name": "tank farm keep-out",
"category": "keep_out",
"shape": { "shape": "circle", "center": [1.3521, 103.8198], "radius_m": 350 },
"band": { "ceiling_m": 120 }
}'
| Field | Required | Type | Meaning |
|---|---|---|---|
name | yes | string | Shown on the map and in every rule firing that cites the zone. |
category | yes | string | One of the categories above. |
shape | yes | object | Tagged by shape: circle (center as [lat, lon], radius_m) or polygon (points, at least 3). |
band | no | object | Vertical extent in metres above ground: floor_m, ceiling_m. Absent ends are open, so {} is every altitude. |
region_id | no | string | Scope to one region, or global when absent. |
params | no | object | Per-category knobs: priority for ISR weight, strength for jam intensity. |
GET /api/zones lists, PUT /api/zones/{id} replaces, DELETE /api/zones/{id}
removes. Writes take effect in the running rules on the next tick; a keep-out drawn
here is a keep-out the solver honours immediately. A 400 names the exact
validation rule: empty name, non-positive radius, a polygon under three points, or
an inverted altitude band.
Regions
A region is a defended place: where the map centres and what a scenario runs over.
GET/POST /api/regions, PUT/DELETE /api/regions/{id}, and
PUT /api/workspace/default-region selects the one the console opens on.
GET /api/meta returns the deployment's own description of itself: its regions and
defaults in one response.
Sensors
There is no sensor surface. A sensor is an asset: placed with
POST /api/assets, listed with GET /api/assets?kind=sensor, and pointed with the
same command call as everything else. See
Assets & commands.
curl -X POST https://<your-backend-host>/api/assets/ast_c81b…/command \
-H "Content-Type: application/json" \
-d '{ "verb": "cue_sensor", "params": { "kind": "target", "track_id": "T-00492" } }'
curl -X POST https://<your-backend-host>/api/assets/ast_c81b…/command \
-H "Content-Type: application/json" \
-d '{ "verb": "cue_release", "params": { "kind": "none" } }'
GET /api/catalog/sensor-profiles lists the placeable profiles. A cue is a request:
the sensor reports where it is actually looking, and the picture renders that report,
not the request. Releasing is a verb rather than an absent parameter, because handing
a latched mount back to arbitration is an act, and an act with no record of it
cannot be arbitrated when two claims land on one camera.
Doctrine, rules and settings
The decision loop runs the stored rule set, gated by the autonomy line and tuned by thresholds. Each has a small CRUD surface, and the whole of it exports as one document:
| Routes | What they hold |
|---|---|
GET/PUT/POST/PATCH/DELETE /api/rules, POST /api/rules/validate, GET /api/rules/library | The rule rows the loop runs, a validator for one row, and the shipped rule packs. |
GET/PUT /api/thresholds | The settings registry: every tunable key, its value, and where the value came from. |
GET /api/decision-config, PUT /api/decision-config/autonomy | The engagement, identification, geography and alerting policies, and the one dial that moves the autonomy line. |
GET/PUT /api/influence | Operator influence over the planner: pinned pairs and weights. |
GET/PUT /api/planning-profiles | The profiles the planner authors one plan per pass for. |
GET /api/strategies | The compiled-in behaviour and solver strategy catalogues. |
GET /api/doctrine | Everything above, aggregated in one read. |
The export is a versioned YAML document, and the import round-trips it:
curl https://<your-backend-host>/api/doctrine/export > doctrine.yaml
curl -X POST https://<your-backend-host>/api/doctrine/import \
-H "Content-Type: application/yaml" --data-binary @doctrine.yaml
That makes doctrine reviewable the way code is: exported, diffed, committed, and
applied to another deployment. The same pattern covers the physical deployment:
GET /api/settings/export returns every asset, link and sensor as one document,
and POST /api/settings/import applies one (with "dry_run": true to see the
report without writing).
curl https://<your-backend-host>/api/settings/export \
-H "X-Api-Key: $DOME_KEY" -H "X-Workspace-Id: $WORKSPACE" > site.json
curl -X POST https://<your-backend-host>/api/settings/import \
-H "Content-Type: application/json" \
-H "X-Api-Key: $DOME_KEY" -H "X-Workspace-Id: $WORKSPACE" \
-d "$(jq '. + {dry_run: true}' site.json)"
The document's own kind is AssetManifest, and stays that: it is a wire string,
and files already committed carry it. The routes say what they do.
Secrets
Credentials for integrations go into a write-only store: PUT /api/secrets/{name}
stores a value, GET /api/secrets lists names and hints, DELETE removes one.
There is deliberately no route that returns a value; a stored secret is referenced
by name (secret://…) in integration config and resolved server-side. 503 means
the store's encryption key is not configured on this deployment.