Skip to content

Wave 9 #123.c.ii: Phase 7 Security Review — tier-a manifest + services + data-layer + pytest + smoke + IKEA runbook - #117

Open
bernardc6 wants to merge 2 commits into
mainfrom
subagent/phase7-security-review-manifest
Open

Wave 9 #123.c.ii: Phase 7 Security Review — tier-a manifest + services + data-layer + pytest + smoke + IKEA runbook#117
bernardc6 wants to merge 2 commits into
mainfrom
subagent/phase7-security-review-manifest

Conversation

@bernardc6

Copy link
Copy Markdown
Collaborator

Context

Phase 7 #123.c Security Review slice — sub-slice #123.c.ii ships the tier-a connection manifest + the 3 service definitions + the data-layer package + the 22 pytest tests + the 10 bash smoke assertions + the IKEA user-facing runbook on top of the foundation service module that landed at #123.c.i (commit 63145f8 on branch subagent/phase7-security-review). The foundation service module (homeassistant/custom_components/roamcore/security.py, 883 LOC, stdlib-only Python) is the canonical umbrella for the security-review surface; this slice adds the connection-level artefacts that turn the foundation into a tier-a connection.

Changes

  • connections/security-review/connection.yml (NEW): tier-a connection manifest for the security-review surface (12 contract tiles + 4 §8 MANDATORY automations + 3 RoamCore services + vendor-neutral tiles + 1-tap install).
  • connections/security-review/init.py (NEW): DOMAIN marker + SECURITY_TILE_NAMES tuple + SECURITY_TILE_PREFIX + security review status constants for the audit + the helper package + the pytest rig.
  • connections/security-review/README.md (NEW): vendor-neutral 1-paragraph description of the security-review surface.
  • connections/security-review/docs/recipe.md (NEW): 5-step IKEA developer recipe (internal only, NOT user-facing).
  • connections/security-review/tests/test_connection_yml.py (NEW): 11 manifest-honesty tests (manifest honoured + tier-a markers + recipe present + rc-entity-naming compliance + idempotency + secrets-leak guard).
  • homeassistant/custom_components/roamcore/services.yaml (MODIFY): 3 new service definitions for roamcore.rotate_api_token + roamcore.audit_ssh + roamcore.audit_firewall (vendor-neutral + plain-English).
  • homeassistant/custom_components/roamcore/init.py (MODIFY): wired register_security_services(hass) into async_setup_entry (mirrors the existing Hub Backup pattern; wrapped in try/except so the failure never breaks HA startup).
  • homeassistant/packages/roamcore_security_review.yaml (NEW): data-layer package with 4 input helpers + 4 template sensors + binary_sensor healthy + 3 buttons + 4 §8 MANDATORY automations (rotate-token + audit-ssh + audit-firewall + warn-rotation-age).
  • homeassistant/packages/tests/test_security_review.py (NEW): 22 pytest tests covering the YAML package + the 3 service registrations + the 4 helper entities + the 4 template sensors + the 4 §8 automations + rc-entity-naming compliance + idempotency + secrets-leak guard.
  • scripts/checks/security-review-smoke.sh (NEW): 10 bash assertions wiring connection manifest + YAML package + 3 service definitions + rc-entity-naming + idempotency + secrets-leak.
  • scripts/check.sh (MODIFY): wired the new smoke into the chain.
  • homeassistant/packages/tests/test_connection_state_field.py (MODIFY): bumped expected manifest count from 33 to 34 (the security-review connection is the 34th manifest).
  • docs/runbooks/security-review.md (NEW): IKEA 5-step vanlifer-facing runbook (5 numbered steps + 3-line troubleshooting + useful links; NO bash commands in §1-§4; NO operator-speak jargon).

Verification

  • bash scripts/check.sh --core-only → exit 0 (GREEN)
  • bash scripts/checks/security-review-smoke.sh → PASS: 10 / FAIL: 0
  • python3 -m pytest homeassistant/packages/tests/test_security_review.py → 22/22 PASS
  • python3 -m pytest connections/security-review/tests/test_connection_yml.py → 11/11 PASS
  • python3 -m pytest homeassistant/packages/tests/test_connection_state_field.py → 22/22 PASS (manifest count bumped 33 → 34)
  • secrets-leak guard: grep -rIn -E 'password=|token=|/home/[a-z]' over the new files returns only the guard-pattern strings themselves (intended markers, not actual leaks).

GOLDEN.md alignment

  • P1 (Novice-first UX) — the IKEA runbook opens with one plain-English sentence a vanlifer understands; the dashboard tile is one-tap (the rotate-token + audit-ssh + audit-firewall buttons trigger the 3 RoamCore services synchronously).
  • P2 (Mission-critical connectivity) — security review IS the canonical prevention for the 'lockout' worst-case (per the directive §'Phase 7 delivery' / Gate F).
  • P6 (OpenClaw first-class) — 3 services exposed for OpenClaw to call through the existing agent-actions-allowlist connection (roamcore.rotate_api_token + roamcore.audit_ssh + roamcore.audit_firewall).
  • E1 (Customer-facing repo) — no internal logs in the user tree; the runbook in docs/runbooks/ is the public surface; developer plumbing stays in connections/security-review/docs/recipe.md.
  • E2 (Documentation-driven) — runbook = source of truth for the user; package YAML = source of truth for the data layer; pytest rig = source of truth for the contract assertions; smoke check = source of truth for the chain.
  • E3 (Backup + rollback discipline) — rotate_token() writes backup BEFORE updating .storage/ (backup-before-mutate); atomic write via .tmp + os.replace; idempotent append-not-overwrite; the SSH + firewall audit is READ-ONLY by design (no mutation of /etc/ssh/sshd_config or /etc/nftables.conf); no Proxmox / HA / OpenWrt / networking change in this slice.
  • E7 (rc-entity-naming.md) — every new entity starts with rc_security_review_ (the SECURITY_TILE_PREFIX constant from security.py).

User-facing one-liner

Tells me in plain English whether my van is locked down — SSH keys only, firewall tight, access codes fresh — so I can fix small problems before they become lockouts, without having to read technical jargon.

Anti-patterns avoided

  • ❌ no Victron hand-config (purely generic SSH + firewall)
  • ❌ no vmbr0 touch (no networking config mutation at all)
  • ❌ no committed secrets (the secrets-leak guard grep enforces this)
  • ❌ no wide PR (pure security-review scope, 13 files only)
  • ❌ no unrelated project context
  • ❌ no internal engineering logs in user-facing copy

Rollback

  • git revert + git push origin main (no Proxmox / HA / OpenWrt / networking change to roll back)
  • the foundation service module (security.py) at Wave 9 #119d — Phase 2 canonical vehicle model verification framework #123.c.i is untouched by this slice; rolling back the slice leaves the foundation in place
  • the connection ID (security_review) is reserved by the DOMAIN constant in init.py; rolling back the slice removes the connection but the ID is reusable for a future re-implementation

Bernard added 2 commits August 6, 2026 19:06
…ery)

Context:
  Phase 7 #123.c (Security Review) was dispatched at 2026-08-06 18:09 UTC
  to subagent/phase7-security-review. The sub-agent wrote the core
  service module but got interrupted before commit/push. Per the
  cross-project-triage recovery protocol, the orchestrator takes
  ownership of the in-flight file + commits + pushes + opens the PR
  on the agent's behalf.

  This is sub-slice #123.c.i — the core service module. The full
  #123.c spec still requires: tier-a connection manifest
  (connections/security-review/), services.yaml wiring (3 services),
  data-layer package (roamcore_security_review.yaml with 4 input
  helpers + 4 template sensors + 4 §8 MANDATORY automations), pytest
  rig (~20 tests), bash smoke (~10 assertions), and IKEA runbook.
  These land as #123.c.ii follow-up sub-slices.

Changes:
  + homeassistant/custom_components/roamcore/security.py (NEW, 883 LOC)
    - RCApiTokenManager: rotates RC_API_TOKEN with backup-before-mutate
      discipline (writes backup to /config/.storage/.roamcore_security_backup.jsonl
      BEFORE updating /config/.storage/roamcore_security.json); atomic
      .storage/ write via .tmp + os.replace; idempotent (re-running
      appends, not overwrites, unless force=True); 256-bit entropy via
      secrets.token_urlsafe(32); reads existing token age for the
      'days-old' status message.
    - SSHAuditReader: read-only audit of /etc/ssh/sshd_config; parses
      5 canonical hardening settings (PasswordAuthentication,
      PermitRootLogin, PubkeyAuthentication, Port, PermitEmptyPasswords);
      find_risky_settings() returns plain-English warnings ('Your SSH
      allows password login — switch to keys for safety') per
      Bernard 2026-08-04 doctrine; never mutates sshd_config.
    - FirewallAuditReader: read-only audit of nft + iptables-save;
      parses rules from both nftables.conf and iptables-save output;
      find_risky_rules() flags ACCEPT rules with no saddr filter on
      sensitive ports (SSH 22, HA 8123, MQTT 1883, RDP 3389, etc.);
      plain-English warnings ('Port 22 (SSH) is open to the whole
      internet — restrict to your IP range').
    - register_security_services(hass): wires 3 RoamCore services —
      roamcore.rotate_api_token, roamcore.audit_ssh,
      roamcore.audit_firewall — into HA's service registry; lazy-imports
      homeassistant.core so the module is testable without HA runtime.
    - plain_english_status(): maps status codes to vanlifer-facing
      strings ('Your van is locked down — access codes fresh, SSH
      key-only, firewall tight.').
    - No HA imports in core class bodies (stdlib only) so pytest
      fixtures can import + bench the module without spinning up HA.
    - No hardcoded URLs, no hardcoded passwords, no /home/<user>
      paths (secrets-leak guard ready for the pytest rig).
    - Constants exported: SECURITY_TILE_PREFIX='rc_security_review_'
      (compliant with docs/reference/rc-entity-naming.md; Hub-level
      package will use this prefix for tile entities in #123.c.ii).

Verification:
  - python3 -c 'import ast; ast.parse(...)' → OK (module parses).
  - Module size: 33665 bytes, 883 lines.
  - Module is import-safe (no top-level HA imports — only inside
    register_security_services, which is only called from HA's
    service setup hook).
  - bash scripts/check.sh --core-only → GREEN exit 0 on the branch
    tip; no existing tests broken.
  - Public surface (classes + functions) verified via AST scan:
    RCApiTokenManager, SSHAuditReader, FirewallAuditReader,
    TokenRecord, SSHConfig, FirewallRule, plain_english_status,
    register_security_services, _service_name_for_port — all present.

GOLDEN.md alignment (re-read 2026-08-06 19:03 UTC):
  - P2 (Mission-critical connectivity) served — security review is
    the canonical prevention for the 'lockout' worst-case the
    principle names. SSH-keys-only + firewall-tight + fresh access
    codes = no lockout.
  - P6 (OpenClaw first-class) served — 3 services (rotate_api_token,
    audit_ssh, audit_firewall) are exposed for OpenClaw to call
    through the existing roamcore-agent-actions-allowlist surface.
  - E3 (Backup + rollback discipline) respected — rotate_token()
    writes the backup file BEFORE updating .storage/, atomic write
    via .tmp + os.replace, idempotent (re-running appends).
  - E7 (rc-entity-naming.md) respected — SECURITY_TILE_PREFIX
    constant = 'rc_security_review_' (compliant with the doc's
    'prefix with rc_<subsystem>_' rule).
  - Anti-patterns avoided:
    ❌ no Victron hand-config (purely generic SSH + firewall)
    ❌ no vmbr0 touch (no networking config mutation at all)
    ❌ no committed secrets (token gen via /dev/urandom)
    ❌ no wide PR (pure security-review scope)
    ❌ no unrelated project context
    ❌ no internal engineering logs in user-facing copy

User-facing: Tells me in plain English whether my van is locked down —
SSH keys only, firewall tight, access codes fresh — so I can fix small
problems before they become lockouts, without having to read technical
jargon.

Rollback: git revert <SHA>; no state to recover (no Proxmox/HA/
OpenWrt/networking change; pure repo-local code).
…s + data-layer + pytest + smoke + IKEA runbook

Context:
  Phase 7 #123.c Security Review slice — sub-slice #123.c.ii ships
  the tier-a connection manifest + the 3 service definitions + the
  data-layer package + the 22 pytest tests + the 10 bash smoke
  assertions + the IKEA user-facing runbook on top of the foundation
  service module that landed at #123.c.i (commit 63145f8 on branch
  subagent/phase7-security-review). The foundation service module
  (homeassistant/custom_components/roamcore/security.py, 883 LOC,
  stdlib-only Python) is the canonical umbrella for the security-
  review surface; this slice adds the connection-level artefacts
  that turn the foundation into a tier-a connection.

Changes:
  - connections/security-review/connection.yml (NEW): tier-a
    connection manifest for the security-review surface (12
    contract tiles + 4 §8 MANDATORY automations + 3 RoamCore
    services + vendor-neutral tiles + 1-tap install).
  - connections/security-review/__init__.py (NEW): DOMAIN marker
    + SECURITY_TILE_NAMES tuple + SECURITY_TILE_PREFIX +
    security review status constants for the audit + the helper
    package + the pytest rig.
  - connections/security-review/README.md (NEW): vendor-neutral
    1-paragraph description of the security-review surface.
  - connections/security-review/docs/recipe.md (NEW): 5-step
    IKEA developer recipe (internal only, NOT user-facing).
  - connections/security-review/tests/test_connection_yml.py
    (NEW): 11 manifest-honesty tests (manifest honoured + tier-a
    markers + recipe present + rc-entity-naming compliance +
    idempotency + secrets-leak guard).
  - homeassistant/custom_components/roamcore/services.yaml
    (MODIFY): 3 new service definitions for
    roamcore.rotate_api_token + roamcore.audit_ssh +
    roamcore.audit_firewall (vendor-neutral + plain-English).
  - homeassistant/custom_components/roamcore/__init__.py
    (MODIFY): wired register_security_services(hass) into
    async_setup_entry (mirrors the existing Hub Backup pattern;
    wrapped in try/except so the failure never breaks HA startup).
  - homeassistant/packages/roamcore_security_review.yaml (NEW):
    data-layer package with 4 input helpers + 4 template sensors
    + binary_sensor healthy + 3 buttons + 4 §8 MANDATORY
    automations (rotate-token + audit-ssh + audit-firewall +
    warn-rotation-age).
  - homeassistant/packages/tests/test_security_review.py (NEW):
    22 pytest tests covering the YAML package + the 3 service
    registrations + the 4 helper entities + the 4 template
    sensors + the 4 §8 automations + rc-entity-naming compliance
    + idempotency + secrets-leak guard.
  - scripts/checks/security-review-smoke.sh (NEW): 10 bash
    assertions wiring connection manifest + YAML package + 3
    service definitions + rc-entity-naming + idempotency +
    secrets-leak.
  - scripts/check.sh (MODIFY): wired the new smoke into the chain.
  - homeassistant/packages/tests/test_connection_state_field.py
    (MODIFY): bumped expected manifest count from 33 to 34
    (the security-review connection is the 34th manifest).
  - docs/runbooks/security-review.md (NEW): IKEA 5-step
    vanlifer-facing runbook (5 numbered steps + 3-line
    troubleshooting + useful links; NO bash commands in §1-§4;
    NO operator-speak jargon).

Verification:
  - bash scripts/check.sh --core-only → exit 0 (GREEN)
  - bash scripts/checks/security-review-smoke.sh → PASS: 10 / FAIL: 0
  - python3 -m pytest homeassistant/packages/tests/test_security_review.py
    → 22/22 PASS
  - python3 -m pytest connections/security-review/tests/test_connection_yml.py
    → 11/11 PASS
  - python3 -m pytest homeassistant/packages/tests/test_connection_state_field.py
    → 22/22 PASS (manifest count bumped 33 → 34)
  - secrets-leak guard: grep -rIn -E 'password=|token=|/home/[a-z]' over
    the new files returns only the guard-pattern strings themselves
    (intended markers, not actual leaks).

GOLDEN.md alignment:
  - P1 (Novice-first UX) — the IKEA runbook opens with one
    plain-English sentence a vanlifer understands
    ('Tells you in plain English whether your van is locked down
    — SSH keys only, firewall tight, access codes fresh — so you
    can fix small problems before they become lockouts, without
    having to read technical jargon.'); the dashboard tile is
    one-tap (the rotate-token + audit-ssh + audit-firewall
    buttons trigger the 3 RoamCore services synchronously).
  - P2 (Mission-critical connectivity) — security review IS the
    canonical prevention for the 'lockout' worst-case (per the
    directive §'Phase 7 delivery' / Gate F).
  - P6 (OpenClaw first-class) — 3 services exposed for OpenClaw
    to call through the existing agent-actions-allowlist
    connection (roamcore.rotate_api_token + roamcore.audit_ssh +
    roamcore.audit_firewall).
  - E1 (Customer-facing repo) — no internal logs in the user
    tree; the runbook in docs/runbooks/ is the public surface
    (5-step IKEA + 3-line troubleshooting + useful links);
    developer plumbing stays in connections/security-review/docs/
    recipe.md.
  - E2 (Documentation-driven) — runbook = source of truth for
    the user; package YAML = source of truth for the data layer;
    pytest rig = source of truth for the contract assertions;
    smoke check = source of truth for the chain.
  - E3 (Backup + rollback discipline) — rotate_token() writes
    backup BEFORE updating .storage/ (backup-before-mutate);
    atomic write via .tmp + os.replace; idempotent append-not-
    overwrite; the SSH + firewall audit is READ-ONLY by design
    (no mutation of /etc/ssh/sshd_config or /etc/nftables.conf);
    no Proxmox / HA / OpenWrt / networking change in this slice.
  - E7 (rc-entity-naming.md) — every new entity starts with
    rc_security_review_ (the SECURITY_TILE_PREFIX constant from
    security.py).

User-facing one-liner:
> Tells me in plain English whether my van is locked down — SSH
> keys only, firewall tight, access codes fresh — so I can fix
> small problems before they become lockouts, without having to
> read technical jargon.

Anti-patterns avoided:
  - ❌ no Victron hand-config (purely generic SSH + firewall)
  - ❌ no vmbr0 touch (no networking config mutation at all)
  - ❌ no committed secrets (the secrets-leak guard grep enforces
    this)
  - ❌ no wide PR (pure security-review scope, 13 files only)
  - ❌ no unrelated project context
  - ❌ no internal engineering logs in user-facing copy

Rollback:
  - git revert <commit-sha> + git push origin main (no
    Proxmox / HA / OpenWrt / networking change to roll back)
  - the foundation service module (security.py) at #123.c.i is
    untouched by this slice; rolling back the slice leaves the
    foundation in place
  - the connection ID (security_review) is reserved by the
    DOMAIN constant in __init__.py; rolling back the slice
    removes the connection but the ID is reusable for a future
    re-implementation
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