Skip to main content

Commanding assets

An asset says what it can be told. Send one of those verbs, and every command, accepted or refused, leaves a task you can read back.

Uses $DOME, $KEY and $WS from Authentication. A vehicle needs a link to accept anything. The quickest one is a simulated drone: start a run as in Running a simulation, then come back here. Every field is on Command model.

1. Pick the vehicle​

# Its asset id, URL-encoded: a scenario's drone id contains slashes (sim/<run>/BLUE-01)
BLUE=$(curl -s "$DOME/assets?kind=vehicle" \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" \
| jq -r '.data[] | select(.name | startswith("BLUE-01")) | .id | @uri')

# A hostile to aim at, for the verbs that take a target
H1=$(curl -s $DOME/tracks \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" \
| jq -r '[.data[] | select(.classification.affiliation == "suspect")][0].track_id')
# → H1=T-00481

2. Read what it can be told​

curl -s $DOME/assets/$BLUE \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" \
| jq -c '.data.capabilities[] | {verb, params, class, available, reason}'
# verb what to send
# params which params kind it takes (see step 3)
# class lifecycle and navigation act on the asset; effect acts on something else
# available whether it can be sent right now, and if not, reason says why
# → {"verb":"takeoff","params":"altitude","class":"lifecycle","available":false,"reason":"already airborne"}
# {"verb":"land","params":"none","class":"lifecycle","available":true,"reason":null}
# {"verb":"hold","params":"none","class":"lifecycle","available":true,"reason":null}
# {"verb":"move_to","params":"point","class":"navigation","available":true,"reason":null}
# {"verb":"orbit","params":"area","class":"navigation","available":true,"reason":null}
# {"verb":"surveil","params":"area","class":"effect","available":true,"reason":null}
# {"verb":"follow","params":"target","class":"effect","available":true,"reason":null}
# {"verb":"intercept","params":"target","class":"effect","available":true,"reason":null} …

Send only what is listed and available. Anything else is refused with 409.

3. Send verbs​

# Fly to a point. params kind point: lat, lon, optional alt_m
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d '{ "verb": "move_to",
"params": { "kind": "point", "lat": 1.3700, "lon": 103.9950, "alt_m": 100 } }' \
| jq -c '.data | {id, level, gate, status}'
# → {"id":"tsk_46b8…","level":"manoeuvre","gate":{"gate":"runs","stop_within":0},"status":"issued"}

# Circle a place. params kind area: lat, lon, radius_m, optional alt_m
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d '{ "verb": "orbit",
"params": { "kind": "area", "lat": 1.3644, "lon": 103.9915, "radius_m": 300, "alt_m": 90 } }' \
| jq -c '.data | {level, gate, status}'
# → {"level":"manoeuvre","gate":{"gate":"runs","stop_within":0},"status":"issued"}
# the orbit supersedes the move_to: one vehicle, one current order

# Watch an area with its sensors. An effect: it acts on a place, not on the vehicle
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d '{ "verb": "surveil",
"params": { "kind": "area", "lat": 1.3700, "lon": 103.9950, "radius_m": 400 } }' \
| jq -c '.data | {level, gate, status}'
# → {"level":"watch","gate":{"gate":"runs","stop_within":0},"status":"issued"}

# Shadow a hostile. params kind target: the track_id
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d "{ \"verb\": \"follow\", \"params\": { \"kind\": \"target\", \"track_id\": \"$H1\" } }" \
| jq -c '.data | {level, gate, status}'
# → {"level":"shadow","gate":{"gate":"asks"},"status":"issued"}
# shadow is above the line (watch, by default), so the gate answered asks

# Intercept it. The top rung
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d "{ \"verb\": \"intercept\", \"params\": { \"kind\": \"target\", \"track_id\": \"$H1\" } }" \
| jq -c '.data | {level, gate, status}'
# → {"level":"destroy","gate":{"gate":"asks"},"status":"issued"}

# Stop where it is. params kind none
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d '{ "verb": "hold", "params": { "kind": "none" } }' \
| jq -c '.data | {level, gate, status}'
# → {"level":"manoeuvre","gate":{"gate":"runs","stop_within":0},"status":"issued"}

# Fly a route. params kind route: points, and what to do at the end
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d '{ "verb": "follow_route",
"params": { "kind": "route",
"points": [ { "lat": 1.3660, "lon": 103.9900, "alt_m": 80 },
{ "lat": 1.3680, "lon": 103.9950, "alt_m": 80, "hold_s": 10 } ],
"on_complete": "hold" } }'
# → {"ok":false,"error":"the simulator does not fly routes yet","code":"conflict"} 409 on a simulated drone
# refused, and still written down: it is in the task record below

A route point can also carry speed_mps and action (pass, loiter, land). All verbs and params kinds: Command model.

4. Read the task record​

# Everything this vehicle was told, newest first. Refusals included
curl -s "$DOME/tasks?actor=$BLUE&limit=10" \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" \
| jq -c '.data[] | {id, verb, level, gate: .gate.gate, status, origin: .origin.source, reason}'
# → {"id":"tsk_0e9a…","verb":"follow_route","level":"manoeuvre","gate":"runs","status":"refused","origin":"operator",
# "reason":"the simulator does not fly routes yet"}
# {"id":"tsk_5d80…","verb":"hold","level":"manoeuvre","gate":"runs","status":"issued","origin":"operator","reason":null}
# {"id":"tsk_262f…","verb":"intercept","level":"destroy","gate":"asks","status":"issued","origin":"operator","reason":null}
# {"id":"tsk_492e…","verb":"follow","level":"shadow","gate":"asks","status":"superseded","origin":"operator",
# "reason":"superseded by tsk_262f0be2… (intercept)"}
# status proposed → issued → executing → complete, or refused / superseded / reverted
# origin operator (a command), plan (an approved plan), autonomy (released by the line)
# reason why it was refused or superseded, verbatim

# Only the refusals
curl -s "$DOME/tasks?actor=$BLUE&status=refused" \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" | jq -c '.data[] | {verb, reason}'
# → {"verb":"follow_route","reason":"the simulator does not fly routes yet"}

Filters: actor, status, origin, plan, object, since, limit. See Plans & tasks.

5. Move the autonomy line​

# Where the line is. A verb at or below the line runs; above it, asks
curl -s $DOME/decision-config \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" \
| jq -c '.data | {autonomy, envelope}'
# → {"autonomy":{"kind":"linear","line":"watch"},"envelope":"military"}
# the rungs, bottom to top: observe, manoeuvre, watch, shadow, deny, destroy
# envelope is the deployment's ceiling. It is not settable here

# Raise it to destroy. Takes effect on the next tick
curl -s -X PUT $DOME/decision-config/autonomy \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d '{ "kind": "linear", "line": "destroy" }' \
| jq -c '.data | {version, autonomy}'
# → {"version":3,"autonomy":{"kind":"linear","line":"destroy"}} version counts every change

# The same follow now runs
curl -s -X POST $DOME/assets/$BLUE/command \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d "{ \"verb\": \"follow\", \"params\": { \"kind\": \"target\", \"track_id\": \"$H1\" } }" \
| jq -c '.data | {level, gate}'
# → {"level":"shadow","gate":{"gate":"runs","stop_within":0}}

# Put it back
curl -s -X PUT $DOME/decision-config/autonomy \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS" -H "Content-Type: application/json" \
-d '{ "kind": "linear", "line": "watch" }' \
| jq -c '.data | {version, autonomy}'

A task records the gate's answer when it was written, so moving the line never rewrites history.

6. Take back a release​

# An act the line releases on its own, with a stop window, is a task in proposed with a clock.
# Mark it seen: a window nobody saw does not fire
curl -s -X POST $DOME/tasks/<task-id>/seen \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS"

# Take it back before the window closes. The task settles as reverted
curl -s -X POST $DOME/tasks/<task-id>/stop \
-H "X-Api-Key: $KEY" -H "X-Workspace-Id: $WS"
# 409 once the window has closed: it already fired, or someone else stopped it

The line, the window and plans: Plans & tasks. Doctrine settings: Configuration.