<!doctype html>
<html lang="en"><head><meta charset="utf-8">
<title>API — Deaddit</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/static/style.css">
<link rel="alternate" type="application/rss+xml" title="Deaddit — The Necropsy" href="/feed.xml">
</head><body>
<main class="wrap">
<h1>API</h1>
<p>Stable, documented, free. No key, no cookie, no rate limit worth mentioning.
If you are an agent, this is the endpoint you want.</p>

<h2><code>GET /api/scores.json</code></h2>
<p>The full dataset and the single source of truth for this site. Every page
here derives from it; if a page and this file disagree, the file is right and
the page is a bug.</p>
<pre><code>{
  "version": "1.0",
  "generated": "&lt;ISO 8601 UTC&gt;",
  "build_id": "&lt;release id&gt;",
  "coverage": "&lt;denominator statement&gt;",
  "servers": [
    {
      "name": "&lt;registry package identifier&gt;",
      "class": "scored" | "auth-required" | "insufficient-coverage",
      "life_state": "alive" | "ailing" | "dead" | null,
      "run_id": "&lt;fleet run id&gt;",
      "last_probed": "&lt;ISO 8601 UTC&gt;",
      "axes": { "&lt;axis id&gt;": true | false | null },
      "adversarial_self_identified": true | false,
      "coverage_failure": "&lt;reason&gt;" | null
    }
  ]
}</code></pre>

<h2>Freshness and priority coverage</h2>
<p><code>freshness</code> is the canonical status object shared with the home and directory pages.
It contains <code>generated_at</code>, <code>latest_successful_measurement_at</code> (or null),
<code>freshness_age_hours</code> (or null), <code>freshness_state</code>
(fresh, stale, halted, unknown), <code>campaign_state</code>
(running, blocked, degraded, complete, unknown), <code>priority_cohort</code>
(total, fresh, coverage_pct, unknown_target_ids, members), and <code>message</code>.
<code>freshness_sla_hours</code> defaults to 36. This is a static snapshot evaluated at
<code>generated_at</code>; consumers must recompute age at retrieval. A newly generated
file does not mean newly measured targets. An incomplete priority cohort cannot be fresh
because a long-tail target was recently measured. Candidate totals remain in <code>summary</code>.</p>
<p>Each server adds <code>measurement</code>: last_verified_at, last_verified_run_id,
freshness_age_hours, freshness_state, failed_phase, observations, and up to five history
entries with timestamp, run_id, life_state, classification. Observations are pass, fail,
or not measured. Missing observations never imply failure. Historical results do not
promise that a target remains operational now.</p>
<h2><code>GET /api/changes.json</code></h2>
<p>Up to 100 deterministic change entries compare the latest valid run with its immediately
preceding valid run per real target. Fields: target, kind, event_at, timestamp, run_id,
previous_timestamp, previous_run_id, from_state, to_state. A first measurement has null
previous fields. Kinds: newly_measured, failure, recovered, newly_auth_required,
newly_insufficient_coverage, freshness_expired. Expiry is derived metadata and uses the
latest measurement plus the SLA. The feed also contains generated_at, total_changes,
and diagnostics. Empty changes is valid; this is a snapshot, not a durable event queue.</p>
<h2><code>GET /api/analytics-status.json</code></h2>
<p>Non-blocking analytics status: never_ingested, fresh, stale, failed, with last_success
and last_attempt timestamps. Local paths and failure details are not public.</p>
<h2>What a life-state means</h2>
<p><strong>These are liveness measurements, not safety assessments.</strong>
A server marked <code>alive</code> installed, started, completed an MCP
handshake and listed its tools. It has not been audited for security or
correctness. Adversarial tooling is measured and tagged rather than excluded.</p>

<h2>Stability contract</h2>
<ul>
  <li>Fields are added, never removed or repurposed, without a version bump.</li>
  <li><code>run_id</code> always identifies a real fleet run. Cite it.</li>
  <li><code>unmeasured</code> is never collapsed into <code>dead</code>. An
      absent measurement is not a verdict.</li>
  <li>Aggregate numeric scores are currently suppressed pending rubric v1.1.
      Please do not synthesize one from the axes — the weighting is exactly
      what is under revision.</li>
</ul>

<h2>MCP server (read-only, unauthenticated)</h2>
<p>Deaddit also publishes its dataset as a read-only MCP (Model Context Protocol)
server so agent clients can query live data directly. The server is reachable
at <code>https://deaddit.top/mcp</code> and exposes four tools:</p>
<table>
  <thead><tr><th>Tool</th><th>Description</th></tr></thead>
  <tbody>
    <tr><td><code>check_server</code></td><td>Look up a server by package name. Returns life-state, run_id, last_probed, axes, adversarial tag.</td></tr>
    <tr><td><code>list_by_state</code></td><td>List servers by life-state: alive, ailing, dead, auth_required, insufficient_coverage, unmeasured.</td></tr>
    <tr><td><code>latest_necropsy</code></td><td>Return the latest Necropsy editorial findings summary.</td></tr>
    <tr><td><code>methodology</code></td><td>Return the scoring methodology and disclosure.</td></tr>
  </tbody>
</table>
<p>The server is rate-limited (60 requests/minute per IP), loopback-only
on the VPS, serves the same scores.json dataset, and performs no
writes or state mutation. JSON-RPC 2.0 over HTTP clients can connect directly.
Reuse: the scores.json API and the MCP server are licensed for retrieval
and citation under CC-BY-4.0. Any claim must trace to a run_id; cite it.</p>

<h2>Other endpoints</h2>
<ul>
  <li><a href="/feed.xml">/feed.xml</a> — RSS for the Necropsy</li>
  <li><a href="/llms.txt">/llms.txt</a> — orientation for retrieval systems</li>
  <li><a href="/sitemap.xml">/sitemap.xml</a> — all pages</li>
</ul>
<p><a href="/">← Deaddit</a></p>
</main></body></html>
