Skip to content

feat(client): deregister the machine from the platform on uninstall - #1782

Merged
mikhailm-coder merged 3 commits into
mainfrom
hotfix/client-uninstall-deregister
Aug 14, 2026
Merged

feat(client): deregister the machine from the platform on uninstall#1782
mikhailm-coder merged 3 commits into
mainfrom
hotfix/client-uninstall-deregister

Conversation

@mikhailm-coder

Copy link
Copy Markdown
Contributor

Summary

Client side of 86ahw2uxm — Remote and local uninstall with agent self-deletion: on uninstall, the client reports itself to the platform so the backend can run its deletion logic (mark the machine deleted, revoke the agent's OAuth client, tool cleanup). Covers every uninstall path — local openframe-client uninstall, dashboard-triggered (via the client-uninstall listener PR), and the 410 tenant-gone flow — because the callback runs inside the shared uninstall flow.

Port of flamingo-stack/openframe-oss-tenant#2207 — the client moved to this repo, so the PR moves with it (the tenant PR is closed as superseded).

New on top of the port — a final retry pass:

  • Credentials are loaded once at DeregistrationService construction and held in memory, so they survive the wipe.
  • A reported flag tracks whether the platform accepted (2xx) or terminally dismissed (401/403/404/410) the call.
  • If the in-flow pass never got through, retry_if_unreported() runs one more bounded attempt round at the very end of the uninstall — after the wipe, right before the process exits (on Windows: before the binary-cleanup script is launched). A transient outage during the first pass no longer permanently loses the deregistration; the tools-uninstall + directory-wipe duration acts as natural spacing between the two windows.

Proposed backend contract (⚠️ needs agreement — @backend)

POST {gateway}/clients/api/agents/uninstall with X-Machine-Id + X-Client-Secret headers (mirrors how /reinstall authenticates the machine). Expected semantics:

  • 2xx → machine deregistered (endpoint must be idempotent: already-deleted → 204, never an error)
  • 401/403/410 → platform already forgot this machine → client proceeds
  • 404 → endpoint not deployed yet → client proceeds (this PR can merge before the backend ships)

Behavior

  • Best-effort, never blocks the wipe: each pass is 3 attempts, 5s apart, 15s HTTP timeout (~55s worst case per pass; the second pass only runs if the first one failed with transient errors).
  • Ordering: the first pass runs after the OS service is stopped, so a live heartbeat can't resurrect the just-deleted machine record, and before directories are wiped, so on-disk state is still intact; the final pass relies on the in-memory credentials only.
  • Reinstall guard: Service::install() invokes the previous binary's uninstall when installing over an existing installation; it sets OPENFRAME_REINSTALL=1 and the uninstall skips deregistration — upgrades never delete the device. Env var rather than a CLI flag so older binaries (which the installer may invoke) ignore it instead of failing on an unknown argument.
  • Credential source: agent_config.json, falling back to the persisted machine-info store (registry / plist / /etc/openframe) if the config is missing or corrupt.

Testing

  • OPENFRAME_VERSION=0.1.0 cargo check clean on host and x86_64-pc-windows-gnu.
  • OPENFRAME_VERSION=0.1.0 cargo test -- --skip test_ensure_admin: 131 passed, 0 failed (includes the terminal-vs-transient status classification tests).
  • E2E requires the backend endpoint; until it ships the callback logs a 404 "already gone" and the uninstall proceeds exactly as today.

🤖 Generated with Claude Code

Ported from openframe-oss-tenant PR #2207 (branch hotfix/client-uninstall-deregister)
as the client moved to this repo, plus a final retry pass: credentials are loaded
up front and held in memory, and if the in-flow deregistration never got through,
one last attempt round runs at the end of the uninstall, after the wipe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The GCP load balancer rejects body-less HTTP/1.1 POSTs with 411 Length
Required before they reach the backend, so deregistration never succeeded.
Send a deletedAt timestamp body; the backend ignores it today but the
request now carries Content-Length and goes through.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikhailm-coder
mikhailm-coder enabled auto-merge (squash) August 14, 2026 13:05
@mikhailm-coder
mikhailm-coder merged commit 5541890 into main Aug 14, 2026
8 of 13 checks passed
@mikhailm-coder
mikhailm-coder deleted the hotfix/client-uninstall-deregister branch August 14, 2026 13: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.

2 participants