Nik Afiq 8f7024edfa
All checks were successful
CI / changes (push) Successful in 19s
CI / test (push) Successful in 24s
CI / build-ai-gateway (push) Successful in 1m6s
CI / build-ha-gateway (push) Successful in 1m3s
CI / build-discord-bot (push) Successful in 1m4s
CI / build-alexa-bridge (push) Successful in 1m15s
CI / build-tts-gateway (push) Successful in 1m5s
CI / build-tts-sidecar (push) Has been skipped
CI / build-tts-model (push) Has been skipped
feat(climate): add SetTemperature method to ClimateService
- Implemented SetTemperature in ClimateService for setting an absolute target temperature.
- Updated ClimateServiceClient and ClimateServiceServer interfaces to include SetTemperature.
- Added corresponding handler and tests for SetTemperature in ClimateGRPC.
- Modified ClimateApp to handle SetTemperature requests without clamping.
- Updated climate.proto to define SetTemperatureRequest message.
- Adjusted Dockerfiles to include alexa-bridge dependencies.
2026-07-25 12:08:24 +09:00

94 lines
4.3 KiB
Markdown

# alexa-bridge
`alexa-bridge` is a self-hosted Alexa Custom Skill backend. It verifies that
incoming HTTPS requests actually came from Alexa, resolves the spoken device
name against entities discovered from `ha-gateway`, and executes the
resulting action over gRPC. Unlike the other three services in this repo, it
is internet-facing — Alexa's servers call directly into it — so it is the
one service whose own inbound edge is untrusted by default and needs its own
request verification, on top of the mTLS it uses (like `ai-gateway` and
`discord-bot`) to call `ha-gateway`.
See [plan.md](plan.md) for the full design, the action-table mapping from
Alexa intents to `ha-gateway` RPCs, and the resolved "Decisions on open
questions" section documenting scope choices (single-setpoint-only climate
control, exact-match-only entity name resolution, no remote/SwitchBot
support, etc.).
## Runtime Flow
1. The service loads `.env`, configures logging and telemetry, blocking-fetches
the initial entity list from `ha-gateway`'s `EntityService` (failing
startup if that fails — nothing can resolve a device name without it),
then starts serving HTTP on `HTTP_PORT`.
2. A background goroutine refreshes the entity list every
`ENTITY_REFRESH_INTERVAL`; a failed periodic refresh only logs and keeps
serving the last-known-good list.
3. Each incoming Alexa request is verified: `SignatureCertChainUrl`/
`Signature` headers (cert chain fetched from Amazon's S3 host and cached,
RSA-SHA1 signature over the raw body), request timestamp (150s replay
tolerance), and the request envelope's application ID against
`ALEXA_SKILL_ID`.
4. Verified `IntentRequest`s are routed by intent name; the `Device` slot is
resolved (exact match, normalized) against the cached entity list, and the
resulting `(entity_id, action, params)` triple is executed via
`ha-gateway`'s `Light`/`Switch`/`Climate` services over mTLS.
5. Resolution and execution failures produce a spoken failure response
rather than an HTTP error — a Dispatch-layer error would surface as
Alexa's own generic failure speech instead of a message this skill
controls.
## Configuration
Environment variables:
| Variable | Default | Description |
| --- | --- | --- |
| `HTTP_PORT` | `8080` | HTTP listen port |
| `ALEXA_SKILL_ID` | — (required) | Verified against the request envelope's application ID |
| `HA_GATEWAY_ADDR` | `ha-gateway.home-services.svc.cluster.local:50051` | gRPC address for `ha-gateway` |
| `HA_GATEWAY_SERVER_NAME` | `ha-gateway.home-services.svc.cluster.local` | Expected server name for mTLS |
| `TLS_DIR` | `/tls` | mTLS client cert dir for the `ha-gateway` call. Unlike `ai-gateway`/`discord-bot`, this defaults **on** rather than empty — alexa-bridge is internet-facing, so mTLS to `ha-gateway` should be on by default rather than opt-in. Set empty to disable for local plaintext dev. |
| `ENTITY_REFRESH_INTERVAL` | `5m` | Periodic entity list refresh cadence |
| `OTEL_ENDPOINT` | empty | OTLP gRPC collector endpoint; empty disables telemetry |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` |
| `LOG_FORMAT` | `json` | `json` or `text` |
Example env file: [.env.example](.env.example)
When `TLS_DIR` is set, the directory must contain `tls.crt`, `tls.key`, and
`ca.crt`.
## Local Run
Start `ha-gateway` first, then run:
```bash
cd alexa-bridge
cp .env.example .env
# edit .env: set ALEXA_SKILL_ID, leave TLS_DIR empty for plaintext local dev
go run ./cmd/bridge
```
`alexa-bridge` doesn't register with Alexa on its own — during development,
point Alexa's request signature verification at a tunneled local endpoint
(e.g. `ngrok`) and set that HTTPS URL as the skill's endpoint in the Alexa
developer console. `/healthz` returns `200 OK` for liveness/readiness probes.
## HTTP API
There is no gRPC surface here (unlike the other three services) — this is a
plain HTTP server:
- `POST /`: the Alexa Custom Skill endpoint. Expects the standard Alexa
request envelope as JSON, with `SignatureCertChainUrl` and `Signature`
headers.
- `GET /healthz`: unauthenticated liveness/readiness check, always `200 OK`.
## Deployment
Kubernetes manifests (Deployment/Service, the `alexa-bridge-tls` mTLS client
cert, and the public HTTPS ingress) live in the separate `homelab` repo, not
here — see [plan.md](plan.md)'s mTLS section and "Decisions on open
questions" #2 for what's drafted there.