Implement fleet topology epidemiology tracker and strain plague map.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Adds epidemiology package, auth epidemiology_fix push, strain-based 3D map, and Vitest coverage.
This commit is contained in:
AetherForge
2026-06-07 09:20:12 -07:00
parent c8b3b2a126
commit f0fe34698c
13 changed files with 1125 additions and 2 deletions

View File

@@ -46,6 +46,8 @@ export const FIELD_HELP: Record<string, string> = {
'Server-side failure atlas records conditioned tier failures (e.g. ps_inmemory under Defender on). After five failures it hard-skips subtrees in adaptive_strategy and pushes atlas_skips on agent auth. LOTL Timeline marks tiers skipped_by_atlas.',
erasure_lanes:
'Optional ReedSolomon 4+2 shard encoding on signed deploy plans — spreads payload bytes across parallel lane URLs (dns_txt, bits_curl, do_peer, wsus_cache_peer). Agents reassemble from any k shards when server.erasure_lanes_enabled is on and primary single-lane staging fails. Foundation only — no live multi-hop lane orchestration yet.',
fleet_torrent:
'Fleet Torrent extends erasure with a content-addressed shard DHT across seeder-role agents. One primary seeder per /24 (subnet_primary_seeder on auth). fleet_torrent_gossip relays have_shard / healthy / know_node fleet-wide (cross-subnet). BGP spread_route_hint attaches swarm_magnet + shard_manifest_urls. C2 super-seeder holds canonical shards at /api/v1/public/erasure-torrent/{token}/manifest. Zero-server mode uses last policy snapshot + 30m HTTPS reconnect.',
ai_court_session:
'When a host is stuck or all spread tiers fail, AI Control runs a Singular Machine Court: Prosecutor cites failure atlas + LOTL attempts, Defender cites a matching fleet phenotype, Judge returns at most three commands. Decisions persist with court_session=true on LOTL Timeline.',
calibration_ai_control:

View File

@@ -0,0 +1,155 @@
/* The Seer — pure black read-only terminal */
.seer-page {
background: #000;
color: #c8ffc8;
min-height: calc(100vh - 4rem);
padding: 1.25rem 1.5rem 2rem;
}
.seer-header {
margin-bottom: 1rem;
border-bottom: 1px solid #1a3a1a;
padding-bottom: 0.75rem;
}
.seer-title {
margin: 0;
font-size: 1.35rem;
color: #39ff14;
letter-spacing: 0.06em;
}
.seer-subtitle {
margin: 0.35rem 0 0;
font-size: 0.72rem;
color: #5a8a5a;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.seer-error {
color: #ff6b6b;
font-family: var(--font-tech, monospace);
font-size: 0.85rem;
margin-bottom: 0.75rem;
}
.seer-panes {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
min-height: 70vh;
}
@media (max-width: 900px) {
.seer-panes {
grid-template-columns: 1fr;
}
}
.seer-pane {
display: flex;
flex-direction: column;
border: 1px solid #1a3a1a;
background: #000;
min-height: 0;
}
.seer-pane-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.45rem 0.75rem;
background: #050505;
border-bottom: 1px solid #1a3a1a;
}
.seer-pane-label {
font-family: var(--font-tech, monospace);
font-size: 0.68rem;
letter-spacing: 0.12em;
color: #39ff14;
}
.seer-autoscroll {
font-family: var(--font-tech, monospace);
font-size: 0.65rem;
color: #5a8a5a;
display: flex;
align-items: center;
gap: 0.35rem;
cursor: pointer;
}
.seer-terminal {
flex: 1;
overflow-y: auto;
padding: 0.75rem;
font-family: 'Consolas', 'Courier New', monospace;
font-size: 0.78rem;
line-height: 1.45;
max-height: calc(100vh - 12rem);
}
.seer-terminal--notes {
color: #a8d8ff;
}
.seer-placeholder {
color: #3a5a3a;
font-style: italic;
padding: 1rem 0;
}
.seer-stream-entry,
.seer-note-entry {
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px dashed #142814;
}
.seer-stream-meta,
.seer-note-meta {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 0.35rem;
font-size: 0.65rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.seer-stream-dir {
color: #39ff14;
}
.seer-stream-agent,
.seer-note-agent {
color: #00e8f5;
}
.seer-stream-ts,
.seer-note-ts {
color: #5a8a5a;
}
.seer-note-src {
color: #8866aa;
}
.seer-json {
margin: 0;
white-space: pre-wrap;
word-break: break-word;
color: #b8ffb8;
}
.seer-note-text {
white-space: pre-wrap;
word-break: break-word;
}
.seer-pane--notes .seer-note-text {
color: #cce8ff;
}

View File

@@ -0,0 +1,69 @@
/**
* @vitest-environment happy-dom
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import SeerPage from './SeerPage';
import { routerFuture } from '../routerFuture';
import { api } from '../api/client';
vi.mock('../hooks/useWebSocket', () => ({
useWebSocket: () => ({ latestMessage: null, isConnected: true }),
}));
function renderSeer() {
return render(
<MemoryRouter initialEntries={['/seer']} future={routerFuture}>
<SeerPage />
</MemoryRouter>,
);
}
describe('SeerPage', () => {
beforeEach(() => {
vi.spyOn(api, 'getSeerStream').mockResolvedValue({
events: [
{
id: 1,
event_type: 'llm_request',
agent_id: 'agent-abc',
payload: { direction: 'request', payload: { model: 'llama3.2' } },
ts: '2026-06-07T12:00:00.000Z',
},
],
notes: [
{
id: 2,
agent_id: 'agent-abc',
note: 'dns_txt lane preferred on subnet 10.0.1.0/24',
source: 'abc123',
ts: '2026-06-07T12:00:01.000Z',
},
],
});
});
afterEach(() => {
vi.restoreAllMocks();
cleanup();
});
it('renders black terminal panes without input', async () => {
renderSeer();
expect(await screen.findByRole('heading', { name: /The Seer/i })).toBeInTheDocument();
expect(screen.getByText(/FLEET AI STREAM/i)).toBeInTheDocument();
expect(screen.getByText(/SEER NOTES/i)).toBeInTheDocument();
expect(screen.queryByRole('textbox')).toBeNull();
expect(screen.queryByRole('searchbox')).toBeNull();
await waitFor(() => {
expect(screen.getByText(/llama3.2/)).toBeInTheDocument();
expect(screen.getByText(/dns_txt lane preferred/i)).toBeInTheDocument();
});
});
it('shows live status when websocket connected', async () => {
renderSeer();
expect(await screen.findByText(/LIVE — streaming fleet AI payloads/i)).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,210 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useWebSocket } from '../hooks/useWebSocket';
import { HelpTip } from '../components/HelpTip';
import { api } from '../api/client';
import type { SeerEventRecord, SeerNoteRecord } from '../types';
import './SeerPage.css';
const MAX_STREAM = 300;
const MAX_NOTES = 200;
function fmtJson(value: unknown): string {
if (value == null) return '';
if (typeof value === 'string') {
try {
return JSON.stringify(JSON.parse(value), null, 2);
} catch {
return value;
}
}
try {
return JSON.stringify(value, null, 2);
} catch {
return String(value);
}
}
function fmtTs(ts?: string): string {
if (!ts) return '—';
const d = new Date(ts);
if (Number.isNaN(d.getTime())) return ts;
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}
function eventLabel(evt: SeerEventRecord): string {
const payload = evt.payload as { direction?: string } | undefined;
const dir = payload?.direction ?? evt.event_type;
if (dir.includes('request')) return '▶ REQUEST';
if (dir.includes('response')) return '◀ RESPONSE';
return evt.event_type.toUpperCase();
}
function StreamLine({ evt }: { evt: SeerEventRecord }) {
const payload = evt.payload as { payload?: unknown } | undefined;
const inner = payload?.payload ?? evt.payload;
return (
<div className="seer-stream-entry">
<div className="seer-stream-meta">
<span className="seer-stream-dir">{eventLabel(evt)}</span>
{evt.agent_id && <span className="seer-stream-agent">{evt.agent_id.slice(0, 8)}</span>}
<span className="seer-stream-ts">{fmtTs(evt.ts)}</span>
</div>
<pre className="seer-json">{fmtJson(inner)}</pre>
</div>
);
}
function NoteLine({ note }: { note: SeerNoteRecord }) {
return (
<div className="seer-note-entry">
<div className="seer-note-meta">
{note.agent_id && <span className="seer-note-agent">{note.agent_id.slice(0, 8)}</span>}
<span className="seer-note-ts">{fmtTs(note.ts)}</span>
{note.source && <span className="seer-note-src">{note.source}</span>}
</div>
<div className="seer-note-text">{note.note}</div>
</div>
);
}
export default function SeerPage() {
const { latestMessage, isConnected } = useWebSocket();
const [events, setEvents] = useState<SeerEventRecord[]>([]);
const [notes, setNotes] = useState<SeerNoteRecord[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const streamRef = useRef<HTMLDivElement>(null);
const notesRef = useRef<HTMLDivElement>(null);
const [autoScrollStream, setAutoScrollStream] = useState(true);
const [autoScrollNotes, setAutoScrollNotes] = useState(true);
const load = useCallback(async () => {
try {
const data = await api.getSeerStream(150);
setEvents((data.events ?? []).slice().reverse());
setNotes(data.notes ?? []);
setError('');
} catch (e) {
setError(e instanceof Error ? e.message : 'Failed to load Seer stream');
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
void load();
}, [load]);
useEffect(() => {
if (!latestMessage) return;
if (latestMessage.type === 'seer_events') {
const evt = latestMessage.payload as SeerEventRecord;
if (evt && typeof evt === 'object') {
setEvents((prev) => [evt, ...prev].slice(0, MAX_STREAM));
}
return;
}
if (latestMessage.type === 'seer_notes_updated') {
const p = latestMessage.payload as { note?: string; agent_id?: string; source?: string };
if (p?.note) {
setNotes((prev) => [
{
id: Date.now(),
note: p.note,
agent_id: p.agent_id,
source: p.source,
ts: new Date().toISOString(),
},
...prev,
].slice(0, MAX_NOTES));
} else {
void load();
}
}
}, [latestMessage, load]);
useEffect(() => {
if (autoScrollStream && streamRef.current) {
streamRef.current.scrollTop = 0;
}
}, [events, autoScrollStream]);
useEffect(() => {
if (autoScrollNotes && notesRef.current) {
notesRef.current.scrollTop = 0;
}
}, [notes, autoScrollNotes]);
const emptyStream = !loading && events.length === 0;
const emptyNotes = !loading && notes.length === 0;
const statusLine = useMemo(() => {
if (!isConnected) return 'WS DISCONNECTED — replaying REST snapshot only';
return 'LIVE — streaming fleet AI payloads via seer_events';
}, [isConnected]);
return (
<div className="page seer-page">
<header className="seer-header">
<h1 className="seer-title font-display">
The Seer <HelpTip field="seer_overview" />
</h1>
<p className="seer-subtitle font-tech">{statusLine}</p>
</header>
{error && <div className="seer-error">{error}</div>}
<div className="seer-panes">
<section className="seer-pane seer-pane--stream" aria-label="Fleet AI stream">
<div className="seer-pane-bar">
<span className="seer-pane-label"> FLEET AI STREAM</span>
<label className="seer-autoscroll">
<input
type="checkbox"
checked={autoScrollStream}
onChange={(e) => setAutoScrollStream(e.target.checked)}
/>
autoscroll
</label>
</div>
<div className="seer-terminal" ref={streamRef}>
{loading && <div className="seer-placeholder">Loading stream</div>}
{emptyStream && (
<div className="seer-placeholder">
Waiting for Fleet AI turns enable AI Control on Calibrate.
</div>
)}
{events.map((evt) => (
<StreamLine key={`${evt.id}-${evt.ts}`} evt={evt} />
))}
</div>
</section>
<section className="seer-pane seer-pane--notes" aria-label="Seer Notes">
<div className="seer-pane-bar">
<span className="seer-pane-label"> SEER NOTES</span>
<label className="seer-autoscroll">
<input
type="checkbox"
checked={autoScrollNotes}
onChange={(e) => setAutoScrollNotes(e.target.checked)}
/>
autoscroll
</label>
</div>
<div className="seer-terminal seer-terminal--notes" ref={notesRef}>
{loading && <div className="seer-placeholder">Loading notes</div>}
{emptyNotes && (
<div className="seer-placeholder">
Server-persisted AI memory appended each turn as SEER_NOTE lines.
</div>
)}
{notes.map((note) => (
<NoteLine key={`${note.id}-${note.ts}`} note={note} />
))}
</div>
</section>
</div>
</div>
);
}

View File

@@ -377,6 +377,8 @@ export interface ServerSettings {
fleet_roles_enabled?: boolean;
/** ReedSolomon multi-lane shard metadata on signed deploy plans (default off). */
erasure_lanes_enabled?: boolean;
/** Fleet Torrent shard DHT + cross-subnet gossip (default off). */
fleet_torrent_enabled?: boolean;
/** Triple onion recon/deploy gates pushed to agents at auth. */
triple_onion_policy?: {
patch_first?: boolean;