Go to file
HomeOS Bootstrap 52c1c5d3ac feat(addons): installable integrations framework + Ring connect UI
First half of the Ring integration (handoff §12: ring-mqtt, cloud token auth):
- Addons registry (static catalogue) + Addon model with encrypted config, so
  the Ring refresh token / credentials are never stored in plaintext.
- AddonService: install / uninstall (wipes secrets) / saveConnection (blank
  password keeps the stored one) / markStatus (from the bridge over MQTT).
- Addons page (new sidebar tab) with per-addon install, Cloud badge, live
  status pill, and a Ring connect modal capturing email/password/2FA — we
  never call Ring ourselves; creds are handed to the ring-mqtt sidecar.
- Uninstall behind a wire-elements confirm (R5). Fixed the confirm target:
  Livewire names App\Livewire\Addons\Index as "addons" (Index suffix dropped),
  and only $wire.$dispatch (not Alpine $dispatch) reaches the modal listener.
- DE/EN localization (R16); doorbell/package/cloud icons.

7 AddonTest cases (registry, install, encryption-at-rest, blank-password
keep, uninstall wipe, status guard). Suite 33 green; 12/12 tabs clean; full
install→connect→connecting→uninstall UI flow browser-verified, 0 console errors.

Backend bridge (ring-mqtt container) + Ring MQTT normalizer land next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:47:02 +02:00
.reviews feat(addons): installable integrations framework + Ring connect UI 2026-07-18 01:47:02 +02:00
app feat(addons): installable integrations framework + Ring connect UI 2026-07-18 01:47:02 +02:00
bootstrap Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
config Phase 5: UniFi presence — poll, person↔client mapping 2026-07-18 00:31:32 +02:00
database feat(addons): installable integrations framework + Ring connect UI 2026-07-18 01:47:02 +02:00
docker feat(pwa): installable app — manifest, service worker, offline shell, icons 2026-07-18 01:25:24 +02:00
lang feat(addons): installable integrations framework + Ring connect UI 2026-07-18 01:47:02 +02:00
public fix(pwa): await service-worker cache write (R15) 2026-07-18 01:27:41 +02:00
resources feat(addons): installable integrations framework + Ring connect UI 2026-07-18 01:47:02 +02:00
routes feat(addons): installable integrations framework + Ring connect UI 2026-07-18 01:47:02 +02:00
sidecar Phase 4: network discovery — sidecar, ingest, "Neue Geräte" + assign 2026-07-18 00:25:17 +02:00
storage Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
tests feat(addons): installable integrations framework + Ring connect UI 2026-07-18 01:47:02 +02:00
.editorconfig Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
.env.example All sidebar tabs are real pages (zero console errors) 2026-07-18 00:16:16 +02:00
.gitattributes Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
.gitignore Fix R15: provision a per-device MQTT credential when assigning a device 2026-07-18 00:50:53 +02:00
.npmrc Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
CLAUDE.md Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
README.md Phase 3: MQTT ingest — Mosquitto bus, Shelly driver, live device state 2026-07-17 22:51:14 +02:00
artisan Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
composer.json Phase 5: UniFi presence — poll, person↔client mapping 2026-07-18 00:31:32 +02:00
composer.lock Phase 5: UniFi presence — poll, person↔client mapping 2026-07-18 00:31:32 +02:00
design-mockup.html Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
docker-compose.yml Fix R15: provision a per-device MQTT credential when assigning a device 2026-07-18 00:50:53 +02:00
handoff.md Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
package-lock.json Tablet control panel: touch tiles, drag-reorder, light colour modal 2026-07-18 00:58:24 +02:00
package.json Tablet control panel: touch tiles, drag-reorder, light colour modal 2026-07-18 00:58:24 +02:00
phpunit.xml Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
rules.md Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00
vite.config.js Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth 2026-07-17 21:06:20 +02:00

README.md

HomeOS

Self-built smart-home control plane — single household, self-hosted, LAN-first. Laravel 13 · Livewire v3 · Tailwind v4 · PostgreSQL 17 + TimescaleDB · Redis/Horizon · Reverb.

Everything runs in Docker; the host needs only Docker (no PHP/Composer/Node). Authoritative spec: handoff.md · conventions: rules.md · guide: CLAUDE.md.

First-run setup

cp .env.example .env                                        # dev defaults work as-is
docker compose build                                        # build the app image
docker compose run --rm app bash docker/app/bootstrap.sh    # deps + key + assets + migrate/seed
bash docker/mosquitto/gen-passwd.sh                         # broker credentials (fills empty MQTT_*_PASSWORD)
docker compose up -d                                        # start the full stack

docker compose up alone is not the first command — vendor/, node_modules, public/build and the Mosquitto passwd file are not committed, so the steps above must run first.

Then open http://localhost and sign in with the seeded dev admin:

Production

.env.example ships development defaults. For a real deployment you must:

  • set APP_ENV=production and APP_DEBUG=false (otherwise exceptions are exposed and the demo household is seeded into your real database);
  • set strong values for DB_PASSWORD, the REVERB_APP_* keys and HOMEOS_ADMIN_PASSWORD (seeding aborts if the admin password is unset outside local/testing), and run php artisan key:generate.

The demo seeder (DemoHomeSeeder) only runs in local/testing.

Everyday commands (in-container, R8)

docker compose exec app php artisan …    # artisan
docker compose exec app composer …       # composer
docker compose exec app npm run build    # rebuild assets (or `npm run dev` for HMR)
docker compose exec app php artisan test # test suite
docker compose logs -f app               # logs

Services & ports

Service Role Host port
app php-fpm + nginx + supervisor ${APP_PORT:-80}
reverb websockets (proxied same-origin via nginx /app) ${REVERB_HOST_PORT:-6001}
horizon · scheduler queue workers · cron
db PostgreSQL 17 + TimescaleDB 127.0.0.1:${DB_HOST_PORT:-5432}
redis cache / queue

All ports and HOST_UID/HOST_GID are env-driven in .env.