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.2.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.2-py3-none-any.whl (32.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: cfs_msgid_sentinel-1.0.2.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.2.tar.gz
Algorithm Hash digest
SHA256 079400de9b7141ac24f126ea2f8131290a22b894bce180724b6cf80b44dfb7c1
MD5 f0edefff3b4d809fa6e32d9ac00b2fe0
BLAKE2b-256 f41aadfeb18dc6fda0e658dd874abbc48ad4d3792f78ac360ae6f92404f849cf

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for cfs_msgid_sentinel-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b77f9cbeb172026d5fdbefbe7a2bd624704a25aa932f8e68b19bd87bd8c580c3
MD5 4a0a84d658580becd9a85e8afb2e35c2
BLAKE2b-256 0f05e717b15dde091c11956d7117bf3dd23932efe4f16a88093bd5be1e0f4077

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