$domain, 'scan' => self::fetch_scan( $domain ), 'exchange' => self::fetch_exchange( $domain ), 'dns' => self::check_dns( $domain ), 'checkedAt' => gmdate( 'c' ), ); set_transient( self::CACHE_KEY, $status, 10 * MINUTE_IN_SECONDS ); return $status; } /** * One-click onboarding: submit to the discovery index, request a fresh * review of the current manifest, and register with the matcher. All * calls are idempotent — safe to click repeatedly. * * @return array Fresh status snapshot after connecting. */ public static function connect() { $domain = self::domain(); $json = array( 'timeout' => 10, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'domain' => $domain ) ), ); // Matcher registration first — a match read registers the domain (the // Exchange auto-submits unknown domains into its pipeline), and the // discovery calls below can be slow (a re-check crawls the site), so // the most important write must not depend on them finishing. wp_remote_get( self::exchange_url() . '/api/match/' . rawurlencode( $domain ), array( 'timeout' => 15 ) ); // Submit covers a never-seen domain; check re-verifies a known one. wp_remote_post( self::scan_url() . '/api/discovery', $json ); wp_remote_post( self::scan_url() . '/api/discovery/check', $json ); return self::refresh(); } /** * The DNS TXT record that verifies domain ownership to the network. * * @param string $domain The site domain. * @return array { name, value } of the record to create. */ public static function dns_record( $domain ) { return array( 'name' => '_ai.' . $domain, 'value' => 'v=aid1 host=' . $domain, ); } /** * Whether a set of TXT records contains the verification record. * * @param array $records Records as returned by dns_get_record(). * @return bool */ public static function txt_present( $records ) { foreach ( (array) $records as $record ) { $txt = is_array( $record ) ? ( $record['txt'] ?? '' ) : (string) $record; if ( false !== stripos( $txt, 'v=aid1' ) ) { return true; } } return false; } /** * Reduce a Scan entity response to the facts the status panel shows. * * @param array $body Decoded /api/discovery/{domain} response. * @return array Status fragment. */ public static function parse_scan_entity( $body ) { $signals = isset( $body['entity']['metadata']['signals'] ) && is_array( $body['entity']['metadata']['signals'] ) ? $body['entity']['metadata']['signals'] : array(); return array( 'reachable' => true, 'indexed' => ! empty( $body['entity'] ), 'signed' => ! empty( $signals['selfSigned']['present'] ), 'hashValid' => ! empty( $signals['hashValid']['present'] ), 'dnsVerified' => ! empty( $signals['dnsVerified']['present'] ), ); } /** * Reduce a matcher response to the facts the status panel shows. * * @param array $body Decoded /api/match/{domain} response. * @return array Status fragment. */ public static function parse_exchange_response( $body ) { $matches = isset( $body['matches'] ) && is_array( $body['matches'] ) ? count( $body['matches'] ) : 0; return array( 'reachable' => true, 'registered' => true, 'matches' => isset( $body['count'] ) ? intval( $body['count'] ) : $matches, 'coldStart' => ! empty( $body['coldStart'] ), ); } /** * Query the discovery index for this domain's standing. * * @param string $domain The site domain. * @return array Status fragment. */ private static function fetch_scan( $domain ) { $response = wp_remote_get( self::scan_url() . '/api/discovery/' . rawurlencode( $domain ), array( 'timeout' => 10 ) ); if ( is_wp_error( $response ) ) { return array( 'reachable' => false ); } $code = wp_remote_retrieve_response_code( $response ); if ( 404 === $code ) { return array( 'reachable' => true, 'indexed' => false, ); } if ( 200 !== $code ) { return array( 'reachable' => false ); } $body = json_decode( wp_remote_retrieve_body( $response ), true ); return self::parse_scan_entity( is_array( $body ) ? $body : array() ); } /** * Query the matcher for this domain's match rows (registers as a side effect). * * @param string $domain The site domain. * @return array Status fragment. */ private static function fetch_exchange( $domain ) { $response = wp_remote_get( self::exchange_url() . '/api/match/' . rawurlencode( $domain ), array( 'timeout' => 10 ) ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return array( 'reachable' => false ); } $body = json_decode( wp_remote_retrieve_body( $response ), true ); return self::parse_exchange_response( is_array( $body ) ? $body : array() ); } /** * Live DNS check for the verification record. * * @param string $domain The site domain. * @return array Status fragment. */ private static function check_dns( $domain ) { if ( ! function_exists( 'dns_get_record' ) ) { return array( 'checked' => false ); } $record = self::dns_record( $domain ); $records = @dns_get_record( $record['name'], DNS_TXT ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- dns_get_record warns on lookup failure. return array( 'checked' => true, 'present' => self::txt_present( $records ), ); } }