API Reference¶
When running agentsniff serve, a REST API is available on 127.0.0.1:9090 by default. Loopback use is credential-free. Set a 32-byte-or-longer AGENTSNIFF_API_TOKEN to enable Basic/Bearer authentication; the server refuses non-loopback binding without one. /api/health remains public.
For API clients, send the token as a Bearer credential. Browsers can use HTTP Basic authentication with username agentsniff and the token as the password. Always use TLS when the listener is remotely reachable. Every state-changing request must use Content-Type: application/json, including endpoints whose body is {}; this also protects credential-free loopback deployments from cross-origin form posts.
Endpoints¶
Credential-free requests must address a literal loopback IP or localhost at
the configured listener port. DNS aliases are rejected to prevent DNS rebinding.
For a reverse proxy or custom hostname, configure an API token. Browser requests
must have a same-authority Origin. Dashboard/API responses cannot be framed.
Result endpoints (/api/scan/results and /api/scan/{scan_id}) accept
?limit=100&offset=0 and return at most 100 agents plus
pagination: {total, offset, limit, next_offset}. Follow next_offset using the
returned scan ID to keep pages consistent. /api/agents accepts the same page
parameters and returns an array. History pages are also capped at 100 entries.
SSE events carry compact progress summaries; fetch result pages for full evidence.
The dashboard loads all result pages automatically.
| Endpoint | Method | Description |
|---|---|---|
GET / |
— | Web dashboard |
GET /api/health |
— | Health check |
POST /api/scan |
JSON body | Start a scan |
POST /api/scan/stop |
JSON {} |
Stop running scan |
GET /api/scan/status |
— | Current scan status |
GET /api/scan/results |
— | Latest scan results |
GET /api/scan/history |
?limit=&offset= |
Previous scan results |
GET /api/scan/{scan_id} |
— | Specific historical scan |
GET /api/scan/sarif |
— | Latest scan as SARIF 2.1.0 JSON |
GET /api/agents |
— | All detected agents |
POST /api/agentpin/verify |
JSON body | Verify a presented AgentPin credential |
POST /mcp |
MCP JSON-RPC | AgentPin verification MCP server |
GET /api/scan/stream |
SSE | Real-time scan streaming |
GET /api/settings |
— | Get alert settings |
PUT /api/settings |
JSON body | Update alert settings |
POST /api/settings/test |
JSON {} |
Send test alert |
GET /api/db/backup |
— | Download a SQLite snapshot or PostgreSQL JSON scan export |
POST /api/db/reset |
JSON {} |
Wipe scan history |
Start a scan with an explicit JSON request:
curl --request POST http://127.0.0.1:9090/api/scan \
--header "Authorization: Bearer ${AGENTSNIFF_API_TOKEN}" \
--header 'Content-Type: application/json' \
--data '{"network":"192.168.1.0/24","detectors":"port_scanner,endpoint_prober"}'
AgentPin verification API and MCP tool¶
Configure the server-side trust anchor, durable pins, and expected audience:
agentpin_trust_bundle_path: "/etc/agentsniff/agentpin-trust-bundle.json"
agentpin_pin_store_path: "~/.agentsniff/agentpin-pins.json"
agentpin_expected_audience: "verifier.example"
The server refuses to start if this configuration is incomplete or unreadable. The trust bundle must be distributed through an authenticated out-of-band channel. A caller cannot submit a trust bundle or audience alongside its credential.
REST clients send a JSON body to POST /api/agentpin/verify:
The endpoint returns the same machine-readable report as
agentsniff verify-agentpin. A rejected credential is a successful HTTP
exchange with "valid": false; configuration and storage failures return
503. Credentials are limited to 64 KiB, and the route uses the same
Basic/Bearer authentication policy as the rest of the management API.
MCP clients connect to POST /mcp. AgentSniff supports current stateless MCP
2026-07-28 (server/discover, tools/list, and tools/call) and stateless
legacy initialization for 2025-11-25 clients. The exposed tool is:
Current-protocol HTTP clients must send matching MCP-Protocol-Version,
Mcp-Method, and, for tools/call, Mcp-Name headers. Verification results
are returned in both structuredContent and text content for compatibility.
The API token can be sent as an Authorization: Bearer ... header.
SSE Streaming¶
The /api/scan/stream endpoint provides real-time scan updates via Server-Sent Events:
Event Types¶
The event tag is carried inside each JSON payload's event field (the SSE stream uses unnamed message events). Possible values:
event value |
Data | Description |
|---|---|---|
scan_started |
{scan_id, network, host_count, detectors} |
Scan has begun |
agent_detected |
{agent: <DetectedAgent>} |
New agent found or updated |
scan_progress |
{message} |
Free-form progress update |
scan_completed |
{scan_id, agent_count, summary} |
Scan finished |
scan_cancelled |
{scan_id, agent_count, summary} |
Partial scan stopped; never used as a baseline |
scan_error |
{message} |
Scan failed |
Example¶
const source = new EventSource('/api/scan/stream');
await fetch('/api/scan', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({network: '192.168.1.0/24'})
});
source.onmessage = (e) => {
const payload = JSON.parse(e.data);
switch (payload.event) {
case 'agent_detected':
console.log(`Found: ${payload.agent.framework} at ${payload.agent.ip_address}:${payload.agent.port}`);
break;
case 'scan_completed':
console.log(`Done: ${payload.agent_count} agents`);
source.close();
break;
}
};
Scan Result Schema¶
{
"scan_id": "a1b2c3d4",
"scope_id": "scope-0123456789abcdef0123456789abcdef",
"started_at": "ISO8601",
"completed_at": "ISO8601",
"target_network": "CIDR",
"agents": [],
"detectors_run": ["port_scanner", "endpoint_prober", "mcp_detector"],
"errors": [],
"diff": {
"baseline_scan_id": "prior123",
"new_assets": ["asset-0123"],
"changed_assets": [],
"unchanged_assets": ["asset-4567"],
"removed_assets": []
}
}
The completed SSE event and historical-scan response also include a summary with presence-confidence counts, trusted and invalid identity counts, and new/changed/removed totals.
Agent Schema¶
{
"id": "asset-0123456789abcdef0123456789abcdef",
"host": "hostname",
"ip_address": "192.168.1.50",
"port": 8000,
"transport": "tcp",
"agent_type": "framework",
"framework": "langchain",
"status": "detected",
"presence_status": "confirmed",
"identity_assurance": "unknown",
"risk_level": "unknown",
"change_status": "new",
"confidence_score": 0.95,
"confidence_level": "confirmed",
"signal_count": 4,
"signals": [...],
"agentpin_identity": null,
"mcp_capabilities": null,
"tls_fingerprint": null,
"first_seen": "ISO8601",
"last_seen": "ISO8601",
"metadata": {}
}
Asset IDs are deterministic for (IP, port, transport); host-level passive observations omit the port. presence_status is unknown, suspected, detected, or confirmed. identity_assurance is independently unknown, self_asserted, signed_untrusted, invalid, or trusted_verified. The compatibility status value verified is emitted only for trusted_verified identity assurance. change_status is new, changed, unchanged, or unclassified.
Detection Signal Schema¶
{
"detector": "endpoint_prober",
"signal_type": "framework_endpoint_match",
"description": "langchain endpoint active at http://192.168.1.50:8000/docs",
"confidence": "high",
"evidence": {
"host": "192.168.1.50",
"port": 8000,
"framework": "langchain",
"path": "/docs",
"url": "http://192.168.1.50:8000/docs"
},
"timestamp": "ISO8601"
}