The contract between the MCS and its external components is JSON over NATS. This page is its
reference: the subjects, the headers, the JetStream streams and the messages. The Rust SDK
(stellar-sdk, types in stellar-contract) and the Python SDK implement it; a component in any
other language sends the same messages. The JSON Schema of each message is printed by
stellar schema.
Tokens#
Every variable part of a subject is a token: ASCII letters, digits, - and _, not empty,
not _ alone. A single-instance component uses _ as its instance token
(stellar.param.psu-lab-2.psu._.voltage). Times are RFC 3339, UTC (in the time scale of the
chain, time.scale). Identifiers of telecommands and runs are ULIDs.
Subjects#
Control plane#
| Subject | Direction | Payload |
|---|---|---|
stellar.ctl.register | instance → reconciler (request) | Registration; reply RegistrationReply |
stellar.ctl.hb.<kind>.<instance> | instance → reconciler | Heartbeat, every period |
stellar.ctl.rpc.<kind>.<instance>.<verb> | anyone → instance (request) | JSON in and out; {"error": "…"} on failure |
Common verbs: status (reply Status), credentials ({"jwt": "…"}), bindings
(Bindings), reregister (the instance sends its registration again). Every driver answers
encode (EncodeRequest → EncodeReply: {"tc", "context"} → {"frame": "<hex>"}) and
decode (DecodeRequest → DecodeReply: {"frame": "<hex>", "context"} → {"samples"}),
which encode or decode without sending or publishing anything (see
Checking a driver on a running MCS). Drivers of
CFDP platforms answer cfdp (CfdpRequest → CfdpStatus); the simulated gateway answers faults,
fault, files, generate, rewrite, corrupt and stream_gap.
Telecommands#
| Subject | Direction | Payload |
|---|---|---|
stellar.tc.submit.<target> | API → executor | a direct telecommand, resolved |
stellar.tc.encode.<codec>.<target> | executor → driver | SemanticTc; durable consumer encode_<codec>_<target> |
stellar.tc.wrap.<transport>.<target> | driver → transport | unit to frame; durable consumer wrap_<transport>_<target> |
stellar.tc.uplink.<gateway>.<target> | driver or transport → gateway | opaque frame; durable consumer uplink_<gateway>_<target> |
stellar.tc.evt.<target>.<tc_id> | executor, driver, gateway → MCS | TcEvent |
Telemetry, files and streams#
| Subject | Direction | Payload |
|---|---|---|
stellar.tm.raw.<gateway>.<target> | gateway → MCS | opaque frame |
stellar.tm.unit.<transport>.<target> | transport → driver | unit received |
stellar.tm.decoded.<target> | driver, transport (link measures), transfer manager (stream measures) → compute stage | list of RawSample |
stellar.param.<target>.<component>.<instance>.<measure> | compute stage → MCS | Sample |
stellar.file.chunk.<target>.<file_id> | driver → transfer manager | chunk of an on-board file, raw bytes |
stellar.file.decode.<target> | transfer manager → drivers (request, queue group decode.<target>) | FileDecode; reply FileDecodeReply |
stellar.stream.<target>.<stream_id> | gateway → MCS | segment of a continuous stream, raw bytes |
stellar.metrics.<gateway>.<target> | gateway → MCS | ThroughputReport, every metrics_period (core NATS) |
stellar.metrics.connector.<name> | reconciler → anyone | ConnectorLag, every 15 s |
Runs, alarms and simulation#
| Subject | Direction | Payload |
|---|---|---|
stellar.run.submit | API, scheduler, alarm service → executor | RunSubmission (run, ir, by, and reaction for the reaction of an alarm, which takes the leases of its targets from any ordinary run) |
stellar.run.evt.<run_id> | executor → anyone | RunEvent |
stellar.run.approval.<run_id> | API → executor | RunAnswer |
stellar.run.control.<run_id> | API, alarm service → executor (request) | RunControl (suspend, resume, abort) |
stellar.alarm.evt.<target>.<component>.<instance>.<alarm> | alarm service → anyone | AlarmEvent |
stellar.alarm.control.<target> | API → alarm service (request) | AlarmCommand |
stellar.sim.evt.<target> | simulated gateway → anyone | {fault, active, at} |
Headers#
| Header | On | Content |
|---|---|---|
Nats-Msg-Id | JetStream messages | De-duplication id: <tc_id> (submission), <tc_id>-encode, <tc_id>-wrap, <tc_id>-uplink (-uplink-<i> for fragments), <tc_id>-<STATE> (events), <run_id>-<n> (run log), <key>-<seq> (alarm transitions) |
Stellar-Config | most messages | Configuration revision that produced the message |
Stellar-Emitted-At | messages of the core | Emission time |
Stellar-Correlation | telecommands, frames, units | Telecommand id; absent on a frame of no telecommand (CFDP PDU, COP-1 retransmission) |
Stellar-Deadline | telecommands, frames, units | Latest uplink time: received later, refused |
Stellar-Fragment | frames | <i>/<n>, from 1: one frame among several of a telecommand |
Stellar-Directive | units on wrap | A COP-1 directive of the link component (unlock, set_vr), arguments as JSON payload |
Stellar-Ground-Time | raw telemetry, units, decoded values, stream segments | Ground reception time |
Stellar-Delivery | raw telemetry, units, decoded values | deferred for data replayed from on-board storage |
Stellar-Link | decoded values, units, chunks | Link of the frame |
Stellar-Driver | decoded values | <software>@<version> of the driver that decoded them |
Stellar-Environment | decoded values, units | Environment whose link parameters were used |
Stellar-File-Generation, Stellar-File-Offset | file chunks | Generation of the file, offset in bytes |
Stellar-Stream-Seq | stream segments | Segment number from 1, consecutive while nothing is lost |
Stellar-User | submitted telecommands | Identity of the operator, traced in PENDING |
Stellar-For-Run | telecommands of the transfer manager | The run that started the transfer, whose lease the telecommand may use |
Streams#
| Stream | Subjects | Retention |
|---|---|---|
TC_COMMANDS | stellar.tc.submit.>, stellar.tc.encode.>, stellar.tc.wrap.>, stellar.tc.uplink.> | 7 days |
TC_EVENTS | stellar.tc.evt.> | 30 days |
TM_RAW | stellar.tm.raw.> | 30 days |
TM_DECODED | stellar.tm.decoded.> | 1 day (values decode again from TM_RAW) |
PARAMS | stellar.param.> | 30 days |
RUNS | stellar.run.submit, stellar.run.evt.>, stellar.run.approval.> | 365 days |
ALARMS | stellar.alarm.evt.> | 90 days |
FILE_CHUNKS | stellar.file.chunk.> | 7 days |
STREAMS | stellar.stream.> | archive.streams.max_age and max_bytes (30 days, 2 TB) |
Components also use a few buckets and object stores: stellar_instances (registrations and
heartbeats, TTL of three periods), stellar_cop1 (FOP state of each link, key
<target>.<link>), stellar_files (chunks and assembled files, read by drivers and files
connectors) and stellar_uploads (contents to upload, read by CFDP drivers). See
NATS and JetStream.
Messages#
Registration#
| Field | Type | Meaning |
|---|---|---|
kind | token | driver, transport, gateway, connector, or a component of the MCS |
instance | token | Unique per kind |
software | {name, version} | Software and semantic version |
heartbeat_period_ms | integer | Between 100 ms and 60 s |
public_key | string | nkey user public key (U…) |
driver | drivers only | codec, catalogue (name@requirement), telecommands, measures (component.name), optional output and params_schema |
transport | transports only | input, output, optional params_schema |
gateway | gateways only | tags, optional uplink and downlink: {link_type, rate_bps} |
The reconciler rejects, with the reason: names that are not tokens, a key that is not an nkey
user key, a version that is not semantic, a heartbeat period out of bounds, a missing or
superfluous section for the kind, a catalogue that is not name@requirement, coverage entries
that are not component.name, an output, input or link type that is not a token, a
params_schema that is not the schema of an object. A new registration of an instance replaces
the previous one.
RegistrationReply: {"status": "accepted"}, {"status": "accepted", "jwt": "…"} or
{"status": "rejected", "reason": "…"}.
Heartbeat and Status#
Heartbeat:health(healthyordegraded),reasonwhen degraded,link_availablefor gateways,sent_at.Status(reply of thestatusverb):kind,instance,software,heartbeat(as last sent),started_at,bound(whether the instance holds a JWT issued by the reconciler).
Bindings#
links: for each link served, target, link, config (revision to stamp as
Stellar-Config), codec, encode, uplink, driver, gateway, and when given params and
environments (overrides by environment, key by key). A link with a transport adds transport,
wrap and units. When the topology declares modes, a link also carries mode (the current
mode of its target), parameters (the parameters of the target in that mode, by parameter:
tcu.anode_voltage, tcu[TCU2].anode_voltage) and parameter_environments (the same, overrides
applied, for each environment that overrides some). The reconciler delivers the bindings again at
each change of mode. An instance no longer bound receives an empty list. components maps the
components the driver knows under another name, name in the target → name in the driver's
catalogue, for a driver of a package the platform imports renamed: the SDKs translate
SemanticTc.component and RawSample.component with it.
A LinkContext (what a driver, transport or gateway knows of a link) is target, link,
environment, params resolved for that environment, mode and parameters (from
parameter_environments of the environment when present, else parameters). See
Modes and Parameters.
SemanticTc#
id (ULID), target, optional link, component, optional instance (the file id for
files), telecommand, args (defaults applied, numbers in the unit of the argument, enum
values as strings, booleans, bytes in hexadecimal), optional environment.
TcEvent#
tc, state (PENDING, REJECTED, ENCODED, ENCODE_FAILED, SENT, SEND_FAILED,
VERIFIED, VERIFY_FAILED, VERIFY_TIMEOUT, COMPLETE, or ECHO, which is not a state),
at, optional detail (reason of a failure), conforming for ECHO, and on PENDING the
telecommand ({component, instance, telecommand}). See
Telecommand Lifecycle.
| Event | Published by |
|---|---|
PENDING, REJECTED, VERIFIED, VERIFY_FAILED, VERIFY_TIMEOUT, COMPLETE | executor |
ENCODED, ENCODE_FAILED, ECHO | driver |
SENT, SEND_FAILED | gateway (or transport, for a unit it cannot frame) |
ENCODE_FAILED, SEND_FAILED, VERIFY_TIMEOUT after executor.ack_timeout | executor |
RawSample and Sample#
RawSample:component, optionalinstance,measure,value(number, boolean, enum value, hexadecimal bytes) before calibration, optionalonboard_time.Sample:value(physical;nullwhen a temporal derived measure becomes unknown), optionalraw,time(on-board time when known, else ground reception),ground_time,link,delivery(realtimeordeferred).
Files#
FileChunk(SDK side):file_id,generation,offset,data.FileDecode:file_id,generation,file_type,object(name instellar_files).FileDecodeReply:{"outcome": "decoded", "samples": n},{"outcome": "not_decodable"}or{"outcome": "failed", "error": "…"}.CfdpRequest:target,file_id,generation,direction(download,upload),file_type,size,source(SHA-256 of an upload instellar_uploads).CfdpStatus:{"state": "running", "progress": n},{"state": "completed"}or{"state": "failed", "error": "…"}.Transfer:target,file_id,generation,direction,file_type,size,checksum,state(REQUESTED,PARTIAL,COMPLETE,VERIFIED,PROCESSED,CORRUPTED,SUPERSEDED),received(ranges[start, end)),priority,by,created_at,updated_at,reason,source.
ThroughputReport and ConnectorLag#
ThroughputReport:uplink_bps,downlink_bps(mean over the window),uplink_bytes,downlink_bytes(since the gateway started),window_ms.ConnectorLag:connector,kind,stream,consumer,pending,unacked,oldest_unread,lag_seconds,retention_seconds,fill,active,at_risk,reason,at.
Runs#
RunEvent:run,seq,at, and aneventwith its fields:started,procedure_started,procedure_finished,step_started,telecommand_sent,telecommand_finished,checked,asked,answered,answer_refused,taken_over,suspended,continued,decision_required,step_finished,finished. See Evidence and Reports.RunAnswer:path,accepted,value,by,roles,token.RunControl:{"action": "suspend", "by"},{"action": "resume", "by", "choice"},{"action": "abort", "by"}.
Alarms#
AlarmEvent:target,component,instance,alarm,previous,state(NORMAL,ACTIVE_UNACK,ACTIVE_ACK,CLEARED_UNACK),severity(warning,critical),value,at,seq,cause(condition,acknowledged,shelved,unshelved),by,shelved_until.AlarmRecord: the current state instellar_alarms:state,previous,severity,active_since,changed_at,value,seq,cause,by,shelved_until.AlarmCommand:alarm({component, instance, alarm}),action({"action": "acknowledge"},{"action": "shelve", "for": "30 min"},{"action": "unshelve"}),by.
Passes and schedules#
Pass, Schedule and ScheduleRule are also bodies of the HTTP API: see the
API reference and Writing a Pass Feeder.
JSON Schemas#
stellar schema <kind> prints the JSON Schema of a message, for editors, tests and components in
other languages:
| Kind | Message |
|---|---|
registration, registration-reply | Registration and its reply |
heartbeat, status | Heartbeat, reply to status |
bindings | Links bound to an instance |
semantic-tc, tc-event | Telecommand to encode, telecommand transition |
raw-sample, sample | Raw value of a driver, measure sample |
throughput | Throughput report of a gateway |
run-event | Event of a run |
encode-request, encode-reply, decode-request, decode-reply | The verbs encode and decode of a driver |
alarm-event, alarm-record, alarm-command | Alarm transition, current state, command |
pass, schedule, schedule-rule | Pass, schedule, recurring rule |
transfer | File transfer |
The same command prints the schemas of the configuration files (catalogue, library,
topology, simulation).
stellar schema registration > registration.schema.json