Code Glue is a Home Assistant addon that allows users to write automations and create entities in TypeScript. It consists of an Vite-based web client and a Node.js server.
- Root: Yarn workspace with
packageManager: "yarn@4.9.2" - apps/client/: Vite web app (React Native Web)
- apps/server/: Node.js/TypeScript server using Digital Alchemy libraries
- packages/paradigm/: Shared TypeScript package
- Client:
apps/client/dist/- Static web export from Vite - Server:
dist/server/- Compiled TypeScript (.mts→.mjs)
/work/ # Working directory
├── dist/
│ ├── server/ # Compiled server code
│ └── client/ # Static web files
├── apps/
│ └── server/
│ └── migrations/ # Database migration files
│ ├── sqlite/
│ ├── mysql/
│ └── postgresql/
├── node_modules/
└── [other files]
/data/ # Persistent volume (Home Assistant provided)
└── synapse_storage.db # SQLite database (persists across restarts)
- Client: Vite, React 19, React Native Web
- Server: Node.js 22, TypeScript 5.9, Fastify, Drizzle ORM, better-sqlite3
- Framework: Digital Alchemy (@digital-alchemy/core, @digital-alchemy/hass, @digital-alchemy/synapse)
- Container: Multi-stage Dockerfile using node:22-bookworm-slim
yarn build # Builds both client (Vite) and server (TypeScript)The main Dockerfile uses a 3-stage build:
- deps stage: Install all dependencies (including devDependencies for build)
- build stage: Run
yarn buildto compile everything - runner stage: Copy built artifacts and production dependencies only
Important: Native modules (like better-sqlite3) are rebuilt in the runner stage for correct architecture.
- ORM: Drizzle ORM with better-sqlite3 driver
- Migration files:
apps/server/migrations/{sqlite,mysql,postgresql}/ - Config:
apps/server/drizzle.config.ts
- Development: Run manually with
yarn server:db:migrate - Production: Automatic via
scripts/docker-entrypoint.shusingdrizzle-kit migrate - Important: The server's
DatabaseInternalsServicealso tries to run migrations but may fail if metadata is not in the expected location. Currently commented out to avoid bootstrap failures.
The server has multiple entry points in dist/server/app/environments/:
- local/ - Development with hot reload (
tsx) - prod/ - Production with proxy setup (runs
start-with-proxy.mjs) - prebuilt/ - Production for prebuilt containers (single process, serves static files)
- Entry:
dist/server/app/environments/prebuilt/main.mjs - Sets
SERVE_STATIC=trueandATTACH_STANDARD_MIDDLEWARE=true - Expects client at
process.cwd() + '/dist/client'→/work/dist/client/ - Runs on port 3789 (configurable via PORT env var)
The server's StaticFileService (apps/server/src/http/services/static.service.mts):
- Looks for client at
path.resolve(process.cwd(), "dist/client") - With workdir
/work, this resolves to/work/dist/client - Current setup: Client copied to
/work/dist/client/in Dockerfile - Serves SPA with catch-all routing (all non-API routes →
index.html)
-
Entrypoint script (
scripts/docker-entrypoint.sh):- Runs database migrations:
cd /work/apps/server && npx drizzle-kit migrate - Sets
DATABASE_URL=file:/data/synapse_storage.db - Starts server:
node dist/server/app/environments/prebuilt/main.mjs
- Runs database migrations:
-
Server bootstrap:
- Initializes Digital Alchemy services
- Connects to Home Assistant (via HASS_TOKEN and HASS_BASE_URL)
- Registers HTTP routes and static file handler
- Starts listening on port 3789
- Type: Local addon (builds from Dockerfile)
- Ingress: Enabled on port 3789
- API Access: Requires
homeassistant_api: trueandhassio_api: true - Architectures: amd64, aarch64, armv7, i386, armhf
SUPERVISOR_TOKEN- Auto-injected by HA SupervisorHASS_TOKEN- Set via addon options or auto-configuredHASS_BASE_URL- Set via addon options or auto-configured
- Cause: Migrations not running before service initialization
- Fix: Ensure
drizzle-kit migrateruns in entrypoint script before server starts
- Cause: Static files not at expected path
- Fix: Ensure client copied to
/work/dist/client/in Dockerfile to matchStaticFileServiceexpectations
- Cause:
DatabaseInternalsServicetries to run migrations but looks in wrong location - Fix: Set
SKIP_AUTO_MIGRATIONS=trueor comment out automatic migration code (since entrypoint handles it)
- Cause: Native modules built for wrong architecture
- Fix: Run
npm rebuild better-sqlite3in runner stage after copying node_modules
- Symptom: Changes not reflected after rebuild
- Fix: Bump
versioninconfig.yamlto force supervisor to rebuild
# Install dependencies
yarn install
# Run server in dev mode
yarn server:start # Uses local environment with tsx
# Run client in dev mode
yarn client:dev # Runs client, server and storybook
# Build everything
yarn build
# Run database migrations
yarn server:db:migrate# Build container
docker buildx build --load -t code-glue-addon:dev .
# Run container (will fail without HA connection)
docker run --rm -p 3789:3789 \
-e HASS_TOKEN=test \
-e HASS_BASE_URL=http://localhost:8123 \
code-glue-addon:devThe repo includes .devcontainer/devcontainer.json using ghcr.io/home-assistant/devcontainer:2-addons.
- Has Docker access built-in
- Can build and test addon directly in HA environment
| Description | Path in Container | Source in Build |
|---|---|---|
| Server code | /work/dist/server/ |
dist/server/ |
| Client static files | /work/dist/client/ |
apps/client/dist/ |
| Migrations | /work/apps/server/migrations/ |
apps/server/migrations/ |
| Database | /data/synapse_storage.db |
Created at runtime (persistent volume) |
| Node modules | /work/node_modules/ |
Copied from build stage |
| Entrypoint | /docker-entrypoint.sh |
scripts/docker-entrypoint.sh |
Before committing changes:
- ✅ Container builds successfully
- ✅ Database migrations run on startup
- ✅ Server starts without errors (check for "bootstrap failed")
- ✅ Static files accessible (no 404s on
/) - ✅ API endpoints respond (check
/api/v1/health) - ✅ No runtime building (no
yarn buildin logs)
- Multi-architecture CI/CD with GitHub Actions
- Publish to GitHub Container Registry (ghcr.io)
- Use pre-built images instead of building in Home Assistant
- Reduce image size by pruning dev dependencies more aggressively
- Use
yarn workspaces focus --productionfor server-only deps - Consider separate base images for different architectures