Skip to content

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.

Bumped only when an existing symbol or struct layout changes; additions are appended (ADR 0004 rule 3).

#define SEYD_ABI_VERSION 1

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
typedef enum seyd_role {
SEYD_ROLE_DRIVER = 0,
SEYD_ROLE_OBSERVER = 1,
} seyd_role;
Value Meaning
SEYD_ROLE_DRIVER
SEYD_ROLE_OBSERVER
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.
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.

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.

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.

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.

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 agent handle.

typedef struct seyd_agent seyd_agent;

ABI version of this library. Compare with SEYD_ABI_VERSION from the header a wrapper was built against.

uint32_t seyd_abi_version(void);

Library version string, e.g. 0.1.0. Static; never free it.

const char *seyd_version(void);

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);

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);

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);

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);

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);

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);

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);

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);

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);

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);

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);

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);

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);

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);

Number of live pilot sessions.

Safety. out must be writable.

enum seyd_status seyd_session_count(struct seyd_agent *agent, uint32_t *out);

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);