Codex R15 on the Ring bridge flagged two issues: - [P1] Concurrent first-sight messages (retained info + motion on bridge startup) could create duplicate devices — no uniqueness on ring_id. Added a partial unique index on (config->>'ring_id'); resolveDevice now creates and, on the unique violation, re-fetches the winner (race-safe). - [P2] The install-gate cache was never invalidated, so ingest was dropped for up to 15s after install and devices kept being created for up to 15s after uninstall. AddonService now forgets the shared cache key on install/uninstall. +3 tests (unique-index guard, repeated-messages-reuse-device, cache invalidation). Suite 43 green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| app | ||
| bootstrap | ||
| config | ||
| database | ||
| docker | ||
| lang | ||
| public | ||
| resources | ||
| routes | ||
| sidecar | ||
| storage | ||
| tests | ||
| .editorconfig | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| .npmrc | ||
| CLAUDE.md | ||
| README.md | ||
| artisan | ||
| composer.json | ||
| composer.lock | ||
| design-mockup.html | ||
| docker-compose.yml | ||
| handoff.md | ||
| package-lock.json | ||
| package.json | ||
| phpunit.xml | ||
| rules.md | ||
| vite.config.js | ||
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 upalone is not the first command —vendor/,node_modules,public/buildand 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:
- admin@homeos.local / homeos-dev ← change this for anything but local dev.
Production
.env.example ships development defaults. For a real deployment you must:
- set
APP_ENV=productionandAPP_DEBUG=false(otherwise exceptions are exposed and the demo household is seeded into your real database); - set strong values for
DB_PASSWORD, theREVERB_APP_*keys andHOMEOS_ADMIN_PASSWORD(seeding aborts if the admin password is unset outside local/testing), and runphp 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.