-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
First stops, in order:
-
curl -s http://<host>:8420/api/health— no auth; should print{"status":"ok"}. -
curl -H "X-API-Key: <key>" http://<host>:8420/api/version— confirms the key and shows version/features. - The logs —
journalctl -u breeze-core -f, or the Docker / NSSM / procd logs. - The diagnostic CLI:
tools/ac-diag.zsh … --autoruns the whole battery below in one go.
The CLIs read config.json for the API key. It's mode 640
(group-readable), so the fix is to add your admin user to the service group
once, then log out and back in (group membership only applies to new
logins):
sudo usermod -aG breeze "$USER" # use your service group; re-login afterward
id # confirm the group shows upAfter that, run the tool without sudo.
Don't sudo acapprove (→ sudo: acapprove: command not found). If
acapprove is a shell alias, sudo starts a fresh shell that has neither
your aliases nor your group — so it can't find the command and couldn't
read the config anyway. If you truly need root, call the script by its real
path:
sudo zsh /opt/breeze-core/tools/ac-approve.zsh \
--base-url http://127.0.0.1:8420 --config /etc/breeze-core/config.json approve <CODE>Pairing codes are short-lived (~60 s) — if approval says the code is unknown/expired, just start pairing again from the client.
The service is up but not paired yet — config.json has no api_key/units.
Run setup_device.py / breeze-core pair (see
INSTALL.md) to discover units
and mint the key, then restart the service. The static UI at / still loads;
only the API needs a config.
The runtime dir must be owned by the service user (it writes
devices.json/programs.json there) — a root-owned dir makes those writes
fail:
sudo chown -R breeze:breeze /etc/breeze-core
sudo chmod 750 /etc/breeze-core && sudo chmod 640 /etc/breeze-core/config.jsonIf the packaged service can read its config but every write to
/etc/breeze-core (approving a pairing, saving programs) dies with a
PermissionError — and there's no AVC in the audit log — check the service's
SELinux domain:
ps -o label= -p "$(systemctl show breeze-core -p MainPID --value)"If it says init_t instead of unconfined_service_t, the executable under
/usr/lib/breeze-core/ is labeled lib_t, which is not a domain-transition
entrypoint (denials from init_t are dontaudit'd — hence the silence).
Packages since 2.6.1 fix this in their post-install; on an older install:
sudo semanage fcontext -a -t bin_t "/usr/lib/breeze-core/breeze-core"
sudo restorecon /usr/lib/breeze-core/breeze-core
sudo systemctl restart breeze-coreRead the reason in the body before re-pairing. A 401 does not
automatically mean the credential is dead, and treating it that way is how a
fleet of clients ends up unpaired at once:
curl -sS -H "X-API-Key: $KEY" http://SERVER:8420/api/units | jq .detail-
reason: "clock_skew","replay"or"incomplete_signature"→retryable: true. The credential is fine, only this request was wrong. For skew the body carriesserver_time, so a client can learn its offset and retry (the Breeze app does). Check the client's clock — a phone or VM that drifted more thanAC_AUTH_SKEW_SECONDS(default 60) is the usual cause. -
reason: "expired","unknown_key","bad_signature","no_credential"→ the device really does need to re-enrol. Expiry is governed byAC_TOKEN_TTL_DAYS(default 90;0= never). -
reason: "bad_api_key"→ the shared enrollment key is wrong. Fix the key; the device credential is not the problem.
The full table is in API.md.
Server side, every rejection is logged with its reason, the client IP and a key
hint: journalctl -u breeze-core -t meow-ac.auth.
Everyone lost their servers at once? That's the signature of two things compounding: a client that discards its credential on any 401, and fail2ban banning a shared NAT address so every device behind it fails together. Both are fixed in current versions — see the
ignoreipguidance in HARDENING.md — but an old client build can still do the first half to itself.
Approval must originate from a private IP. Behind a reverse proxy, every
request looks like 127.0.0.1 unless you forward the real client: set
AC_BEHIND_PROXY=1, run uvicorn with
--proxy-headers --forwarded-allow-ips 127.0.0.1, and make the proxy
overwrite X-Forwarded-For with the real peer (nginx $remote_addr;
Caddy does this by default with no trusted_proxies). Appending XFF is
spoofable — see HARDENING.md.
Behind a proxy the app binds loopback only by design — use the proxied
HTTPS URL, not :8420. On the LAN, check the bind address (a LAN IP, not
0.0.0.0) and that the firewall allows 8420 from your subnet
(INSTALL.md §6).
GET /api/units/scan (Breeze Core ≥ 3.0.0) TCP-scans ports 6440–6449 across
the server's own private /24. If it comes up empty:
-
Wrong subnet. It autodetects the server's primary private network; a
multi-homed host or an unusual layout may guess wrong. Pass an explicit
CIDR:
GET /api/units/scan?subnet=192.168.1.0/24. - Behind a reverse proxy / loopback bind. The scan uses the server's own LAN interface (not where uvicorn binds), so it still works behind nginx — but if the box genuinely isn't on the units' L2 network (e.g. a different VLAN), it can't see them. Add by IP instead.
-
Firewalled units. Some firmware only answers on
6444; a host firewall between the server and the units will hide them. The manual add by IP path still works (it runs real discovery). -
Non-private target refused. The scanner only scans RFC-1918 ranges and
caps the host count — it will 400 on a public or oversized
subnet.
Add your LAN to ignoreip; don't let a client spray 401s (an expired token
repeated across many units can trip a jail).
The service errors until you've paired (run Pair AC units first). SmartScreen warns on the unsigned installer → More info → Run anyway. See WINDOWS.md.
Discovery (UDP broadcast) needs --network host; a bind-mounted state dir
must be writable by UID 1001 (chown 1001:0). See DOCKER.md.
They come in two equivalent forms — pick whichever your install has:
-
Built into the binary/packages (v2.6.0+):
breeze-core diag,breeze-core approve <CODE>,breeze-core devices,breeze-core revoke <token_id>— same flags as the zsh tool below (--auto,--unit,--base-url,--config,--token,--pair,--no-pair,--forget-token,--with-control-test). On a source or Windows install the same commands are available aspython -m meow_ac.cli diag …. -
The original zsh scripts (
tools/ac-diag.zsh,tools/ac-approve.zsh) for source installs — zero dependencies beyondzsh,curl,jq.
Both forms speak only HTTP (they never touch the server's internals), read
config.json for the key, and share the same device-token cache
(~/.config/ac-diag/token), so pairing once covers both.
ac-diag / breeze-core diag checks, in one run: the background
service (when run on the server host — is breeze-core running, and
enabled to start at boot? detects systemd / OpenRC / runit / procd / Windows
sc), connectivity (/api/health), server
version + build commit + advertised features (/api/version), the auth
posture (rejects no-key/wrong-key, and detects whether the control API is
token-gated), paired devices with expiry warnings, config
secret-sanitisation (asserts /api/config leaks no key/token), the
batch-state endpoint, input-validation (unknown-unit → 404,
out-of-range control → 422), per-unit state/latency/enum checks, and the
scheduler + programs status.
Because control/config/programs routes need a device token as well as the
key, the tool obtains one automatically: it self-enrols on the LAN (start →
approve → poll, all key-authenticated) and caches the token under
~/.config/ac-diag/ — or pass --token, set $AC_DIAG_TOKEN, or --pair
to re-mint. The minted token shows up as ac-diag in ac-approve list and
is revocable.
# full read-only diagnostic on every unit (self-pairs on the LAN if needed)
./tools/ac-diag.zsh --base-url http://<host>:8420 --config /etc/breeze-core/config.json --auto
# just one unit (by id or name), or an interactive menu with no args
./tools/ac-diag.zsh --unit "Living Room"
./tools/ac-diag.zsh --token <DEVICE_TOKEN> # use a token you already have
./tools/ac-diag.zsh --forget-token # drop the cached token
# approve a pairing code / list / revoke (run on the LAN)
./tools/ac-approve.zsh --base-url http://<host>:8420 --config /etc/breeze-core/config.json approve <CODE>
./tools/ac-approve.zsh --base-url http://<host>:8420 --config /etc/breeze-core/config.json listBreeze Core · Breeze for Android · Packages · AGPL-3.0
Start here
Install it
Use it
Reference
Run it safely
Develop and port