20 bite-sized tasks covering: common/ skeleton, JS/CSS/icons/shell
extraction, TransportManager hooks (onModuleTap, onTransportChange),
form_factor Pydantic field with TDD, round/ rewire to consume common/
via <link>/<script>, deploy.sh + image build updates, three regression
gates (pytest green, diffoscope timestamp-only deltas, manual Zero W
bench), and PR opening. Phase 2 plan to be written after Phase 1 merges.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pi 400 shares BCM2711 with Pi 4B, runs the same arm64 image. DTB
auto-selection picks bcm2711-rpi-400.dtb at boot. firstboot.sh records
the board flavour. USB peripheral mode requirement (GPIO 5V power) is
unchanged. Integrated keyboard is recognised via libinput.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-phase design: extract remote-ui/common/ (Phase 1, refactor round/
with no behavioural change), then add remote-ui/square/ targeting Pi 4B
+ official 7" 800x480 touchscreen as a dual-pane kiosk — round UI in
Chromium at (0,0)+480x480 plus a PySide6 right column at (480,0)+320x480
with Alerts, Module detail, Console, and Mode controls tabs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brainstormed spec for replacing the plaintext auth.toml admin login with
secubox-users-backed argon2id auth, RFC 6238 TOTP 2FA (mandatory for
admins), kill-on-disable session revocation, CLI/API parity via a single
secubox_users.engine module, and feature-flagged 3-phase rollout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sub-project E of #49. POST /api/v1/metablogizer/webhook receives Gitea
push events for metablog-* repos, verifies HMAC-SHA256, git pulls the
site dir under a per-site asyncio.Lock, invalidates load_sites() cache
(#111), conditionally reloads nginx if site.json:domain changed.
Companion scripts/metablog-webhook-install.sh registers the webhook on
all 166 metablog-* repos via Gitea API (idempotent).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sub-project D of #49. Extends existing /metablogizer/ list view with
3 columns (version, streamlit_app, last_updated) + filter box + sort
+ 60s polling. New site.html drill-in page that surfaces every
site.json field plus 3 external links (live site, Gitea repo,
Streamlit app). Tag history defers to Gitea's UI (browser auth)
because the repos are private and browser-side proxying through
a stored token is heavier than the MVP warrants.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sub-project C of #49. JSON Schema draft-07 + Python validator/enricher +
backfill script + API extension. Permissive validation (log warnings,
don't reject). version + last_updated derived from git when site.json
lacks them. Backfill creates complete site.json for the 105 sites
without one (auto-detects streamlit_app via Gitea repo presence) and
preserves the 61 existing ones unless --force.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
28 Streamlit apps ingested into gitea.gk2.secubox.in/gandalf/streamlit-*.
First pass had 20 failures from pre-existing broken Gitea repo stubs;
bulk-deleted via API and second pass cleaned to 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sub-project F of #49. Audit shows 30 directory-form apps in
/srv/streamlit/apps/ (23 already have .git/, 7 don't), running
inside the existing streamlit LXC at 10.100.0.50.
Design:
- Mirror all 30 apps to gandalf/streamlit-<app> on Gitea
- Retarget existing .git + push history for the 23 (preserves
any local commits); git init + push for the 7
- Tag v1.0.0 on initial state
- streamlitctl deploy <app> --from-gitea --tag <vX.Y.Z>:
clone tag into /srv/streamlit/apps/<app>/ with backup of
the current content; restart running instance if any
- streamlitctl rollback <app>: restore latest backup
- New API fields: current_tag, deployed_at (read from
.deploy.json or git describe --tags --exact-match)
YAGNI: no multi-version side-by-side, no container per
version, no webhook trigger.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): Health banner live panel design (ref #92)
Three public banner sections sharing one polling/CORS pipeline:
- VisitorOrigin: nft set seen_src + GeoLite2-ASN.mmdb, threshold-gated
rollup, raw IPs discarded before persistence
- LiveHosts: HAProxy admin socket, 60 x 1-min ring buffer over req_tot
deltas, hostname-heuristic frontend filter
- CertStatus: scan /etc/letsencrypt/live + cryptography parse, classify
valid / expiring_soon / expiring_critical / expired
Each section fails independently; section hidden on enabled=false,
empty entries, or fetch error. All three endpoints are unauthenticated,
CORS-open, Cache-Control max-age=300.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(plan): Health banner live panel implementation plan (ref #92)
14-task TDD plan: tests scaffold -> config helpers -> three aggregators
(visitor-origin / live-hosts / cert-status) -> FastAPI lifespan wiring ->
nftables ruleset -> geoipupdate timer -> debian packaging -> banner v1.3.0
-> README + tracking docs -> full-suite verification + PR.
Also reconciles spec with codebase conventions: service user is 'secubox'
(not 'secubox-metrics'); config lives in /etc/secubox/secubox.conf, not a
separate metrics.toml.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(metrics): Scaffold pytest layout for new aggregators (ref #92)
Adds tests/__init__.py and conftest.py that wire packages/secubox-metrics/api
and the repo-wide common/ onto sys.path so individual aggregator modules can
be imported in isolation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(core): Add visitor_origin / live_hosts / cert_status config helpers (ref #92)
Three new section helpers in secubox_core.config that merge defaults with
operator-supplied TOML overrides. Each section defaults to enabled=false so
the live-panel aggregators stay quiet on systems that haven't opted in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(metrics): Use tmp_path fixture + drop unused import (ref #92)
Switches the config-helper tests from a hard-coded /tmp path to pytest's
tmp_path fixture, matching the pattern in packages/secubox-haproxy/tests/.
Removes the now-unused 'from unittest.mock import patch' line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(metrics): VisitorOrigin aggregator (ref #92)
Pure-Python aggregator that polls the nft seen_src set, resolves ASNs via
GeoLite2 mmdb, and emits a threshold-gated top-N rollup. Private/loopback IPs
are skipped at lookup time; raw IPs never leave the function scope; the
threshold gate runs before persistence so the cache file never contains
attributable counts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(metrics): VisitorOrigin code-review followups (ref #92)
- mmdb auto-reopen on mtime change (Important): close + reopen when stat
reports a different mtime; previously _mmdb_mtime was dead state.
- _read_nft_set defensive parse (Important): guard against {elem: str} shapes
to prevent a latent TypeError.
- current() returns a defensive copy (Minor): no more by-reference leak of
internal state.
- Drop unused ip_address import and unused monkeypatch parameter (Minor).
- Tighten tiebreak test to assert ASN order, not just count order (Minor).
- Add test for mtime-based reopen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(metrics): Pin VisitorOrigin error-path behaviour (ref #92)
Regression tests for refresh_once: disabled config, missing mmdb, and nft
subprocess failure must all yield a non-throwing degraded payload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(metrics): LiveHosts aggregator with 60x1-min ring buffer (ref #92)
Reads HAProxy admin socket via raw AF_UNIX, parses 'show stat' CSV, filters
internal frontends (leading underscore or no dot), ring-buffers per-frontend
deltas over 60 minutes, and emits a top-N hostname rollup. Counter-reset
detection (cur < prev) yields a fresh-baseline bucket instead of a negative
delta. current() returns a defensive copy mirroring the VisitorOrigin fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(metrics): Pin LiveHosts CSV parser + missing-socket paths (ref #92)
Tests the show-stat CSV parser against the real HAProxy column order and
asserts that an absent admin socket returns a degraded payload rather than
raising.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(metrics): CertStatus aggregator (ref #92)
Scans /etc/letsencrypt/live for cert.pem files, classifies each by days
remaining (valid / expiring_soon / expiring_critical / expired) using the
operator's warn_days/critical_days thresholds, and emits a summary + soonest
next-renewal host. A single corrupt PEM never kills the scan. Days remaining
computed with math.ceil so a cert expiring in 2.99d reports 3d, consistent
with certbot/renewal tooling expectations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(metrics): correct expired boundary when using math.ceil (ref #92)
math.ceil maps any cert that expired within the last 24 h to days=0,
which the previous `days < 0` guard treated as expiring_critical instead
of expired. Changing the guard to `days <= 0` closes the gap: with ceil,
days=0 means actual remaining time is in (-86400, 0] — i.e. already
past or exactly at expiry — so classifying it as expired is correct.
All four existing tests continue to pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(metrics): Wire three live-panel aggregators into FastAPI lifespan (ref #92)
Adds the three asyncio background tasks under a single lifespan and exposes
their current() payloads on /api/v1/metrics/{visitor-origin,live-hosts,cert-status}
with a 5-min Cache-Control. Endpoints stay unauthenticated by design — the
aggregators only emit threshold-gated, hostname-only data.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(metrics): Await cancelled lifespan tasks + tidy import order (ref #92)
- Important: lifespan finally now awaits gather(*tasks, return_exceptions=True)
after cancel(), so blocking subprocess/socket I/O in aggregator refreshes
doesn't race uvicorn's shutdown timeout.
- Minor: move 'from contextlib import asynccontextmanager' into the stdlib
import group at the top of the file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(metrics): Ship nftables ingress tap for visitor-origin (ref #92)
Private inet secubox_metrics table with a timeout'd src-IP set, hooked from
prerouting at priority -300 so additions happen before secubox-firewall's
filter chain decides whether to drop the packet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(metrics): Weekly GeoLite2 ASN refresh timer (ref #92)
Conditional on /etc/secubox/secrets/maxmind.conf existing, so the unit is a
silent no-op on installs that haven't supplied a license key. RandomizedDelay
spreads load when many boxes deploy together.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* build(metrics): Package live-panel deps, nft ruleset, and geoipupdate timer (ref #92)
- control: add python3-maxminddb, python3-cryptography, geoipupdate, nftables
- rules: install nftables/ and systemd/ assets
- postinst: secubox -> haproxy group, cache + secrets + GeoIP dirs,
nftables reload, timer enable
- service: ReadWritePaths gains /var/cache/secubox
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(metrics): Run geoipupdate as secubox + restart svc on upgrade (ref #92)
- secubox-geoipupdate.service: User/Group=secubox so .mmdb files inherit the
ownership the metrics service expects when reading them. Previously the
unit ran as root and created root-owned files that secubox-metrics could
not open.
- postinst: switch 'systemctl start' to 'systemctl restart' so the secubox
user's new haproxy-group membership is picked up by an already-running
service after an upgrade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(banner): v1.3.0 live panel — visitor origin, live hosts, cert status (ref #92)
Three independent fetch loops on a shared 30s cadence, three DOM sections,
per-section hide on enabled=false / empty / fetch error. Uses existing
design tokens (gold/cyan/matrix-green) so no new CSS variables are added.
A failing section never affects the others.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(banner): Correct banner element id lookup in live-panel (ref #92)
The new live-panel sectionContainer() helper looked up
getElementById('sbx-health-banner'), but the actual banner element is
created with id='health-banner'. The mismatch made banner null, so the
three live-panel sections were silently never appended to the DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: Session 160 — Health Banner Live Panel (ref #92)
README documents the three new endpoints + config blocks. HISTORY / WIP /
MIGRATION-MAP entries describe the feature, the spec/plan paths, and the
session's outcome.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(metrics): Address whole-branch review findings (ref #92)
- Wrap _read_nft_set / _read_haproxy_stats in asyncio.to_thread so blocking
I/O (subprocess up to 5s, AF_UNIX recv up to 2s) no longer stalls the
event loop on every refresh tick.
- Replace falsy current() guard with explicit _refreshed flag. Previously,
a successful refresh that produced entries=[] would fall through to the
on-disk cache, serving stale non-empty data during low-traffic periods.
- Move geoipupdate from Depends to Recommends. It lives in bookworm/contrib,
so a hard dependency breaks 'apt install secubox-metrics' on systems
without contrib enabled. The aggregator already degrades gracefully when
the mmdb is absent, making Recommends the correct strength. README
documents the contrib note.
- prerm stops + disables secubox-geoipupdate.timer/service so 'apt remove'
doesn't leave an orphan timer firing weekly with a missing unit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
166 sites ingested into gitea.gk2.secubox.in/gandalf/metablog-*.
First pass had 72 failures from pre-existing broken Gitea repo
stubs; bulk-deleted via API and second pass cleaned to 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sub-project B of #49. Audit shows 166 sites in /srv/metablogizer/sites/:
83 have local .git pointing at unreachable old Gitea (192.168.255.1:3001
or git.gk2/droplet-sites), 83 are raw files.
Design:
- All 166 land at gandalf/metablog-<site> on gitea.gk2.secubox.in (single
namespace, even for the 12 ex-droplet-sites)
- Existing .git: retarget remote + push history (preserves 1 commit)
- Missing .git: git init + initial commit
- ENABLE_PUSH_CREATE_USER=true in Gitea app.ini so push creates repos
- SSH auth via gandalf's enrolled key; no API token plumbing
- Idempotent: skip if remote HEAD matches local HEAD
- v1.0.0 tag on HEAD after first push (per issue text)
- output/ingest-report.json captures per-site status
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec+plan): WebUI obfuscation design and implementation plan (ref #44)
Spec: docs/superpowers/specs/2026-05-12-webui-obfuscation-design.md
Plan: docs/superpowers/plans/2026-05-12-webui-obfuscation.md
The design locks the SecuBox WebUI to admin.<HOSTNAME>.<DOMAIN_SUFFIX>
via strict-regex ACLs in HAProxy AND nginx, both driven by
/etc/default/secubox as the single source of truth. New package
secubox-defaults ships the env file; secubox-haproxy API exposes
/webui/admin-domain (info), /webui/nginx-config (JWT, rendered vhost),
/webui/refresh (JWT, cache invalidate). A safe renderer script writes
the nginx vhost atomically; haproxyctl + Python /generate both inject
the strict ACL at the top of http-in and https-in frontends.
Plan has 15 tasks with TDD steps, exact file paths, and complete code
in every step (no TBD/TODO). Acceptance criteria + integration test
script included.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(secubox-defaults): scaffold package skeleton (ref #44)
* feat(secubox-defaults): ship /etc/default/secubox env file (ref #44)
* feat(secubox-defaults): postinst autodetect + dpkg-trigger (ref #44)
* feat(secubox-haproxy): webui_identity helper + tests (ref #44)
Implement webui_identity module that parses /etc/default/secubox
and exposes canonical admin URL + regex pattern for HAProxy acl.
New module:
- api/webui_identity.py: Parses SECUBOX_HOSTNAME and SECUBOX_DOMAIN_SUFFIX
with LRU cache and invalidation support.
Tests (TDD):
- tests/test_webui_identity.py: 5 test cases covering basic parsing,
missing hostname validation, custom suffix handling, comment handling,
and cache invalidation.
- tests/conftest.py: sys.path bootstrap for api/ imports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(webui_identity): catch OSError on defaults read + test (ref #44)
* feat(secubox-haproxy): GET /webui/admin-domain endpoint (ref #44)
* feat(secubox-haproxy): GET /webui/nginx-config rendered vhost (ref #44)
* feat(secubox-haproxy): POST /webui/refresh cache invalidation (ref #44)
* feat(secubox-haproxy): secubox-render-nginx-webui safe renderer (ref #44)
* feat(haproxyctl): inject strict-regex WebUI ACL at frontend top (ref #44)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(secubox-haproxy): /generate symmetry with strict WebUI ACL (ref #44)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(secubox-haproxy): dpkg-trigger refresh on secubox-defaults-changed (ref #44)
* build(secubox-haproxy): depend on secubox-defaults (ref #44)
* test(infra): integration test for WebUI obfuscation (ref #44)
* docs: HISTORY Session 159 — WebUI obfuscation (ref #44)
Records the complete WebUI obfuscation work: new secubox-defaults
package, three /webui/* endpoints, nginx renderer script, haproxyctl
+ Python generator symmetry, dpkg trigger plumbing, integration test.
Includes notes on the brainstorming → spec → plan → subagent-driven
execution flow and the 6 fix iterations caught by reviewers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sub-project A of #49: expose existing Gitea LXC (10.100.0.40) at
https://gitea.gk2.secubox.in/ and ssh://git@gitea.gk2.secubox.in:2222/.
Uses existing wildcard cert; backend nginx_vhosts (direct, no
mitmproxy WAF for git smart-HTTP traffic).
Records haproxyctl regression #91 as a hard constraint: edit
haproxy.cfg manually with backup, never run haproxyctl vhost add
until the generator is fixed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Display cert expiry days for current domain
- Thresholds: >7j ok, 3-7j warn, <3j error
- Backend: get_ssl_status() in health API
- Frontend: SSL line in banner after score
Reference: CM-SSL-BANNER-2026-05-12
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Brainstorming-validated spec for `scripts/agent-worktree.sh` plus a new
CLAUDE.md doctrine section. Enables parallel multi-agent work by binding
each task to one GitHub issue, one branch, and one external worktree
under `~/CyberMindStudio/secubox-deb-worktrees/`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17 TDD tasks covering: scaffolding, render_header for 4 comment styles,
detect_existing tri-state, apply() per language with placement rules
(Python shebang+encoding, Bash shebang, HTML doctype, Markdown
frontmatter), walk() with skip-list and enrollment allowlist, CLI
dispatch, self-hosting, GitHub Actions workflow, README and CLAUDE.md
updates, smoke tests, and PR opening. Phase B/C left as operational
follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brainstormed design for adding the SPDX-CMSD-1.0 header to every
first-party source file (~2,170 across 6 languages), backed by a
reusable Python tool (scripts/license-headers.py), a CI check, and
a phased per-package rollout. Spec covers scope, header rendering
per language, placement rules, tool architecture, CI integration
with an enrollment allowlist, and verification steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brainstormed design for staging a signed APT repo at output/repo/
with amd64 + arm64 (mochabin) packages for bookworm, using existing
Go CLI and shell tooling. No network operations - user pushes
artifacts to apt.secubox.in out-of-band.
Layered build: base -> tier-lite -> tier-standard -> tier-pro
GPG: persistent ~/.gnupg/secubox/
License: CMSD-1.0 embedded in staged tree
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hybrid approach:
- Client operations (apt setup, clone) in pure Go
- Server operations (apt init/publish/sync) wrap existing shell scripts
Features:
- secubox apt: full repo management + client setup
- secubox clone: interactive bootstrap wizard for new systems
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Define Manifest struct with kernel, partitions, boot, output
- Implement Generate() from Profile and Board
- Add ToYAML() for serialization with header comments
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
17 tasks with TDD approach:
- Go CLI with cobra/viper (gen, build, fetch, ota, info)
- Profile loader with inheritance merger
- Board configuration and tweaks
- Package scanner for debian/secubox.yaml
- Manifest and Makefile generation
- Interactive wizard with promptui
- Hardware detection
- APT repository with lintian compliance
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Big Bang migration plan from C3BOX (OpenWrt) to SecuBox-DEB (Debian):
- 4 phases: Prepare, Build, Sync, Cutover
- LXC containers for Mail, NextCloud, Matrix, Gitea
- Host-native gateway services (HAProxy, CrowdSec, nftables)
- Data migration strategy for 94 SSL certs + service data
- Cutover procedure with rollback plan
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Design spec for migrating mitmproxy WAF module from OpenWrt to Debian:
- WAF-in mode only (HAProxy backend inspection)
- Single unified secubox-mitmproxy package
- LXC container with secubox_waf.py threat detection addon
- 18 FastAPI endpoints for status, alerts, HAProxy integration, WAF rules
- CrowdSec integration for automatic IP banning
- Full WebUI with status, settings, and filters pages
- TOML configuration format
Session 90
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 tasks with 37 steps covering:
- Modal system and toast notifications
- Form validation and API error handling
- VHost CRUD (add/edit/delete)
- Backend CRUD (add/edit/delete)
- Server CRUD (nested under backends)
- Certificate CRUD (request/delete)
- Table enhancements with action buttons
Single file modification pattern maintained.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Design spec for adding CRUD operations to HAProxy dashboard:
- VHost management (add/edit/delete)
- Backend management with nested servers
- Certificate request and deletion
- Modal system with P31 Phosphor theme
- Toast notifications for feedback
- Client-side validation
Approach A: Minimal Enhancement (~450 lines in single file)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 4 operating modes: Dashboard, Local, Flash, Gateway
- Web Remote control at :8080 (touchless)
- Auto-detect + flag file override for mode switching
- Staged failover with visual feedback
- Unified Python Agent architecture
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13-task TDD implementation plan covering:
- MenuItem and MenuState data models
- Static menu definitions for 6-slice radial menus
- MenuNavigator state machine
- Slice detection from touch coordinates
- RadialRenderer for framebuffer display
- ActionExecutor with LocalAPI integration
- TouchHandler menu mode integration
- Component wiring in main.py
- Menu icon generation
- Comprehensive test coverage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive design for radial menu touch interface:
- 6-slice pie navigation optimized for circular display
- Dual-scope control (local Pi Zero + remote SecuBox)
- Gesture-based interaction (tap, swipe, long press)
- Hierarchical menu structure with dynamic data loading
- Error handling and visual feedback patterns
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Complete testing guide covering:
- Gateway emulator installation and usage
- Unit test execution (13 tests)
- Agent-gateway integration testing
- API module verification
- Troubleshooting section
Part of Eye Remote v2.0.0 implementation (Task 14).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>