QuickZTNA User Guide
Home Machine Registration & Lifecycle Machine Delete & Cleanup

Machine Delete & Cleanup

What We’re Testing

Machine deletion is handled by POST /api/machine-admin with action: "delete". It requires admin privileges and performs a cascade cleanup:

Records DELETED (ephemeral data):

  • relay_sessions — active relay connections
  • posture_reports — device posture data
  • key_exchanges — WireGuard key exchange records (both requester and responder)
  • segmentation_group_members — department/group memberships
  • node_key_signatures — node key verification records
  • remote_desktop_sessions — active remote desktop sessions
  • dns_records — MagicDNS A record for the machine (best-effort)

Records SET NULL (audit/compliance data preserved):

  • session_recordings — remote shell/desktop recordings (expensive R2 assets)
  • access_review_decisions — governance review records
  • jit_access_grants — just-in-time access grants

On the client side, ztna logout clears local tokens and state files but preserves the org ID for future re-login.

Your Test Setup

MachineRole
Win-A Admin — perform deletion from dashboard/API
🐧 Linux-C Target machine to delete (use a test machine, not your primary!)

WARNING: Deletion is irreversible. For this test, consider registering a temporary test machine rather than deleting a production one.


ST1 — Delete Machine via Dashboard

What it verifies: An admin can delete a machine from the dashboard, removing it from the org.

Steps:

  1. First, register a temporary test machine. On 🐧 Linux-C :
# Create a disposable registration
ztna logout
export ZTNA_AUTH_KEY="tskey-auth-YOUR_REUSABLE_KEY"
ztna up --auth-key "$ZTNA_AUTH_KEY" --hostname test-delete-me
  1. Verify it appears on the dashboard:

    • On Win-A , open https://login.quickztna.com/machines
    • Find test-delete-me in the list
  2. Click on test-delete-me to open the detail page.

  3. Click Delete (or the trash icon). Confirm the deletion.

  4. Verify:

    • The machine disappears from the list
    • Refresh the page to confirm

Pass: Machine removed from the list. No error on deletion. Page refreshes cleanly without the deleted machine.

Fail / Common issues:

  • FORBIDDEN (403) — only org admins can delete machines. Check your role.
  • Machine still appears after refresh — browser cache. Hard refresh with Ctrl+F5.

ST2 — Delete Machine via API

What it verifies: The delete action in /api/machine-admin removes the machine and cascades cleanup.

Steps:

  1. Register another temporary machine (or reuse the flow from ST1):
ztna up --auth-key "$ZTNA_AUTH_KEY" --hostname test-api-delete
  1. Get the machine ID:
TOKEN="YOUR_ADMIN_TOKEN"
ORG_ID="YOUR_ORG_ID"

MACHINE_ID=$(curl -s "https://login.quickztna.com/api/db/machines?org_id=eq.$ORG_ID&name=eq.test-api-delete&select=id" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json; print(json.load(sys.stdin)['data'][0]['id'])")

echo "Machine ID: $MACHINE_ID"
  1. Delete via API:
curl -s -X POST https://login.quickztna.com/api/machine-admin \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"action\":\"delete\",\"machine_id\":\"$MACHINE_ID\"}" | python3 -m json.tool

Expected response:

{
  "success": true,
  "data": {
    "deleted": "machine-id-here"
  }
}
  1. Verify the machine is gone:
curl -s "https://login.quickztna.com/api/db/machines?id=eq.$MACHINE_ID&select=id,name" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

Expected: Empty data array:

{
  "success": true,
  "data": []
}

Pass: Delete returns the machine ID in deleted field. Subsequent lookup returns empty results.

Fail / Common issues:

  • NOT_FOUND (404) — the machine ID is wrong or already deleted.
  • FORBIDDEN (403) — admin privileges required.

ST3 — Client-Side Cleanup via ztna logout

What it verifies: ztna logout clears local tokens and state files, leaving the machine in a clean state for re-registration.

Steps:

  1. On 🐧 Linux-C , with VPN running:
ztna down

Expected:

Stopped VPN daemon (PID 12345)
VPN stopped.
  1. Clear local state:
ztna logout

Expected output:

Logged out. Tokens and state cleared. (org_id preserved for re-login)
  1. Verify state is cleared:
ztna status

Expected:

QuickZTNA Status
================
Authenticated: false
               (run 'ztna login' to authenticate)
Machine:       Not registered (run 'ztna up')
  1. Check that tokens file is removed:
ls -la ~/.config/ztna/tokens.json 2>&1

Expected: No such file or directory — the tokens file was deleted.

  1. Check that state file is removed:
ls -la ~/.config/ztna/state.json 2>&1

Expected: No such file or directory — the state file was deleted.

Pass: ztna logout outputs the confirmation message. Authenticated: false. Both tokens.json and state.json are removed. Org ID is preserved in the config for convenient re-login.

Fail / Common issues:

  • Authenticated: true after logout — the tokens file may not have been cleared. Delete manually: rm ~/.config/ztna/tokens.json ~/.config/ztna/state.json
  • Encrypted files (.enc suffix) may remain — ztna logout removes both the plaintext and encrypted variants.

ST4 — Verify Cascade Cleanup

What it verifies: Related records (relay sessions, posture reports, group memberships) are cleaned up when a machine is deleted.

Steps:

  1. Before deleting the machine, check if related records exist. On Win-A :
# Check posture reports for the machine
curl -s "https://login.quickztna.com/api/db/posture_reports?machine_id=eq.$MACHINE_ID&select=id" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

# Check group memberships
curl -s "https://login.quickztna.com/api/db/segmentation_group_members?machine_id=eq.$MACHINE_ID&select=id" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
  1. Delete the machine (as in ST2).

  2. After deletion, verify cascade:

# Posture reports should be gone
curl -s "https://login.quickztna.com/api/db/posture_reports?machine_id=eq.$MACHINE_ID&select=id" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

# Group memberships should be gone
curl -s "https://login.quickztna.com/api/db/segmentation_group_members?machine_id=eq.$MACHINE_ID&select=id" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

Expected: All queries return empty data: [] after deletion.

Pass: Related records are cleaned up. No orphaned data in posture_reports, segmentation_group_members, relay_sessions, or key_exchanges.

Fail / Common issues:

  • Posture reports still exist — the cascade DELETE may have failed. Check the backend logs.
  • If session recordings existed for this machine, they should still exist but with machine_id: null (SET NULL behavior for compliance data preservation).

ST5 — Deleted Machine Cannot Heartbeat

What it verifies: After server-side deletion, the client’s heartbeat is rejected because the node key no longer exists in the database.

Steps:

  1. On 🐧 Linux-C , start the VPN (if tokens are still valid from a previous session):
ztna up

The machine will attempt to heartbeat. Since the machine record was deleted from the server, the heartbeat will fail.

  1. The expected behavior is:

    If the machine was deleted server-side while the client is running:

    • The next heartbeat returns INVALID_NODE_KEY (401)
    • The client may show connection errors or disconnect

    If the client was stopped, machine deleted, then client restarted:

    • ztna up will attempt to re-register (creating a new machine record with a new ID and IP)
  2. Check the status:

ztna status

Expected (if heartbeat rejected): The machine may show as disconnected or display an authentication error.

Pass: Deleted machine’s node key is rejected by the backend. The client cannot masquerade as a machine that no longer exists.

Fail / Common issues:

  • Client re-registers automatically — this is expected behavior with auth-key or cached tokens. The backend creates a new machine record. The old machine ID is gone.
  • No error visible — the client may silently reconnect. Check that the machine ID is different from the deleted one.

Cleanup: Re-register Linux-C properly for future tests:

ztna logout
ztna login --github  # or --auth-key
ztna up

Summary

Sub-testWhat it provesPass condition
ST1Dashboard deletionMachine removed from list after delete
ST2API deletiondelete action returns machine ID, lookup returns empty
ST3Client-side cleanupztna logout clears tokens and state, Authenticated: false
ST4Cascade cleanupRelated records (posture, groups, relays) deleted; audit data preserved
ST5Post-deletion heartbeatDeleted machine’s heartbeat rejected with INVALID_NODE_KEY