Skip to content

iroh: authenticate the client to a private JWT-gated relay (bind-path token) - #7826

Closed
lawrencecchen wants to merge 2 commits into
feat-ios-device-list-v2from
feat-iroh-relay-auth
Closed

lawrencecchen wants to merge 2 commits into
feat-ios-device-list-v2from
feat-iroh-relay-auth

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Threads an optional relay auth token (a cmux-signed EdDSA JWT) through the cmux-iroh bind path so the client can use a private, JWT-gated iroh relay instead of being denied.

What

  • New relay_auth_token: *const c_char param on cmux_iroh_endpoint_bind and cmux_iroh_endpoint_bind_relay_only (and bind_impl). When a custom relay_url and a non-empty token are supplied, the relay map is built as RelayMap::from(url).with_auth_token(token); otherwise behavior is unchanged (token ignored on the default n0 map, on disabled relays, or when absent).
  • Relay-mode construction refactored into a relay_mode() helper shared by both bind entrypoints.
  • C header + Swift wrappers (CmxIrohByteTransport(+FFI), CmxIrohByteListener, factory) thread an optional relayAuthToken: String? = nil.

Security

Bind-path only. The token is never placed in route JSON, pairing codes, or logs, so it cannot leak through the dial/route-serialization paths (which is why MobileRouteResolver/CmxTransport are deliberately untouched). All call sites pass nil today; minting the JWT via the web API is a follow-up (TODOs at the composition roots), so this PR is behavior-neutral.

Why now

A private relay fleet (separate repo) is live and requires a valid cmux JWT; anonymous is denied. Proven end-to-end against the deployed relay: a valid token relays real traffic, missing/bad tokens are denied (relay logs: allow for the valid endpoints, deny for the rest). This PR is the client half so cmux can present that token.

Tests

  • Rust: new bind_relay_mode_applies_auth_token_only_to_custom_map asserts the token lands only on a custom map (present with URL+token; absent for null/empty; token-without-URL falls back to RelayMode::Default). cargo build/test/clippy -D warnings pass.
  • swift build passes for CMUXMobileCore, CmuxIrohFFI, CmuxMobileIrohTransport.
  • App-target Swift files (MobileIrohHostLane, cmuxApp) pass nil with matching labels; CI compiles them here (not locally checkable without Xcode).

Based on feat-ios-device-list-v2 (#7327), which already added RelayMode::Custom.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Touches relay authentication and a breaking FFI bind signature, but tokens are bind-only, excluded from routes, and all entry points still pass nil until JWT minting ships; the iroh 1.0.2 bump adds dependency regression surface.

Overview
Adds optional relay authentication at endpoint bind so clients can home on a custom, JWT-gated private relay, and bumps the pinned iroh crate from 1.0.0-rc.1 to 1.0.2 (with matching Cargo.lock churn).

The C FFI gains a relay_auth_token argument on cmux_iroh_endpoint_bind and cmux_iroh_endpoint_bind_relay_only. Relay selection is centralized in a relay_mode() helper: with a custom relay_url and a non-empty token, the map is built as RelayMap::from(url).with_auth_token(token); otherwise behavior is unchanged (token ignored on default n0 map, disabled relays, or missing URL). The token is documented as never logged or included in route JSON; dial/connect APIs are unchanged.

Swift threads relayAuthToken through the listener, dial transport, factory, and a shared bindEndpoint helper. On dial, a non-empty token switches the local bind from the default fleet to the peer’s relay URL for authenticated homing, while relay URL hints on connect stay as before. Mac host and iOS app still pass nil (TODO: mint via web API), so production behavior stays neutral until token wiring lands.

A Rust unit test bind_relay_mode_applies_auth_token_only_to_custom_map locks in token placement rules.

Reviewed by Cursor Bugbot for commit 266adb1. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Adds optional bind-time JWT auth for custom iroh-relay so clients can use a private, token-gated relay. Also bumps iroh to 1.0.2. Behavior is unchanged unless both a custom relay_url and a non-empty token are provided.

  • New Features

    • Added relay_auth_token to cmux_iroh_endpoint_bind and cmux_iroh_endpoint_bind_relay_only; applied only with a custom relay_url and relays enabled, otherwise ignored.
    • Introduced relay_mode() to build the relay map and attach the token only to a custom RelayMap.
    • Swift: CmxIrohByteTransport(+FFI), CmxIrohByteListener, and CmxIrohByteTransportFactory accept relayAuthToken and pass it at bind; token is never in dial hints, route JSON, pairing codes, or logs.
    • Tests: new Rust test asserts the token is set only for custom maps; Rust and Swift builds pass.
  • Dependencies

    • Bumped iroh from 1.0.0-rc.1 to 1.0.2; updated lockfile to match the relay fleet.

Written for commit 266adb1. Summary will update on new commits.

Review in cubic

…path

The client can now authenticate to a private, JWT-gated iroh relay. When a custom
relay_url AND a non-empty relay_auth_token are supplied to the bind FFI, the relay
map is built as RelayMap::from(url).with_auth_token(token); otherwise behavior is
unchanged (token ignored on the default n0 map, disabled relays, or when absent).

Scope: bind path only. The token is never placed in route JSON, pairing codes, or
logs, so it cannot leak through the dial/route-serialization paths. All Swift call
sites pass nil for now (default no-token behavior); minting the JWT via the web API
is a follow-up (TODOs left at the composition roots).

Verified: cargo build/test/clippy pass (new test asserts the token lands only on a
custom map); swift build passes for CMUXMobileCore, CmuxIrohFFI, CmuxMobileIrohTransport.
@vercel

vercel Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 12, 2026 4:46am
cmux-staging Building Building Preview, Comment Jul 12, 2026 4:46am

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 60b560fa-c0b4-4b76-b456-b29a7c22e989

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-iroh-relay-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds bind-time auth for private iroh relays. The main changes are:

  • Adds an optional relay auth token to the Rust/C bind entrypoints.
  • Builds authenticated custom relay maps when a custom relay URL and token are provided.
  • Threads relayAuthToken through the Swift listener, transport, and factory APIs.
  • Updates the iroh dependency and lockfile for the relay auth API.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • The relay token is copied into owned relay-map storage before the temporary C string can expire.
  • Current app call sites still pass no token, so existing runtime behavior is preserved until token minting is added.

Important Files Changed

Filename Overview
Native/cmux-iroh/src/lib.rs Adds relay auth token handling to the bind path and applies it only to custom relay maps.
Packages/Shared/CmuxIrohFFI/Sources/CmuxIrohFFI/include/cmux_iroh_ffi.h Updates the C bind signatures and comments for the optional relay auth token.
Packages/Shared/CmuxMobileIrohTransport/Sources/CmuxMobileIrohTransport/CmxIrohByteTransport+FFI.swift Adds the shared Swift bind wrapper that passes relay URL and relay auth token into the FFI.
Packages/Shared/CmuxMobileIrohTransport/Sources/CmuxMobileIrohTransport/CmxIrohByteTransport.swift Threads the optional relay auth token through dialer endpoint creation while keeping dial hints separate.
Packages/Shared/CmuxMobileIrohTransport/Sources/CmuxMobileIrohTransport/CmxIrohByteListener.swift Stores and forwards the optional relay auth token when starting listener endpoints.

Reviews (2): Last reviewed commit: "cmux-iroh: bump iroh 1.0.0-rc.1 -> 1.0.2..." | Re-trigger Greptile

} else if let Some(url) = custom_relay {
let relay_map = RelayMap::from(url);
let relay_map = if let Some(token) = relay_auth_token {
relay_map.with_auth_token(token)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Borrowed Token Can Escape

When Swift passes relayAuthToken through withCString, that buffer is only valid for the FFI call. If with_auth_token stores the &str instead of cloning it into the relay map, the endpoint can later read freed memory while authenticating to the relay, causing corrupt auth headers or a crash. Make the ownership boundary explicit before storing the token in the long-lived relay configuration.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a81299c63c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let relayAuth = relayAuthToken
// Preserve the existing default-map bind when no token is available;
// a non-empty token makes the peer relay the local authenticated map.
let bindRelay = relayAuth?.isEmpty == false ? relay : nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify relay before sending auth tokens

When relayAuthToken is non-empty, this treats whatever relay_url came from the route as the authenticated relay. That URL is not a trusted token audience: cmux_iroh_endpoint_route_json serializes iroh's current home relay for both default/public relays and user-supplied routes, and CmxIrohByteTransportFactory holds one token for every .iroh route. Once the follow-up starts passing a real cmux JWT, a stale or malicious route can make the client present that token to an arbitrary relay, and default routes are also converted into a single custom relay map instead of using the intended default map; only bind with the token after matching the route relay to the token's expected relay/audience.

Useful? React with 👍 / 👎.

Matches the #7327 crate bump + the 1.0.2 relay fleet. The relay_auth_token bind
path (RelayMap::from(url).with_auth_token) compiles unchanged on 1.0.2.
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 266adb14 Deployed Jul 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants