The desired state of the MCS — what the platforms look like, which targets exist and how to reach them, which procedures operators run — lives in a Git repository of plain text files. Changes go through merge requests; the CI checks them with the same compiler as the editor and the services; after the merge, the CI publishes the compiled snapshot, and the reconciler applies it at the next safe points.
The MCS applies what the repository contains and imposes no editing role: who may change a
catalogue, and who must approve a procedure, is decided by the review process of your forge
(protected branches, required approvals). The MCS only applies the status of the packages.
Layout#
topology.yaml environments, targets, links, output connectors (optional)
stellar.yaml packages taken from other repositories (optional)
packages.lock their commit and hash, written by `stellar fetch`
packages/
platform-v3/catalogue.yaml a catalogue package
lab-psu/catalogue.yaml
obc-v1/catalogue.yaml
platform-v3-steps/ a library of steps and procedures
package.yaml name, version, status, catalogue dependencies
specification.proc steps and procedures
operations.proc
transfers.proc
stellar.lock validated versions, written by `stellar lock`
simulations/ simulated targets, for dry runs, tests and training
platform-v3-sim.yaml
lab-psu-sim.yaml
runs/ run requests, for dry runs in CI (a convention)
hot-standby.yaml| Path | Content | Reference |
|---|---|---|
topology.yaml | Environments and their policies, targets, links, output connectors. Optional: a subsystem repository may publish packages only | Targets |
stellar.yaml | Packages taken from other repositories, by git repository, tag and path | Several repositories |
packages.lock | The commit and the hash of each of them, written by stellar fetch | Several repositories |
packages/<dir>/catalogue.yaml | A catalogue package: one version of a platform | Catalogue Packages |
packages/<dir>/package.yaml | The manifest of a library of steps and procedures | Libraries and Validation |
packages/<dir>/*.proc | Steps and procedures of that library | Steps and Procedures |
packages/<dir>/stellar.lock | The validated versions of the steps and procedures of the library | Compilation, Snapshots and Locks |
simulations/<name>.yaml | A simulated target, compiled against the catalogue of its platform | Simulated Targets |
runs/*.yaml | Run requests, by convention, for dry runs in CI | Running Procedures |
Every directory under packages/ holds one version of one package. The name and version come
from the file itself (package: and version:), not from the directory name; to keep two
versions of a platform, use two directories.
A catalogue package#
# packages/lab-psu/catalogue.yaml
package: lab-psu
version: 1.0.0
status: released
components:
psu:
description: The bench power supply feeding the platform.
measures:
voltage: {type: f32, unit: V, max_age: 5s, description: Output voltage.}
current: {type: f32, unit: A, max_age: 5s, description: Output current.}
output_enabled: {type: bool, max_age: 5s, description: Whether the output is on.}
telecommands:
output_on:
description: Switches the output on.
verify: [{output_enabled: true, within: 3s}]A catalogue is also an exchange format: it can be generated in the CI of an avionics subsystem
from its types, its ICD or its TM/TC database, and published as a versioned package. The
compiler validates it like a hand-written one (the example satlink-bench catalogue is generated
by a script from the DSL of the bench).
A library#
# packages/platform-v3-steps/package.yaml
package: platform-v3-steps
version: 1.4.2
status: released
depends:
platform-v3: 1.4.x
lab-psu: 1.xdepends lists the catalogue packages the roles of its steps and procedures use, with a version
requirement: exact (1.4.0), any patch (1.4.x) or any minor (1.x, or 1). The highest
matching version present in the repository is used, and recorded in the lock.
Status of packages#
status is draft (the default) or released. It is a policy applied by the environment:
- a target engaged in an environment without
allow_draftmust implement a released catalogue, checked at compile time (topology::draft-not-allowed); - a run in such an environment refuses a draft library or catalogue at launch
(
run::draft-not-allowed).
Moving from draft to released follows your review process.
Versions and immutability#
A published version never changes in place: a change is a new version. Drivers know catalogues
by version (platform-v3@^1.4 accepts 1.4.0 and the following 1.x), never by hash. The lock of
the libraries that use a catalogue reveals a version modified in place: stellar check --locked
fails.
Descriptions (description fields and description "…" lines) are outside of this rule: they
travel beside the compiled catalogue, so rewording them changes neither the hash of a catalogue
nor the lock of a step. See Compilation, Snapshots and Locks.
Working on the repository#
| Command | Effect | Exit code |
|---|---|---|
stellar check [DIR] | Compiles everything; prints diagnostics, then what must be validated again | 1 on an error |
stellar check [DIR] --locked | Same, for CI | 1 on an error or a pending validation |
stellar check [DIR] --format json | One JSON object per line: diagnostics, then revalidations | as above |
stellar lock [DIR] | Rewrites every stellar.lock from the current sources | 1 on an error |
stellar compile [DIR] [--output FILE] [--publish NATS_URL] | Writes the snapshot, prints its hash, optionally makes it current | 1 on an error |
stellar run --dry REQUEST --repository DIR | Runs a procedure on ephemeral simulated targets | 1 when the procedure fails |
stellar schema <kind> | Prints the JSON Schema of catalogue, library, topology or simulation files | 0 |
Compilation parameters (executor.default_retry, derived.max_window) come from the global
configuration (--config stellar.yaml or STELLAR_CONFIG); defaults apply without it. Use the
same values in CI as in the deployment, since the retry policy and the time windows are part of
what is validated.
Editors help with the files:
- The VS Code extension and the language server give live diagnostics,
completion and hover in
.procfiles and the YAML files. - The web editor edits a draft branch in the browser and opens the merge request.
stellar schema catalogue > catalogue.schema.jsongives any YAML editor the schema of catalogues.
The merge request workflow#
sequenceDiagram
participant A as Author
participant G as Git forge
participant CI
participant N as NATS (stellar_ir)
participant R as Reconciler
A->>A: edit, stellar check
A->>A: stellar lock (when ready for review)
A->>G: push, open merge request
G->>CI: stellar check . --locked
CI-->>G: pass / fail
G->>G: review and approval (validates the lock diff)
G->>CI: merge on the default branch
CI->>N: stellar compile . --publish
N->>R: current snapshot changes
R->>R: applies it at the safe points- The author changes catalogues, steps or procedures and runs
stellar check, thenstellar lockonce the change is ready for review. - The merge request shows the
stellar.lockdiff: every step and procedure it validates again, including the procedures that only use a changed shared step. Approving the merge request is the validation. - CI runs
stellar check --locked: it fails on any error, and on any change whose lock was not updated, so an unreviewed change cannot be merged. - After the merge, the CI of the default branch runs
stellar compile --publish; the reconciler applies the snapshot at the next safe points.
GitHub Actions#
jobs:
check:
runs-on: ubuntu-latest
container: ${{ vars.STELLAR_CLI_IMAGE }} # an image providing `stellar`
steps:
- uses: actions/checkout@v4
- run: stellar fetch . # packages of other repositories (stellar.yaml), if any
- run: stellar check . --lockedGitLab CI#
stellar-check:
image: $STELLAR_CLI_IMAGE # an image providing `stellar`
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- stellar fetch . # packages of other repositories (stellar.yaml), if any
- stellar check . --locked
stellar-dry-runs:
image: $STELLAR_CLI_IMAGE # with a nats-server binary
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- for request in runs/*.yaml; do stellar run --dry "$request" --repository . || exit 1; done
stellar-publish:
image: $STELLAR_CLI_IMAGE
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- stellar compile . --publish "$STELLAR_NATS_URL"Mark the check job as required (protected branch, "Pipelines must succeed") so that a failing
check blocks the merge request. No CLI image is published yet: build one providing stellar
(cargo install --path tooling/cli), and add a nats-server binary for dry runs. The publish job
needs credentials allowed to write stellar_ir and stellar_config: pass them with
--nats-credentials (STELLAR_NATS_CREDENTIALS), and --nats-ca for a TLS server.
Several repositories#
A subsystem team can keep its own repository that only holds packages — a catalogue, a library
of procedures —, without topology.yaml: stellar check compiles it like any other, and a tag
publishes a version. Another repository takes them by declaring them in stellar.yaml:
# stellar.yaml of the satellite repository
packages:
hsc-100: # the name of the package
git: https://github.com/stellar-systems-eu/control-usecase-payload-integration
tag: v1.0.0 # or `rev:` a commit or a branch
path: packages/hsc-100 # its directory in that repository
hsc-100-ops:
git: https://github.com/stellar-systems-eu/control-usecase-payload-integration
tag: v1.0.0
path: packages/hsc-100-opsstellar fetch # into the cache, commits and hashes in packages.lock
stellar check --locked # as in CI
stellar fetch --update # take the current commit of each tag or revision- Fetched once, read offline.
stellar fetchis the only command that reaches the network, throughgitand its credentials. It keeps a mirror of each repository and checks the package out at the commit of its tag in the cache:$STELLAR_CACHE, else$XDG_CACHE_HOME/stellar, else~/.cache/stellar. Nothing is copied into the repository. The compiler, the editor and the language server read the packages from the cache. - Locked.
packages.lock, to commit, records for each package its declaration, the commit of its tag and the hash of its files.stellar fetchkeeps the commit locked even when the tag moved;--updatetakes the new one. A package whose files in the cache no longer match its hash is refused (package::external-changed), andstellar fetchchecks it out again. - Resolved like the others.
usesanddependsresolve the packages ofstellar.yamlas those ofpackages/: the platform imports the camera, the libraries depend on it, and their locks hold its hash as for a local package. - Read-only and released. A package taken from another repository must be the one declared
(
package::external-name) andstatus: released(package::external-draft). To change it, publish a new version in its own repository.
| Code | When |
|---|---|
package::external-invalid | Neither or both of tag and rev |
package::external-unlocked | Declared but not in packages.lock as declared: run stellar fetch |
package::external-not-fetched | Locked but not in the cache: run stellar fetch |
package::external-changed | Its files in the cache no longer match the lock |
package::external-name, package::external-draft | Not the package declared, or a draft |
In CI, run stellar fetch before stellar check --locked, and keep the cache between jobs
(STELLAR_CACHE) to work without the network once it is filled. The playground of the
onboarding, which compiles in the browser, does not fetch packages of other repositories.
The example repository#
examples/config in the source repository is a complete, CI-checked example: the platform-v3,
lab-psu, obc-v1 and satlink-bench catalogues, a topology with simulated, bench, flatsat and
flight targets, the platform-v3-steps library with the procedures of the specification, two
simulations and a run request. Your First Procedure uses
it.