seahaven-door-unlock-api/README.md

155 lines
8.7 KiB
Markdown
Raw Normal View History

# Sea Haven Door Unlock API
![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)
![AWS CDK](https://img.shields.io/badge/AWS-CDK-FF9900?logo=amazonaws&logoColor=white)
![CI](https://github.com/Sea-Haven-Industries/seahaven-door-unlock-api/actions/workflows/ci.yaml/badge.svg)
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
AWS Lambda middleware that allows Yealink desk phones to unlock the front door and manage lockdown profiles via LenelS2 Elements.
```
Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) * feat: add gateway token authorizer to door-unlock API (INFRA-99) All three routes (GET /unlock, /lockdown, /lockdown/status) were AuthorizationType NONE — auth relied solely on each handler checking the ?token= query param. Add a REQUEST-type HTTP API Lambda authorizer (door-unlock-api-authorizer) that validates the SAME ?token= value the Yealink XML Browser keys already send, against the existing /seahaven/door-unlock/auth-token SSM SecureString, and attach it to all three routes. Transparent to the phones: identity source is $request.querystring.token (exactly what the type-17 XML Browser keys send via GET), simple response {isAuthorized}, fail-closed, 5-min results cache. Token is cached in module scope so warm invocations skip SSM. GET is kept (not switched to POST): the Yealink type-17 XML Browser keys are GET-only and render the returned Yealink XML — they cannot issue a POST body or custom headers. POST is therefore deferred to avoid bricking the door keys. Handlers retain their own token check as defense-in-depth. Purely additive change set; no existing Lambda or integration is modified. * chore: complete CI/CD migration to GitHub Actions (INFRA-2) GitHub Actions (ci.yaml + deploy.yaml via the Sea Haven reusable workflows) is the proven deploy path. Remove the now-orphaned buildspec.yml and update the README CI/CD and architecture sections. The legacy CodePipeline was already deleted (2026-06-05); the leftover CodeBuild project seahaven-door-unlock-api-build and its IAM role seahaven-door-unlock-api-codebuild have now also been decommissioned.
2026-06-08 18:03:30 -04:00
Yealink T54W/T58W → HTTPS GET (?token=) → API Gateway (token authorizer) → Lambda → LenelS2 Elements API
```
## Architecture
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
- **API Gateway (HTTP API)** — `GET /unlock`, `GET /lockdown`, `GET /lockdown/status` with throttling (5 burst / 2 sustained req/sec)
Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) * feat: add gateway token authorizer to door-unlock API (INFRA-99) All three routes (GET /unlock, /lockdown, /lockdown/status) were AuthorizationType NONE — auth relied solely on each handler checking the ?token= query param. Add a REQUEST-type HTTP API Lambda authorizer (door-unlock-api-authorizer) that validates the SAME ?token= value the Yealink XML Browser keys already send, against the existing /seahaven/door-unlock/auth-token SSM SecureString, and attach it to all three routes. Transparent to the phones: identity source is $request.querystring.token (exactly what the type-17 XML Browser keys send via GET), simple response {isAuthorized}, fail-closed, 5-min results cache. Token is cached in module scope so warm invocations skip SSM. GET is kept (not switched to POST): the Yealink type-17 XML Browser keys are GET-only and render the returned Yealink XML — they cannot issue a POST body or custom headers. POST is therefore deferred to avoid bricking the door keys. Handlers retain their own token check as defense-in-depth. Purely additive change set; no existing Lambda or integration is modified. * chore: complete CI/CD migration to GitHub Actions (INFRA-2) GitHub Actions (ci.yaml + deploy.yaml via the Sea Haven reusable workflows) is the proven deploy path. Remove the now-orphaned buildspec.yml and update the README CI/CD and architecture sections. The legacy CodePipeline was already deleted (2026-06-05); the leftover CodeBuild project seahaven-door-unlock-api-build and its IAM role seahaven-door-unlock-api-codebuild have now also been decommissioned.
2026-06-08 18:03:30 -04:00
- **Token authorizer Lambda** — a REQUEST-type Lambda authorizer validates the `?token=` query-string value (the same shared secret the phones already send) against the `/seahaven/door-unlock/auth-token` SSM parameter, so unauthenticated callers are rejected at the gateway (401/403) before any handler runs. Identity source is `$request.querystring.token`; results are cached 5 minutes. Fail-closed. The handlers also re-validate the token as defense-in-depth.
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
- **Unlock Lambda** — validates a shared auth token, calls the Elements `TemporaryUnlock` command
- **Lockdown Lambda** — toggles lockdown profiles (start/stop) and checks status, returns Yealink XML TextScreen responses
- **Lockdown Poller Lambda** — VPC-connected, polls Elements API every 15 seconds for lockdown status (runs 4x per 1-minute EventBridge schedule)
- **SSM Parameter Store** — stores the Elements API key, auth token, door ID, and phone IPs
- **Secrets Manager** — stores the Yealink phone admin password
- **Custom Domain** — `doorunlock.seahaven.com` via Route 53 + ACM wildcard cert
- **CloudWatch alarms** — one error alarm per Lambda (unlock, lockdown, authorizer, poller); each fires on `Errors > 0` and notifies the cross-stack `site-alerts` SNS topic (ALARM state only)
## Infrastructure (CDK)
All infrastructure is defined as code with the **AWS CDK v2 (TypeScript)**; `aws-cdk-lib` is pinned to `2.261.0`. The whole system is a single CloudFormation stack.
### Layout
```
bin/app.ts # CDK app entry point
lib/door-unlock-stack.ts # DoorUnlockStack — all resource definitions
lambda/
├── unlock/unlock-handler.ts # Unlock Lambda
├── lockdown/lockdown-handler.ts # Lockdown Lambda
├── poller/lockdown-poller.ts # Lockdown Poller Lambda
├── authorizer/authorizer-handler.ts # Token authorizer Lambda
└── blf-sync/blf-sync-handler.ts # 3CX department BLF sync Lambda
cdk.json # CDK config (app command, watch, context flags)
```
### `bin/app.ts`
Instantiates `DoorUnlockStack` with an explicit `stackName` of `seahaven-door-unlock-api`, pinned to account `328440206208` / `us-east-1`.
### `lib/door-unlock-stack.ts`
Defines every resource the stack owns:
- The five Lambda functions (Node 24.x, arm64, 60-day log retention), bundled from TypeScript with esbuild
- The HTTP API (`door-unlock-api`), its `GET /unlock`, `GET /lockdown`, and `GET /lockdown/status` routes, throttling, and JSON access logging
- The `HttpLambdaAuthorizer` token authorizer (identity source `$request.querystring.token`, 5-minute result cache)
- The EventBridge rule that invokes the poller once a minute, plus the poller's VPC config and security group (imported VPC/subnets, egress to the Elements API and phone LAN)
- The EventBridge rule that invokes department BLF sync daily at 09:00 UTC, plus imports of the afterhours 3CX XAPI secrets
- The custom domain, ACM certificate import, and Route 53 A record for `doorunlock.seahaven.com`
- Imports of the SSM parameters, the phone-password secret, the afterhours 3CX XAPI secrets, and the `site-alerts` SNS topic, with the corresponding `grantRead` IAM permissions
- The five per-Lambda CloudWatch error alarms
### `cdk.json`
CDK configuration committed to the repo. The `app` command runs `npx tsx bin/app.ts`, so the TypeScript entry point executes directly via `tsx` (no separate compile step). It also carries the `watch` include/exclude globs and the CDK feature-flag `context`.
### Commands
```bash
npx cdk synth # synthesize the CloudFormation template
npx cdk diff # diff against the deployed stack
npx cdk deploy # deploy (see Manual Deployment below)
```
The same commands are also exposed as npm scripts (`npm run synth`, `npm run diff`, `npm run deploy`).
## Documentation
The canonical map of Sea Haven's AWS infrastructure lives in Confluence. This project's `seahaven-door-unlock-api` stack is represented there as a Mermaid subgraph.
- **[AWS Architecture Map](https://seahaven.atlassian.net/wiki/spaces/IT/pages/1540098)** (Confluence, IT space, page 1540098)
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
## Lockdown Profiles
Two lockdown profiles are configured:
| Profile | Elements ID | Line Key |
|---------|-------------|----------|
| Bohemia - Whole Building | `4b4a3e6b-c903-4cce-8cd6-288612bf0542` | 3 |
| Ronkonkoma - Whole Building | `ff9876bc-c54f-472e-aef9-d2bffd4b7cf7` | 4 |
Pressing the line key toggles the lockdown on/off and displays the current status on the phone screen.
**Known limitation:** Line key LED color does not currently change to reflect lockdown status. The T58W's XML Browser key type (17) does not support persistent LED color changes via Push XML or Execute commands — LED commands are transient and immediately overridden by the phone's key type management.
## SSM Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| `/seahaven/door-unlock/elements-api-key` | SecureString | LenelS2 Elements API key |
| `/seahaven/door-unlock/auth-token` | SecureString | Shared secret embedded in the Yealink DSS key URL |
| `/seahaven/door-unlock/door-id` | String | Elements device ID for the front door reader |
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
| `/seahaven/door-unlock/phone-ips` | String | Comma-separated phone IPs for lockdown poller |
## Secrets Manager
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
| Secret | Description |
|--------|-------------|
| `door-unlock-api/phone-password` | Yealink phone admin password for Push XML |
## CI/CD
Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) * feat: add gateway token authorizer to door-unlock API (INFRA-99) All three routes (GET /unlock, /lockdown, /lockdown/status) were AuthorizationType NONE — auth relied solely on each handler checking the ?token= query param. Add a REQUEST-type HTTP API Lambda authorizer (door-unlock-api-authorizer) that validates the SAME ?token= value the Yealink XML Browser keys already send, against the existing /seahaven/door-unlock/auth-token SSM SecureString, and attach it to all three routes. Transparent to the phones: identity source is $request.querystring.token (exactly what the type-17 XML Browser keys send via GET), simple response {isAuthorized}, fail-closed, 5-min results cache. Token is cached in module scope so warm invocations skip SSM. GET is kept (not switched to POST): the Yealink type-17 XML Browser keys are GET-only and render the returned Yealink XML — they cannot issue a POST body or custom headers. POST is therefore deferred to avoid bricking the door keys. Handlers retain their own token check as defense-in-depth. Purely additive change set; no existing Lambda or integration is modified. * chore: complete CI/CD migration to GitHub Actions (INFRA-2) GitHub Actions (ci.yaml + deploy.yaml via the Sea Haven reusable workflows) is the proven deploy path. Remove the now-orphaned buildspec.yml and update the README CI/CD and architecture sections. The legacy CodePipeline was already deleted (2026-06-05); the leftover CodeBuild project seahaven-door-unlock-api-build and its IAM role seahaven-door-unlock-api-codebuild have now also been decommissioned.
2026-06-08 18:03:30 -04:00
GitHub Actions, using the Sea Haven reusable workflows:
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) * feat: add gateway token authorizer to door-unlock API (INFRA-99) All three routes (GET /unlock, /lockdown, /lockdown/status) were AuthorizationType NONE — auth relied solely on each handler checking the ?token= query param. Add a REQUEST-type HTTP API Lambda authorizer (door-unlock-api-authorizer) that validates the SAME ?token= value the Yealink XML Browser keys already send, against the existing /seahaven/door-unlock/auth-token SSM SecureString, and attach it to all three routes. Transparent to the phones: identity source is $request.querystring.token (exactly what the type-17 XML Browser keys send via GET), simple response {isAuthorized}, fail-closed, 5-min results cache. Token is cached in module scope so warm invocations skip SSM. GET is kept (not switched to POST): the Yealink type-17 XML Browser keys are GET-only and render the returned Yealink XML — they cannot issue a POST body or custom headers. POST is therefore deferred to avoid bricking the door keys. Handlers retain their own token check as defense-in-depth. Purely additive change set; no existing Lambda or integration is modified. * chore: complete CI/CD migration to GitHub Actions (INFRA-2) GitHub Actions (ci.yaml + deploy.yaml via the Sea Haven reusable workflows) is the proven deploy path. Remove the now-orphaned buildspec.yml and update the README CI/CD and architecture sections. The legacy CodePipeline was already deleted (2026-06-05); the leftover CodeBuild project seahaven-door-unlock-api-build and its IAM role seahaven-door-unlock-api-codebuild have now also been decommissioned.
2026-06-08 18:03:30 -04:00
- **`.github/workflows/ci.yaml`** — on pull requests to `main`, runs the `ci-typescript-cdk` reusable workflow (build, lint, synth).
- **`.github/workflows/deploy.yaml`** — on push to `main`, runs the `cd-cdk` reusable workflow which assumes the `githubdeploy-seahaven-door-unlock-api` OIDC role (`AWS_DEPLOY_ROLE_ARN` repo secret) and runs `cdk deploy`.
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
Gateway token authorizer + finish CI/CD migration (INFRA-99, INFRA-2) (#32) * feat: add gateway token authorizer to door-unlock API (INFRA-99) All three routes (GET /unlock, /lockdown, /lockdown/status) were AuthorizationType NONE — auth relied solely on each handler checking the ?token= query param. Add a REQUEST-type HTTP API Lambda authorizer (door-unlock-api-authorizer) that validates the SAME ?token= value the Yealink XML Browser keys already send, against the existing /seahaven/door-unlock/auth-token SSM SecureString, and attach it to all three routes. Transparent to the phones: identity source is $request.querystring.token (exactly what the type-17 XML Browser keys send via GET), simple response {isAuthorized}, fail-closed, 5-min results cache. Token is cached in module scope so warm invocations skip SSM. GET is kept (not switched to POST): the Yealink type-17 XML Browser keys are GET-only and render the returned Yealink XML — they cannot issue a POST body or custom headers. POST is therefore deferred to avoid bricking the door keys. Handlers retain their own token check as defense-in-depth. Purely additive change set; no existing Lambda or integration is modified. * chore: complete CI/CD migration to GitHub Actions (INFRA-2) GitHub Actions (ci.yaml + deploy.yaml via the Sea Haven reusable workflows) is the proven deploy path. Remove the now-orphaned buildspec.yml and update the README CI/CD and architecture sections. The legacy CodePipeline was already deleted (2026-06-05); the leftover CodeBuild project seahaven-door-unlock-api-build and its IAM role seahaven-door-unlock-api-codebuild have now also been decommissioned.
2026-06-08 18:03:30 -04:00
The legacy CodePipeline/CodeBuild deploy path has been fully decommissioned.
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
## Manual Deployment
```bash
npm install
npx cdk deploy
```
## Phone Configuration
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
Configure DSS keys on the Yealink T54W/T58W (via phone web UI or 3CX):
- **Key 2 — Unlock Door**
- Type: URL
- Value: `https://doorunlock.seahaven.com/unlock?token=<auth-token>`
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
- **Keys 3-4 — Lockdown Toggle**
- Type: XML Browser (17)
- Value: `https://doorunlock.seahaven.com/lockdown?token=<auth-token>&profile=bohemia|ronkonkoma`
## 3CX Provisioning Templates
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
Custom 3CX templates are included with door unlock and lockdown URLs hardcoded.
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
| Template | Model | Key 2 | Keys 3-4 | Display |
|----------|-------|-------|----------|---------|
| `yealinkT54W-door-unlock.ph.xml` | T54W | Unlock Door | Managed by 3CX BLF | Dim after 5 min, never sleep |
| `yealinkT54W-door-unlock-with-sp.ph.xml` | T54W | Unlock Door | SP1-3 via BLF sync | Dim after 5 min, never sleep |
Add lockdown mode and CI/CD pipeline (#3) * Add lockdown profile toggle endpoints with T58W linekey support Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names. * Add lockdown poller Lambda and fix lockdown handler responses - Add VPC-connected poller Lambda that monitors lockdown status via Elements API every 15 seconds (4 polls per 1-min EventBridge schedule) - Handle Elements API rate limits (429) with retry-after support - Fix lockdown handler to use TextScreen XML instead of Execute XML (Execute shows globe icon on T58W, TextScreen renders properly) - Fix Elements API status parsing to be case-insensitive - Trust toggle action instead of re-checking status (eventual consistency) - Configure push_xml.server = any in T58W template for Push XML support - Clear action_url.setup_completed (poller replaces boot-time check) - Update README with lockdown architecture and known LED limitation Note: T58W line key LED color does not change to reflect lockdown status. Execute LED commands are transient on the T58W - the phone's XML Browser key type immediately overrides them. * Add buildspec for CodePipeline CI/CD * Update README with CI/CD pipeline details
2026-05-01 18:52:37 -04:00
| `yealinkT58W-door-unlock.ph.xml` | T58W | Unlock Door | Lockdown Toggle (Bohemia/Ronkonkoma) | Default T58W display settings |
Department colleague BLFs are not encoded in these templates. A scheduled Lambda (`door-unlock-api-blf-sync`) writes each Yealink user's 3CX BLF list from that user's first non-DEFAULT 3CX department, excluding the phone's own extension. Extension 100 is always included, even when the XAPI Users list omits it. Unlock and lockdown URL keys stay hardcoded in the template. Shared parking on the T54W+SP template is written by the sync job as 3CX SharedParking BLFs.
| Template | Reserved (never write) | Sync-owned parking | Own line | Managed department BLFs | Personal |
| --- | --- | --- | --- | --- | --- |
| `yealinkT54W-door-unlock.ph.xml` | `blf2` | none | `blf1` | `blf3`–`blf12` | `blf13+` |
| `yealinkT54W-door-unlock-with-sp.ph.xml` | `blf2` | `blf3`–`blf5` (SP1–SP3) | `blf1` | `blf6`–`blf15` | `blf16+` |
| `yealinkT58W-door-unlock.ph.xml` | `blf2`–`blf4` | none | `blf1` | `blf5`–`blf14` | `blf15+` |
The job authenticates to 3CX XAPI with the existing `afterhours-shift-manager/3cx-*` Secrets Manager values. Invoke `door-unlock-api-blf-sync` with `DRY_RUN=true` for a proposed-XML log and no writes. Set `SMOKE_EXTENSION` to PATCH a single extension. The daily EventBridge rule runs at `09:00 UTC` (05:00 ET during EDT).