api.schweitz.net is internet-facing with no forward-auth, and every
GET under /housekeeping/* (health, devices, areas, scenes, scripts,
automations, history) was unauthenticated. It answers 500 today only
because Home Assistant's token is dead; once that token is restored it
would serve the house's device list to anyone.
Reads now go through require_caller("office", "tatlock", "user").
tatlock is in the list because tatlock's Housekeeper
(tatlock/src/agents/housekeeper/client.py) is the only caller of these
routes today and already sends Bearer CORE_API_KEY when it is set.
Writes are untouched — control_device, activate_scene, run_script and
toggle_automation still depend on get_admin_user. Whether tatlock may
write is a separate decision, not this one.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Dashboard domain with Quick Links CRUD + reorder endpoints
- Dashboard widgets management endpoints
- Database migrations for quick_links and dashboard_widgets tables
- Static file controller for Organizr widgets
- Default local user when OIDC is disabled
- Domain-based architecture refactor (src/domains/, src/shared/)
- Test suite updated for new structure (285 tests passing)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>