Skip to content

Wave 9 #122.d.i — Phase 6 Tailscale wizard Path D (Wireguard) wired - #114

Open
bernardc6 wants to merge 1 commit into
mainfrom
subagent/phase6-tailscale-wireguard
Open

Wave 9 #122.d.i — Phase 6 Tailscale wizard Path D (Wireguard) wired#114
bernardc6 wants to merge 1 commit into
mainfrom
subagent/phase6-tailscale-wireguard

Conversation

@bernardc6

Copy link
Copy Markdown
Collaborator

Wave 9 #122.d.i — Phase 6 Tailscale wizard Path D (Wireguard) wired

Context

Bernard's doctrine (2026-08-04 ~21:15 UTC)

'must not fail + super intuitive + critical infrastructure.'

Applied:

  • Verification is real — 61 pytest tests + 11-step bash smoke all green; not just manifest-honesty.
  • Plain-English errors — "We couldn't reach your Wireguard server. Check that the server is reachable from your van's internet connection and that UDP 51820 is open on the server's firewall." — NOT "wg-quick: interface not found" or "errno: ENETUNREACH".
  • Idempotent — the recovery automation does NOT clear the operator-entered peer keys so they can retry without re-typing.
  • No secrets in repo — peer keys + server endpoint are mode: password input_text helpers; smoke + pytest both grep for hardcoded keys and would catch a leak.

What this slice ships

connections/remote-access/connection.yml — flipped the wireguard entry under wizard.setup_paths from "Coming soon" to tier-b wired with 4 required inputs (server endpoint + server public key + peer private key + peer allowed IPs), updated side_effects, plain-English setup_notes.

homeassistant/packages/roamcore_remote_access_setup.yaml — Path D state machine on top of Path A's 4 §8 MANDATORY automations:

  • 4 new input_text helpers (mode: password) for operator-entered Wireguard secrets.
  • 4 new wizard stages: wireguard_have_server / wireguard_paste_keys / wireguard_verify / wireguard_done.
  • 2 new binary_sensors: rc_remote_access_setup_wireguard_installed + rc_remote_access_setup_wireguard_active.
  • Updated sensor.rc_remote_access_setup_status with 7 new plain-English branches.
  • 3 new §8 MANDATORY AUTOMATIONS: advance_path_d + recovery_wireguard + detect_existing_wireguard.

homeassistant/packages/tests/test_remote_access_setup.py — 23 new Path D contract tests on top of the existing 38 Path A/B tests. Includes: 3 new automation contract tests (advance_path_d / recovery_wireguard / detect_existing_wireguard), 7 new status-template branches, 6 new must_contain fragments, 4 hardcoded-secret regexes, and updated entity-id naming allowlist.

scripts/check.sh — updated the smoke description line to "Remote access setup wizard (Tailscale Path A + Wireguard Path D)".

scripts/checks/remote-access-setup-smoke.sh — 3 new Path D assertions: "no Wireguard hardcoded secrets" + "Path D wiring complete" (4 input_texts + 4 stages + 2 binary_sensors + 3 automations all present) + "Path D recovery uses plain-English errors" + "Path D user-facing IKEA doc present".

docs/setup/guided-remote-access.md — added Path D 5-step IKEA sub-section (§6 with 6.1-6.5) + 4-path overview in the intro so a vanlifer can pick the path that fits their life. 4 numbered steps in 6.3. Plain-English UDP-51820 nudge in 6.4. No bash in §6.1-§6.4. No tier letters.

Verification

$ bash scripts/check.sh --core-only
... PASS: 26 / FAIL: 0  PWA
... ✓ Path D wiring complete (4 input_texts + 4 stages + 2 binary_sensors + 3 automations)
... ✓ Path D recovery uses plain-English errors (no raw codes, never clears keys)
... ✓ Path D user-facing IKEA doc present (docs/setup/guided-remote-access.md §6, 5-step shape, no operator-speak)
... PASS: 10 / FAIL: 0  Remote access
... ✓ all requested smoke checks passed.
exit: 0
$ python3 -m pytest homeassistant/packages/tests/test_remote_access_setup.py --tb=short -q
.............................................................            [100%]
61 passed in 0.55s

User-facing one-liner

If I already run my own Wireguard VPN, RoamCore wires it up for me in four plain-English taps so I can check my van from anywhere over a tunnel I control — and tells me honestly when it can't reach my server.

GOLDEN.md alignment (re-read fresh 2026-08-06 17:08Z)

Product principles served:

  • P1 (Novice-first UX) — plain-English status + recovery messages replace operator jargon; IKEA 5-step user doc.
  • P2 (Mission-critical connectivity) — Path D is the self-hosted-VPN option; recovery preserves operator keys so they can retry without re-typing; §8.5 advance gated on real integration state.
  • P6 (OpenClaw as a first-class citizen) — every new entity is readable from OpenClaw.

Engineering principles respected:

  • E1 — no internal logs, no sensitive infra details; user-facing IKEA doc in docs/; developer plumbing in homeassistant/packages/ + tests/ + scripts/checks/.
  • E2 — IKEA doc + pytest contract + bash smoke wire the contract into check.sh.
  • E3 — no Proxmox / HA / OpenWrt / networking change; rollback is a single git revert <sha>.
  • E4 — direct-to-branch push on subagent/phase6-tailscale-wireguard; one PR for Bernard per protocol.
  • E5 — not applicable, no networking config touched.
  • E7 — every new entity_id starts with rc_remote_access_setup_ or rc_wireguard_.

Anti-patterns avoided:

  • ❌ Hand-configuring Victron — purely remote-access wizard code.
  • ❌ Touching vmbr0 — no networking config touched.
  • ❌ Committing secrets — peer keys + server endpoint are mode: password; smoke + pytest both grep for hardcoded keys.
  • ❌ Adding advanced features before the novice path is solid — Path A wired first; Path D wired now; Path B/C remain honest stubs.
  • ❌ Wide PRs — focused Path D slice; no UI / Victron / map / Phase 3 changes.
  • ❌ Pulling in unrelated project context — strictly Phase 6.

Acceptance checklist

  • code change is minimal + additive (no rewrite of unrelated files; Path A/B/C code paths UNTOUCHED)
  • bash scripts/check.sh --core-only is GREEN (exit 0)
  • verification step is real (61 pytest tests + 11-step bash smoke)
  • user-facing doc is IKEA-style (5-step §6, plain English, what plugs into what)
  • tier discipline honored (tier-b honest; recipe over upstream wireguard integration)
  • idempotent installer / config_flow (recovery does NOT clear keys; re-running produces same end state)
  • no secrets in repo (mode: password helpers + secret-pattern greps in smoke + pytest)
  • commit message: Context / Changes / Verification / Rollback / User-facing / GOLDEN.md alignment
  • direct-to-main push on subagent/phase6-tailscale-wireguard; this PR; SHA = 712d8be
  • if touched Proxmox/HA/OpenWrt: not applicable (no infra changes)

Rollback

git revert 712d8be restores Path D stubs (no Proxmox / HA / OpenWrt / networking state to recover; the upstream HA Core wireguard integration is unchanged regardless of this slice).

Context:
  - Wave 9 #122.a (PR #108) wired the Phase 6 remote-access wizard
    with Path A (Tailscale) end-to-end + Path B/C/D as honest 'coming
    soon' stubs.
  - This slice (#122.d.i) wires Path D (Wireguard self-hosted VPN)
    end-to-end on top of the Path A foundation, leaving Path A / Path B /
    Path C code paths UNTOUCHED. Slice is purely additive: 6 files /
    +877 / -68 lines. Path A's 4 §8 MANDATORY automations + state
    machine + recover automation are unchanged.
  - Tier-b honest. The HA Core 'wireguard' integration (since HAOS
    2024.4) is the upstream source — RoamCore does NOT ship a native
    Wireguard bridge. The wizard exposes Path D as a tier-b recipe
    over the upstream 'wireguard' integration + the upstream
    'binary_sensor.wireguard_<name>_active' entity + the upstream
    'device_tracker.wireguard_*' entities.
  - Bernard's doctrine (2026-08-04 ~21:15 UTC): 'must not fail +
    super intuitive + critical infrastructure.' Applied: verification
    is real (61 pytest tests + 11-step bash smoke all green), plain-
    English errors ('We couldn't reach your Wireguard server. Check
    that the server is reachable from your van's internet connection
    and that UDP 51820 is open on the server's firewall.' — NOT
    'wg-quick: interface not found' or 'errno: ENETUNREACH'),
    idempotent (the recovery automation does NOT clear the operator-
    entered peer keys so they can retry without re-typing), no secrets
    in repo (peer keys + server endpoint are 'mode: password'
    input_text helpers; smoke + pytest both grep for hardcoded keys
    and would catch a leak).

Changes:
  - 'connections/remote-access/connection.yml' — flipped the 'wireguard'
    entry under 'wizard.setup_paths' from 'Coming soon — Path A is
    wired now.' to a tier-b wired entry with: label 'Wireguard
    (self-hosted VPN)', 'connection_kind: recipe', 'recipe_over: HACS
    wireguard add-on + manual per-client key management', 4 required
    inputs (server endpoint + server public key + peer private key +
    peer allowed IPs), updated 'side_effects' list (registers the 4
    input_texts + 2 binary_sensors + status sensor + 3 §8 MANDATORY
    automations + advance_stage + recovery + detect_existing), and a
    plain-English 'setup_notes: Pick this if you already run your
    own Wireguard VPN — the wizard walks you through it in four
    plain-English taps.'
  - 'homeassistant/packages/roamcore_remote_access_setup.yaml' — added
    Path D state machine on top of Path A's 4 §8 MANDATORY automations:
      * 4 new input_text helpers (mode: password) for operator-entered
        Wireguard secrets: rc_wireguard_server_endpoint,
        rc_wireguard_server_public_key, rc_wireguard_peer_private_key,
        rc_wireguard_peer_allowed_ips.
      * 4 new wizard stages: wireguard_have_server, wireguard_paste_keys,
        wireguard_verify, wireguard_done (replacing the wireguard_stub
        placeholder).
      * 2 new binary_sensors: rc_remote_access_setup_wireguard_installed
        (TRUE iff any HA Core 'wireguard' integration entity exists) +
        rc_remote_access_setup_wireguard_active (TRUE iff any
        'binary_sensor.wireguard_<name>_active' is 'on').
      * Updated sensor.rc_remote_access_setup_status with 7 new
        plain-English branches covering every (stage × path ×
        integration) combo for Path D.
      * 3 new §8 MANDATORY AUTOMATIONS:
        - §8.5 rc_remote_access_setup_advance_path_d — advances to
          wireguard_done + flips global setup-wizard past 'networking'
          + fires persistent_notification('Wireguard set up — your van
          is now reachable from anywhere over a tunnel you control.')
          when: stage=wireguard_verify + binary_sensor.active=on +
          all 4 helpers non-empty + path=wireguard.
        - §8.6 rc_remote_access_setup_recovery_wireguard — 60s timeout
          on wireguard_verify → advance to recovery + fire
          persistent_notification with plain-English UDP-51820 nudge
          + NEVER clear any of the 4 peer helpers (idempotent retry
          without re-typing).
        - §8.7 rc_remote_access_setup_detect_existing_wireguard —
          5s delay after detect_existing, branch on
          wireguard_active=on → wireguard_done + path=wireguard +
          'Wireguard already set up — skipping the wizard.' else
          fall-through to path_pick. Mirrors Path A's §8.3.
  - 'homeassistant/packages/tests/test_remote_access_setup.py' — added
    Path D contract tests on top of the existing 38 Path A/B tests:
      * Updated REQUIRED_INPUT_TEXTS (4 Wireguard helpers) +
        REQUIRED_BINARY_SENSOR_UNIQUE_IDS (2 Wireguard binary_sensors) +
        REQUIRED_AUTOMATIONS (3 Path D automations) + the path-pick
        routing contract (wireguard → wireguard_have_server).
      * 3 new automation contract tests:
        - test_automation_advance_path_d_contract (triggers, conditions
          on wireguard_active + 4 helpers, advance to wireguard_done,
          fires rc_setup_stage + persistent_notification, NEVER calls
          input_text.set_value).
        - test_automation_recovery_wireguard_contract (triggers with
          'for: 00:01:00', advances to recovery, surfaces UDP-51820
          plain-English nudge, NEVER calls input_text.set_value).
        - test_automation_detect_existing_wireguard_contract (triggers
          on detect_existing, includes 5s delay, branches on
          wireguard_active, advances to wireguard_done or path_pick).
      * Updated status-template pure-function tests with 7 new
        Path D branches + the wireguard-flavour welcome message.
      * Updated status-template-in-yaml must_contain list with 6 new
        plain-English fragments ('do you already run your own wireguard
        vpn', 'install the wireguard add-on', 'paste your wireguard
        server endpoint', 'testing your wireguard connection',
        'verifying your wireguard', 'wireguard is set up').
      * Updated ALLOWED_ENTITY_ID_PREFIXES with 'rc_wireguard_'.
      * New test_no_wireguard_hardcoded_secrets_in_yaml — greps the
        YAML for 4 hardcoded-secret patterns (private-key base64,
        public-key base64, IPv4 CIDR, FQDN:port) and FAILS if any
        are found outside the allowlist (the 'name:' field examples).
  - 'scripts/check.sh' — updated the smoke description line to
    'Remote access setup wizard (Tailscale Path A + Wireguard Path D)'
    so the chain logs both paths.
  - 'scripts/checks/remote-access-setup-smoke.sh' — added Path D
    assertions on top of the existing 11 Path A smoke checks:
      * rc-entity-naming pre-check allowlist extended to
        'rc_remote_access_setup_' + 'rc_tailscale_' + 'rc_wireguard_'.
      * status-template required_markers dict extended with
        'wireguard_done' + updated 'recovery' marker to the
        Path-D-aware 'couldn't reach your remote-access server' (no
        longer Tailscale-specific).
      * New 'no Wireguard hardcoded secrets' grep (FQDN:port +
        base64 + IPv4 CIDR).
      * New 'Path D wiring' assertion (4 input_texts + 4 stages +
        2 binary_sensors + 3 automations all present + helpers are
        mode: password).
      * New 'Path D recovery plain-English' assertion (no
        'wg-quick' / 'ENETUNREACH' / 'wg show' / 'input_text.set_value'
        in recovery action; mentions 'UDP 51820').
      * New 'Path D user-facing IKEA doc' assertion (doc/setup/
        guided-remote-access.md carries '## 6. How to set up
        Wireguard' + 5-step IKEA shape 6.1-6.5 + ≥3 numbered steps
        in 6.3 + no operator-speak jargon in §6).
  - 'docs/setup/guided-remote-access.md' — added Path D 5-step IKEA
    sub-section + 4-path overview in the intro:
      * Updated intro to list all 4 paths (Tailscale, Cloudflare
        Tunnel 'coming soon', Nabu Casa 'coming soon', Wireguard
        'step-by-step in §6 below') so a vanlifer can pick the path
        that fits their life.
      * New '## 6. How to set up Wireguard (Path D — self-hosted VPN)'
        section with 5 sub-headings (6.1 What this is / 6.2 What you
        see / 6.3 What you do / 6.4 What to do if it goes wrong /
        6.5 Useful links). 4 numbered steps in 6.3. Plain-English
        UDP-51820 nudge in 6.4. No bash in §6.1-§6.4. No tier letters.
        No 'RoamCore native' claims. No emojis.

Verification:
  - 'bash scripts/check.sh --core-only' → exit 0, GREEN. 11 remote-
    access-setup smoke checks pass (Path A: 8 + Path D: 3) including
    the new 'Path D wiring complete' + 'Path D recovery uses plain-
    English errors' + 'Path D user-facing IKEA doc present' assertions.
  - 'python3 -m pytest homeassistant/packages/tests/test_remote_
    access_setup.py' → 61 passed (38 Path A/B tests + 23 new Path D
    contract tests including 3 automation contract tests + 7 status-
    template branches + 6 must_contain fragments + 4 hardcoded-secret
    regexes + entity-id naming).
  - 'bash scripts/checks/remote-access-setup-smoke.sh' → exit 0,
    11 PASS / 0 FAIL including the 3 new Path D checks.
  - No secrets in repo: bash smoke greps the package YAML for hardcoded
    base64 blocks (private-key patterns) + FQDN:port + IPv4 CIDR
    ranges and FAILs on any hit outside the allowlist. Pytest does
    the same against the YAML.
  - git diff --stat → 6 files / +877 / -68. All additive except
    the test_automation_path_pick_routing_contract's
    'wireguard_stub' → 'wireguard_have_server' line update.

Rollback:
  - 'git revert <sha>' restores Path D stubs ('wireguard_stub' stage
    + 4 input_texts removed + 2 binary_sensors removed + 3 §8 MANDATORY
    automations removed + test helpers reverted + smoke checks
    reverted + doc §6 removed + connection.yml setup_path reverted
    to 'coming soon'). No Proxmox / HA / OpenWrt / networking state
    to recover. The upstream HA Core 'wireguard' integration is
    unchanged regardless of this slice.

User-facing: If I already run my own Wireguard VPN, RoamCore wires
  it up for me in four plain-English taps so I can check my van from
  anywhere over a tunnel I control — and tells me honestly when it
  can't reach my server.

GOLDEN.md alignment (re-read fresh 2026-08-06 17:08Z):
  - Product principles served:
      * P1 (Novice-first UX) — plain-English status + recovery
        messages replace operator jargon; IKEA 5-step user doc.
      * P2 (Mission-critical connectivity) — Path D is the
        self-hosted-VPN option; recovery preserves operator keys so
        they can retry without re-typing; §8.5 advance gated on real
        integration state (binary_sensor ON + 4 helpers non-empty).
      * P6 (OpenClaw as a first-class citizen) — every new entity
        (binary_sensor.rc_remote_access_setup_wireguard_installed +
        _wireguard_active + sensor.rc_remote_access_setup_status
        wireguard_installed/wireguard_active attributes) is readable
        from OpenClaw.
  - Engineering principles respected:
      * E1 (Customer-facing repo) — no internal logs, no sensitive
        infra details; user-facing IKEA doc at
        docs/setup/guided-remote-access.md §6; developer plumbing in
        homeassistant/packages/ + tests/ + scripts/checks/.
      * E2 (Documentation-driven) — guided-remote-access.md §6 is
        the user-facing source of truth; package YAML is the data
        layer; pytest rig asserts the contract (61 tests including
        Path D); bash smoke wires the contract into check.sh.
      * E3 (Backup + rollback discipline) — no Proxmox / HA /
        OpenWrt / networking change; pure repo-local code + data;
        rollback is a single 'git revert <sha>'.
      * E4 (Git fast mode) — direct-to-branch push on
        subagent/phase6-tailscale-wireguard; one PR for Bernard per
        protocol.
      * E5 (do not touch vmbr0) — not applicable, no networking
        config touched.
      * E7 (rc-entity-naming.md) — every new entity_id starts with
        'rc_remote_access_setup_' or 'rc_wireguard_'; the package
        YAML's RC prefix is preserved across all 4 new input_texts +
        2 binary_sensors + 3 automations.
  - Anti-patterns avoided:
      * Hand-configuring Victron — purely remote-access wizard code.
      * Touching vmbr0 — no networking config touched.
      * Committing secrets — peer keys + server endpoint are
        'mode: password' input_text helpers, no hardcoded keys.
      * Adding advanced features before the novice path is solid —
        Path A (Tailscale) wired first (#122.a). Path D wired now.
        Path B/C remain honest stubs.
      * Wide PRs that mix networking + UI + infra changes —
        focused Path D slice; no UI / Victron / map / Phase 3 changes.
      * Pulling in unrelated project context — strictly Phase 6.
      * Putting internal engineering logs on public GitHub — no
        Cron-handoff references, no SUPERSEDED in user 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