seconds between attempts */ public function backoff(): array { return [30, 120]; } public $timeout = 2200; public function __construct(public string $uuid) { $this->onConnection('provisioning'); $this->onQueue('provisioning'); } public function handle(): void { $host = Host::query()->where('uuid', $this->uuid)->first(); if ($host === null) { return; } foreach ($host->runs()->get() as $run) { // Block until the runner lock is free (a long step may hold it for up // to the step timeout), then delete — cascades events + resources. Cache::lock('run:'.$run->uuid, 2200)->block(2100, fn () => $run->delete()); } // Re-read after waiting on the run locks above: a registration that was // still running may have written dns_record_id in the meantime, and the // stale copy loaded before the wait would skip the deletion and then // delete the row that held the id. $host->refresh(); // Legacy: a host onboarded before RegisterHostDns moved off the public // Hetzner zone may still carry a public record id here — remove it so // a deleted host does not leave its (already-published) management // address behind. Hosts onboarded since never set this column; see // clupilot:prune-host-dns for the ones already published elsewhere. // // The record identifier lives on the row about to be deleted, so this // has to happen first — otherwise the host's management address stays // published with nothing left to clean it up. if (filled($host->dns_record_id)) { // Deliberately NOT swallowed: the row about to be deleted holds the // only reference to that record, so continuing past a failure would // leave the machine's management address published for good with no // way left to find it. Failing here lets the queue retry, and a // permanently broken DNS provider becomes a visible failed job // rather than a silent leak. app(HetznerDnsClient::class)->deleteRecord($host->dns_record_id); } // The internal hostsdir entry (vpn-dns) — same reasoning as above, // for the name every host has actually been getting since. The name // lives on the row about to be deleted, so it has to come out first. if (filled($host->dns_name)) { app(HostDnsDirectory::class)->remove($host->dns_name); } if (filled($host->wg_pubkey)) { // Tombstone the console's view of this peer as well. The FK only // nulls host_id, which would leave an enabled-but-absent row behind: // shown forever as "wird angewendet", and re-enabling it would put a // deleted host's key back on the hub. Soft-deleting hands it to the // same machinery every other revocation uses — the removal below // clears the tombstone, and a sync retries if that removal is lost. VpnPeer::query()->where('public_key', $host->wg_pubkey)->delete(); RemoveWireguardPeer::dispatch($host->wg_pubkey); } $host->delete(); } }