Skip to content

controlplane: garbage-collect stale users and disconnected access passes #4189

Description

@elitegreg

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions