Skip to content

Wave 9 #122.d.iv — Phase 6 Tailscale wizard: mDNS roamcore.local fallback (Gate E 'local survives Tailscale failure') - #118

Open
bernardc6 wants to merge 1 commit into
mainfrom
subagent/phase6-local-mdns-fallback
Open

Wave 9 #122.d.iv — Phase 6 Tailscale wizard: mDNS roamcore.local fallback (Gate E 'local survives Tailscale failure')#118
bernardc6 wants to merge 1 commit into
mainfrom
subagent/phase6-local-mdns-fallback

Conversation

@bernardc6

Copy link
Copy Markdown
Collaborator

Summary

Wave 9 #122.d.iv — Phase 6 Tailscale wizard sub-slice: mDNS roamcore.local fallback so local survives Tailscale failure (Gate E verbatim).

When Tailscale is down, the operator can still reach their dashboard from their phone on the same WiFi as the Hub — at roamcore.local, no IP needed.

What's new

  • NEW package homeassistant/packages/roamcore_local_mdns_fallback.yaml (separate from roamcore_remote_access_setup.yaml to avoid file-scope conflicts with Wave 9 #119b — Phase 2 canonical vehicle model mapping layer #122.a/b/c/d.i):

    • input_boolean.rc_local_mdns_fallback_enabled (default ON, no operator input)
    • input_text.rc_local_mdns_hostname (default roamcore, operator-editable)
    • input_button.rc_local_mdns_retest
    • shell_command.rc_local_mdns_probe (5s timeout via avahi-resolve / getent)
    • binary_sensor.rc_local_mdns_resolvable (UNKNOWN until first probe — doesn't pretend fallback is up)
    • sensor.rc_local_mdns_resolved_ip + sensor.rc_local_mdns_status (3 plain-English states)
    • 3 §8 MANDATORY AUTOMATIONS:
      • §8.M.1 rc_local_mdns_register_on_start — registers hostname on HA startup
      • §8.M.2 rc_local_mdns_probe_periodic — probes every 60s
      • §8.M.3 rc_local_mdns_fallback_tile_surfacing — surfaces fallback notification on retest
  • NEW pytest rig homeassistant/packages/tests/test_local_mdns_fallback.py32 tests covering YAML parse + every required helper + every §8 automation contract + hostname operator-editable + status template covers all 4 (enabled × resolvable × ip) combos + status copy has no operator jargon + idempotency + single shell_command (no duplicate mDNS service) + rc-entity-naming compliance + no secrets/hard-coded IPs leaked + IKEA doc 5-step shape + IKEA doc translation table + no SUPERSEDED banner.

  • NEW bash smoke scripts/checks/local-mdns-fallback-smoke.sh13 assertions: file presence + YAML parse + rc-naming + helpers/automations/templates present + status coverage + no jargon + secrets-leak + idempotency + IKEA doc 5-step + §8.M.3 wiring + pytest rig green.

  • NEW IKEA doc docs/setup/local-access-fallback.md — 5 numbered sections (What this is / What you see / What you do / What to do if it goes wrong / Useful links) + operator→vanlifer translation table. Opens with one plain-English sentence.

  • MOD connections/remote-access/connection.yml — +35 lines: local_mdns_fallback entry under wizard.setup_paths as a separate entry from the four remote paths (A/B/C/D), explicitly documented as the Gate E safety net that is always-on by default.

  • MOD scripts/check.sh — +1 line wiring scripts/checks/local-mdns-fallback-smoke.sh into the core-only chain.

Tier discipline

Tier-b recipe over the HA Core zeroconf integration (since 2022.x — exposes an mDNS service registration service that this slice consumes) + a shell_command fallback to avahi-set-hostname for Hub installs where the upstream zeroconf integration isn't loaded yet. HAOS ships an avahi-based mDNS responder by default; this slice wires the canonical RoamCore hostname (roamcore.local) into the dashboard fallback path.

Verification

  • bash scripts/check.sh --core-onlyexit 0 (GREEN)
  • bash scripts/checks/local-mdns-fallback-smoke.sh13/13 PASS
  • python3 -m pytest homeassistant/packages/tests/test_local_mdns_fallback.py -v32/32 PASS

Doctrine / GOLDEN.md alignment

  • Product principle 1 (novice-first UX): the IKEA doc opens with one plain-English sentence; the dashboard status copy is one of 3 plain-English states.
  • Product principle 2 (mission-critical connectivity): this slice IS the safety net for Gate E — keeps the dashboard reachable when the primary remote-access path is down.
  • Engineering principle 1 (customer-facing repo): user-facing doc lives in docs/setup/, NOT memory/ or Cron-handoff/.
  • Engineering principle 5 (do not touch vmbr0): no networking change at all — pure repo-local code that orchestrates the upstream zeroconf integration.
  • Engineering principle 7 (rc-entity-naming): every entity_id starts with rc_local_mdns_; tested explicitly.
  • Anti-patterns avoided:
    • No secrets committed (tskey- / ts-auth- / hard-coded IPs all asserted absent).
    • No wide PR (additive + tiny, not a rewrite).
    • No "RoamCore ships a native mDNS engine" tier-claim stub — the YAML honestly calls itself a tier-b recipe over the upstream zeroconf integration.
    • No SUPERSEDED banner / no Cron-handoff / no internal jargon in the user-facing tree.

Rollback

Revert this commit + any follow-up. The slice is purely additive (new package + new files + tiny additive edits to connection.yml and check.sh); no destructive ops, no Proxmox/HA/OpenWrt/vmbr0 touched.

…back

Context: Gate E requires 'local survives Tailscale failure' — if the
operator's Tailscale tailnet ever goes down (account paused, weak
cellular for the VPN, Hub's internet offline), they must still be
able to reach the dashboard from their phone on the same WiFi as
the Hub. This slice wires the canonical RoamCore hostname
(roamcore.local) into the dashboard fallback path so the operator
can reach the Hub at the friendly name without typing an IP.

The slice is additive — no overlap with #122.a (wizard),
#122.d.ii (QR code), or #122.b/c/d.i (the four remote paths).
NEW package homeassistant/packages/roamcore_local_mdns_fallback.yaml:
- input_boolean.rc_local_mdns_fallback_enabled (default on, no
  operator input required).
- input_text.rc_local_mdns_hostname (operator-editable, default
  'roamcore' — purely cosmetic for operators who already have a
  'roamcore' device on their network).
- input_button.rc_local_mdns_retest (operator-facing re-probe).
- shell_command.rc_local_mdns_probe (5s timeout via avahi-resolve
  / getent — never blocks the dashboard).
- binary_sensor.rc_local_mdns_resolvable (UNKNOWN until first
  probe — doesn't pretend the fallback is up).
- sensor.rc_local_mdns_resolved_ip + sensor.rc_local_mdns_status
  (3 plain-English states: reachable / reachable via direct IP /
  fallback unavailable).
- 3 §8 MANDATORY automations: §8.M.1 register hostname on HA
  startup, §8.M.2 probe every 60s, §8.M.3 fallback tile surfacing
  on retest (auto-recovery via persistent_notification with the
  direct-IP hint).
NEW pytest rig (32 tests): YAML parses + every required helper
present + every §8 automation contract + hostname is operator-
editable + status template covers all 4 (enabled × resolvable × ip)
combos + status copy has no operator jargon + idempotency
(re-parsing produces identical dict) + single shell_command
(no duplicate mDNS service) + rc-entity-naming compliance + no
secrets / hard-coded IPs leaked + IKEA doc has exactly 5
numbered sections + IKEA doc has operator→vanlifer translation
table + IKEA doc has no SUPERSEDED banner.
NEW bash smoke (13 assertions): file presence + YAML parse +
rc-naming + helpers/automations/templates present + status
coverage + no jargon + secrets-leak + idempotency + single
shell_command + IKEA doc 5-step shape + §8.M.3 wiring + pytest
rig green.
NEW IKEA doc docs/setup/local-access-fallback.md: 5 numbered
sections (What this is / What you see / What you do / What to do
if it goes wrong / Useful links) + operator→vanlifer translation
table. Opens with one plain-English sentence a vanlifer would
understand.
MOD connections/remote-access/connection.yml: +35 lines to add
local_mdns_fallback as a separate wizard.setup_paths entry —
explicitly NOT one of the four remote-access paths (A/B/C/D);
documented as the Gate E safety net that always-on by default.
MOD scripts/check.sh: +1 line wiring scripts/checks/local-mdns-
fallback-smoke.sh into the core-only chain.

User-facing: If Tailscale ever stops working, you can still open
your RoamCore dashboard from your phone on the same WiFi as the
Hub — at 'roamcore.local', no IP needed.

Verification:
- bash scripts/check.sh --core-only → exit 0 (GREEN)
- bash scripts/checks/local-mdns-fallback-smoke.sh → 13/13 PASS
- python3 -m pytest homeassistant/packages/tests/test_local_mdns_fallback.py -v
  → 32/32 PASS

Rollback: revert this commit + the other agents' #122.d.ii commits
on the next reset. The slice is purely additive (new package +
new files + tiny additive edits to connection.yml and check.sh);
no destructive ops, no Proxmox/HA/OpenWrt/vmbr0 touched.

GOLDEN.md alignment:
- Product principle 1 (novice-first UX): the IKEA doc opens with
  one plain-English sentence; the dashboard status copy is one of
  3 plain-English states (no bash, no entity IDs).
- Product principle 2 (mission-critical connectivity): this slice
  is the safety net that keeps the dashboard reachable when the
  primary remote-access path (Tailscale) is down.
- Engineering principle 1 (customer-facing repo): user-facing doc
  lives in docs/setup/, NOT memory/ or Cron-handoff/.
- Engineering principle 2 (documentation-driven): MkDocs site is
  the public surface; this slice adds an IKEA doc to the public
  tree.
- Engineering principle 4 (git fast mode): direct-to-main push on
  subagent/phase6-local-mdns-fallback.
- Engineering principle 5 (do not touch vmbr0): no networking
  change at all — this slice is repo-local code that orchestrates
  the upstream HA Core  integration.
- Engineering principle 7 (rc-entity-naming): every entity_id
  starts with rc_local_mdns_; tested explicitly in the pytest rig.
- Anti-patterns avoided:
  - No secrets committed (tskey- / ts-auth- / hard-coded IPs all
    asserted absent in pytest + smoke).
  - No wide PR mixing unrelated changes (additive to the
    connection.yml + check.sh, not a rewrite).
  - No 'RoamCore ships a native mDNS engine' tier-claim stubs —
    the YAML honestly calls itself a tier-b recipe over the
    upstream HA Core  integration.
  - No SUPERSEDED banner / no Cron-handoff / no internal jargon
    in the user-facing tree.
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.

1 participant