feat(security): expose security status on /health for shield icon

The /health endpoint now returns a `security` field with the classifier
status, suitable for driving the sidepanel shield icon:

  {
    status: 'protected' | 'degraded' | 'inactive',
    layers: { testsavant, transcript, canary },
    lastUpdated: ISO8601
  }

Backend plumbing:
  * server.ts imports getStatus from security.ts (pure-string, safe in
    compiled binary) and includes it in the /health response.
  * sidebar-agent.ts writes ~/.gstack/security/session-state.json when the
    classifier warmup completes (success OR failure). This is the cross-
    process handoff — server.ts reads the state file via getStatus() to
    surface the result to the sidepanel.

The sidepanel rendering (SVG shield icon + color states + tooltip) is a
follow-up commit in the extension/ code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-04-19 19:08:01 +08:00
parent 1a1a182251
commit 7e9600ffc8
2 changed files with 19 additions and 2 deletions

View File

@@ -25,7 +25,7 @@ import {
runContentFilters, type ContentFilterResult,
markHiddenElements, getCleanTextWithStripping, cleanupHiddenMarkers,
} from './content-security';
import { generateCanary, injectCanary } from './security';
import { generateCanary, injectCanary, getStatus as getSecurityStatus } from './security';
import { handleSnapshot, SNAPSHOT_FLAGS } from './snapshot';
import {
initRegistry, validateToken as validateScopedToken, checkScope, checkDomain,
@@ -1447,6 +1447,11 @@ async function start() {
queueLength: messageQueue.length,
},
session: sidebarSession ? { id: sidebarSession.id, name: sidebarSession.name } : null,
// Security module status — drives the shield icon in the sidepanel.
// Returns {status: 'protected'|'degraded'|'inactive', layers: {...}}.
// Source of truth is ~/.gstack/security/session-state.json, written
// by sidebar-agent as the classifier warms up.
security: getSecurityStatus(),
}), {
status: 200,
headers: { 'Content-Type': 'application/json' },