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.
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.
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 --versionFor development from a clone:
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
relay --versionOn Linux or macOS, activate with source .venv/bin/activate. Normal analysis is entirely
offline after installation.
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.
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.
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.
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.jsonThe 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.
| 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.
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.
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.
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.
