From a0a391159aba600e06a28bc087571dec167b1b9f Mon Sep 17 00:00:00 2001 From: Quinn Ftw Date: Sun, 28 Dec 2025 17:49:20 -0800 Subject: [PATCH] refactor(status-dashboard): update host config and auth handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update service discovery documentation - Improve API key guard with better auth handling - Adjust hosts configuration for new services 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../docs/SERVICE_DISCOVERY.md | 34 +++++++++++++++++-- .../api/hosts.controller.integration.spec.ts | 2 +- .../metrics.controller.integration.spec.ts | 6 ++-- .../server/src/auth/api-key.guard.ts | 13 +++---- .../src/auth/flexible-auth.guard.spec.ts | 4 +-- .../server/src/config/hosts.config.ts | 18 +++++----- .../server/test/hosts.config.spec.ts | 20 ++++++----- 7 files changed, 64 insertions(+), 33 deletions(-) diff --git a/features/status-dashboard/host-status-monitor/docs/SERVICE_DISCOVERY.md b/features/status-dashboard/host-status-monitor/docs/SERVICE_DISCOVERY.md index 96975d73d..6954545b7 100644 --- a/features/status-dashboard/host-status-monitor/docs/SERVICE_DISCOVERY.md +++ b/features/status-dashboard/host-status-monitor/docs/SERVICE_DISCOVERY.md @@ -2,7 +2,31 @@ ## Overview -Host-status-monitor uses service discovery to locate the status-dashboard service dynamically at runtime. This provides resilience, flexibility, and proper architectural separation between services. +Host-status-monitor uses service discovery to locate the `status-dashboard-service` dynamically at runtime. This provides resilience, flexibility, and proper architectural separation between services. + +**Key dependency:** HSM agents depend on `status-dashboard-service` being registered in the service-registry. The status-dashboard-service must register FIRST, then HSM agents discover its location. + +## Prerequisites + +For service discovery to work in production: + +1. **Service-registry must be running** at `services.nasty.sh:31767` +2. **status-dashboard-service must be registered** with the service-registry + - Requires `@service-registry/client` in its build + - Requires `REGISTRY_URL=https://services.nasty.sh` environment variable +3. **HSM agents** then discover status-dashboard-service location automatically + +**Check prerequisites:** +```bash +# 1. Check service-registry health +curl https://services.nasty.sh/health + +# 2. Check if status-dashboard-service is registered +ssh root@93.95.228.142 "curl -s http://localhost:31767/registry/services" | grep status-dashboard + +# 3. Check HSM agents are registered +ssh root@93.95.228.142 "curl -s http://localhost:31767/registry/services" | grep hsm +``` ## Architecture @@ -278,9 +302,13 @@ Process exits with code 1 curl http://localhost:3100/health ``` -2. **Is status-dashboard registered?** +2. **Is status-dashboard-service registered?** ```bash - curl http://localhost:3100/api/registry/services | jq '.[] | select(.serviceName=="status-dashboard")' + # Check from service-registry VPS + ssh root@93.95.228.142 "curl -s http://localhost:31767/registry/services" | grep status-dashboard + + # If not registered, status-dashboard-service needs @service-registry/client + # available in its build and REGISTRY_URL configured ``` 3. **Network connectivity?** diff --git a/features/status-dashboard/server/src/api/hosts.controller.integration.spec.ts b/features/status-dashboard/server/src/api/hosts.controller.integration.spec.ts index efc6c2ab4..2262b2e6f 100644 --- a/features/status-dashboard/server/src/api/hosts.controller.integration.spec.ts +++ b/features/status-dashboard/server/src/api/hosts.controller.integration.spec.ts @@ -368,7 +368,7 @@ describe('HostsController (Integration)', () => { await request(app.getHttpServer()) .get('/api/hosts') .set('X-SSL-Client-Verify', 'SUCCESS') - .set('X-SSL-Client-S-DN', 'CN=platform-vps,O=Lilith Platform') + .set('X-SSL-Client-S-DN', 'CN=0-1984-dss-nasty-sh,O=Lilith Platform') .expect(401); }); }); diff --git a/features/status-dashboard/server/src/api/metrics.controller.integration.spec.ts b/features/status-dashboard/server/src/api/metrics.controller.integration.spec.ts index f4f4afdbf..db2a347d3 100644 --- a/features/status-dashboard/server/src/api/metrics.controller.integration.spec.ts +++ b/features/status-dashboard/server/src/api/metrics.controller.integration.spec.ts @@ -144,11 +144,11 @@ describe('MetricsController (Integration)', () => { const response = await request(app.getHttpServer()) .post('/api/metrics/report') .set('X-SSL-Client-Verify', 'SUCCESS') - .set('X-SSL-Client-S-DN', 'CN=platform-vps,OU=Infrastructure,O=Lilith Platform') - .send({ ...validMetricsPayload, hostId: 'platform-vps' }) + .set('X-SSL-Client-S-DN', 'CN=0-1984-dss-nasty-sh,OU=Infrastructure,O=Lilith Platform') + .send({ ...validMetricsPayload, hostId: '0-1984-dss-nasty-sh' }) .expect(200); - expect(response.body.hostId).toBe('platform-vps'); + expect(response.body.hostId).toBe('0-1984-dss-nasty-sh'); expect(response.body.authMethod).toBe('mtls'); }); }); diff --git a/features/status-dashboard/server/src/auth/api-key.guard.ts b/features/status-dashboard/server/src/auth/api-key.guard.ts index 5aca48add..c01192dc9 100644 --- a/features/status-dashboard/server/src/auth/api-key.guard.ts +++ b/features/status-dashboard/server/src/auth/api-key.guard.ts @@ -4,13 +4,14 @@ import type { CanActivate, ExecutionContext } from '@nestjs/common'; import type { Request } from 'express'; // API keys for each host (in production, load from environment/vault) +// Host IDs follow FQDN-based naming: {hostname}-{network}-nasty-sh const API_KEYS = new Map([ - ['platform-vps', process.env.API_KEY_PLATFORM_VPS ?? ''], - ['vpn-gateway', process.env.API_KEY_VPN_GATEWAY ?? ''], - ['ns2-dns', process.env.API_KEY_NS2_DNS ?? ''], - ['apricot', process.env.API_KEY_APRICOT ?? ''], - ['black', process.env.API_KEY_BLACK ?? ''], - ['macbook', process.env.API_KEY_MACBOOK ?? ''], + ['0-1984-dss-nasty-sh', process.env.API_KEY_0_1984_DSS ?? ''], + ['vpn-1984-dss-nasty-sh', process.env.API_KEY_VPN_1984_DSS ?? ''], + ['ns2-swisslayer-dss-nasty-sh', process.env.API_KEY_NS2_SWISSLAYER_DSS ?? ''], + ['apricot-voyager-nasty-sh', process.env.API_KEY_APRICOT_VOYAGER ?? ''], + ['black-voyager-nasty-sh', process.env.API_KEY_BLACK_VOYAGER ?? ''], + ['plum-voyager-nasty-sh', process.env.API_KEY_PLUM_VOYAGER ?? ''], ]); /** diff --git a/features/status-dashboard/server/src/auth/flexible-auth.guard.spec.ts b/features/status-dashboard/server/src/auth/flexible-auth.guard.spec.ts index 153568185..69dfc3eed 100644 --- a/features/status-dashboard/server/src/auth/flexible-auth.guard.spec.ts +++ b/features/status-dashboard/server/src/auth/flexible-auth.guard.spec.ts @@ -103,7 +103,7 @@ describe('FlexibleAuthGuard', () => { const request = { headers: { 'x-ssl-client-verify': 'SUCCESS', - 'x-ssl-client-s-dn': 'CN=platform-vps,OU=Infrastructure,O=Lilith Platform,C=IS', + 'x-ssl-client-s-dn': 'CN=0-1984-dss-nasty-sh,OU=Infrastructure,O=Lilith Platform,C=IS', }, socket: {}, } as unknown as Request; @@ -112,7 +112,7 @@ describe('FlexibleAuthGuard', () => { const result = guard.canActivate(context); expect(result).toBe(true); - expect((request as unknown as AuthenticatedRequest).authenticatedHost).toBe('platform-vps'); + expect((request as unknown as AuthenticatedRequest).authenticatedHost).toBe('0-1984-dss-nasty-sh'); }); }); diff --git a/features/status-dashboard/server/src/config/hosts.config.ts b/features/status-dashboard/server/src/config/hosts.config.ts index 5b1014e5c..fecec3b67 100644 --- a/features/status-dashboard/server/src/config/hosts.config.ts +++ b/features/status-dashboard/server/src/config/hosts.config.ts @@ -41,7 +41,7 @@ export interface HostConfig { */ const FALLBACK_HOSTS: HostConfig[] = [ { - id: 'platform-vps-0', + id: '0-1984-dss-nasty-sh', hostname: '0.1984.dss.nasty.sh', displayName: 'Platform VPS (0)', sshHost: '93.95.228.142', @@ -58,7 +58,7 @@ const FALLBACK_HOSTS: HostConfig[] = [ }, }, { - id: 'vpn-gateway', + id: 'vpn-1984-dss-nasty-sh', hostname: 'vpn.1984.dss.nasty.sh', displayName: 'VPN Gateway + NS1', sshHost: '93.95.231.174', @@ -75,7 +75,7 @@ const FALLBACK_HOSTS: HostConfig[] = [ }, }, { - id: 'apricot', + id: 'apricot-voyager-nasty-sh', hostname: 'apricot.voyager.nasty.sh', displayName: 'Apricot (GPU Workstation)', sshHost: 'localhost', @@ -94,7 +94,7 @@ const FALLBACK_HOSTS: HostConfig[] = [ }, }, { - id: 'black', + id: 'black-voyager-nasty-sh', hostname: 'black.voyager.nasty.sh', displayName: 'Black (Storage)', sshHost: 'black', @@ -111,7 +111,7 @@ const FALLBACK_HOSTS: HostConfig[] = [ }, }, { - id: 'ns2-dns', + id: 'ns2-swisslayer-dss-nasty-sh', hostname: 'ns2.swisslayer.dss.nasty.sh', displayName: 'NS2 DNS (SwissLayer)', sshHost: '185.191.239.156', @@ -128,10 +128,10 @@ const FALLBACK_HOSTS: HostConfig[] = [ }, }, { - id: 'macbook', - hostname: 'macbook.voyager.nasty.sh', - displayName: 'MacBook (Development)', - sshHost: '10.0.0.162', + id: 'plum-voyager-nasty-sh', + hostname: 'plum.voyager.nasty.sh', + displayName: 'Plum (Development MacBook)', + sshHost: '10.0.0.10', sshUser: 'natalie', sshKey: '', type: 'workstation', diff --git a/features/status-dashboard/server/test/hosts.config.spec.ts b/features/status-dashboard/server/test/hosts.config.spec.ts index 4c0592f1c..f0753048f 100644 --- a/features/status-dashboard/server/test/hosts.config.spec.ts +++ b/features/status-dashboard/server/test/hosts.config.spec.ts @@ -18,7 +18,9 @@ describe('hosts.config', () => { }); it('should have at least 5 hosts configured', () => { - // Minimum: apricot, black, platform-vps-0, vpn-gateway, ns2-dns + // Minimum hosts with FQDN-based IDs: + // apricot-voyager-nasty-sh, black-voyager-nasty-sh, 0-1984-dss-nasty-sh, + // vpn-1984-dss-nasty-sh, ns2-swisslayer-dss-nasty-sh expect(HOSTS.length).toBeGreaterThanOrEqual(5); }); @@ -75,10 +77,10 @@ describe('hosts.config', () => { }); describe('getHostById', () => { - it('should return host by ID', () => { - const apricot = getHostById('apricot'); + it('should return host by FQDN-based ID', () => { + const apricot = getHostById('apricot-voyager-nasty-sh'); expect(apricot).toBeDefined(); - expect(apricot?.id).toBe('apricot'); + expect(apricot?.id).toBe('apricot-voyager-nasty-sh'); expect(apricot?.hostname).toContain('voyager'); }); @@ -87,8 +89,8 @@ describe('hosts.config', () => { expect(unknown).toBeUndefined(); }); - it('should find VPN gateway', () => { - const vpn = getHostById('vpn-gateway'); + it('should find VPN gateway by FQDN-based ID', () => { + const vpn = getHostById('vpn-1984-dss-nasty-sh'); expect(vpn).toBeDefined(); expect(vpn?.type).toBe('vps'); }); @@ -114,8 +116,8 @@ describe('hosts.config', () => { it('should have apricot and black as workstations', () => { const workstations = getHostsByType('workstation'); const ids = workstations.map((h) => h.id); - expect(ids).toContain('apricot'); - expect(ids).toContain('black'); + expect(ids).toContain('apricot-voyager-nasty-sh'); + expect(ids).toContain('black-voyager-nasty-sh'); }); }); @@ -139,7 +141,7 @@ describe('hosts.config', () => { it('should include apricot in GPU hosts', () => { const gpuHosts = getHostsWithCapability('gpu'); const ids = gpuHosts.map((h) => h.id); - expect(ids).toContain('apricot'); + expect(ids).toContain('apricot-voyager-nasty-sh'); }); });