*/ public static function all(): array { return [ new Check( key: 'onboarding.ssh_key', group: self::GROUP, severity: Check::SEVERITY_BLOCKING, label: __('readiness.onboarding.ssh_key'), breaks: __('readiness.onboarding.ssh_key_breaks'), tab: 'integrations', // EstablishSshTrust only deploys the PUBLIC half. Every step // after it (HostStep's own shell helper), and later every // Traefik write on a host already onboarded (SshTraefikWriter), // connects with THIS private key. satisfied: filled(app(SecretVault::class)->get('ssh.private_key')), ), new Check( key: 'onboarding.secrets_key', group: self::GROUP, severity: Check::SEVERITY_BLOCKING, label: __('readiness.onboarding.secrets_key'), breaks: __('readiness.onboarding.secrets_key_breaks'), tab: 'integrations', // isUsable() rather than a bare filled() on the config value — // SecretCipher's own docblock warns that a second, shorter // rule that only checks emptiness lets a malformed-but-non- // empty key sail through, exactly the gap that once let two // other call sites reach an uncaught RuntimeException. satisfied: app(SecretVault::class)->isUsable(), ), new Check( key: 'onboarding.vpn_config_key', group: self::GROUP, severity: Check::SEVERITY_BLOCKING, label: __('readiness.onboarding.vpn_config_key'), breaks: __('readiness.onboarding.vpn_config_key_breaks'), tab: 'integrations', // Same reuse-not-reimplement reasoning as isUsable() above: // ConfigVault::available() is the one place that already knows // what counts as a usable key. satisfied: ConfigVault::available(), ), new Check( key: 'onboarding.wg_hub', group: self::GROUP, severity: Check::SEVERITY_BLOCKING, label: __('readiness.onboarding.wg_hub'), breaks: __('readiness.onboarding.wg_hub_breaks'), tab: 'integrations', // Two of three is no half a tunnel, it is none: ConfigureWireguard // writes the peer config from all three at once. Subnet is read // straight from config(), never through Settings/ProvisioningSettings // — that class's own docblock explains why: the compose file // (vpn-dns, vpn-gateway) reads the same CLUPILOT_WG_SUBNET at // container boot, so a console-only override would let the // console show one value while the running containers use another. satisfied: filled(ProvisioningSettings::wgHubPublicKey()) && filled(ProvisioningSettings::wgEndpoint()) && filled((string) config('provisioning.wireguard.subnet')), ), new Check( key: 'onboarding.datacenter', group: self::GROUP, severity: Check::SEVERITY_BLOCKING, label: __('readiness.onboarding.datacenter'), breaks: __('readiness.onboarding.datacenter_breaks'), tab: 'datacenters', satisfied: Datacenter::query()->exists(), ), ]; } }