// website/src/pages/Sentinel.jsx // Live: SpacePromoter topology (from /health), infra event log, trigger buttons import { useState, useEffect, useCallback } from 'react'; import { api } from '../lib/api'; import { relativeTime } from '../lib/utils'; import Card from '../components/Card'; import Badge from '../components/Badge'; const EVENT_COLORS = { promotion: 'yellow', recovery: 'green', health_degraded: 'red', kv_write_failed: 'red', }; function NodeCard({ name, node }) { const alive = node?.is_alive; const status = node?.last_status; return (

{node?.role ?? name}

{alive ? (status === 'ok' ? 'healthy' : 'degraded') : 'down'}

{node?.name ?? name}

{node?.url ?? '—'}

failures

{node?.consecutive_failures ?? 0}

successes

{node?.consecutive_successes ?? 0}

last check

{node?.last_check_at ? relativeTime(node.last_check_at) : '—'}

); } function InfraEvent({ ev }) { let parsed; try { parsed = typeof ev === 'string' ? JSON.parse(ev) : ev; } catch { parsed = { event_type: 'unknown', message: String(ev) }; } return (
{parsed.event_type ?? '?'}

{parsed.message ?? '—'}

{parsed.timestamp && (

{relativeTime(parsed.timestamp)}

)}
); } export default function Sentinel() { const [health, setHealth] = useState(null); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const [evLoading, setEvLoading] = useState(true); const [triggering, setTriggering] = useState(false); const [triggered, setTriggered] = useState(null); const [triggerError, setTriggerError] = useState(null); const fetchHealth = useCallback(async () => { try { const d = await api.health(); setHealth(d); } catch { /* offline */ } finally { setLoading(false); } }, []); const fetchEvents = useCallback(async () => { setEvLoading(true); try { const d = await api.infra.events(); setEvents(Array.isArray(d) ? d : d?.events ?? []); } catch { setEvents([]); } finally { setEvLoading(false); } }, []); useEffect(() => { fetchHealth(); fetchEvents(); const iv = setInterval(fetchHealth, 30_000); return () => clearInterval(iv); }, [fetchHealth, fetchEvents]); const triggerEvent = async (type) => { setTriggering(true); setTriggerError(null); try { await api.sentinel.trigger(type); setTriggered(type); setTimeout(() => setTriggered(null), 4000); } catch (e) { setTriggerError(e.message); } finally { setTriggering(false); } }; const promoter = health?.promoter ?? null; const nodes = promoter?.nodes ?? {}; const sentinelActive = health?.sentinel_active; const promoterActive = health?.promoter_active; return (

Sentinel

God Layer. Gemini 2.5 Pro. Dedicated key. Watches everything.

{triggerError && (
✗ {triggerError}
)}
{[ { label: 'Sentinel', value: loading ? '—' : (sentinelActive ? 'Active' : 'Inactive'), sub: 'Gemini 2.5 Pro. Dedicated key.', variant: loading ? 'gray' : (sentinelActive ? 'green' : 'red'), }, { label: 'SpacePromoter', value: loading ? '—' : (promoterActive ? 'Running' : 'Off'), sub: promoter ? `${promoter.check_interval_seconds}s interval · failovers: ${promoter.failover_count ?? 0}` : 'Health loop + CF KV promotion', variant: loading ? 'gray' : (promoterActive ? 'green' : 'gray'), }, { label: 'Degraded?', value: promoter?.in_degraded_state ? 'YES' : 'No', sub: `Max failovers: ${promoter?.max_failover_attempts ?? 5}`, variant: promoter?.in_degraded_state ? 'red' : 'green', }, ].map(({ label, value, sub, variant }) => (

{label}

{value}

{sub}

))}

Space Topology

{loading ? (
Loading topology...
) : Object.keys(nodes).length === 0 ? (

No topology data.

SpacePromoter inactive or Brain offline.

) : (
{Object.entries(nodes).map(([name, node]) => ( ))}
)}
{[ { when: 'Every request', what: 'Reads KV routing table. Validates Space health. <50ms.' }, { when: 'Every failure', what: 'Instant incident analysis → Notion page → KV rerouting → Discord DM.' }, { when: 'Sunday 23:59', what: '1M context window reads full week logs → structured report → Notion + Discord.' }, { when: 'Project start', what: 'Reads brief → writes operational plan: DevOps, memory arch, MOA config, tool assignments.' }, ].map(({ when, what }) => (
{when}
{what}
))}
{[ { label: 'Trigger Audit', type: 'weekly_audit' }, { label: 'Health Check', type: 'health_check' }, { label: 'Force KV Sync', type: 'routing_override' }, ].map(({ label, type }) => ( ))}
{evLoading ? (
Loading...
) : events.length === 0 ? (

No events. System stable.

) : (
{events.slice(-20).reverse().map((ev, i) => )}
)}

No reports yet.

First report: this Sunday 23:59 UTC.

); }