Skip to main content

cFS MsgID Sentinel (cfs-msgid-sentinel)

Prevent silent runtime failures caused by cFS MsgID collisions.

cfs-msgid-sentinel scans NASA cFS header files, extracts DEFAULT_*_TOPICID definitions, classifies each topic into one of the four collision domains (channels), computes MsgIDs, and reports collisions/near-misses as:

  • CLI output (table / JSON / Markdown summary)
  • GitHub Actions annotations (::error / ::warning)
  • GitHub Job Summary ($GITHUB_STEP_SUMMARY)
  • GitHub Action outputs ($GITHUB_OUTPUT)

Quick start (30 seconds)

Run locally (from repo)

python -m pip install .
cfs-msgid-sentinel --scan-path .

Run locally (editable dev install)

python -m pip install -e ".[dev]"
cfs-msgid-sentinel --scan-path .

Run in CI (GitHub Actions)

Add .github/workflows/msgid-check.yml:

name: MsgID collision check
on: [push, pull_request]

jobs:
  msgid-sentinel:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: ./
        with:
          scan-paths: "."
          topicid-pattern: "**/*_topicids.h"
          msgid-pattern: "**/*_msgids.h"
          fail-on-collision: "true"
          near-miss-gap: "2"
          report-format: both

cFS collision lab (local, repo-internal)

This repo includes a local-only collision lab that clones upstream NASA cFS into ./cFS/ (gitignored), then generates 10 apps under cFS/apps/ with intentionally duplicated TopicIDs so cfs-msgid-sentinel can be validated against a real-looking tree.

1) Clone cFS into this repo (gitignored)

git clone --depth 1 https://github.com/nasa/cFS.git cFS

Also ensure cFS/ remains ignored (see .gitignore).

2) Generate 10 collision-heavy apps + per-app MESSAGES.md

python scripts/generate_collision_lab_apps.py --cfs-root cFS --count 10 --prefix msgid_collision

Each generated app contains:

  • fsw/inc/<app>_topicids.h
  • MESSAGES.md (expected TopicID/MsgID values for that app)

3) Expected collisions

Collision design:

  • Apps: msgid_collision_01msgid_collision_10
  • Bases (defaults):
    • PLATFORM_CMD: 0x1800
    • PLATFORM_TLM: 0x0800
  • Intentional duplicates: TopicIDs 0x00010x0004 are reused across all 10 apps for both CMD-like and TLM-like names.

Expected collision groups (8 total):

Channel TopicID MsgID
PLATFORM_CMD 0x0001 0x1801
PLATFORM_CMD 0x0002 0x1802
PLATFORM_CMD 0x0003 0x1803
PLATFORM_CMD 0x0004 0x1804
PLATFORM_TLM 0x0001 0x0801
PLATFORM_TLM 0x0002 0x0802
PLATFORM_TLM 0x0003 0x0803
PLATFORM_TLM 0x0004 0x0804

The machine-readable expected-collisions file is expected_collisions.json, which is generated by scripts/generate_collision_lab_apps.py and gitignored (local artifact).

Usage

CLI examples

# Basic scan
cfs-msgid-sentinel --scan-path .

# If your headers live under specific subtrees
cfs-msgid-sentinel --scan-path apps --topicid-pattern "**/fsw/inc/*_topicids.h"

# Add near-miss warnings (IDs within N of each other, per channel)
cfs-msgid-sentinel --scan-path . --near-miss-gap 3

# Machine-readable output
cfs-msgid-sentinel --scan-path . --format json

# CI-friendly (no ANSI), but still return success even if collisions exist
cfs-msgid-sentinel --scan-path . --no-color --no-fail-on-collision

# Emit an audit report (also writes collusion-report.txt)
cfs-msgid-sentinel --scan-path . --report

GitHub Actions examples

Typical cFS mission repo

- uses: ./
  with:
    scan-paths: "."
    topicid-pattern: "**/*_topicids.h"
    msgid-pattern: "**/*_msgids.h"
    fail-on-collision: "true"
    near-miss-gap: "0"
    report-format: both

Consume JSON output (allocation-map)

- uses: ./
  id: sentinel
  with:
    report-format: json
    fail-on-collision: "false"

- name: Print summary
  run: |
    echo '${{ steps.sentinel.outputs.allocation-map }}' | jq '.summary'

Configuration: how it finds headers (and what it expects)

1) What gets scanned

  • Root(s):
    • CLI: --scan-path <path> (comma-separated roots)
    • Action: scan-paths (comma-separated roots)
  • Topic ID headers:
    • CLI: --topicid-pattern (default **/*_topicids.h)
    • Action: topicid-pattern (default **/*_topicids.h)
    • The parser looks for:
#define DEFAULT_<NAME>_TOPICID <hex_or_decimal>
  • MsgID headers (Tier-1 channel classification):
    • CLI: --msgid-pattern (default **/*_msgids.h)
    • Action: msgid-pattern (default **/*_msgids.h)
    • From msgid-pattern, the scanner also derives a sibling pattern for *_msgid_values.h.

2) How MsgIDs are computed

cfs-msgid-sentinel computes:

Final MsgID = Base | TopicID

Topic IDs are collision-checked within each of the four channels:

  • PLATFORM_CMD
  • PLATFORM_TLM
  • GLOBAL_CMD
  • GLOBAL_TLM

3) Where base addresses come from

Base addresses come from (highest priority first):

  1. Explicit overrides (CLI flags / Action inputs): cmd-base, tlm-base, global-cmd-base, global-tlm-base
  2. Auto-detected mapping header: default_cfe_core_api_msgid_mapping.h (if present anywhere under your scan roots)
  3. Built-in defaults:
    • PLATFORM_CMD: 0x1800
    • PLATFORM_TLM: 0x0800
    • GLOBAL_CMD: 0x1860
    • GLOBAL_TLM: 0x0860

4) Expected project structure (minimal)

You don’t need a special layout—just ensure the scan root(s) include your headers:

<mission_root>/
  apps/
    <app>/
      fsw/inc/<app>_topicids.h
  ...
  cfe/
    ... *_msgids.h
    ... *_msgid_values.h
  ...

GitHub Action inputs / outputs

Inputs

Input Description Default
scan-paths Root directories to scan (comma-separated) .
topicid-pattern Glob pattern(s) for topic ID headers (comma-separated) **/*_topicids.h
msgid-pattern Glob pattern(s) for MsgID headers (comma-separated) **/*_msgids.h
cmd-base Platform command MsgID base address 0x1800
tlm-base Platform telemetry MsgID base address 0x0800
global-cmd-base Global command MsgID base address 0x1860
global-tlm-base Global telemetry MsgID base address 0x0860
fail-on-collision Fail the workflow if a collision is detected true
near-miss-gap Warn about topic IDs within N of each other (0 to disable) 0
report-format Output format: summary, json, or both both

Outputs

Output Description
collision-count Number of collisions found
has-collisions true if any collisions were found
allocation-map JSON string containing summary + full allocation map

Troubleshooting

  • No topic ID files found

    • Check you’re scanning the right root (--scan-path / scan-paths).
    • Tighten or loosen --topicid-pattern / topicid-pattern.
  • Lots of heuristic classification

    • Ensure your *_msgids.h headers are included by --msgid-pattern / msgid-pattern.
    • If your repo uses different filenames, set a custom msgid-pattern.
  • Different base addresses than the defaults

    • Prefer committing a correct default_cfe_core_api_msgid_mapping.h under the scan roots.
    • Or override bases via CLI flags / Action inputs.

Development

Local development

Create a virtualenv, install dev deps, and run the safety-critical quality gate:

python -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"

python -m pip check
ruff format --check .
ruff check .
pylint src/cfs_msgid_sentinel
mypy src
bandit -q -r src
lizard -l python -C 10 src
pytest

Manual smoke run (fixtures)

cfs-msgid-sentinel --scan-path tests/fixtures --topicid-pattern "**/real/*_topicids.h"

Required real-workspace validation

CI runners cannot access your local cFS tree, so this is a required local gate:

python scripts/validate_real_workspace.py --scan-path /home/aero/Desktop/cFS/apps

Build / distribution smoke

python -m pip install build
python -m build
python -m pip install dist/*.whl
cfs-msgid-sentinel --help

License

Apache-2.0. See LICENSE.

Release files for cfs-msgid-sentinel 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cfs-msgid-sentinel 1.0.2
File Size Uploaded
cfs_msgid_sentinel-1.0.2.tar.gz 31.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cfs-msgid-sentinel 1.0.2
File Interpreter ABI Platform
cfs_msgid_sentinel-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 64.6 kB

Release files / cfs_msgid_sentinel-1.0.2.tar.gz

Download URL cfs_msgid_sentinel-1.0.2.tar.gz
Size 31.6 kB
Tags Source
SHA-256 checksum
How to use checksums
079400de9b7141ac24f126ea2f8131290a22b894bce180724b6cf80b44dfb7c1
BLAKE2b-256 checksum
How to use checksums
f41aadfeb18dc6fda0e658dd874abbc48ad4d3792f78ac360ae6f92404f849cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / cfs_msgid_sentinel-1.0.2-py3-none-any.whl

Download URL cfs_msgid_sentinel-1.0.2-py3-none-any.whl
Size 32.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b77f9cbeb172026d5fdbefbe7a2bd624704a25aa932f8e68b19bd87bd8c580c3
BLAKE2b-256 checksum
How to use checksums
0f05e717b15dde091c11956d7117bf3dd23932efe4f16a88093bd5be1e0f4077
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

1.0.2 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page