Parent: #4193
Problem
Nothing garbage-collects user accounts or access passes. A user account occupies its (client_ip, user_type) PDA until the owner or a USER_ADMIN holder deletes it. When a validator is decommissioned without running doublezero disconnect, that account stays forever, holding a device seat, a tunnel slot, a dz_ip, and a live tunnel on the device. The next tenant of the IP inherits the mess.
Access passes have the same problem from the other side. After a clean disconnect the pass remains at (client_ip, old_key) with connection_count = 0 and status = Disconnected, still carrying epoch entitlement and multicast allowlists for a keypair that no longer has the address.
We already have a trustworthy liveness signal and are not using it. The device's metrics_publisher writes BGP state onchain, and only that publisher can (smartcontract/programs/doublezero-serviceability/src/processors/user/set_bgp_status.rs:60-77):
if device.metrics_publisher_pk != *payer_account.key {
return Err(DoubleZeroError::NotAllowed.into());
}
...
user.bgp_status = value.bgp_status;
user.last_bgp_reported_at = slot;
if value.bgp_status == BGPStatus::Up {
user.last_bgp_up_at = slot;
}
Proposed change
A reconciler service holding USER_ADMIN that periodically:
- Deletes users whose
bgp_status has been Down, and whose last_bgp_up_at / last_bgp_reported_at are older than a configured threshold. Start conservative (multiple days) and tighten with evidence.
- Closes access passes with
connection_count == 0 whose entitlement has lapsed, subject to the DZF_LOCKED flag, which exists precisely to keep automated reconcilers away from foundation-managed passes.
- Emits metrics and logs per reclaimed account so we can see how often this fires and validate the threshold before trusting it.
This needs no program change and no new instruction. It is the highest-value, lowest-risk piece of the parent issue: it frees the IP before the next tenant shows up, which removes most of the recurring pain without anyone having to file a support request.
Depends on the client-side owner check landing first. Until then a stale account can be brought Up by the new tenant that adopted it, and the reconciler would skip exactly the accounts it should be collecting.
Open questions
- Where does this live? The monitor already watches serviceability accounts (
controlplane/monitor/internal/serviceability/watcher.go), so it may be the natural host.
- Should deletion be staged (notify, then delete) for users whose owner is a known validator, or is BGP-down-for-N-days sufficient signal on its own?
- Threshold as a constant, or in
GlobalConfig so it can be tuned without a redeploy?
Testing verification
- A user with BGP down past the threshold is deleted, its device counters and access-pass
connection_count decrement, and its tunnel disappears from the device config.
- A user with BGP down but inside the threshold is untouched.
- A
DZF_LOCKED pass is never closed.
Parent: #4193
Problem
Nothing garbage-collects user accounts or access passes. A user account occupies its
(client_ip, user_type)PDA until the owner or aUSER_ADMINholder deletes it. When a validator is decommissioned without runningdoublezero disconnect, that account stays forever, holding a device seat, a tunnel slot, adz_ip, and a live tunnel on the device. The next tenant of the IP inherits the mess.Access passes have the same problem from the other side. After a clean disconnect the pass remains at
(client_ip, old_key)withconnection_count = 0andstatus = Disconnected, still carrying epoch entitlement and multicast allowlists for a keypair that no longer has the address.We already have a trustworthy liveness signal and are not using it. The device's
metrics_publisherwrites BGP state onchain, and only that publisher can (smartcontract/programs/doublezero-serviceability/src/processors/user/set_bgp_status.rs:60-77):Proposed change
A reconciler service holding
USER_ADMINthat periodically:bgp_statushas beenDown, and whoselast_bgp_up_at/last_bgp_reported_atare older than a configured threshold. Start conservative (multiple days) and tighten with evidence.connection_count == 0whose entitlement has lapsed, subject to theDZF_LOCKEDflag, which exists precisely to keep automated reconcilers away from foundation-managed passes.This needs no program change and no new instruction. It is the highest-value, lowest-risk piece of the parent issue: it frees the IP before the next tenant shows up, which removes most of the recurring pain without anyone having to file a support request.
Depends on the client-side owner check landing first. Until then a stale account can be brought
Upby the new tenant that adopted it, and the reconciler would skip exactly the accounts it should be collecting.Open questions
controlplane/monitor/internal/serviceability/watcher.go), so it may be the natural host.GlobalConfigso it can be tuned without a redeploy?Testing verification
connection_countdecrement, and its tunnel disappears from the device config.DZF_LOCKEDpass is never closed.