2026-10-13 10:33:31 +02:00
import { resolve } from 'node:path' ;
import { createApp } from './app.mjs' ;
import { openStore } from './store.mjs' ;
import { startTelemetry } from './telemetry.mjs' ;
import { startSIEM } from './siem.mjs' ;
import { startAuditRetention } from './audit-retention.mjs' ;
const env = process . env ;
const publicUrl = ( env . PUBLIC_URL || 'http://127.0.0.1:4310' ). replace ( /\/$/ , '' );
const u = new URL ( publicUrl );
if ( u . protocol !== 'https:' && ! [ '127.0.0.1' , 'localhost' , '[::1]' ]. includes ( u . hostname )) throw Error ( 'Remote management requires HTTPS' );
if ( u . protocol === 'http:' && ( ! env . ADMIN_TOKEN || env . ADMIN_TOKEN . length < 32 )) throw Error ( 'Local development requires ADMIN_TOKEN of at least 32 characters' );
if ( u . protocol === 'http:' && env . HOST &&! [ '127.0.0.1' , 'localhost' , '::1' ]. includes ( env . HOST )) throw Error ( 'Development HTTP management must bind only to loopback' );
if ( u . protocol === 'https:' && ( ! env . OIDC_ISSUER ||! env . OIDC_CLIENT_ID )) throw Error ( 'HTTPS management requires enterprise OIDC configuration; shared bootstrap administrator tokens are disabled' );
const config = { publicUrl , secure : u . protocol === 'https:' , adminToken : env . ADMIN_TOKEN , oidcIssuer : env . OIDC_ISSUER , oidcClientId : env . OIDC_CLIENT_ID , oidcClientSecret : env . OIDC_CLIENT_SECRET , oidcAdminGroup : env . OIDC_ADMIN_GROUP || 'safetynet-admins' , oidcGroupsClaim : env . OIDC_GROUPS_CLAIM || 'groups' , aiBaseUrl : env . AI_BASE_URL , aiKey : env . AI_API_KEY , aiModel : env . AI_MODEL , inspectorUrl : env . INSPECTOR_URL , inspectorToken : env . INSPECTOR_TOKEN };
2026-10-13 16:58:15 +02:00
config . companyNewsAllowedHosts = ( env . COMPANY_NEWS_ALLOWED_HOSTS || '' ). split ( ',' ). map ( v => v . trim (). toLowerCase ()). filter ( Boolean );
const store = openStore ( resolve ( env . DATA_DIR || 'data' ),{ auditSigningKeyFile : env . AUDIT_SIGNING_KEY_FILE });
2026-10-13 10:33:31 +02:00
config . evidenceKeyFile = env . EVIDENCE_KEY_FILE ;
config . evidenceRetentionDays = Number ( env . EVIDENCE_RETENTION_DAYS || 30 );
config . oidcDeviceGroup = env . OIDC_DEVICE_GROUP ;
config . oidcOperatorGroup = env . OIDC_OPERATOR_GROUP ;
config . oidcAuditorGroup = env . OIDC_AUDITOR_GROUP ;
2026-10-13 16:58:15 +02:00
config . oidcAnalystGroup = env . OIDC_ANALYST_GROUP ;
2026-10-13 10:33:31 +02:00
config . requireIndependentPolicyApproval = env . REQUIRE_INDEPENDENT_POLICY_APPROVAL === 'true' ;
config . requirePQManagement = env . REQUIRE_PQ_MANAGEMENT_CHANNEL !== 'false' ;
if ( u . protocol === 'https:' &&! config . requirePQManagement ) throw Error ( 'Remote deployment requires the protected management channel' );
config . aiEndpointId = env . AI_ENDPOINT_ID || 'default' ;
const telemetry = startTelemetry ();
const siem = startSIEM ( store , resolve ( env . DATA_DIR || 'data' ));
config . siemStatus = ()=> siem . status ();
2026-10-13 16:58:15 +02:00
config . siemControl = siem ;
2026-10-13 10:33:31 +02:00
const retention = startAuditRetention ( store , siem ,{ days : Number ( env . AUDIT_RETENTION_DAYS || 0 )});
config . auditRetentionStatus = ()=> retention . status ();
const server = createApp ( store , config ). listen ( Number ( env . PORT || 4310 ), env . HOST || '127.0.0.1' ,()=> console . log ( `SafetyNet management: ${ publicUrl } ` ));
const timer = setInterval (()=>{
for ( const table of [ 'sessions' , 'logins' , 'tickets' , 'enrollments' ]) store . db . prepare ( `DELETE FROM ${ table } WHERE expires<?` ). run ( Date . now ());
}, 60000 ); timer . unref ();
async function stop (){ clearInterval ( timer ); retention . stop (); siem . stop (); server . close ( async ()=>{ store . db . close (); await telemetry ? . shutdown (); process . exit ( 0 );});}
process . on ( 'SIGINT' , stop ); process . on ( 'SIGTERM' , stop );