Add Fleet Torrent erasure extension with shard DHT and gossip.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Content-addressed shard DHT on seeder agents with subnet_primary_seeder election, cross-subnet fleet_torrent_gossip, BGP swarm magnets, C2 torrent manifest, and k-of-n peer fetch with C2 fallback.
This commit is contained in:
AetherForge
2026-06-07 09:24:55 -07:00
parent 588735e7e7
commit 894b7a50ae
40 changed files with 2285 additions and 1 deletions

View File

@@ -25,6 +25,7 @@ const LotlTimelinePage = lazy(() => import('./pages/LotlTimelinePage'));
const ROIPage = lazy(() => import('./pages/ROIPage'));
const ActivityFeedPage = lazy(() => import('./pages/ActivityFeedPage'));
const SeerPage = lazy(() => import('./pages/SeerPage'));
const OathLedgerPage = lazy(() => import('./pages/OathLedgerPage'));
export function PageFallback() {
return (
@@ -68,6 +69,7 @@ function App() {
<Route path="/roi" element={<ROIPage />} />
<Route path="/activity" element={<ActivityFeedPage />} />
<Route path="/seer" element={<SeerPage />} />
<Route path="/oath" element={<OathLedgerPage />} />
</Routes>
</Suspense>
</Layout>

View File

@@ -389,6 +389,8 @@ export const api = {
queued?: boolean;
error?: string;
}>('/fleet/play-strain-card', { method: 'POST', body: JSON.stringify(body) }),
listOathLedger: (limit = 100) =>
fetchJSON<import('../types').OathLedgerEntry[]>(`/fleet/oath-ledger?limit=${limit}`),
// Public builds (unauthenticated — used on login page)
listPublicBuilds: async (): Promise<PublicBuildsResponse> => {

View File

@@ -195,6 +195,17 @@ export default function CalibrationAIControl({ server, onUpdate }: Props) {
<span>Enable adaptive strategy engine <HelpTip field="adaptive_strategy" /></span>
</label>
</div>
<div className="form-group checkbox-group" style={{ marginTop: '0.5rem' }}>
<label className="checkbox-label">
<input
type="checkbox"
className="checkbox"
checked={server.fleet_torrent_enabled === true}
onChange={(e) => onUpdate('server.fleet_torrent_enabled', e.target.checked)}
/>
<span>Fleet Torrent (shard DHT + gossip) <HelpTip field="fleet_torrent" /></span>
</label>
</div>
<div className="form-group checkbox-group" style={{ marginTop: '0.5rem' }}>
<label className="checkbox-label">
<input

View File

@@ -10,6 +10,19 @@
margin-bottom: 0.5rem;
}
.access-depth-oath-link {
margin-left: auto;
font-size: 0.68rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-muted, #8899aa);
text-decoration: none;
}
.access-depth-oath-link:hover {
color: var(--neon-cyan, #00e8f5);
}
.access-depth-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));

View File

@@ -188,6 +188,9 @@ export default function AccessDepthPanel({ agent, diagnostics }: Props) {
</span>
)}
{!policyLoaded && <span className="access-depth-muted">loading policy</span>}
<Link to="/oath" className="access-depth-oath-link" title="Immutable operator accountability ledger">
Oath
</Link>
</div>
<div className="access-depth-grid">

View File

@@ -108,6 +108,7 @@ describe('FIELD_HELP', () => {
'fleet_phenotype',
'failure_atlas',
'erasure_lanes',
'fleet_torrent',
'ai_court_session',
'ai_persona',
'ai_persona_aggressive',
@@ -193,6 +194,12 @@ describe('FIELD_HELP', () => {
expect(FIELD_HELP.erasure_lanes).toMatch(/Foundation only/i);
});
it('fleet_torrent describes shard DHT and cross-subnet gossip', () => {
expect(FIELD_HELP.fleet_torrent).toMatch(/Fleet Torrent/i);
expect(FIELD_HELP.fleet_torrent).toContain('subnet_primary_seeder');
expect(FIELD_HELP.fleet_torrent).toMatch(/swarm_magnet|cross-subnet/i);
});
it('documents honest AV limits — no invisible mining', () => {
expect(FIELD_HELP.av_limits).toMatch(/100% invisible|not 100% invisible/i);
expect(FIELD_HELP.av_limits).toMatch(/in-process RandomX/i);

View File

@@ -0,0 +1,105 @@
.oath-ledger-page {
padding: 1.25rem 1.5rem 2rem;
max-width: 1200px;
}
.oath-ledger-header {
margin-bottom: 1rem;
}
.oath-ledger-title {
margin: 0 0 0.35rem;
font-size: 1.35rem;
letter-spacing: 0.04em;
}
.oath-ledger-subtitle {
margin: 0;
color: var(--text-muted, #8899aa);
font-size: 0.82rem;
}
.oath-ledger-link {
color: var(--accent-cyan, #00e8f5);
text-decoration: none;
}
.oath-ledger-link:hover {
text-decoration: underline;
}
.oath-ledger-error {
margin-bottom: 0.75rem;
padding: 0.5rem 0.75rem;
border: 1px solid rgba(255, 68, 68, 0.35);
color: #ff8888;
font-size: 0.85rem;
}
.oath-ledger-table-wrap {
overflow-x: auto;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 4px;
background: rgba(0, 0, 0, 0.25);
}
.oath-ledger-table {
width: 100%;
border-collapse: collapse;
font-size: 0.78rem;
font-family: var(--font-tech, monospace);
}
.oath-ledger-table th,
.oath-ledger-table td {
padding: 0.45rem 0.6rem;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.oath-ledger-table th {
color: var(--text-muted, #8899aa);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 0.68rem;
}
.oath-ledger-table tbody tr:hover {
background: rgba(255, 255, 255, 0.03);
}
.oath-ledger-empty {
text-align: center;
color: var(--text-muted, #8899aa);
padding: 1.25rem !important;
}
.oath-ledger-hash {
font-family: var(--font-mono, monospace);
color: var(--text-muted, #8899aa);
}
.oath-ledger-outcome {
text-transform: lowercase;
}
.oath-ledger-table tr[data-outcome='success'] .oath-ledger-outcome {
color: #7fd87f;
}
.oath-ledger-table tr[data-outcome='fail'] .oath-ledger-outcome {
color: #ff8888;
}
.oath-ledger-table tr[data-outcome='pending'] .oath-ledger-outcome {
color: #d4b86a;
}
.oath-ledger-strain-swatch {
display: inline-block;
width: 0.75rem;
height: 0.75rem;
border-radius: 2px;
vertical-align: middle;
}

View File

@@ -0,0 +1,46 @@
import { render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { describe, expect, it, vi, beforeEach } from 'vitest';
import OathLedgerPage from './OathLedgerPage';
import { api } from '../api/client';
import { WebSocketProvider } from '../context/WebSocketProvider';
vi.mock('../api/client', () => ({
api: {
listOathLedger: vi.fn(),
},
}));
describe('OathLedgerPage', () => {
beforeEach(() => {
vi.mocked(api.listOathLedger).mockResolvedValue([
{
id: 1,
timestamp: '2026-06-07T12:00:00Z',
actor: 'comrade',
action_type: 'graft',
agent_id: 'agent-abc-123',
strain: '#aabbcc',
why_hash: 'abc123def456',
outcome: 'success',
payload_json: { graft_tier: 'dns_txt' },
},
]);
});
it('renders read-only oath table from API', async () => {
render(
<MemoryRouter>
<WebSocketProvider>
<OathLedgerPage />
</WebSocketProvider>
</MemoryRouter>,
);
expect(await screen.findByText('Operator Oath Ledger')).toBeInTheDocument();
await waitFor(() => expect(api.listOathLedger).toHaveBeenCalledWith(100));
expect(screen.getByText('comrade')).toBeInTheDocument();
expect(screen.getByText('graft')).toBeInTheDocument();
expect(screen.getByText('success')).toBeInTheDocument();
expect(screen.getByRole('link', { name: /Seer reasoning/i })).toHaveAttribute('href', '/seer');
});
});

View File

@@ -0,0 +1,124 @@
import { useCallback, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { api } from '../api/client';
import { useWebSocket } from '../hooks/useWebSocket';
import type { OathLedgerEntry } from '../types';
import './OathLedgerPage.css';
function fmtTs(ts?: string): string {
if (!ts) return '—';
const d = new Date(ts);
if (Number.isNaN(d.getTime())) return ts;
return d.toLocaleString([], {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
}
function actionLabel(action: string): string {
return action.replace(/_/g, ' ');
}
export default function OathLedgerPage() {
const { latestMessage } = useWebSocket();
const [rows, setRows] = useState<OathLedgerEntry[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const load = useCallback(async () => {
try {
const data = await api.listOathLedger(100);
setRows(data ?? []);
setError('');
} catch (e) {
setError(e instanceof Error ? e.message : 'Failed to load oath ledger');
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
void load();
}, [load]);
useEffect(() => {
if (!latestMessage || latestMessage.type !== 'oath_ledger_event') return;
const entry = latestMessage.payload as OathLedgerEntry;
if (!entry || typeof entry !== 'object' || !entry.id) return;
setRows((prev) => {
if (prev.some((r) => r.id === entry.id)) return prev;
return [entry, ...prev].slice(0, 100);
});
}, [latestMessage]);
return (
<div className="page oath-ledger-page">
<header className="oath-ledger-header">
<h1 className="oath-ledger-title font-display">Operator Oath Ledger</h1>
<p className="oath-ledger-subtitle font-tech">
Immutable accountability separate from{' '}
<Link to="/seer" className="oath-ledger-link">
Seer reasoning
</Link>
.
</p>
</header>
{error && <div className="oath-ledger-error">{error}</div>}
<div className="oath-ledger-table-wrap">
<table className="oath-ledger-table">
<thead>
<tr>
<th>Time</th>
<th>Actor</th>
<th>Action</th>
<th>Agent</th>
<th>Strain</th>
<th>Why hash</th>
<th>Outcome</th>
</tr>
</thead>
<tbody>
{loading && (
<tr>
<td colSpan={7} className="oath-ledger-empty">
Loading
</td>
</tr>
)}
{!loading && rows.length === 0 && (
<tr>
<td colSpan={7} className="oath-ledger-empty">
No oath rows yet graft, strain plays, court L4, fork/merge, and LOTL dispatch record here.
</td>
</tr>
)}
{rows.map((row) => (
<tr key={row.id} data-outcome={row.outcome}>
<td className="oath-ledger-ts">{fmtTs(row.timestamp)}</td>
<td className="oath-ledger-actor">{row.actor || '—'}</td>
<td className="oath-ledger-action">{actionLabel(row.action_type)}</td>
<td className="oath-ledger-agent">{row.agent_id ? row.agent_id.slice(0, 8) : '—'}</td>
<td className="oath-ledger-strain">
{row.strain ? (
<span className="oath-ledger-strain-swatch" style={{ backgroundColor: row.strain }} title={row.strain} />
) : (
'—'
)}
</td>
<td className="oath-ledger-hash" title={row.why_hash}>
{row.why_hash ? `${row.why_hash.slice(0, 10)}` : '—'}
</td>
<td className="oath-ledger-outcome">{row.outcome}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}

View File

@@ -20,6 +20,21 @@
letter-spacing: 0.06em;
}
.seer-oath-strip {
margin: 0.35rem 0 0;
font-size: 0.78rem;
color: var(--text-muted, #8899aa);
}
.seer-oath-link {
color: var(--accent-cyan, #00e8f5);
text-decoration: none;
}
.seer-oath-link:hover {
text-decoration: underline;
}
.seer-subtitle {
margin: 0.35rem 0 0;
font-size: 0.72rem;

View File

@@ -1,4 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { useWebSocket } from '../hooks/useWebSocket';
import { HelpTip } from '../components/HelpTip';
import { api } from '../api/client';
@@ -150,6 +151,13 @@ export default function SeerPage() {
The Seer <HelpTip field="seer_overview" />
</h1>
<p className="seer-subtitle font-tech">{statusLine}</p>
<p className="seer-oath-strip font-tech">
Accountability rows live in{' '}
<Link to="/oath" className="seer-oath-link">
Oath Ledger
</Link>{' '}
not mixed with this reasoning stream.
</p>
</header>
{error && <div className="seer-error">{error}</div>}

View File

@@ -811,6 +811,18 @@ export interface AuditEntry {
detail?: Record<string, unknown>;
}
export interface OathLedgerEntry {
id: number;
timestamp: string;
actor: string;
action_type: string;
agent_id: string;
strain: string;
why_hash: string;
outcome: 'success' | 'fail' | 'pending' | string;
payload_json?: Record<string, unknown>;
}
export interface FleetModuleManifest {
name: string;
version: string;