Skip to content

feat(device): add secure relayer fallback - #92

Merged
ngleanh merged 4 commits into
mainfrom
codex/relayer-handoff
Jul 13, 2026
Merged

feat(device): add secure relayer fallback#92
ngleanh merged 4 commits into
mainfrom
codex/relayer-handoff

Conversation

@ngleanh

@ngleanh ngleanh commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

FF1 playlist delivery is LAN-only, so sandboxed and cross-subnet clients cannot reach devices and bypass the shared relayer path. The CLI also has no secure way to acquire or retain a device topic. Closes #43.

Why it matters

LAN-first delivery remains fast while relayer fallback makes remote/cross-subnet control reliable and observable. Topics are transferred E2EE with a user-compared key commitment and stored per device in the operating system credential vault instead of config files.

Acceptance checks

  • ff-cli device pair exchanges a device-scoped topic through handoff.feralfile.com, compares the security check, and stores it in the OS keyring.
  • Unreachable LAN delivery prints an explicit notice and falls back through the configured relayer; HTTP/application rejections do not fallback.
  • npm ci, npm run verify (421 tests), bundle/native-keyring smoke, production broker handoff, and real FF1 relayer cast all pass.

Notes

Production relayer deployments may still use the optional FF1_RELAYER_API_KEY compatibility gate; no shared key is bundled.

Comment thread tests/relayer-fallback.test.ts Fixed
Comment thread tests/relayer-fallback.test.ts Fixed
Comment thread tests/relayer-fallback.test.ts Fixed

@feralfile-bot feralfile-bot left a comment

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.

Reviewers: 3 (approve: 1, request-changes: 2).

Critical issues

  • Two reviewers independently flagged the same blocker: the no-mDNS interactive manual path asks for Device ID or host, but discoverAndSelectDevice only returns hostValue and never returns discoveredId for an ff1-... answer. device add then persists id: discoveredId, so the saved device has no stable ID, and device pair fails with “has no stable device ID.” This directly breaks the cross-subnet/manual pairing workflow this PR is intended to enable. Please preserve the entered FF1 ID on this path and cover it.

Medium issues

  • Manual --id input is uppercased in src/commands/device.ts, while mDNS discovery preserves lowercase IDs, and findExistingDeviceEntry/upsertDevice compare IDs case-sensitively. A device added manually as FF1-SKYZ2E3A can later rediscover as ff1-skyz2e3a and fail identity matching, including the address-bridge path that explicitly rejects mismatched IDs. Normalize IDs consistently at storage/discovery boundaries or compare identity keys case-insensitively.

Missing tests

  • Add coverage for the no-mDNS interactive manual path where the user enters an FF1 ID, the device is saved with that stable ID, and device pair can proceed.
  • Add coverage for the manual uppercase ID followed by lowercase mDNS rediscovery case, or otherwise lock in a single canonical casing across discovery, lookup, upsert, topic-store usage, and config persistence.

No PR discussion exceptions or deferred follow-ups were provided, so both issues remain active for this review.

@feralfile-bot
feralfile-bot requested a review from lpopo0856 July 10, 2026 06:01
@feralfile-bot

Copy link
Copy Markdown
Contributor

Automated human review request for PR #92.

Risk zone: red
Responsibility area: FF1 device/runtime and relayer trust path
Why this needs human review: This PR changes a trust-sensitive device control path: encrypted pairing, credential vault storage, relayer fallback, and a new native keyring dependency. That is high-blast-radius and maps to the FF1 device/runtime stewardship boundary.

This PR needs targeted human review on the scoped areas below, not a full second pass of the whole PR.

  • @lpopo0856: please review review the pairing flow, secure topic storage, and LAN-to-relayer fallback boundaries in src/commands/device.ts, src/utilities/ff1-device.ts, src/utilities/handoff-client.ts, src/utilities/handoff-crypto.ts, src/utilities/topic-store.ts, and src/utilities/ff1-relayer.ts. Context: FF1 runtime, live device reliability, and the service behind feralfile.com; device-side connection is shared with Sang (Brandon Yeh).

@ngleanh

ngleanh commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the bot manual-ID findings in ab0c021:

  • interactive no-mDNS ID input now retains a stable ID for device pair
  • device identity lookup/upsert is case-insensitive and stores IDs canonically
  • added regressions for manual ID retention and uppercase-manual/lowercase-mDNS rediscovery

The commit hook passes 423 tests, lint, and copy checks.

@ngleanh
ngleanh requested a review from feralfile-bot July 10, 2026 06:16

@feralfile-bot feralfile-bot left a comment

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.

Reviewers: 3 (approve: 0, comment: 0, request-changes: 3).

Critical issues

  • ff-cli device remove now depends on OS keyring availability before it updates config.json. All three reviewers raised this, and re-review confirms it: src/commands/device.ts:326-331 calls getTopicId(removed.id) and deleteTopicId(removed.id) inside the main command try before writing the updated device list. getTopicId directly calls the native keyring backend in src/utilities/topic-store.ts:39-43, so a locked/unavailable/failing credential store aborts removal via the outer catch at src/commands/device.ts:334-336. Removing a configured device should still succeed; topic cleanup should be best-effort, with a warning or non-fatal cleanup failure.

Missing tests

  • Add command-level coverage for device remove when topic cleanup throws. The current tests cover topic-store behavior and remove lookup paths, but not the regression where a device with an id cannot be removed because getTopicId or deleteTopicId fails. The expected behavior should be: config is still updated, the device is removed, and any credential cleanup failure is reported non-fatally.

Considered but not re-raised

  • I am not re-raising the earlier manual-ID findings from the PR discussion because the thread says they were addressed in ab0c021 with regression coverage.

@ngleanh

ngleanh commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the bot keyring-removal finding in b137f10:

  • persist the device removal to config.json before credential cleanup
  • treat keyring lookup/deletion failures as non-fatal and print a warning
  • add regressions for both lookup and deletion failure paths

npm run verify passes with 425 tests, build/smoke checks, lint, and copy checks. Fresh-context review verdict: accept.

@ngleanh
ngleanh requested a review from feralfile-bot July 10, 2026 06:32

@feralfile-bot feralfile-bot left a comment

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.

Reviewers: 3 (approve: 3, comment: 0, request-changes: 0).

No consolidated findings remain. The prior manual-ID and keyring-removal concerns were explicitly addressed in follow-up commits and are no longer active review findings.

@ngleanh
ngleanh merged commit 0997254 into main Jul 13, 2026
6 checks passed
@ngleanh
ngleanh deleted the codex/relayer-handoff branch July 13, 2026 07:12
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.

Add relayer transport path for sandboxed and app-visible FF1 control

3 participants