Skip to content

RELAY v1.11 §10.3 Adapt(), full rust-FuSa lifecycle + TARA, CLI rename - #11

Merged
SoundMatt merged 1 commit into
mainfrom
fix/backlog-2026-07
Jul 27, 2026
Merged

RELAY v1.11 §10.3 Adapt(), full rust-FuSa lifecycle + TARA, CLI rename#11
SoundMatt merged 1 commit into
mainfrom
fix/backlog-2026-07

Conversation

@SoundMatt

Copy link
Copy Markdown
Owner

Summary

Fixes every open issue from the 2026-07-27 RELAY ecosystem audit of rust-RCP.

Issues fixed

#5 [P0] capabilities doc falsely declares "adapt": true
Implemented a real Adapt() (rcp::adapt(ctrl), src/adapt.rs) per RELAY spec §10.3: wraps a Controller as an async relay::Caller/relay::Node, backed by new to_message()/from_message()/response_to_message() conversions matching §15.7.5 exactly. "adapt": true is now a true statement rather than a stale hardcoded claim. Also populated "optional_interfaces": ["LoaningController"] (previously [] despite LoaningController being implemented).

#6 [P1] No RELAY Rust binding
Added a vendored relay module (src/relay.rs — Message, Protocol, Version, Context, SubscriberOptions, the four error sentinels, async Node/Caller traits) per §18.3, since no published relay-rs crate exists yet (matches the rust-CAN sibling's own vendoring approach). Exported RELAY_SPEC_VERSION from the crate root. Command/Response/Status now derive Serialize/Deserialize with the exact §15.5 JSON field names (including Command's type rename and base64-encoded payload). Zone/Priority/CommandType/ResponseStatus get hand-written numeric serde (bare integers, matching Go's wire format) rather than a full conversion to #[repr(u8)] enum — that representation change would ripple through every module using .0/Ord/Hash on them for no wire-format difference, so I kept the existing newtype-struct representation and just made it serialize correctly.

#8 [P1] CLI binary name rcp not rust-rcp
Renamed the [[bin]] target and TOOL const to rust-rcp per §13.2. Library crate name stays rcp, matching §13.3's mod <protocol> convention (unaffected — rust-CAN's own lib crate name diverges from this pattern for unrelated reasons, so I left ours as-is since no issue flagged it). Updated CI workflow build/binary paths accordingly.

#9 [P1] Cargo.toml version never bumped for v0.2.0 tag
Bumped Cargo.toml to 0.3.0 (MINOR bump — this PR adds new spec-conformance capability) so the next tag matches the crate version again. The existing v0.2.0 GitHub tag itself permanently carries Cargo.toml version 0.1.0 in its historical commit; I did not rewrite tag history to "fix" that, per the task's git-safety constraints — going forward the invariant holds again.

#10 [P2] README self-contradicts on spec version
Bumped the whole crate to target RELAY spec v1.11 (current), fixing the v1.6/v1.10 contradiction and every other stale "1.10" mention I found (SAFETY_PLAN.md, .fusa-dfmea.json, .fusa-reqs.json's REQ-SPEC-001 text, the CI job name).

#4 Add a TARA and the full rust-FuSa CI lifecycle
Added a hand-authored ISO/SAE 21434 TARA (tara.json: 6 assets, 8 threat scenarios, 5 cybersecurity goals) grounded in RCP's actual attack surface (wire framing, E2E CRC/replay, authz allowlist bypass, rate-limiter/priority-queue DoS, TLS downgrade, watchdog suppression) per §20.4. Added a rust-fusa CI job that installs rsfusa and runs the full lifecycle — check/trace/tara/cyber/qualify/release gating, lint/analyze/comp/fmea/boundary/hara/safety-case/iso26262 best-effort — mirroring rust-CAN's own CI job. Along the way I discovered .fusa.json and .fusa-reqs.json were written against an older, incompatible rsfusa config schema (the current released rsfusa couldn't even parse them — missing field 'standard'), so I migrated both to the schema the current tool actually expects, verified locally against the real rsfusa binary and against rust-CAN's own known-good files.

Issue left open

#7 [P1] Crate is fully synchronous, violating §18.3's async-primary model
Left open with an explanatory comment. This PR does deliver a genuinely async surface — the new relay::Node/Caller Adapt() boundary (the surface RELAY interop actually depends on) is async fn throughout, dispatching the blocking Controller/Registry calls via tokio::task::spawn_blocking. But converting the entire ~11k-line core Controller/Registry API (used by all ~40 sibling modules — bridges, safety controllers, etc.) to async fn is a much larger and riskier rewrite than the rest of this pass, and the crate's synchronous design is plausibly intentional for no_std/embedded targets rather than an oversight (the README described it that way, even if the crate isn't literally #![no_std] today). I didn't want to force that architectural call under this fix pass; the issue's own suggested fix offers exactly this "raise as spec feedback" path as an alternative to a full rewrite.

Verification

  • cargo fmt --all -- --check — clean
  • cargo clippy --all-targets --all-features -- -D warnings — clean
  • cargo test --all-targets (debug + release) — 385 tests, all passing
  • cargo llvm-cov --all-targets --fail-under-lines 90 — 90.91% lines, passes
  • cargo audit — clean, no known vulnerabilities in new deps (tokio, chrono, base64, async-trait)
  • relay conform --strict target/release/rust-rcp — PASS (built and run locally against the real relay v1.11.1 CLI)
  • relay interop target/release/rust-rcprcp-status vector EQUIVALENT
  • rsfusa check/trace/tara/cyber/qualify/release — all run cleanly against this branch with the real rsfusa v0.3.8 binary (built from github.com/SoundMatt/rust-FuSa)
  • scripts/fusa-gap-check.sh — 357/357 requirements (100%) traced

Test plan

  • CI green on this PR (lint, cross-platform test matrix, coverage, fuzz smoke, benchmark smoke, relay conform --strict, relay interop, RELAY spec unit conformance, IEC 62443 cyber gap check, new rust-FuSa full lifecycle job, SBOM, security audit)

… rename

- Add a vendored `relay` module (src/relay.rs) implementing RELAY spec
  §18.3's Message/Protocol/Version/Context/SubscriberOptions/Error types and
  the async Node/Caller traits, since no published relay-rs crate exists yet.
- Implement a real `Adapt()` (src/adapt.rs `adapt()`) per §10.3: wraps a
  `Controller` as an async `relay::Caller`/`Node`, dispatching the blocking
  Controller API via `spawn_blocking` and applying the SubscriberOptions
  back-pressure policy (§10.5) through a dedicated AdaptQueue. Backed by new
  `to_message()`/`from_message()`/`response_to_message()` conversions
  matching the §15.7.5 field mappings exactly.
- `rcp capabilities` now legitimately reports `"adapt": true` (a real
  Adapt() exists) and `"optional_interfaces": ["LoaningController"]`
  (previously empty despite LoaningController being implemented).
- Export `RELAY_SPEC_VERSION` from the crate root (§18.3) and derive
  `Serialize`/`Deserialize` on `Command`/`Response`/`Status` with the exact
  §15.5 JSON field names; `Zone`/`Priority`/`CommandType`/`ResponseStatus`
  get hand-written numeric serde (bare integers, matching Go's wire format)
  rather than derived, since they stay newtype structs — converting them to
  `#[repr(u8)] enum` per §18.3's illustrative Rust snippet would be a much
  larger breaking change across every module that uses `.0`/Ord/Hash on
  them, for no wire-format difference.
- Bump SPEC_VERSION/RELAY_SPEC_VERSION to 1.11 (current RELAY spec) and fix
  README's self-contradictory v1.6/v1.10 claims plus other stale "1.10"
  mentions (SAFETY_PLAN.md, .fusa-dfmea.json, .fusa-reqs.json REQ-SPEC-001).
- Rename the CLI binary `rcp` -> `rust-rcp` per §13.2 (lowercase
  `<lang>-<protocol>`); update CI/docs accordingly. Library crate name is
  unchanged (`rcp`), matching §13.3's `mod <protocol>` convention.
- Bump Cargo.toml to 0.3.0 so the next tag matches the crate version again
  (the existing v0.2.0 tag was cut with Cargo.toml still at 0.1.0; that
  historical tag can't be fixed retroactively, but this keeps future
  releases in sync) — MINOR bump since this adds new spec-conformance
  capability, not just a patch.
- Add a hand-authored ISO/SAE 21434 TARA (tara.json: assets, threat
  scenarios, cybersecurity goals) grounded in RCP's actual attack surface
  (wire framing, E2E CRC/replay, authz, rate limiting, TLS bridge) per
  §20.4, and a `rust-fusa` CI job running the full rsfusa lifecycle (check/
  trace/tara/cyber/qualify/release, gating; lint/analyze/comp/fmea/boundary/
  hara/safety-case/iso26262 best-effort) per §20.1 item 2 — rust-CAN's CI is
  the template. Migrated .fusa.json/.fusa-reqs.json to the schema the
  current rsfusa release actually expects (both were written against an
  older, incompatible schema and failed to parse at all).
- Left issue #7 (fully-synchronous core Controller/Registry API) open: a
  full async rewrite of the ~11k-line core API is a much larger, riskier
  change than this pass's scope, and the crate's sync design is plausibly
  intentional for no_std/embedded targets rather than an oversight — see
  the issue comment for the detailed rationale. The RELAY-facing boundary
  (Adapt()) is genuinely async now, which is the surface RELAY interop
  actually depends on.

Closes #4
Closes #5
Closes #6
Closes #8
Closes #9
Closes #10

Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
@SoundMatt
SoundMatt merged commit 3834832 into main Jul 27, 2026
16 checks passed
@SoundMatt
SoundMatt deleted the fix/backlog-2026-07 branch July 27, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant