Stellar ControlMission control · by Stellar Systems v0.1.0

Operations

Scheduling

Runs bound to passes, their states, recurring rules and the validation of plans by a supervisor.

A run is either launched by hand or bound to a pass. A schedule binds a run request to a pass, with a start relative to its AOS. The scheduler, a stateless service, watches the passes and the schedules, evaluates each schedule again whenever its pass changes, and submits the run once, at its start, if it still fits in the pass. Recurring rules create such schedules by themselves for every pass that matches a filter.

Where planning is mandatory (hazardous_requires_plan), a run that sends a hazardous telecommand starts only from a schedule, and a supervisor validates its plan beforehand: see Hazardous Confirmations.

A schedule#

YAML
schedule:
  - id: sch-0142
    pass: gs-a-2026-10-02T1014Z-sat1
    start: aos + 30 s
    run: "Hot standby test"
    targets: {sat: sat1-fm}
    inputs: {tcu: TCU2}
FieldRequiredMeaning
idyesIdentifier, a token (letters, digits, -, _)
passyesPass, by its stored identifier or that of one of its contributions
startyesStart relative to the AOS: aos, or aos + <duration>
runyesProcedure
targetsyesTarget of each role; one of them must be the target of the pass
inputsnoInput values, as in a run request (28 V, TCU2, true)
librarynoLibrary of the procedure, when several have a procedure of that name
environmentnoEnvironment; by default the last environment of the target of the pass

start is aos or aos + followed by a duration: aos + 30 s, aos + 2 min, aos+90s. Any other form (los - 30 s) is refused.

The MCS fills in the rest of the schedule, which the API and the CLI show:

FieldMeaning
statescheduled, at_risk, cancelled, fired or missed
reasonsWhy it is at risk, cancelled or missed
targetTarget of the pass
irHash of the resolved run in stellar_ir
max_durationMaximum duration of the run, computed at compile time
sendsWhether the run sends telecommands
needs_validationWhether a supervisor must validate the plan before it starts
validationThe validation of the plan, while it holds
fire_atStart time on the current pass
run_idRun submitted, once fired
by, updated_atAuthor and last change

Creating a schedule#

Shell
stellar schedules add schedules.yaml

stellar schedules add posts each entry of schedule: to POST /v1/schedules, and each entry of rules: to POST /v1/schedule-rules (see Recurring rules). A file can hold both.

At creation, the MCS:

  1. finds the pass (a pass named by the identifier of a contribution is recorded under its stored identifier) and checks that a role of the schedule targets it;
  2. resolves the run request on the current configuration, with plan set to the pass, and stores the resolved run in stellar_ir;
  3. records its maximum duration, whether it sends telecommands, and whether it needs a validation;
  4. checks that it fits in the pass (below), and that its start is in the future.

A schedule that does not fit, a start already past, an unknown pass or a request that does not resolve is refused with 422 schedule::refused, with the reasons. A body that does not parse is refused with 422 schedule::invalid. The environment of the run decides who may create it (see Identity and Roles).

A schedule not fired yet can be replaced: post it again under the same identifier. Replacing a schedule drops its validation. A fired schedule cannot be replaced.

Fitting in the pass#

A schedule is scheduled only when all these rules hold on its pass as it is:

  • Before the LOS. The run, from its start and for its maximum duration, ends before LOS minus scheduler.margin (30 s by default).
  • In order. It does not start before the possible end of a run scheduled before it on the same pass. Several runs share a pass, one after the other.
  • Booked in orbit. In in_orbit, the pass is booked.
  • Uplink. A run that sends telecommands needs a pass with uplink.
  • Validated. A schedule that needs a validation has one that still holds.

The maximum duration of a procedure is computed at compile time, retries included; see Safety Rules and Maximum Duration.

States#

stateDiagram-v2
    [*] --> scheduled
    [*] --> at_risk: needs a validation
    scheduled --> at_risk: pass changed, validation fell
    at_risk --> scheduled: pass corrected, plan validated
    scheduled --> fired: start reached, run fits
    scheduled --> missed: rest of the pass too short
    at_risk --> missed: start reached while at risk
    scheduled --> cancelled: pass or operator
    at_risk --> cancelled: pass or operator
    fired --> [*]
    missed --> [*]
    cancelled --> [*]
StateMeaning
scheduledWill start at its time
at_riskWill not start unless its pass changes again, or its plan is validated; see reasons
cancelledIts pass was cancelled, or an operator cancelled it
firedIts run was submitted; run_id names it
missedIts time came while at risk, or the rest of the pass was too short: nothing was sent

cancelled, fired and missed are final. An at_risk schedule becomes scheduled again when its pass is corrected. A schedule whose pass is unknown is at_risk.

Continuous evaluation#

The scheduler watches stellar_passes and stellar_schedule. It evaluates every schedule not final at each change and at least every second, and writes a change of state with its reasons by a conditional write: a concurrent change by an operator is evaluated again at the next round.

At its start time, a scheduled schedule is fired if the run still fits in the rest of the pass — from now, for its maximum duration, before LOS minus the margin. Otherwise it is missed.

Firing once#

The scheduler submits the run on stellar.run.submit with Nats-Msg-Id = <schedule>-<pass>, and by = schedule <id>. JetStream deduplicates a second submission: a scheduler instance that submitted the run and stopped before recording it finds the duplicate on its next round, and the schedule is fired. Several scheduler instances can run at the same time.

The run is then an ordinary run: follow it with stellar watch <run>, answer its questions, suspend it. See Running Procedures.

Cancelling a schedule#

Shell
stellar schedules cancel sch-0142

POST /v1/schedules/{id}/cancel cancels a schedule that is not final, as the operator of X-Stellar-User (--as). Its reason records who cancelled it. A final schedule answers 409 schedule::final, a schedule changed meanwhile 409 schedule::changed.

Plans validated by a supervisor#

In an environment where a supervisor confirms hazardous telecommands (hazardous_confirmation contains supervisor), a schedule whose run sends a hazardous telecommand is marked needs_validation at creation. It stays at_risk ("awaiting the validation of the plan by a supervisor") and does not start until a validation holds.

Shell
stellar schedules --as bob --role supervisor validate sch-0142      # with identity: declared
STELLAR_TOKEN=… stellar schedules validate sch-0142                 # with identity: jwt

POST /v1/schedules/{id}/validate is reserved to an identity with the supervisor role, allowed by the environment of the run (403 schedule::not-supervisor otherwise). It records what was validated:

JSON
{"by": "bob", "at": "…", "pass": "gs-a-2026-10-02T1014Z-sat1",
 "aos": "2026-10-02T10:14:32Z", "los": "2026-10-02T10:24:05Z", "ir": "5f2c…"}

At every evaluation, the scheduler compares the validation with the schedule and its pass: same resolved run (procedure, inputs, targets), same pass, same AOS and LOS. At the first difference, the validation falls for good ("the validation of bob fell: the window of pass … changed") and the schedule is at_risk again; a new validation is needed. A schedule still without a validation at its start is missed, and nothing is sent.

Recurring rules#

A rule makes a schedule on every pass of a target that matches its filter. It suits routine activities, such as a housekeeping dump on every booked pass above 20°:

YAML
rules:
  - id: hk
    target: sat1-fm
    filter: {status: booked, min_elevation: 20 deg}
    start: aos + 30 s
    run: "Housekeeping dump"
    targets: {sat: sat1-fm}
    inputs: {tcu: TCU1}
FieldRequiredMeaning
idyesIdentifier, a token
targetyesTarget of the passes; a role must target it
filternoPasses it applies to; every pass of the target without
start, run, targets, inputs, library, environmentAs in a schedule
Filter keyMatches
statusPasses of this status (booked)
min_elevationPasses whose maximum elevation is at least this angle (20 deg)
stationPasses over this station
uplinkPasses with (true) or without (false) uplink

A cancelled pass never matches.

At creation, the rule's request is resolved on the current configuration. A procedure that sends a hazardous telecommand is refused: it cannot run from a rule. Rules are stored in the stellar_schedule_rules bucket.

At each evaluation, the scheduler creates the schedule <rule>-<pass> (with by = rule <id>) for each matching pass that has none yet and whose start is in the future. It goes through the same checks as an explicit schedule; a refusal (a run that does not fit, for instance) is logged and nothing is scheduled for that pass. Removing a rule keeps the schedules it already made.

Shell
stellar schedules rules                  # the rules
stellar schedules remove-rule hk         # no new schedule from hk

Listing schedules#

Shell
stellar schedules                        # schedules not final
stellar schedules --pass gs-a-2026-10-02T1014Z-sat1
stellar schedules --all                  # fired, missed and cancelled too
text
sch-0142  gs-a-2026-10-02T1014Z-sat1  2026-10-02T10:15:02Z  « Hot standby test »  scheduled
hk-gs-a-2026-10-02T1327Z-sat1  gs-a-2026-10-02T1327Z-sat1  2026-10-02T13:27:32Z  « Housekeeping dump »  at_risk: pass `gs-a-2026-10-02T1327Z-sat1` is receive-only and the run sends telecommands

The options of stellar schedules (--pass, --all, --api, --as, --role, --token) come before its subcommand.

CLI and API#

CLIAPIEffect
stellar schedules [--pass P] [--all]GET /v1/schedules (?pass=, ?all=true)List schedules, by start
—GET /v1/schedules/{id}One schedule
stellar schedules add <file>POST /v1/schedules, POST /v1/schedule-rulesCreate or replace schedules and rules
stellar schedules cancel <id>POST /v1/schedules/{id}/cancelCancel a schedule
stellar schedules validate <id>POST /v1/schedules/{id}/validateValidate the plan, as a supervisor
stellar schedules rulesGET /v1/schedule-rulesList rules
stellar schedules remove-rule <id>DELETE /v1/schedule-rules/{id}Remove a rule

The scheduler counts the runs it submits in the Prometheus counter stellar_scheduler_runs_total, labelled by outcome (fired, missed).

See also#

Stellar Control · v0.1.0

↑↓ to moveEnter to open