Conversation
- Add WebSocket over TLS with self-signed cert generation and user cert support - Implement HMAC-SHA256 device authentication (deviceId + nonce + timestamp) - Add nonce replay protection with configurable TTL window - Add device approval registry with JSON persistence - Implement JWT session token issuance on connect and refresh on heartbeat - Add append-only JSONL audit log for security events - Add CLI with --insecure flag for explicit plaintext mode - Update shared protocol schemas with timestamp, signature, and heartbeat types - Add unit tests for auth, state, and TLS primitives - Add integration tests for connect auth and heartbeat refresh Closes #3
There was a problem hiding this comment.
Pull request overview
Implements a secured gateway WebSocket handshake/session flow and updates the shared protocol to support timestamped/HMAC-authenticated connect messages plus heartbeat-based session refresh.
Changes:
- Extends shared handshake protocol with
timestamp,signature, and heartbeat request/ack types + JSON schema generation and tests. - Adds gateway modules for TLS (self-signed or user-provided certs), HMAC verification, nonce replay protection, device registry persistence, audit logging, and JWT session tokens.
- Introduces gateway CLI/config parsing plus unit/integration tests for connect auth and heartbeat refresh.
Reviewed changes
Copilot reviewed 39 out of 40 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks new gateway deps (Fastify/WebSocket, JWT, self-signed TLS). |
| packages/shared/src/protocol/json-schema.ts | Adds JSON schema generation for heartbeat request/ack. |
| packages/shared/src/protocol/index.ts | Re-exports new handshake types/schemas. |
| packages/shared/src/protocol/handshake.ts | Adds connect timestamp/signature fields and heartbeat schemas. |
| packages/shared/src/tests/validate.test.ts | Updates validation test data for new connect fields. |
| packages/shared/src/tests/json-schema.test.ts | Asserts heartbeat schemas are generated. |
| packages/shared/src/tests/handshake.test.ts | Adds schema tests for connect+heartbeat types. |
| packages/gateway/package.json | Adds CLI bin + gateway runtime deps and start script. |
| packages/gateway/README.md | Documents CLI flags, TLS behavior, connect auth flow, session tokens. |
| packages/gateway/test/unit/session-token.test.ts | Unit tests for JWT issue/verify/refresh behavior. |
| packages/gateway/test/unit/nonce-store.test.ts | Unit tests for nonce replay window/eviction. |
| packages/gateway/test/unit/hmac-auth.test.ts | Unit tests for HMAC computation/verification + timestamp skew. |
| packages/gateway/test/unit/device-registry.test.ts | Unit tests for JSON persistence + approval/shared secret access. |
| packages/gateway/test/unit/certificate-manager.test.ts | Unit tests for cert generation/loading and error cases. |
| packages/gateway/test/smoke.test.ts | Ensures key gateway APIs are exported from package entry. |
| packages/gateway/test/integration/connect-auth.integration.test.ts | Integration coverage for connect auth (HMAC, skew, nonce replay, approval). |
| packages/gateway/test/integration/heartbeat-refresh.integration.test.ts | Integration coverage for heartbeat ack + token refresh/invalid token close. |
| packages/gateway/test/helpers/test-gateway.ts | Test harness to spin up an in-memory gateway and WS helpers. |
| packages/gateway/src/tls/tls-options.ts | Builds Fastify TLS options via cert manager; supports insecure mode. |
| packages/gateway/src/tls/index.ts | TLS module barrel exports. |
| packages/gateway/src/tls/certificate-manager.ts | Loads user certs or generates/persists a managed self-signed cert. |
| packages/gateway/src/state/types.ts | Adds state record types (device, nonce, audit event). |
| packages/gateway/src/state/nonce-store.ts | In-memory nonce replay protection store with TTL eviction. |
| packages/gateway/src/state/index.ts | State module barrel exports. |
| packages/gateway/src/state/device-registry.ts | JSON device registry with approval and shared-secret lookup. |
| packages/gateway/src/server/register-websocket-routes.ts | Implements WS connect handshake validation and post-handshake routing. |
| packages/gateway/src/server/index.ts | Server module barrel exports. |
| packages/gateway/src/server/heartbeat-handler.ts | Verifies/refreshes session tokens and returns heartbeat ack. |
| packages/gateway/src/server/create-gateway-server.ts | Creates/starts Fastify server with WS routes and generated JWT secret fallback. |
| packages/gateway/src/server/connection-context.ts | Tracks active connections and session token state. |
| packages/gateway/src/index.ts | Expands package exports to include new gateway modules. |
| packages/gateway/src/config/parse-cli.ts | Parses CLI flags into a GatewayConfig. |
| packages/gateway/src/config/gateway-config.ts | Defines gateway config shape and defaults. |
| packages/gateway/src/cli.ts | CLI entrypoint for starting the gateway. |
| packages/gateway/src/auth/session-token.ts | JWT issue/verify/refresh helpers with AuthError mapping. |
| packages/gateway/src/auth/index.ts | Auth module barrel exports. |
| packages/gateway/src/auth/hmac-auth.ts | HMAC SHA-256 compute/verify + timestamp skew check. |
| packages/gateway/src/auth/errors.ts | Defines gateway auth error codes and error type. |
| packages/gateway/src/audit/index.ts | Audit module barrel exports. |
| packages/gateway/src/audit/audit-log.ts | Append-only JSONL audit logger and event helpers. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| server.get("/ws", { websocket: true }, (socket) => { | ||
| void processHandshake(socket, deps); |
There was a problem hiding this comment.
The WebSocket route handler passed to @fastify/websocket is treated as a ws socket, but the plugin’s route handler typically receives a connection wrapper (e.g., connection.socket). If socket here is the wrapper, processHandshake will call send/close/on on the wrong object and fail at runtime. Adjust the handler to pass the actual WebSocket instance (connection.socket) into processHandshake.
| server.get("/ws", { websocket: true }, (socket) => { | |
| void processHandshake(socket, deps); | |
| server.get("/ws", { websocket: true }, (connection) => { | |
| void processHandshake(connection.socket, deps); |
| jwtSecret: string, | ||
| ): SessionTokenPayload { | ||
| try { | ||
| const decoded = verify(token, jwtSecret); |
There was a problem hiding this comment.
verifySessionToken() calls jsonwebtoken.verify(token, jwtSecret) without restricting accepted algorithms. To reduce algorithm-confusion risk and keep verification aligned with issueSessionToken defaults, pass an explicit algorithms allow-list (e.g., only HS256).
| const decoded = verify(token, jwtSecret); | |
| const decoded = verify(token, jwtSecret, { algorithms: ["HS256"] }); |
|
@copilot open a new pull request to apply changes based on the comments in this thread |
|
@aaronolds I've opened a new pull request, #8, to work on those changes. Once the pull request is ready, I'll request review from you. |
- Make DeviceRecord.name optional (no name required in test registrations) - Handle all RawData types in waitForSocketMessage (string/Buffer/Buffer[]/ArrayBuffer) - Replace NonceStore composite key with nested Map to prevent key collisions - Add Subject Alternative Names (SAN) to self-signed cert generation - Make ConnectSchema.authToken optional (gateway never validates it) - Restrict verifySessionToken to HS256 algorithm only - Update handshake tests to reflect authToken is now optional Co-authored-by: aaronolds <1485194+aaronolds@users.noreply.github.com>
fix(gateway): address security and correctness issues from PR review
…, and audit enhancements - JSONL TranscriptWriter with append-only per-session logs, fsync, and crash recovery - SQLite OperationalStore replacing JSON DeviceRegistry with WAL mode and migrations - EncryptedFileSecretStore (AES-256-GCM + scrypt) and KeychainSecretStore backends - Typed audit events with automatic field redaction for sensitive data - JSON-to-SQLite one-time device migration with idempotent .migrated marker - Wire OperationalStore and SecretStore into gateway startup, auth, and RPC handlers - Add secretsBackend, masterPassphrase, and fsyncWrites config fields - Persistence barrel exports from gateway package Closes #6
Closes #3