C ABI (seyd.h)
Generated from sdks/c/include/seyd.h, which cbindgen produces from packages/seyd-ffi;
the descriptions are the Rust doc comments (ADR 0004). Lifecycle:
seyd_agent_create → seyd_channel_add (once per channel) → seyd_agent_start →
seyd_push_frame / seyd_push_message → seyd_agent_stop → seyd_agent_destroy.
Callbacks arrive on one dedicated Seyd thread and must not block; seyd_push_* never
blocks and may be called from any thread. See A robot in C for the guide.
Constants
Section titled “Constants”SEYD_ABI_VERSION
Section titled “SEYD_ABI_VERSION”Bumped only when an existing symbol or struct layout changes; additions are appended (ADR 0004 rule 3).
#define SEYD_ABI_VERSION 1Enumerations
Section titled “Enumerations”seyd_status
Section titled “seyd_status”Result of every fallible call. SEYD_OK is zero; everything else is an
error, and seyd_last_error carries a human-readable reason.
typedef enum seyd_status { SEYD_OK = 0, /** * A null pointer, a non-UTF-8 string, or an out-of-range value. */ SEYD_ERR_INVALID_ARG = 1, /** * Called in the wrong lifecycle state (e.g. `seyd_channel_add` after start). */ SEYD_ERR_STATE = 2, /** * The configuration is not usable (e.g. an unknown QoS profile). */ SEYD_ERR_CONFIG = 3, /** * Sockets, discovery or the certificate failed; the agent did not start. */ SEYD_ERR_NETWORK = 4, /** * No channel with that id. */ SEYD_ERR_NOT_FOUND = 5, /** * Reserved: the send path is saturated. Not currently returned — the * engine drops under pressure by design (whole frame or nothing) and * reports it through `frames_dropped_backlog` in [`seyd_counters`]. */ SEYD_ERR_BACKPRESSURE = 6, SEYD_ERR_INTERNAL = 7,} seyd_status;| Value | Meaning |
|---|---|
SEYD_OK |
|
SEYD_ERR_INVALID_ARG |
A null pointer, a non-UTF-8 string, or an out-of-range value. |
SEYD_ERR_STATE |
Called in the wrong lifecycle state (e.g. seyd_channel_add after start). |
SEYD_ERR_CONFIG |
The configuration is not usable (e.g. an unknown QoS profile). |
SEYD_ERR_NETWORK |
Sockets, discovery or the certificate failed; the agent did not start. |
SEYD_ERR_NOT_FOUND |
No channel with that id. |
SEYD_ERR_BACKPRESSURE |
Reserved: the send path is saturated. Not currently returned — the engine drops under pressure by design (whole frame or nothing) and reports it through frames_dropped_backlog in seyd_counters. |
SEYD_ERR_INTERNAL |
seyd_role
Section titled “seyd_role”typedef enum seyd_role { SEYD_ROLE_DRIVER = 0, SEYD_ROLE_OBSERVER = 1,} seyd_role;| Value | Meaning |
|---|---|
SEYD_ROLE_DRIVER |
|
SEYD_ROLE_OBSERVER |
seyd_signal_state
Section titled “seyd_signal_state”typedef enum seyd_signal_state { SEYD_SIGNAL_CONNECTED = 0, SEYD_SIGNAL_DISCONNECTED = 1, /** * Authentication was refused; `detail` says why. The client keeps * retrying, but this is almost always a configuration problem. */ SEYD_SIGNAL_DENIED = 2,} seyd_signal_state;| Value | Meaning |
|---|---|
SEYD_SIGNAL_CONNECTED |
|
SEYD_SIGNAL_DISCONNECTED |
|
SEYD_SIGNAL_DENIED |
Authentication was refused; detail says why. The client keeps retrying, but this is almost always a configuration problem. |
seyd_channel_kind
Section titled “seyd_channel_kind”typedef enum seyd_channel_kind { /** * Encoded pictures, FEC-protected, whole frame or nothing. */ SEYD_CHANNEL_VIDEO = 0, /** * Robot → pilot messages. */ SEYD_CHANNEL_SENSOR = 1, /** * Pilot → robot messages; delivered to `on_command`. */ SEYD_CHANNEL_COMMAND = 2,} seyd_channel_kind;| Value | Meaning |
|---|---|
SEYD_CHANNEL_VIDEO |
Encoded pictures, FEC-protected, whole frame or nothing. |
SEYD_CHANNEL_SENSOR |
Robot → pilot messages. |
SEYD_CHANNEL_COMMAND |
Pilot → robot messages; delivered to on_command. |
Structs
Section titled “Structs”seyd_config
Section titled “seyd_config”How the robot is configured. Strings are borrowed for the duration of the
seyd_agent_create call only; Seyd copies what it keeps.
typedef struct seyd_config { /** * Robot id as enrolled with the cloud. Required. */ const char *robot_id; /** * Signal server WebSocket URL. Required. */ const char *signal_url; /** * Ed25519 credential file; created on first run. NULL → `/var/lib/seyd/robot.key`. */ const char *credential_path; /** * UDP port for QUIC. 0 → 4433. */ uint16_t quic_port; bool ipv6; /** * Attempt PCP/NAT-PMP/UPnP port mapping during discovery. */ bool port_mapping; /** * Skip discovery and advertise this address only (LAN testing). NULL to discover. */ const char *host_override; /** * QoS ceiling by name. NULL → `balanced`. */ const char *qos_profile; /** * Concurrent pilot sessions. 0 → 4. */ uint32_t max_sessions;} seyd_config;| Field | Meaning |
|---|---|
robot_id |
Robot id as enrolled with the cloud. Required. |
signal_url |
Signal server WebSocket URL. Required. |
credential_path |
Ed25519 credential file; created on first run. NULL → /var/lib/seyd/robot.key. |
quic_port |
UDP port for QUIC. 0 → 4433. |
ipv6 |
|
port_mapping |
Attempt PCP/NAT-PMP/UPnP port mapping during discovery. |
host_override |
Skip discovery and advertise this address only (LAN testing). NULL to discover. |
qos_profile |
QoS ceiling by name. NULL → balanced. |
max_sessions |
Concurrent pilot sessions. 0 → 4. |
seyd_callbacks
Section titled “seyd_callbacks”Callbacks, all optional (leave a field NULL to ignore that event). They are invoked on one dedicated Seyd thread and must not block. Pointers passed to a callback are valid only for the duration of the call.
typedef struct seyd_callbacks { /** * Passed back verbatim as the first argument of every callback. */ void *user; void (*on_session_started)(void *user, const char *session_id, enum seyd_role role, const char *path_label); /** * Park actuators here: this fires whether the pilot said goodbye or vanished. */ void (*on_session_ended)(void *user, const char *session_id, const char *reason); void (*on_command)(void *user, uint8_t channel, const uint8_t *data, size_t len); /** * Seyd's transport-observable targets for the publisher, as the * `video-config` JSON of docs/protocol/seydd.md (`maxBitrateKbps`, * `latencyBudgetMs`, `maxGopMs`, `preferIntraRefresh`, `suggestedFps`, * `reason`). The only place Seyd talks down to the encoder. Keyframes are * on demand (ADR 0009): honour `on_recovery_request`, and treat `maxGopMs` * as a long ceiling rather than a cadence. */ void (*on_requested_config)(void *user, const char *json); /** * A pilot needs a recovery point: `kind` is `ltr`, `intra_refresh` or `idr`. */ void (*on_recovery_request)(void *user, uint8_t channel, const char *kind, const char *reason); /** * The relayed simulcast layer changed (ADR 0008). Seyd switches on that * layer's next keyframe; a publisher that can force one should, which turns * "next keyframe" from up to a GOP into immediately. `reason` is `up` or * `down`. */ void (*on_layer_changed)(void *user, uint8_t channel, uint8_t layer, const char *name, const char *reason); /** * The robot is unreachable while signaling is down. `detail` is NULL * except for `SEYD_SIGNAL_DENIED`. */ void (*on_signal_state)(void *user, enum seyd_signal_state state, const char *detail); /** * Candidates were gathered and announced; the `NatReport` as JSON. */ void (*on_nat_report)(void *user, const char *json);} seyd_callbacks;| Field | Meaning |
|---|---|
user |
Passed back verbatim as the first argument of every callback. |
on_session_started |
|
on_session_ended |
Park actuators here: this fires whether the pilot said goodbye or vanished. |
on_command |
|
on_requested_config |
Seyd’s transport-observable targets for the publisher, as the video-config JSON of docs/protocol/seydd.md (maxBitrateKbps, latencyBudgetMs, maxGopMs, preferIntraRefresh, suggestedFps, reason). The only place Seyd talks down to the encoder. Keyframes are on demand (ADR 0009): honour on_recovery_request, and treat maxGopMs as a long ceiling rather than a cadence. |
on_recovery_request |
A pilot needs a recovery point: kind is ltr, intra_refresh or idr. |
on_layer_changed |
The relayed simulcast layer changed (ADR 0008). Seyd switches on that layer’s next keyframe; a publisher that can force one should, which turns “next keyframe” from up to a GOP into immediately. reason is up or down. |
on_signal_state |
The robot is unreachable while signaling is down. detail is NULL except for SEYD_SIGNAL_DENIED. |
on_nat_report |
Candidates were gathered and announced; the NatReport as JSON. |
seyd_channel_config
Section titled “seyd_channel_config”One channel, added before the agent starts. Channels are numbered from 1 in the order they are added, and that numbering is what the pilot sees.
typedef struct seyd_channel_config { enum seyd_channel_kind kind; /** * Channel name shown to the pilot. Required. */ const char *name; /** * Codec string for video (e.g. `avc1.42001f`). NULL → `json`. */ const char *codec; /** * Nominal frames per second for video; 0 otherwise. */ uint32_t fps;} seyd_channel_config;| Field | Meaning |
|---|---|
kind |
|
name |
Channel name shown to the pilot. Required. |
codec |
Codec string for video (e.g. avc1.42001f). NULL → json. |
fps |
Nominal frames per second for video; 0 otherwise. |
seyd_counters_t
Section titled “seyd_counters_t”Engine counters, sampled atomically-ish (each field is atomic; the set is not a consistent snapshot).
typedef struct seyd_counters_t { uint64_t frames_in; uint64_t frames_sent; uint64_t frames_dropped_backlog; uint64_t frames_skipped_stale; uint64_t keyframes_requested; uint64_t chunks_sent; uint64_t parity_sent; uint64_t bytes_sent;} seyd_counters_t;| Field | Meaning |
|---|---|
frames_in |
|
frames_sent |
|
frames_dropped_backlog |
|
frames_skipped_stale |
|
keyframes_requested |
|
chunks_sent |
|
parity_sent |
|
bytes_sent |
Opaque handles
Section titled “Opaque handles”seyd_agent
Section titled “seyd_agent”Opaque agent handle.
typedef struct seyd_agent seyd_agent;Functions
Section titled “Functions”seyd_abi_version
Section titled “seyd_abi_version”ABI version of this library. Compare with SEYD_ABI_VERSION from the
header a wrapper was built against.
uint32_t seyd_abi_version(void);seyd_version
Section titled “seyd_version”Library version string, e.g. 0.1.0. Static; never free it.
const char *seyd_version(void);seyd_last_error
Section titled “seyd_last_error”The reason for the calling thread’s most recent failure. Valid until the next failing call on this thread; never free it.
const char *seyd_last_error(void);seyd_init_logging
Section titled “seyd_init_logging”Install a tracing subscriber writing to stderr, filtered by directive
(e.g. info, seyd_core=debug). NULL → info. Optional; call at most
once, before creating an agent.
Safety.
directive must be a valid C string or NULL.
enum seyd_status seyd_init_logging(const char *directive);seyd_agent_create
Section titled “seyd_agent_create”Create an agent. Does no network work — add channels, then start.
Safety.
cfg and callbacks must be valid pointers; out receives an owned handle
that must be released with seyd_agent_destroy.
enum seyd_status seyd_agent_create(const struct seyd_config *cfg, const struct seyd_callbacks *callbacks, struct seyd_agent **out);seyd_channel_add
Section titled “seyd_channel_add”Add a channel. Only valid before seyd_agent_start. out_channel_id
receives the id to pass to seyd_push_*; may be NULL.
Safety.
agent must come from seyd_agent_create; cfg must be valid.
enum seyd_status seyd_channel_add(struct seyd_agent *agent, const struct seyd_channel_config *cfg, uint8_t *out_channel_id);seyd_channel_add_layer
Section titled “seyd_channel_add_layer”Declare one simulcast layer of a video channel (ADR 0008): the same picture,
encoded again at a different operating point. Only valid before
seyd_agent_start, on a channel added by seyd_channel_add.
Call once per layer, in any order. activate_above_kbps is the ABR target at
or above which the layer is the right choice; the lowest layer is the base
and must be 0. A channel with fewer than two layers behaves exactly as one
with none, and seyd_push_frame keeps working unchanged.
Feed every declared layer with seyd_push_frame_layer. Seyd relays one and
drops the rest, switching on the target layer’s next keyframe, so the
alternative encoding is always ready and a switch costs no reconnect.
Safety.
agent must come from seyd_agent_create; name must be a valid C string.
enum seyd_status seyd_channel_add_layer(struct seyd_agent *agent, uint8_t channel, const char *name, uint32_t activate_above_kbps);seyd_agent_start
Section titled “seyd_agent_start”Bind, discover, announce and start serving. Blocks until the agent is
announced (discovery takes up to a second or two). Callbacks begin after
this returns SEYD_OK.
Safety.
agent must come from seyd_agent_create.
enum seyd_status seyd_agent_start(struct seyd_agent *agent);seyd_agent_stop
Section titled “seyd_agent_stop”Stop serving and tear down sessions. Blocks until the callback thread has finished, so no callback runs after this returns. Idempotent.
Safety.
agent must come from seyd_agent_create.
enum seyd_status seyd_agent_stop(struct seyd_agent *agent);seyd_agent_destroy
Section titled “seyd_agent_destroy”Stop if running, then release the handle. The handle is invalid afterwards.
Safety.
agent must come from seyd_agent_create and must not be used again.
void seyd_agent_destroy(struct seyd_agent *agent);seyd_push_frame
Section titled “seyd_push_frame”Hand one encoded access unit to a video channel. Never blocks: a frame that
arrives while the previous one is still going out is dropped by admission
control (keyframes are never dropped), counted in
frames_dropped_backlog. capture_ts_us is the source’s capture clock and
travels to the pilot, which paces presentation on it (ADR 0005).
Safety.
data must point to len readable bytes.
enum seyd_status seyd_push_frame(struct seyd_agent *agent, uint8_t channel, const uint8_t *data, size_t len, bool keyframe, uint64_t capture_ts_us);seyd_push_frame_layer
Section titled “seyd_push_frame_layer”As seyd_push_frame, naming which simulcast layer the picture belongs to
(ADR 0008). Feed every declared layer; Seyd relays one.
On a channel that declared no layers this is seyd_push_frame and layer is
ignored, so a wrapper may always call this one.
Safety.
data must point to len readable bytes.
enum seyd_status seyd_push_frame_layer(struct seyd_agent *agent, uint8_t channel, uint8_t layer, const uint8_t *data, size_t len, bool keyframe, uint64_t capture_ts_us);seyd_push_message
Section titled “seyd_push_message”Send a message on a sensor channel. Never blocks.
Safety.
data must point to len readable bytes.
enum seyd_status seyd_push_message(struct seyd_agent *agent, uint8_t channel, const uint8_t *data, size_t len);seyd_channel_id
Section titled “seyd_channel_id”Look up a channel id by name; useful when a wrapper configures by name.
Safety.
name must be a valid C string; out_channel_id must be writable.
enum seyd_status seyd_channel_id(struct seyd_agent *agent, const char *name, uint8_t *out_channel_id);seyd_set_qos_profile
Section titled “seyd_set_qos_profile”Switch the QoS ceiling by name (latency, balanced, quality).
Safety.
name must be a valid C string.
enum seyd_status seyd_set_qos_profile(struct seyd_agent *agent, const char *name);seyd_set_status_json
Section titled “seyd_set_status_json”Publish free-form robot status to the console, as a JSON object. NULL clears it.
Safety.
json must be a valid C string or NULL.
enum seyd_status seyd_set_status_json(struct seyd_agent *agent, const char *json);seyd_session_count
Section titled “seyd_session_count”Number of live pilot sessions.
Safety.
out must be writable.
enum seyd_status seyd_session_count(struct seyd_agent *agent, uint32_t *out);seyd_counters
Section titled “seyd_counters”Snapshot the engine counters.
Safety.
out must point to a writable seyd_counters_t.
enum seyd_status seyd_counters(struct seyd_agent *agent, struct seyd_counters_t *out);