docs(admin): document asynchronous remote-admin execution - #4490
Conversation
Remote admin commands, session-passkey acquisition, and remote config import all became asynchronous in #4485/#4486 (issue #4482), but that shipped with no user-facing documentation. This fills the gap in the Remote Node Support section: - asynchronous execution + completion polling as numbered behaviors - the 202 + operation-id contract, the poll endpoint, and the fact that a cached passkey still answers 200 immediately - the privacy property the implementation already holds but never stated: operation records keep the command name, source/destination, lifecycle state, and timestamps — never command parameters or session keys - lifecycle states, ten-minute terminal retention, and that a restart surfaces as an interrupted operation rather than a success - Confirmation Semantics for favorite/ignore: confirmed vs rejected vs timeout-is-uncertain, and why the optimistic update survives a timeout - partial config imports reporting what failed alongside what landed The prose originates from @wilhel1812's PR #4483, which solved the same issue independently and documented it properly. That PR was preempted by #4485 landing first; this ports its documentation, adapted to the endpoint and error-code names that actually shipped. Co-Authored-By: wilhel1812 <wilhel1812@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015cgD5kSurLjdeVkZ6PcQD9
|
Claude finished @Yeraze's task —— View job PR Review: docs(admin): document asynchronous remote-admin executionTasks:
This is a docs-only PR documenting async remote-admin behavior. I verified all factual claims against Accuracy: Verified ✅Every claim I checked holds:
Minor Issues1. Small inaccuracy in the privacy description (line 378–380) The docs say: "Operation records hold only the command name, the source and destination identifiers, lifecycle state, and timestamps." The 2. The docs correctly note the cached passkey returns
3. Lifecycle state for The import route sets status to Suggestion (Non-blocking)The "Other remote commands are reported as successful once transmitted" note (line 407–408) is accurate but could be slightly misleading — it implies no ACK is awaited at all, but the implementation does handle TX_DISABLED failures. The distinction is specifically "no routing ACK is awaited" vs. "reported as failed if transmit is disabled." Not a blocking issue. SummaryThe documentation is accurate, well-written, and adds real user-facing value that was genuinely missing. The above items are all minor — none block merging. The privacy property verification is a good addition; it's the kind of claim that's easy to assert but nice to have grounded against the interface definition. Verdict: Approved ✅ — clean docs-only PR with verified factual accuracy. The adaptations from #4483 are faithfully mapped to the shipped API names. |
Review follow-ups, all corrections to claims I made: - The privacy sentence omitted userId, which AdminOperation does store for the ownership check. On a claim about what is retained, an incomplete list is the one kind of error that actually matters — now names the requesting administrator explicitly. - Spell out both halves of the ensure-session-passkey contract: an uncached acquisition returns 202, a cached one answers 200. The earlier wording described only the 200 case and left the async path implicit. - Note that not every command visits every lifecycle state — config import goes from awaiting_passkey straight to terminal, since it sends a sequence of packets rather than awaiting one routing ACK. - Clarify that "successful once transmitted" means no routing ACK is awaited, not that the command cannot fail: an unsendable command (e.g. TX_DISABLED) still fails. Co-Authored-By: wilhel1812 <wilhel1812@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015cgD5kSurLjdeVkZ6PcQD9
|
All four addressed — thanks, and the first one mattered more than "minor". 1. 2. 3. 4. "successful once transmitted" — fixed. Now says no routing ACK is awaited, and that this is not the same as never failing: an unsendable command ( |
|
Claude finished @Yeraze's task —— View job PR Review: docs(admin): document asynchronous remote-admin executionTasks:
This is the re-review after Yeraze addressed all four points from the first pass. I re-read the changed section and re-verified every updated claim against the implementation. All Four Fixes Verified ✅1.
Correctly now includes "the requesting administrator" — 2.
Both cases are now explicitly described. Confirmed against 3.
This accurately captures the divergent path. The import route at 4. "Successful once transmitted" clarification (lines 411–414)
The distinction between "no routing ACK awaited" and "never fails" is now explicit. Accurate — Additional Accuracy Spot-ChecksA few things I also verified while re-reading:
SummaryAll four issues from the previous review are correctly addressed. The documentation is accurate, well-grounded in the implementation, and fills a genuine gap. No blocking issues. Verdict: Approved ✅ |
Documents the async remote-admin behavior that shipped in #4485 / #4486 (issue #4482) with no user-facing docs.
Credit and context
The prose here originates from @wilhel1812's PR #4483. They reported the underlying bug (with browser network logs and host-level
MAX_RETRANSMIT/ proxy correlation), it became issue #4482, and they then opened a complete fix — including the documentation — at 13:43Z on 2026-08-01.I opened #4485 at 14:28Z, 45 minutes later, without checking for open PRs against the issue, and it merged first. That left #4483 conflicting through no fault of its author. The two implementations converged on the same design; the one thing theirs had that mine did not was this documentation.
This ports it, adapted to the endpoint and error-code names that actually shipped. #4483 can be closed with a link here once this lands.
What it documents
In the Remote Node Support section:
202 Accepted+ an opaque operation ID, polled atGET /api/admin/operations/:id, both admin-authenticated, an operation visible only to the admin who started itensure-session-passkeyandimport-configfollow the same pattern, except a cached passkey still answers200immediately since no mesh round-trip is neededAdminOperationrather than copying the claim across.OPERATION_NOT_FOUND, which the client reports as interrupted, not successPASSKEY_TIMEOUT,TX_DISABLED,IMPORT_CONFIG_FAILED,OPERATION_NOT_FOUNDNew Confirmation Semantics subsection for favorite/unfavorite/ignore/unignore — confirmed vs. rejected vs. timeout-is-uncertain, and why the optimistic update survives a timeout but not a rejection. That behavior was implemented in #4485 (including the new ignore/unignore parity) but documented nowhere a user would look.
Also notes that a partial config import reports the channels that failed alongside those that landed, so "1 channel" is distinguishable from "1 of 2 channels" (#4486).
Adaptations from #4483
Their doc described their API; these are the shipped names:
GET /api/admin/commands/:operationIdGET /api/admin/operations/:idpending/running+ separate phase axispending→awaiting_passkey→sending→awaiting_acksucceeded/failed/timed_out/rejectedsucceeded/failed(ACK outcome carried in the result)REMOTE_PASSKEY_TIMEOUT,TRANSPORT_FAILURE,ROUTING_REJECTED,ADMIN_OPERATION_NOT_FOUNDPASSKEY_TIMEOUT,TX_DISABLED,IMPORT_CONFIG_FAILED,OPERATION_NOT_FOUNDWorth recording: @wilhel1812's four-state terminal model is more expressive than what shipped. Surfacing
timed_outandrejectedas operation states beats burying both inresult.ack, since a caller reading onlystatuslearns more. The shipped version's advantage was needing no change to the frontend's existing ACK handling. Reworking it now would churn recently-reviewed code, so it's better as its own issue than as part of a docs PR.Testing
Docs-only — no code changes. Every factual claim was checked against the merged implementation (
adminOperationService.ts,adminRoutes.ts,api.ts) rather than carried over on trust.🤖 Generated with Claude Code
https://claude.ai/code/session_015cgD5kSurLjdeVkZ6PcQD9