Skip to content

fix(darwin): fall back when CoreWLAN lacks permission - #191

Open
Herby9000 wants to merge 1 commit into
shazow:mainfrom
Herby9000:fix/darwin-corewlan-permission-fallback
Open

fix(darwin): fall back when CoreWLAN lacks permission#191
Herby9000 wants to merge 1 commit into
shazow:mainfrom
Herby9000:fix/darwin-corewlan-permission-fallback

Conversation

@Herby9000

Copy link
Copy Markdown

Summary

  • keep native CoreWLAN as the primary Darwin scanner
  • fall back to system_profiler SPAirPortDataType only when CoreWLAN reports Location Services / permission denial
  • preserve current-network active state from the profiler fallback when networksetup -getairportnetwork is also unavailable
  • retain separate SSID/security variants and preserve both CoreWLAN and fallback errors if both paths fail

Why

On an ad-hoc-signed CLI on macOS 26.5.2, CoreWLAN scanning is denied/redacted because the calling binary has no stable TCC Location Services authorization. The previous system_profiler backend still returns visible and current-network information because it runs as an Apple platform binary.

A CoreWLAN-only implementation therefore regresses from one active/visible network to none in the same execution context. A stable signed helper/app with an interactive location authorization flow could make pure CoreWLAN work, but that changes the project's distribution and UX. This patch keeps the fast native path for authorized callers while restoring compatibility for ordinary CLI builds.

Live verification

Apple Silicon, macOS 26.5.2, ad-hoc/linker-signed binary. SSIDs were redacted from the report:

json_valid=true
network_count=3
visible_count=2
active_count=1
known_count=1
stderr_empty=true

The number of nearby networks varied during repeated scans, but active-state recovery and valid JSON output were stable.

Tests

go test -race -count=1 ./...
go vet ./...

Added coverage for:

  • permission-denied CoreWLAN → successful profiler fallback
  • both fallback errors remaining discoverable with errors.Is
  • current/other network classification, RSSI, and security parsing
  • SSIDs beginning with awdl, containing : , or matching profiler section labels
  • separate open/WPA variants sharing an SSID
  • unknown security remaining SecurityUnknown
  • active-state propagation across duplicate same-security records

Follow-up to #190.

@shazow

shazow commented Jul 22, 2026

Copy link
Copy Markdown
Owner

@Herby9000 is the fallback only necessary for the scanning condition? Or is all the functionality by CoreWLAN more restrictive than our previous v0.13.0 tagged darwin backend implementation and we should revert the CoreWLAN changes?

Can you do an analysis to see if CoreWLAN is worth it? What are the pros/cons?

@Herby9000

Copy link
Copy Markdown
Author

I compared the current backend, v0.13.0, and the live behavior on the same Apple Silicon/macOS 26.5.2 host.

Scope: CoreWLAN is scan-only

CoreWLAN is only used by scanVisibleNetworks. Everything else still uses the same command-based mechanisms as v0.13.0:

  • interface discovery, power state, current association, preferred networks, connect, forget, and radio control: networksetup
  • password lookup/update: security

So the Location Services restriction does not affect joining, forgetting, toggling Wi-Fi, preferred-network enumeration, or keychain operations through CoreWLAN; those paths do not use CoreWLAN at all. The fallback only needs to replace visible-network scanning (and can supply active-state metadata when networksetup -getairportnetwork is unavailable).

What CoreWLAN buys us

  • It is the public API intended to perform an actual Wi-Fi scan. system_profiler collects a report and does not promise ScanAuto/ScanForce freshness semantics.
  • It returns structured CWNetwork objects rather than locale-/format-/indentation-sensitive text.
  • It exposes one record per AP, including BSSID, RSSI, channel/band (converted to frequency), and security capabilities. This supports the newer backend model and lets us keep same-SSID security variants separate.
  • It provides native error domains/codes, so permission, timeout, unsupported, and unavailable-device failures can be classified.
  • When already authorized, it avoids spawning and parsing system_profiler. I could not benchmark the successful authorized path on this host because this ad-hoc CLI cannot acquire stable TCC authorization.

Costs / regressions

  • Modern macOS treats scan SSID/BSSID data as location-sensitive. An ad-hoc/rebuilt CLI has no stable identity/authorization flow, so CoreWLAN either returns kCWOperationNotPermittedErr or records with redacted SSIDs. That is a real regression for the project's normal CLI distribution if there is no fallback.
  • CoreWLAN requires cgo, Objective-C, framework linking, Darwin CI/release runners, and architecture-specific release handling. A no-cgo Darwin build currently reports unsupported.
  • Correctly requesting Location authorization would require a stable signed app/helper and interactive UX, which is much larger than a CLI backend change.

system_profiler has the opposite tradeoff: it works here because an Apple platform binary performs the collection, and it restores the old behavior without requiring wifitui itself to receive location permission. But it is slow, text-format dependent, and the current/v0.13 parser cannot provide BSSID/frequency.

Live comparison (identifiers withheld)

Three fresh runs each:

  • CoreWLAN-only main: 8.049s, then 0.115s, 0.078s; each returned 1 saved network, 0 visible/active/AP records, plus the permission diagnostic.
  • PR fix(darwin): fall back when CoreWLAN lacks permission #191 hybrid: 8.203s, 7.999s, 7.900s; each returned a valid list with an active visible network and 17–19 AP observations; no scan diagnostic.
  • direct system_profiler SPAirPortDataType: 7.912s, 7.985s, 7.861s.

That confirms the hybrid's ~8s denied-path cost is almost entirely system_profiler; the CoreWLAN denial itself is fast after the first attempt. Nearby AP counts naturally varied between scans.

Recommendation

I would not revert the broader Darwin/CoreWLAN changes. Keep CoreWLAN primary and retain the narrowly scoped system_profiler compatibility fallback in #191. This preserves the structured/fresh native path for authorized packaged callers while restoring v0.13-like behavior for ordinary CLI builds.

I would also consider allowing the same profiler fallback for ErrNotSupported (especially CGO_ENABLED=0 Darwin builds), not just ErrScanPermissionDenied. I would not automatically fall back on every timeout/generic CoreWLAN failure: that adds ~8 seconds and can hide genuine radio/framework failures. Permission/redaction and unsupported are the cases where the alternate mechanism is known to address the cause.

One nuance: this keeps CoreWLAN "worth it" primarily as the preferred long-term scanner and richer data source. For today's ad-hoc CLI binaries, the profiler fallback will likely be the common path until distribution provides a stable signing identity and location authorization flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants