Skip to content

add Remote Access utility (UPnP / NAT-PMP hole-punch)#560

Open
IrosTheBeggar wants to merge 1 commit intomasterfrom
claude/condescending-hertz-964525
Open

add Remote Access utility (UPnP / NAT-PMP hole-punch)#560
IrosTheBeggar wants to merge 1 commit intomasterfrom
claude/condescending-hertz-964525

Conversation

@IrosTheBeggar
Copy link
Copy Markdown
Owner

Summary

  • New admin-UI toggle that asks the router (via UPnP, optionally NAT-PMP) to open a port to mStream so the library is reachable from outside the home LAN — no manual port forwarding required.
  • All pure Node: nat-api dep, no bundled binaries. The pre-existing rpn/FRP scaffolding is left untouched for a future reverse-tunnel fallback PR.
  • Hardened against the nat-api library's known NAT-PMP crash path with a narrow uncaughtException guard and timeouts on every map/unmap call.

What's in here

  • Config — new remoteAccess Joi block (src/state/config.js) alongside the untouched rpn options.
  • Runtimesrc/state/remote-access.js owns the nat-api client, lease-renewal timer, kill-queue cleanup, crash guard, and pending-promise reject hooks so a mid-op library crash never leaves an HTTP handler hung.
  • APIGET /api/v1/admin/remote-access for status, POST /api/v1/admin/remote-access/toggle that branches on enabled. In-flight guard returns 409 on concurrent toggles. Reuses the existing admin-auth middleware.
  • Server integrationremoteAccess.setup() after server.listen(); reboot() now calls teardown() first so router state stays in sync when port / protocol changes.
  • UI — new "Remote Access" section in webapp/admin/ with an enable toggle, a single "Also try NAT-PMP (experimental)" checkbox, public-port and lease-seconds inputs, live status (public URL, lease countdown, last error), and a security notice.
  • Docs — OpenAPI spec updated with new tag, paths, RemoteAccessStatus schema, and 403/409 responses.

Known limitations

  • NAT-PMP path is behind an experimental checkbox — the underlying nat-api library can throw from inside its UDP 'message' handler on malformed datagrams. We catch it via a scoped uncaughtException handler and keep the server alive, but discourage the option by default.
  • nat-api itself is unmaintained (last publish ~7y). Fine for v1 alpha; worth swapping for @libp2p/upnp-nat or a newer lib if we want to go GA.
  • Mapping release on process exit is best-effort: Node's exit event can't await async UDP traffic, so cleanup relies on the lease TTL (default 2h) if the shutdown isn't graceful.
  • Velvet UI parity is out of scope; this lands only in the default admin panel.

Test plan

  • Verify on a UPnP-enabled consumer router: toggle on → public URL shown in admin panel, accessible from off-LAN (cellular).
  • Confirm the router admin page shows the UPnP entry appear / disappear across toggle on/off.
  • On a network without UPnP, confirm lastError populates and the LAN-side server still works.
  • Opt into NAT-PMP (experimental checkbox) on a network where it misbehaves; confirm the server stays alive and the crash-guard warning shows up in logs.
  • Change the mStream port via the admin UI while Remote Access is enabled → confirm the old router mapping is released and a new one is established post-reboot.
  • Fire two concurrent POST /toggle requests → one gets 200, the other gets 409.
  • npm run lint and npm run docs:api:validate clean (baseline of ~55 pre-existing lint issues should be unchanged).

🤖 Generated with Claude Code

New admin feature that asks the user's router to open a port via UPnP
(and optionally NAT-PMP) so mStream can be reached from outside the
home LAN without manual router port forwarding.

- Config: new `remoteAccess` Joi block alongside the existing (untouched)
  `rpn` options reserved for the future FRP-based fallback.
- Runtime: `src/state/remote-access.js` owns the nat-api client, lease
  renewal timer, kill-queue cleanup, and a narrow uncaughtException guard
  that swallows only nat-api's known NAT-PMP crash path so a library bug
  can't take down the server. map/unmap calls are timeout-guarded to
  prevent hung HTTP responses.
- API: `GET /api/v1/admin/remote-access` for status and a single
  `POST /api/v1/admin/remote-access/toggle` endpoint that branches on
  the `enabled` flag. Serialized via an in-flight guard (409 on concurrent
  toggles). Gated by the existing admin-auth middleware in
  `src/api/admin.js`.
- Server integration: boot-time `remoteAccess.setup()` after listen(),
  and `reboot()` now tears down the mapping first so router state stays
  in sync across port / protocol changes.
- UI: new "Remote Access" section in webapp/admin/ with an enable toggle,
  a single "Also try NAT-PMP (experimental)" checkbox, public-port and
  lease-seconds inputs, live status (public URL, lease countdown, last
  error), and a security notice.
- Docs: OpenAPI spec updated with the new tag, paths, RemoteAccessStatus
  schema, and 403/409 responses.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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