Skip to content

fix(admin): decouple remote commands from HTTP requests - #4483

Open
wilhel1812 wants to merge 1 commit into
Yeraze:mainfrom
wilhel1812:codex/issue-4482-async-remote-admin
Open

fix(admin): decouple remote commands from HTTP requests#4483
wilhel1812 wants to merge 1 commit into
Yeraze:mainfrom
wilhel1812:codex/issue-4482-async-remote-admin

Conversation

@wilhel1812

Copy link
Copy Markdown
Contributor

Summary

  • return 202 Accepted for validated remote admin commands and execute passkey acquisition/transmission outside the Express request lifecycle
  • add an admin-protected, in-memory operation status API with UUID identifiers, explicit phases/statuses, stable error codes, and five-minute terminal retention
  • keep the existing frontend promise/loading/toast flow by polling operation status, including optimistic timeout semantics and explicit rejection handling for favorite/ignore commands
  • preserve synchronous 200 behavior for local-node commands and add no new UI
  • document the asynchronous API and confirmation/failure semantics

Root cause

A remote admin request could synchronously spend up to 45 seconds acquiring a session passkey and then another 30 seconds awaiting a routing ACK. Holding the HTTP request open for that mesh work made otherwise valid commands vulnerable to proxy/origin disconnects and HTML 502 responses.

API compatibility

  • Local commands retain their existing synchronous 200 response.
  • Remote commands now return 202 with an operation snapshot.
  • The bundled frontend polls GET /api/admin/commands/:operationId until a terminal result, so existing control disabling, command sequencing, and toast behavior are preserved.
  • Favorite, unfavorite, ignore, and unignore await routing ACKs. ACK timeout remains uncertain/optimistic; explicit rejection does not mutate client state.

Validation

Passed:

  • targeted operation, route, API, and frontend tests: 117/117
  • npm run lint:ci
  • npm run typecheck
  • npm run build
  • npm run build:server
  • npm run docs:build

Repository baselines observed locally:

  • npm run lint reports the existing raw-lint baseline (2,275 findings); the enforced lint ratchet passes and improves touched-file counts.
  • npm run typecheck:tests reports 405 existing test-type errors, with none in files changed by this PR.
  • npm run test:run completed 12,917 passing tests and reported eight failures in unrelated MeshCore virtual-node timing and link-preview URL-validation tests. The same two files remain reproducibly failing when run alone in this local Node 25 environment; no code in those paths is changed here.

Fixes #4482

@Yeraze

Yeraze commented Aug 1, 2026

Copy link
Copy Markdown
Owner

@wilhel1812 — an explanation is owed here, and the short version is that this PR was preempted by my mistake.

What happened

You reported the bug with browser network logs and host-level MAX_RETRANSMIT / proxy correlation, that became #4482, and you opened this PR with a complete fix at 13:43Z on 2026-08-01.

I started implementing at roughly the same time and opened #4485 at 14:28Z — 45 minutes after this one — without checking whether a PR already existed against the issue. It merged first, followed by #4486 for the remaining blocking endpoints (/ensure-session-passkey, /import-config). That is why this PR now shows as conflicting: not because anything is wrong with it, but because a duplicate landed on top of it. That check was mine to do and I skipped it.

Your work is not discarded

The two implementations converged on the same design — 202 + an in-memory operation registry with stable error codes, client polling, local-node commands staying synchronous, no new UI. Where they differ, yours was ahead in two places:

Documentation. Mine shipped with none. Yours documented the async contract, the lifecycle, the retention window, and the confirmation semantics. That is ported in #4490, adapted to the endpoint and error-code names that shipped, with a Co-Authored-By trailer for you. It also carries a privacy property you wrote down and I never did — that operation records keep only the command name, source/destination, lifecycle state and timestamps, never command parameters or session keys. I verified it holds for the merged code too; it just had no home until you wrote it.

Your status model is better than what shipped. Surfacing timed_out and rejected as distinct operation states beats burying both inside result.ack, since a caller reading only status learns more. What shipped won on not requiring a frontend change, which is the weaker argument. That is now tracked as #4492, crediting you.

Leaving this open for now

I am not closing it while #4490 is still unmerged — if that PR is rejected or reworked, your prose here is the source of record. Once #4490 lands, this can be closed with a link to it and to #4492.

Sorry for the wasted effort. The concrete change on my side is checking for open PRs against an issue before writing code for it.

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.

[BUG] Remote admin HTTP requests can block up to 75s (passkey + ACK waits), causing upstream 502s under RF retransmission

2 participants