Skip to main content

Detect NASA cFS MsgID collisions by scanning topic IDs

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cfs_msgid_sentinel-1.0.0.tar.gz (31.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cfs_msgid_sentinel-1.0.0-py3-none-any.whl (32.9 kB view details)

Uploaded Python 3

File details

Details for the file cfs_msgid_sentinel-1.0.0.tar.gz.

File metadata

  • Download URL: cfs_msgid_sentinel-1.0.0.tar.gz
  • Upload date:
  • Size: 31.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cfs_msgid_sentinel-1.0.0.tar.gz
Algorithm Hash digest
SHA256 5084c076b45be80e4f0cf01540b128d23b12bb2e7bf70885b7e57446146d4639
MD5 aef51ac2d01c16d65cce48192c93498c
BLAKE2b-256 71bee123169d2fd1094b686e467bf066c1df9ad5c5c1de51c1ec6896c0359f2c

See more details on using hashes here.

File details

Details for the file cfs_msgid_sentinel-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for cfs_msgid_sentinel-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 02f05f7e103dcf5751e990b36111a9d153f237fa885801d2bac983ebd87eb665
MD5 f45d3b54ff76a5af18f7bef3e6afb891
BLAKE2b-256 49750b5637b5ef621aeb2e952b37063dcc9d8dcac2738ffcc5019c83169dd3f3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page