Skip to content

Repository files navigation

Relay

Relay — local timing-risk review for control programs

CI Release License: MIT

Relay shows what blocking code prevents from running.

Relay is a local-first source-review tool for timing and blocking risks in polyglot control programs. Version 0.2.1 is an alpha: useful as a review assistant, but intentionally not presented as compiler-grade semantic analysis or safety certification. Analysis never executes, imports, compiles, or uploads the source being inspected.

Why I built Relay

I kept coming back to one awkward question while reading control code: if this call waits here, what quietly stops running somewhere else? A plain warning about delay() was not enough for me. I wanted to see the reachable call path, the time involved, and the tasks whose timing contracts could be missed.

That thought became Relay. I built it as a local-first project because firmware and control code can be sensitive, and a review tool should not need to upload that source just to be useful.

Install

Relay 0.2.1 supports Python 3.9–3.14. Install the verified wheel from the GitHub release:

python -m pip install https://github.com/devkyato/Relay/releases/download/v0.2.1/relay_lint-0.2.1-py3-none-any.whl
relay --version

For development from a clone:

python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
relay --version

On Linux or macOS, activate with source .venv/bin/activate. Normal analysis is entirely offline after installation.

Try it in five minutes

relay check examples
relay check examples/arduino/blocking_robot.ino --format markdown --output relay-report.md
relay explain examples/arduino/blocking_robot.ino --level beginner
relay summary examples/arduino/blocking_robot.ino --config relay.toml.example --duration 5s
relay schema report --output relay-report.schema.json
relay rules
relay doctor .

I usually start with relay check examples, then narrow the command to one file once a finding looks interesting. Typical output looks like this:

ANALYSIS MODELS: cpp=structural C-family model
MODEL LIMIT: Structural models do not provide compiler-grade type, build, or dispatch semantics.

HIGH RLY101 blocking_robot.ino:16
delay() blocks its current execution context for approximately 2.000 seconds.
Confidence: medium

Exit status is non-zero when a finding reaches --fail-on (default: warning). Formats are text, json, sarif, and markdown. Use --suggest or --suggestions-out relay-suggestions.md for reviewable patterns; Relay never edits source in 0.2.1 and never claims a suggestion is automatically safe for machinery.

How it works

Relay discovers supported source files within per-file, file-count, and aggregate byte limits. Python is parsed with the standard AST and qualified class/nested-function identities. Other languages use explicitly labelled structural models with qualified, conservative call resolution: ambiguous method names are left unresolved rather than collapsed.

The rule engine distinguishes cooperative async waits from thread-blocking calls, evaluates simple constant arithmetic, and only attaches timing impact to configured tasks that can reach a finding. relay summary lists declared periods and detected risks; it does not claim to simulate a schedule.

Oh—on this part, I deliberately made the model impossible to miss in text, Markdown, JSON, and SARIF output. A source location is a fact; a structural call path or overlapping timing period is an estimate. Every finding includes severity, confidence, evidence, and a suggested direction so you can decide what holds up on the real target.

Timing contracts

Copy relay.toml.example to relay.toml, or run relay init (which refuses to overwrite an existing file):

[project]
target = "esp32"
entrypoints = ["setup", "loop"]

[tasks.update_motors]
every = "20ms"
execution_context = "control-loop"

[tasks.emergency_stop]
maximum_latency = "10ms"
execution_context = "control-loop"
safety_critical = true

[functions]
update_motors = "updateMotors"
emergency_stop = "checkEmergencyStop"

See configuration and the timing model.

Machine-readable contracts

Relay bundles versioned JSON Schemas inside the installed package:

relay schema report --output relay-report.schema.json
relay schema config --output relay-config.schema.json
relay check . --format json --output relay-report.json

The report schema validates JSON schema version 2.0. The configuration schema describes the data produced by parsing relay.toml; Relay still performs the final cross-field validation. See schemas and integrations.

Supported sources and rules

Language Extensions Analysis model
Python / MicroPython .py, .pyw Standard AST, file-local qualified symbols
C, C++, Arduino .c, .h, .cc, .cpp, .cxx, .ino and header variants Structural C-family model
JavaScript / TypeScript .js, .jsx, .mjs, .cjs, .ts, .tsx, .mts, .cts Structural language model
Java / Kotlin .java, .kt, .kts Structural language model
C# .cs Structural language model
Go .go Structural language model
Rust .rs Structural language model
Swift .swift Structural language model
Ruby .rb Structural language model
PHP .php Structural language model

Versioned rule IDs RLY101–RLY115 cover known blocking waits, polling and connection loops, selected network calls, Arduino timing hazards, configured safety-critical reachability, reachability-scoped timing contracts, and repeated sensor reads. A rule's applicability and confidence depend on the language model. Run relay rules or read the rule reference.

Python API

from relay import RelayConfig, analyse_path

result = analyse_path("robot.ino", config=RelayConfig.default())
for finding in result.findings:
    print(finding.rule_id, finding.message)

The public AnalysisResult, Finding, Severity, and Confidence models are typed.

What Relay is not

I thought carefully about this boundary: Relay should explain suspicious timing paths, not pretend to certify them. It is not a compiler, whole-program type resolver, scheduler simulator, hardware simulator, formal-verification system, replacement for tests on real devices, or a guarantee of real-time behaviour. Structural models cannot reliably see macros, conditional builds, overloads, virtual dispatch, library internals, interrupts, electrical conditions, scheduler details, or worst-case execution time.

Security, contributing, and licence

Source files are treated as untrusted data. Relay bounds individual and aggregate input, refuses a symlink analysis root, does not follow directory symlinks, does not run external commands during analysis, and writes reports atomically without overwriting analysed source or symlinks. See security, limitations, roadmap, support, contributing, and SECURITY.md.

This is a personal open-source project by devkyato, shared under the MIT License.

About

Local-first, conservative timing-risk source review for Python and common compiled and scripting languages.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages