Declared actions
The outbound half of a data source — typed actions it may send back, off until an administrator enables them
A data source is inbound by default. It may also declare actions: named, versioned operations it can send back to the system it reads, such as a vehicle command. An action exists only if the manifest declares it. With no declaration there is no outbound path, whatever the connection could physically send.
Declaring an action grants nothing:
- Off until enabled. An organization administrator enables each declared action, and the
enablement binds that exact definition (
declaredActionDigest). A release, an upgrade or a migration that changes the definition leaves the new one disabled until it is enabled again. - Requested through the gateway only. A request is the platform operation
action.tasking.dispatch. The gateway refuses an action the installed source does not declare, and the kernel refuses one that is not enabled, before anything is prepared. - Per-request approval when declared. With
requiresApproval: true, each request is held for a person to approve that exact request. The approver must be an organization administrator and cannot be the requester. While it waits, the gateway answersapproval_awaiting, with the approval id and expiry. That answer is a refusal, not an outage, so do not retry it. A denied or expired request answersapproval_deniedorapproval_expired.
Declaring one
import { MAVLINK_SET_MESSAGE_INTERVAL_OPERATION } from "@scrydon/sdk-authoring/data-sources";
import type { DataSourceAction } from "@scrydon/sdk-authoring/data-sources/manifest";
const setMessageInterval = {
id: "set-message-interval",
displayName: "Set GLOBAL_POSITION_INT report interval",
capability: "invoke",
operation: { name: MAVLINK_SET_MESSAGE_INTERVAL_OPERATION, version: 1 },
target: { param: "vehicle" },
params: {
vehicle: { type: "string", required: true, maxLength: 64 },
// … every input the action takes, typed and bounded
},
preconditions: [],
requiresControlAuthority: false,
classification: "internal",
expirySeconds: 30,
retry: "once-within-grant",
correlation: "acknowledgement",
verification: { kind: "none" },
cancellable: false,
requiresApproval: true,
} satisfies DataSourceAction;Every safety-relevant choice is explicit; none has a default. expirySeconds is at most 300. It
bounds both how long an admitted request may wait to be dispatched and how long its grant lives. A
source declares at most 32 actions, and defineDataSource() refuses actions on a code-flavor
source. The complete drone example is in apps/docs/examples/mavlink-drone/manifest.ts.
Requesting one
A request to action.tasking.dispatch names the installed source, the version of it the caller
means, the action and its inputs:
| Field | What it is |
|---|---|
sourceId | The installed source key: <definitionId>, or <definitionId>--<slug> for an extra instance. |
sourceVersion | The installed source's descriptor version, sha256: followed by 64 hex digits. |
actionId, operationVersion | The declared action's id and its operation.version. |
parameters | The inputs, checked against the declared params. Any other input is refused. |
idempotencyKey | Required. A retry with the same key gets the same answer. |
The caller supplies sourceVersion; the gateway does not fill it in. It is
dataSourceDescriptorVersion(manifest) from @scrydon/sdk-authoring/data-sources, computed over
the manifest the source was installed with. It pins the request to the definition the caller
means: once the source is upgraded or redeployed with a different manifest, the old version no
longer matches, and the gateway refuses the request as source_not_installed instead of running a
definition the caller never reviewed. Compute it again from the new manifest.
No read returns the version yet, so a caller computes it from the manifest it installed.
Where an action is sent
Every action goes to the platform's simulated destination, with one exception. An action is sent over a real MAVLink link only when all of the following hold:
- Its
operation.nameisMAVLINK_SET_MESSAGE_INTERVAL_OPERATION. SET_MESSAGE_INTERVAL only changes which telemetry a vehicle streams, and it is the only command that may ever be sent over a link. - The installed source is a MAVLink link pinned to
MAVLINK_EXTENDED_DIALECT_DIGEST, the dialect that carries COMMAND_LONG and COMMAND_ACK. On a link pinned to any other dialect, the request is refused (DECLARED_ACTION_LINK_DIALECT_LACKS_COMMANDS); it is never quietly simulated. - The deployment has turned link egress on (
ACTION_LINK_EGRESS_ENABLED, off by default). Until then the request stays queued, and expires unsentexpirySecondsafter it was admitted.
The command is signed with the link's own key, fenced to the replica that holds the link at the moment of sending, and correlated to the vehicle's COMMAND_ACK.
What an action reports
| State | Meaning |
|---|---|
| Queued | Admitted and waiting for the dispatcher. If it is not dispatched within expirySeconds of admission, it ends Expired and is never sent. |
| Dispatching | Handed to the dispatcher under a short-lived grant. |
| Expired | Refused before it was sent, for example because the grant was stale or the link had moved. Nothing was sent. |
| Sent | Handed to the channel; no answer yet. For an action declared correlation: "none", nothing ever answers, so Sent is its final state. |
| Accepted / In progress | The destination acknowledged it. It is never sent again after an acknowledgement. If no final answer follows within five minutes of its last report, it ends Outcome unknown. |
| Completed / Rejected | The destination's final answer. |
| Outcome unknown | It may have been sent, but no answer arrived after one retry, no final answer followed an acknowledgement within five minutes, or the dispatcher stopped before reporting. |
Outcome unknown holds the target. While an action on a target is Outcome unknown, no later
action on that target at the same destination is dispatched. The same target name on another link
is a different vehicle and is not held. An organization administrator ends the hold with
action.outbound.reconcile, recording what was observed (succeeded or failed). Recording "still
unknown" leaves the action, and the hold, as they were.