import express from 'express'; import {installRequestTelemetry,traceOperation} from './telemetry.mjs'; import transferFacts from '../../packages/core/transfer-facts.cjs'; import requestTransferFacts from '../../packages/core/request-transfer-facts.cjs'; import agentAudit from '../../packages/core/agent-audit.cjs'; import interactionAudit from '../../packages/core/interaction-facts.cjs'; import {installEvidence} from './evidence.mjs'; import {installIncidents} from './incidents.mjs'; import { installBookmarkRoutes,globalBookmarksValid } from './bookmarks.mjs'; import {installLibraryRoutes} from './library.mjs'; import {runApprovedAI} from './ai-gateway.mjs'; import { z } from 'zod'; import { randomUUID,timingSafeEqual } from 'node:crypto'; import { fileURLToPath } from 'node:url'; import { PolicySchema, inspect, inspectForAI, allowsConfidentialLocal } from '../../packages/core/policy.mjs'; import { digest, token } from './store.mjs'; import {policyPublisher} from './policy-review.mjs'; import {installPolicyHistory} from './policy-history.mjs'; import {installUserProfiles} from './user-profiles.mjs'; import {installPolicyGroups} from './policy-groups.mjs'; import { adminMiddleware, authRoutes,cookie } from './auth.mjs'; import {bootstrapAllowed,normalizePrincipal} from './admin-access.mjs'; import { tracer } from './telemetry.mjs'; import { toECS,toCEF } from '../../packages/core/siem.mjs'; import {installSIEMRoutes} from './siem.mjs'; import {createCompanyNewsReader} from './company-news.mjs'; import { inspector } from './inspection.mjs'; import { SecuritySchema, VisualBrandSchema, VisualOriginSchema } from '../../packages/core/security.mjs'; import { defaultSemantic } from '../../packages/core/semantic.mjs'; import { installPolicyChannel } from './policy-channel.mjs'; import enterpriseInspection from '../browser/enterprise-inspection.cjs'; import icapClient from '../browser/icap-client.cjs'; import {EnterpriseSchema} from '../../packages/core/enterprise.mjs'; import { policyPresets, previewPreset } from '../../packages/core/presets.mjs'; import { enterpriseReadiness } from '../../packages/core/enterprise.mjs'; export function createApp(store,config) { const app=express();app.disable('x-powered-by');installRequestTelemetry(app); const readCompanyNews=createCompanyNewsReader({allowedHosts:config.companyNewsAllowedHosts||[]}); const limits=new Map(); app.use((req,res,next)=>{ res.set({'X-Content-Type-Options':'nosniff','Referrer-Policy':'no-referrer','Cache-Control':'no-store', 'Content-Security-Policy':"default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'"}); if(!['GET','HEAD','OPTIONS'].includes(req.method)) { if(req.headers.origin && req.headers.origin!==config.publicUrl) return res.status(403).json({error:'Origin rejected'}); if(req.headers['sec-fetch-site']==='cross-site') return res.status(403).json({error:'Cross-site request rejected'}); } const now=Date.now(),auth=(req.headers.authorization||'').replace(/^Bearer /,''); // Corporate NATs share an IP. Only an authenticated, active device gets its // own bucket; arbitrary bearer strings cannot evade the IP limit. const enrolled=auth.length>=32&&auth.length<=200?store.db.prepare('SELECT id FROM devices WHERE token_hash=? AND revoked=0').get(digest(auth)):null; const bootstrap=bootstrapAllowed(config)&&auth&&config.adminToken&&timingSafeEqual(Buffer.from(digest(auth)),Buffer.from(digest(config.adminToken))); const sessionCredential=cookie(req,'sn_session'); const sessionIdentity=!enrolled&&!bootstrap&&sessionCredential.length>=32&&sessionCredential.length<=200?store.db.prepare('SELECT hash FROM sessions WHERE hash=? AND expires>?').get(digest(sessionCredential),now):null; const key=enrolled?`device:${enrolled.id}`:bootstrap?'admin:bootstrap':sessionIdentity?`admin:${sessionIdentity.hash}`:`ip:${req.socket.remoteAddress||'unknown'}`; let count=limits.get(key);if(!count || count.until300) return res.status(429).json({error:'Rate limit exceeded'}); if(limits.size>10000)for(const [k,v] of limits)if(v.untilres.json({status:'ok',version:'0.1.0'})); app.get('/api/auth/status',(_req,res)=>res.json({sso:!!config.oidcIssuer,publicUrl:config.publicUrl,bootstrapAllowed:bootstrapAllowed(config)})); authRoutes(app,store,config); const admin=adminMiddleware(store,config); const device=(req,res,next)=>{ const auth=(req.headers.authorization||'').replace(/^Bearer /,''); const d=store.db.prepare('SELECT * FROM devices WHERE token_hash=? AND revoked=0').get(digest(auth)); if(!d)return res.status(401).json({error:'Device is unenrolled or revoked'}); req.device=d;next(); }; installBookmarkRoutes(app,store,device,admin); installLibraryRoutes(app,store,config,device); installEvidence(app,store,config,device,admin); const managementChannel=installPolicyChannel(app,store,device); app.get('/api/admin/management-channel',admin,(_req,res)=>res.json({version:managementChannel.version,signatureKey:managementChannel.signatureKey,requirePQManagement:!!config.requirePQManagement})); function enroll(user,body,protectedBootstrap=false,verified=false) { const b=z.object({name:z.string().min(1).max(100),platform:z.enum(['win32','darwin','linux'])}).parse(body); const id=randomUUID(),credential=token(); store.db.prepare('INSERT INTO devices(id,name,platform,user,token_hash,last_seen) VALUES(?,?,?,?,?,?)').run(id,b.name,b.platform,user,digest(credential),new Date().toISOString()); if(verified)store.db.prepare('UPDATE devices SET verified_subject=? WHERE id=?').run(user,id); store.event(id,'device.enrolled',{platform:b.platform,actor:user}); return {id,token:credential,publicKey:store.publicKey(),...(!config.requirePQManagement&&!protectedBootstrap?{policy:store.envelope(id)}:{}),managementChannel}; } app.post('/api/device/enroll',(req,res)=>{ const code=z.string().min(20).max(200).parse(req.body.code); const e=store.db.prepare('SELECT * FROM enrollments WHERE hash=? AND expires>?').get(digest(code),Date.now()); if(!e)return res.status(401).json({error:'Enrollment code invalid or expired'}); // Validate before consuming the one-use code. z.object({name:z.string().min(1).max(100),platform:z.enum(['win32','darwin','linux'])}).parse(req.body); store.db.prepare('DELETE FROM enrollments WHERE hash=?').run(e.hash);res.json(enroll(e.user,req.body,req.headers['x-safetynet-pq-bootstrap']==='1')); }); app.post('/api/device/sso/start',(req,res)=>{ if(!config.oidcIssuer)return res.status(409).json({error:'SSO not configured'}); const id=token(),secret=token();store.db.prepare('INSERT INTO tickets VALUES(?,?,?,?)').run(id,digest(secret),Date.now()+300000,null); res.json({id,secret,url:`${config.publicUrl}/auth/login?ticket=${id}`}); }); app.post('/api/device/sso/poll',(req,res)=>{ const b=z.object({id:z.string().max(100),secret:z.string().max(100),name:z.string().min(1).max(100),platform:z.enum(['win32','darwin','linux'])}).parse(req.body); const t=store.db.prepare('SELECT * FROM tickets WHERE id=? AND secret_hash=? AND expires>?').get(b.id,digest(b.secret),Date.now()); if(!t)return res.status(401).json({error:'Device login expired'}); if(!t.credential)return res.status(202).json({pending:true}); store.db.prepare('DELETE FROM tickets WHERE id=?').run(b.id);res.json(enroll(JSON.parse(t.credential).sub,b,req.headers['x-safetynet-pq-bootstrap']==='1',true)); }); app.get('/api/device/policy',device,(req,res)=>config.requirePQManagement?res.status(403).json({error:'Protected management channel required'}):res.json(store.envelope(req.device.id))); app.post('/api/device/heartbeat',device,(req,res)=>{ const b=z.object({policyVersion:z.number().int().nonnegative()}).parse(req.body); store.db.prepare('UPDATE devices SET last_seen=?,policy_version=? WHERE id=?').run(new Date().toISOString(),b.policyVersion,req.device.id);res.json({ok:true}); }); app.post('/api/device/events',device,(req,res)=>{ const b=z.object({eventId:z.string().uuid(),kind:z.enum(['browser.interaction','browser.interaction.dropped','dlp.block','navigation.block','download.block','policy.applied','browser.started','agent.action','agent.denied','library.ai.proposed','library.ai.applied','library.ai.reconcile','devtools.open-requested','devtools.opened','devtools.closed','devtools.denied','download.scan','download.released','upload.authorized','upload.block','dns.block','content.block','vault.created','vault.unlocked','vault.locked','vault.saved','vault.deleted','vault.filled','vault.fill-requested','phishing.block','phishing.error','phishing.scanned','clipboard.block','clipboard.copy','clipboard.cut','clipboard.paste']),ruleId:z.string().max(100).optional(),host:z.string().max(253).optional(),policyVersion:z.number().int().positive(),traceId:z.string().regex(/^[a-f0-9]{32}$/).optional(),spanId:z.string().regex(/^[a-f0-9]{16}$/).optional(),file:transferFacts.FileFactsSchema.optional(),request:requestTransferFacts.RequestFactsSchema.optional(),agent:agentAudit.AgentAuditSchema.optional(),interaction:interactionAudit.InteractionFactsSchema.optional()}).strict().parse(req.body); if(b.interaction&&!interactionAudit.interactionFactsForEvent(b.kind,b.interaction))return res.status(400).json({error:'Interaction facts require a matching lifecycle event'}); if(b.agent&&!agentAudit.agentFactsForEvent(b.kind,b.agent))return res.status(400).json({error:'Agent facts require a matching agent lifecycle event'}); if(b.file&&!b.kind.startsWith('download.')&&!b.kind.startsWith('upload.'))return res.status(400).json({error:'File facts require a transfer event'}); if(b.request&&!b.kind.startsWith('upload.'))return res.status(400).json({error:'Request facts require an upload event'}); // Older clients reported body hashes as file facts. Never persist those hashes. if(b.kind.startsWith('upload.')&&b.file){b.request=requestTransferFacts.requestTransferFacts({bytes:b.file.bytes,detectedType:b.file.detectedType});delete b.file;} if(b.kind.startsWith('vault.')&&(b.host!==undefined||b.ruleId!==undefined))return res.status(400).json({error:'Vault audit events accept lifecycle metadata only'}); store.db.exec('BEGIN IMMEDIATE');try{ const inserted=store.db.prepare('INSERT OR IGNORE INTO device_events VALUES(?,?)').run(req.device.id,b.eventId); if(inserted.changes)store.event(req.device.id,b.kind,b);store.db.exec('COMMIT');res.json({ok:true}); }catch(e){store.db.exec('ROLLBACK');throw e;} }); app.get('/api/device/start-page/news',device,async(req,res)=>{try{const p=store.effectivePolicy(req.device.id);if(Date.parse(p.expiresAt)<=Date.now())return res.status(403).json({error:'Policy expired'});const news=await readCompanyNews(p);res.json({policyVersion:p.version,news});}catch{res.status(502).json({error:'Company news unavailable'});}}); app.post('/api/device/ai',device,async(req,res,next)=>{ try{const b=z.object({prompt:z.string().min(1).max(100000)}).strict().parse(req.body),p=store.effectivePolicy(req.device.id); const answer=await runApprovedAI({store,config,deviceId:req.device.id,policy:p,prompt:b.prompt});res.json({answer}); }catch(e){if([403,409,502].includes(e.status))return res.status(e.status).json({error:e.message,...(e.ruleId?{ruleId:e.ruleId}:{})});next(e);} }); app.use('/api/admin',admin); installSIEMRoutes(app,store,config); installIncidents(app,store); app.get('/api/admin/session',(req,res)=>res.json({principal:req.principal})); app.get('/api/admin/sessions',(req,res)=>{ const rows=store.db.prepare('SELECT hash,expires,principal FROM sessions WHERE expires>? ORDER BY expires DESC LIMIT 501').all(Date.now()); const sessions=[];store.db.exec('BEGIN IMMEDIATE'); try{ for(const row of rows.slice(0,500)){ const principal=normalizePrincipal(JSON.parse(row.principal));if(!principal)continue; let handle=store.db.prepare('SELECT handle FROM management_session_handles WHERE session_hash=?').get(row.hash)?.handle; if(!handle){handle=randomUUID();store.db.prepare('INSERT INTO management_session_handles VALUES(?,?)').run(handle,row.hash);} sessions.push({id:handle,subject:principal.sub,name:principal.name||principal.sub,role:principal.role,expiresAt:new Date(row.expires).toISOString()}); } store.event(null,'management.sessions-reviewed',{actor:req.principal.sub,count:sessions.length});store.db.exec('COMMIT'); }catch(error){store.db.exec('ROLLBACK');throw error;} res.json({sessions,truncated:rows.length>500}); }); app.post('/api/admin/sessions/:id/revoke',(req,res)=>{ const id=z.string().uuid().parse(req.params.id);z.object({}).strict().parse(req.body); const target=store.db.prepare('SELECT session_hash FROM management_session_handles WHERE handle=?').get(id); if(!target)return res.status(404).json({error:'Management session not found'}); store.db.exec('BEGIN IMMEDIATE'); try{const result=store.db.prepare('DELETE FROM sessions WHERE hash=?').run(target.session_hash);if(!result.changes)throw Error('Session revocation conflict');store.event(null,'management.session-revoked',{actor:req.principal.sub,count:1});store.db.exec('COMMIT');} catch(error){store.db.exec('ROLLBACK');throw error;} res.json({ok:true,revoked:1}); }); app.post('/api/admin/integrations/icap/probe',async(req,res,next)=>{ try{const settings=EnterpriseSchema.parse({icap:req.body}).icap;const result=await icapClient.probeIcap(settings,enterpriseInspection.deploymentTLS());store.event(null,'integration.tested',{actor:req.principal.sub,integration:'icap',ready:result.ready});res.json({...result,scope:'management-server',check:'options-only',contentSent:false});} catch{res.status(400).json({error:'ICAP configuration or deployment certificates are unavailable'});} }); app.get('/api/admin/presets',(_req,res)=>res.json({presets:policyPresets})); app.get('/api/admin/integrations',(_req,res)=>res.json({readiness:enterpriseReadiness(store.get('policy'))})); app.post('/api/admin/presets/preview',(req,res)=>{const b=z.object({id:z.string().max(80),policy:PolicySchema}).strict().parse(req.body);if(!policyPresets.some(p=>p.id===b.id))return res.status(400).json({error:'Unknown enterprise preset'});res.json(previewPreset(b.policy,b.id));}); const publisher=policyPublisher(app,store,config); installPolicyGroups(app,store,publisher); installUserProfiles(app,store,publisher); installPolicyHistory(app,store,publisher); app.put('/api/admin/security/blocklists',(req,res)=>{ const b=z.object({expectedVersion:z.number().int().positive(),blockedHosts:z.array(z.string()).max(10000),blockedIPs:z.array(z.string()).max(10000)}).strict().parse(req.body); SecuritySchema.parse({dns:{blockedHosts:b.blockedHosts,blockedIPs:b.blockedIPs}}); const p=PolicySchema.parse(store.get('policy'));if(p.version!==b.expectedVersion)return res.status(409).json({error:'Policy version conflict'}); p.security.dns.blockedHosts=b.blockedHosts;p.security.dns.blockedIPs=b.blockedIPs;p.version++; publisher.submit(req,res,p,'security.blocklists-published'); }); app.post('/api/admin/security/visual-reference',async(req,res,next)=>{ try{ const b=z.object({expectedVersion:z.number().int().positive(),brandId:z.string().min(1).max(80),label:z.string().min(1).max(100),allowedOrigins:z.array(VisualOriginSchema).min(1).max(50),data:z.string().max(1800000).regex(/^[A-Za-z0-9+/]*={0,2}$/),scope:z.enum(['viewport','login-form'])}).strict().parse(req.body); if(store.get('policy').version!==b.expectedVersion)return res.status(409).json({error:'Policy version conflict'}); const bytes=Buffer.from(b.data,'base64');if(!bytes.length||bytes.length>1024*1024||bytes.toString('base64')!==b.data)return res.status(400).json({error:'Reference must be canonical base64 PNG up to 1 MB'}); const {fingerprintPNG}=await import('../../packages/core/visual-image.cjs');const result=fingerprintPNG(bytes); if(result.usable!==true)return res.status(400).json({error:'Reference has insufficient visual detail; use a trusted nonblank login screenshot'}); const reference={hash:result.hash,algorithm:'phash-dct32-v1',scope:b.scope,viewport:{width:result.width,height:result.height}}; const brand=VisualBrandSchema.parse({id:b.brandId,label:b.label,allowedOrigins:b.allowedOrigins,references:[reference]}); store.event(null,'phishing.reference-derived',{actor:req.principal.sub,ruleId:b.brandId,version:b.expectedVersion}); const quality=z.object({contrast:z.number().finite(),stddev:z.number().finite(),dynamicRange:z.number().finite(),entropy:z.number().finite(),detail:z.number().finite()}).parse(result.quality); res.json({brand,quality:{...quality,width:result.width,height:result.height},warning:'Draft only. Explicitly publish signed policy to enable this reference. Perceptual similarity is not proof of a phishing page; use trusted reference images and validate false positives.'}); }catch(e){next(e);} }); app.get('/api/admin/teaching',(_req,res)=>res.json({drafts:store.db.prepare('SELECT * FROM teaching ORDER BY time DESC LIMIT 100').all().map(r=>({...r,reference:JSON.parse(r.reference)}))})); app.post('/api/admin/teaching',async(req,res,next)=>{ try{const b=z.object({label:z.string().min(1).max(100),data:z.string().max(1400000).regex(/^[A-Za-z0-9+/]*={0,2}$/),filename:z.string().max(200)}).strict().parse(req.body);const bytes=Buffer.from(b.data,'base64');if(!bytes.length||bytes.length>1024*1024)return res.status(413).json({error:'Reference document limit is 1 MB'}); const reference=await inspector(config,'/reference-document',{data:b.data,filename:b.filename,policy:store.get('policy')}); const id=randomUUID();store.db.prepare('INSERT INTO teaching VALUES(?,?,?,?,?,?,?,?)').run(id,new Date().toISOString(),req.principal.sub,b.label,'draft',JSON.stringify(reference),null,null);store.event(null,'teaching.drafted',{actor:req.principal.sub,referenceId:id});res.json({id,reference,status:'draft'}); }catch(e){next(e);} }); app.post('/api/admin/teaching/:id/publish',(req,res)=>{ const b=z.object({expectedVersion:z.number().int().positive(),approve:z.literal(true),mode:z.enum(['complete','available-fingerprints']).default('complete')}).strict().parse(req.body),draft=store.db.prepare("SELECT * FROM teaching WHERE id=? AND status='draft'").get(req.params.id); if(!draft)return res.status(404).json({error:'Draft not found'});const p=PolicySchema.parse(store.get('policy'));if(p.version!==b.expectedVersion)return res.status(409).json({error:'Policy version conflict'}); const reference=JSON.parse(draft.reference);if(!reference.registrationReady&&b.mode==='complete')return res.status(409).json({error:'Complete local inspection required before publication; available-fingerprints mode can explicitly approve partial coverage'});p.dlp.rules.push({id:draft.id,label:draft.label,kind:'sha256',value:reference.sha256,action:'block'}); for(const [index,value] of reference.fragmentHashes.slice(0,1000).entries())p.dlp.rules.push({id:`${draft.id}:${index}`,label:draft.label,kind:'fragment-sha256',value,action:'block'});p.version++;PolicySchema.parse(p); const coverage=reference.registrationReady?'complete':'partial';publisher.submit(req,res,p,'teaching.published',{type:'teaching',id:draft.id,coverage,mode:b.mode},{coverage}); }); app.get('/api/admin/overview',(req,res)=>{ let policy,migrationRequired=false;try{policy=store.get('policy');}catch(error){if(!(error instanceof z.ZodError))throw error;const raw=store.getRaw('policy');policy=PolicySchema.parse({...raw,dlp:{...raw.dlp,semantic:defaultSemantic()}});migrationRequired=true;} const devices=store.db.prepare('SELECT id,name,platform,user,revoked,last_seen,policy_version,document_analysis_enabled FROM devices ORDER BY last_seen DESC').all().map(d=>({...d,documentAnalysisEnabled:d.document_analysis_enabled===null?policy.inspection.documentAnalysisEnabled:!!d.document_analysis_enabled})); const alerts=store.db.prepare('SELECT * FROM alerts ORDER BY id DESC LIMIT 100').all(); const events=store.db.prepare('SELECT * FROM events ORDER BY id DESC LIMIT 100').all().map(e=>({...e,metadata:JSON.parse(e.metadata)})); res.json({policy,migrationRequired,publicKey:store.publicKey(),devices,events,alerts,principal:req.principal,siem:config.siemStatus?.()||null,auditRetention:config.auditRetentionStatus?.()||{enabled:false},governance:{requireIndependentPolicyApproval:!!config.requireIndependentPolicyApproval}}); }); app.get('/api/admin/audit/integrity',async(_req,res)=>res.json(await traceOperation('audit.verify',{},()=>store.auditIntegrity.verify()))); app.get('/api/admin/audit/entries',(req,res)=>{ const q=z.object({after:z.coerce.number().int().nonnegative().default(0),limit:z.coerce.number().int().min(1).max(1000).default(100)}).strict().parse(req.query); res.json(store.auditIntegrity.entries(q)); }); app.get('/api/admin/events/export',(req,res)=>{ const query=z.object({after:z.coerce.number().int().nonnegative().default(0),limit:z.coerce.number().int().min(1).max(1000).default(100),format:z.enum(['ecs','cef']).default('ecs')}).parse(req.query); const rows=store.db.prepare('SELECT * FROM events WHERE id>? ORDER BY id LIMIT ?').all(query.after,query.limit).map(e=>({...e,metadata:JSON.parse(e.metadata),audit:store.auditIntegrity.linkage(e.id)})); res.setHeader('X-SafetyNet-Next-Cursor',String(rows.at(-1)?.id??query.after)); res.type(query.format==='ecs'?'application/x-ndjson':'text/plain').send(rows.map(e=>query.format==='ecs'?JSON.stringify(toECS(e,store.get('installationId'))):toCEF(e,store.get('installationId'))).join('\n')+(rows.length?'\n':'')); }); app.put('/api/admin/policy',(req,res)=>{ const p=PolicySchema.parse(req.body),previous=store.getRaw('policy'); if(!globalBookmarksValid(store,p))return res.status(400).json({error:'Global policies require empty personal bookmarks and distinct company IDs'}); if(p.version!==previous.version+1)return res.status(409).json({error:'Refresh the console: version must increase by exactly one'}); const expires=Date.parse(p.expiresAt);if(expires<=Date.now() || expires>Date.now()+30*86400000)return res.status(400).json({error:'Policy expiry must be within the next 30 days'}); publisher.submit(req,res,p); }); app.post('/api/admin/enrollments',(req,res)=>{ const user=z.string().min(1).max(200).parse(req.body.user),code=token(); store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+600000,user); store.event(null,'enrollment.created',{actor:req.principal.sub,user});res.json({code,expiresInSeconds:600}); }); app.post('/api/admin/devices/:id/revoke',(req,res)=>{ const r=store.db.prepare('UPDATE devices SET revoked=1 WHERE id=? AND revoked=0').run(req.params.id); if(!r.changes)return res.status(404).json({error:'Active device not found'}); store.event(req.params.id,'device.revoked',{actor:req.principal.sub});res.json({ok:true}); }); app.patch('/api/admin/devices/:id/inspection',(req,res)=>{ const b=z.object({expectedVersion:z.number().int().positive(),documentAnalysisEnabled:z.boolean()}).strict().parse(req.body),p=store.get('policy'); if(p.version!==b.expectedVersion)return res.status(409).json({error:'Policy version conflict'}); if(!store.db.prepare('SELECT id FROM devices WHERE id=? AND revoked=0').get(req.params.id))return res.status(404).json({error:'Active device not found'}); p.version++;publisher.submit(req,res,p,'inspection.policy-published',{type:'inspection',deviceId:req.params.id,enabled:b.documentAnalysisEnabled},{documentAnalysisEnabled:b.documentAnalysisEnabled}); }); app.post('/api/admin/policy/test',async(req,res,next)=>{ try{const b=z.object({text:z.string().max(100000),policy:PolicySchema}).parse(req.body);let result=inspect(Buffer.from(b.text),b.policy); if(result.decision==='allow'&&b.policy.dlp.semantic.enabled)result=await inspector(config,'/inspect',{data:Buffer.from(b.text).toString('base64'),policy:b.policy});res.json(result); }catch(e){next(e);} }); app.post('/api/admin/semantic/reference',async(req,res,next)=>{ try{ const b=z.object({description:z.string().min(10).max(4000)}).strict().parse(req.body); const result=await inspector(config,'/embed',{data:Buffer.from(b.description).toString('base64'),policy:store.get('policy')}); if(!result.coverage.complete||!result.coverage.extractionComplete)return res.status(409).json({error:'Complete local text inspection is required to create a semantic reference'}); store.event(null,'semantic.reference-created',{actor:req.principal.sub,model:result.model,revision:result.revision,dimensions:result.dimensions});res.json(result); }catch(e){next(e);} }); app.post('/api/admin/semantic/document-reference',async(req,res,next)=>{ try{ const b=z.object({data:z.string().max(1400000).regex(/^[A-Za-z0-9+/]*={0,2}$/),filename:z.string().min(1).max(200)}).strict().parse(req.body),bytes=Buffer.from(b.data,'base64'); if(!bytes.length||bytes.length>1048576||bytes.toString('base64')!==b.data)return res.status(413).json({error:'Semantic reference document must be canonical base64 up to 1 MiB'}); const result=await inspector(config,'/embed',{data:b.data,policy:store.get('policy')}); if(!result.coverage.complete||!result.coverage.extractionComplete)return res.status(409).json({error:'Complete extraction and chunk coverage are required for a semantic document reference'}); store.event(null,'semantic.document-reference-created',{actor:req.principal.sub,model:result.model,revision:result.revision,dimensions:result.dimensions});res.json(result); }catch(e){next(e);} }); app.use(express.static(fileURLToPath(new URL('./public',import.meta.url)),{index:'index.html'})); app.use((error,_req,res,_next)=>{ config.onError?.(error); const invalid=error instanceof z.ZodError || error.type==='entity.parse.failed'; res.status(invalid?400:error.type==='entity.too.large'?413:[400,403,404,409].includes(error.status)?error.status:500).json({error:invalid?'Request validation failed':'Request failed',...(error instanceof z.ZodError?{issues:error.issues.map(v=>({path:v.path.join('.'),message:v.message}))}:{})}); }); return app; } async function readLimited(response,maxBytes) { const chunks=[];let size=0; for await(const chunk of response.body){size+=chunk.length;if(size>maxBytes){await response.body.cancel().catch(()=>{});throw Error('AI response too large');}chunks.push(chunk);} return Buffer.concat(chunks).toString('utf8'); }