Something went wrong. Try again.
declarative relay deployment on hetzner relay-eval.waow.tech
atproto relay
Something went wrong. Try again.
17 kB · 295 lines
HTML
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3Cfilter id='g'%3E%3CfeGaussianBlur stdDeviation='1.8'/%3E%3C/filter%3E%3ClinearGradient id='b' x1='0' y1='1' x2='1' y2='0'%3E%3Cstop offset='0%25' stop-color='%233fb950'/%3E%3Cstop offset='45%25' stop-color='%2358a6ff'/%3E%3Cstop offset='100%25' stop-color='%23bc8cff'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpolyline points='4,28 11,11 16,19 28,3' fill='none' stroke='url(%23b)' stroke-width='5' stroke-linecap='round' stroke-linejoin='round' opacity='0.2' filter='url(%23g)'/%3E%3Cpolyline points='4,28 11,11 16,19 28,3' fill='none' stroke='url(%23b)' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' opacity='0.8'/%3E%3Cpolyline points='4,28 11,11 16,19 28,3' fill='none' stroke='%23fff' stroke-width='1' stroke-linecap='round' stroke-linejoin='round' opacity='0.35'/%3E%3C/svg%3E"><title>missing repo API · relay-eval</title><meta property="og:title" content="missing repo API · relay-eval"><meta property="og:description" content="query missing-repo counts and retained details for an atproto relay"><meta property="og:type" content="website"><style> :root { --bg: #0d1117; --fg: #c9d1d9; --muted: #8b949e; --border: #21262d; --border-strong: #30363d; --surface: #161b22; --surface-soft: rgba(22, 27, 34, 0.42); --accent: #58a6ff; --green: #6fbf73; --red: #f85149; --yellow: #d29922; --violet: #bc8cff; --cyan: #39d2c0; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace; font-size: 13px; line-height: 1.6; background: var(--bg); color: var(--fg); -webkit-font-smoothing: antialiased; padding: 2rem 1.2rem 3rem; } body::before { content: ''; position: fixed; inset: 0; pointer-events: none; z-index: -1; background: linear-gradient(180deg, rgba(88, 166, 255, 0.045), transparent 18rem), linear-gradient(110deg, rgba(57, 210, 192, 0.045), transparent 38%), linear-gradient(290deg, rgba(188, 140, 255, 0.035), transparent 44%); } .wrap { max-width: 1040px; margin: 0 auto; } header { display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem; flex-wrap: wrap; margin-bottom: 0.65rem; } h1 { font-size: 1.35rem; font-weight: 650; letter-spacing: 0; } h2 { font-size: 0.88rem; font-weight: 650; margin-bottom: 0.35rem; } h3 { font-size: 0.78rem; font-weight: 600; margin-bottom: 0.25rem; color: var(--fg); } p { color: var(--muted); max-width: 78ch; margin: 0.25rem 0 0.75rem; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } code { color: var(--fg); } .back { color: var(--muted); font-size: 0.76rem; } .back a { color: var(--muted); border-bottom: 1px dotted var(--muted); } .back a:hover { color: var(--fg); border-color: var(--fg); } .subtitle { color: var(--muted); font-size: 0.86rem; margin-bottom: 1.1rem; } .hero { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(17rem, 0.7fr); gap: 0.8rem; align-items: stretch; margin-bottom: 1rem; } .hero > *, .grid > *, .split > * { min-width: 0; } .hero-main, .quick-card, .panel, .path-card { background: linear-gradient(180deg, rgba(22, 27, 34, 0.62), rgba(13, 17, 23, 0.38)); border: 1px solid rgba(139, 148, 158, 0.13); border-radius: 8px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 16px 36px rgba(0, 0, 0, 0.16); backdrop-filter: blur(18px) saturate(1.2); -webkit-backdrop-filter: blur(18px) saturate(1.2); } .hero-main { padding: 1rem 1.05rem; } .hero-main p:last-child { margin-bottom: 0; } .quick-card { padding: 0.85rem 0.9rem; } .quick-card .endpoint { margin-bottom: 0.55rem; } .chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.65rem; } .chip { display: inline-flex; align-items: center; gap: 0.32rem; padding: 0.22rem 0.43rem; border: 1px solid rgba(139, 148, 158, 0.13); border-radius: 999px; background: rgba(13, 17, 23, 0.34); color: var(--muted); font-size: 0.68rem; } .dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex: 0 0 auto; } .red { color: var(--red); } .yellow { color: var(--yellow); } .green { color: var(--green); } .cyan { color: var(--cyan); } .violet { color: var(--violet); } .muted { color: var(--muted); } .grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.7rem; margin: 0.9rem 0; } .path-card { padding: 0.8rem; --tone: var(--accent); } .path-card.live { --tone: var(--red); } .path-card.full { --tone: var(--accent); } .path-card.audit { --tone: var(--yellow); } .path-card h3 { display: flex; align-items: center; gap: 0.42rem; } .path-card h3::before { content: ''; width: 0.42rem; height: 0.42rem; border-radius: 50%; background: var(--tone); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tone) 13%, transparent); flex: 0 0 auto; } .path-card p { font-size: 0.74rem; margin-bottom: 0.55rem; } .panel { padding: 0.9rem 1rem; margin: 0.8rem 0; } .endpoint { color: var(--fg); font-size: 0.76rem; overflow-wrap: anywhere; word-break: break-word; background: rgba(13, 17, 23, 0.48); border: 1px solid rgba(139, 148, 158, 0.12); border-radius: 6px; padding: 0.42rem 0.58rem; margin: 0.45rem 0; } pre { overflow-x: auto; white-space: pre; tab-size: 2; color: var(--fg); background: rgba(13, 17, 23, 0.62); border: 1px solid rgba(139, 148, 158, 0.12); border-radius: 6px; padding: 0.66rem 0.75rem; margin: 0.5rem 0 0.8rem; font: 0.72rem/1.55 'SF Mono', 'Cascadia Code', 'Fira Code', monospace; } .callout { border: 1px solid rgba(210, 153, 34, 0.16); background: rgba(210, 153, 34, 0.055); padding: 0.55rem 0.75rem; margin: 0.55rem 0 0.35rem; color: var(--muted); border-radius: 6px; } .callout strong { color: var(--yellow); font-weight: 600; } table { width: 100%; border-collapse: collapse; margin-top: 0.45rem; } th { text-align: left; color: var(--muted); font-weight: 500; text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.67rem; border-bottom: 1px solid var(--border-strong); padding: 0.42rem 0.5rem; } td { border-bottom: 1px solid var(--border); padding: 0.46rem 0.5rem; color: var(--muted); vertical-align: top; } td:first-child { color: var(--fg); white-space: nowrap; } .pill { display: inline-flex; align-items: center; gap: 0.28rem; border: 1px solid rgba(139, 148, 158, 0.13); border-radius: 999px; padding: 0.1rem 0.42rem; color: var(--muted); background: rgba(13, 17, 23, 0.34); font-size: 0.66rem; } .split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(17rem, 0.8fr); gap: 0.8rem; align-items: start; } .mini-list { display: grid; gap: 0.5rem; margin-top: 0.45rem; } .mini-item { display: grid; grid-template-columns: 5.5rem 1fr; gap: 0.6rem; padding: 0.45rem 0; border-bottom: 1px solid var(--border); } .mini-item:last-child { border-bottom: 0; } .mini-item strong { font-weight: 600; color: var(--fg); font-size: 0.72rem; } .mini-item span { color: var(--muted); font-size: 0.72rem; } .refs { font-size: 0.7rem; color: var(--muted); margin-top: 1.2rem; } .refs a { color: var(--muted); border-bottom: 1px dotted var(--muted); } .refs a:hover { color: var(--accent); border-color: var(--accent); } @media (max-width: 820px) { .hero, .split, .grid { grid-template-columns: 1fr; } } @media (max-width: 600px) { body { padding: 1.1rem 0.85rem 2rem; font-size: 14px; } h1 { font-size: 1.12rem; } .panel, .hero-main, .quick-card, .path-card { padding: 0.75rem; } table { display: block; overflow-x: auto; } .mini-item { grid-template-columns: 1fr; gap: 0.1rem; } }</style></head><body><div class="wrap"><header> <h1>missing repo API</h1> <span class="back"><a href="/">back to relay-eval</a></span></header>
<p class="subtitle">Query the repos a relay missed during a relay-eval measurement window.</p>
<section class="hero"> <div class="hero-main"> <h2>What This API Means</h2> <p>A missing repo is first a <strong>stream comparison fact</strong>: relay-eval saw repository activity for a DID on at least one observed synchronization stream during a run, but did not see that DID on the relay being inspected.</p> <p>Resolver checks, PDS hosts, and repo status are supporting evidence. They help explain the gap, but they are not the denominator.</p> <div class="chips"> <span class="chip"><span class="dot" style="background:var(--red)"></span> stream gap</span> <span class="chip"><span class="dot" style="background:var(--yellow)"></span> unverified</span> <span class="chip"><span class="dot" style="background:var(--muted)"></span> inactive evidence</span> </div> </div> <div class="quick-card"> <h2>Fastest Useful Query</h2> <p>Use this when you want the most recent retained detail rows for one relay.</p> <div class="endpoint">GET /api/latest/diffs?relay=<relay-host>&limit=5000</div> <pre><code>curl -fsS 'https://relay-eval.waow.tech/api/latest/diffs?relay=relay.fire.hose.cam&limit=5000' | jq</code></pre> </div></section>
<section class="grid" aria-label="query paths"> <div class="path-card full"> <h3>Gap Counts</h3> <p>Complete missing-row counts for the selected run. Unchecked rows are aggregate counts, not all concrete DID rows.</p> <div class="endpoint">/api/runs/:id/diffs/summary?relay=...</div> </div> <div class="path-card live"> <h3>Likely Stream Debt</h3> <p>Only checked rows where the repo looks active elsewhere and absent from this relay.</p> <div class="endpoint">/api/latest/diffs?relay=...&lane=live</div> </div> <div class="path-card audit"> <h3>Audit The Unknowns</h3> <p>Inspect resolver-budget leftovers, resolver failures, malformed DIDs, and inactive evidence.</p> <div class="endpoint">lane=unchecked | review | inactive</div> </div></section>
<section class="panel"> <h2>Start With A Stable Snapshot</h2> <p>The summary endpoint gives complete counts, facets, lower-bound estimates, and the concrete <code>run_id</code>. Use that <code>run_id</code> for pagination so every detail page comes from the same measurement run.</p> <div class="endpoint">GET /api/latest/diffs/summary?relay=<relay-host></div> <pre><code>curl -fsS 'https://relay-eval.waow.tech/api/latest/diffs/summary?relay=relay.fire.hose.cam' \ | jq '{run_id,total,checked,unchecked,estimate,classes:.facets.classifications}'</code></pre> <div class="callout"><strong>Lower bounds:</strong> when <code>estimate.checked_limited</code> is true, <code>live_gap_display</code> includes <code>+</code>. Treat that as "at least this many checked live gaps; unchecked rows remain."</div></section>
<section class="panel"> <h2>Pull Retained Detail</h2> <p>This emits tab-separated <code>DID</code>, <code>classification</code>, and optional <code>PDS</code> columns for concrete rows retained in the selected run. Use the summary endpoint for complete unchecked counts.</p> <pre><code>relay='relay.fire.hose.cam'run_id="$(curl -fsS "https://relay-eval.waow.tech/api/latest/diffs/summary?relay=${relay}" | jq -r '.run_id')"cursor=''
while true; do url="https://relay-eval.waow.tech/api/runs/${run_id}/diffs?relay=${relay}&limit=5000" [ -n "$cursor" ] && url="${url}&cursor=${cursor}"
page="$(curl -fsS "$url")" echo "$page" | jq -r '.diffs[] | [.did, .classification, (.pds // "")] | @tsv'
cursor="$(echo "$page" | jq -r '.next_cursor // empty')" [ -z "$cursor" ] && breakdone</code></pre> <p><code>next_cursor</code> is a resume token. It is not a page number; pass it back as <code>cursor</code> to continue after the last emitted row.</p></section>
<section class="split"> <div class="panel"> <h2>Parameters</h2> <table> <thead><tr><th>parameter</th><th>meaning</th></tr></thead> <tbody> <tr><td><code>relay</code></td><td>Relay host to inspect, for example <code>relay.fire.hose.cam</code>.</td></tr> <tr><td><code>limit</code></td><td>Rows per response. Defaults to 1000 and is capped at 5000.</td></tr> <tr><td><code>cursor</code></td><td>Resume token from the previous response's <code>next_cursor</code>.</td></tr> <tr><td><code>lane</code></td><td>Semantic bucket: <code>live</code>, <code>unchecked</code>, <code>review</code>, <code>inactive</code>, or <code>checked</code>.</td></tr> <tr><td><code>classification</code></td><td>Exact stored classifier label such as <code>active_missing</code>.</td></tr> <tr><td><code>q</code></td><td>Substring search over the DID.</td></tr> <tr><td><code>pds_host</code></td><td>Filter by resolved PDS host, with or without <code>https://</code>.</td></tr> </tbody> </table> </div> <div class="panel"> <h2>Useful Examples</h2> <div class="mini-list"> <div class="mini-item"><strong class="red">live only</strong><span><code>/api/latest/diffs?relay=relay.fire.hose.cam&lane=live&limit=5000</code></span></div> <div class="mini-item"><strong class="yellow">unchecked</strong><span><code>/api/latest/diffs?relay=relay.fire.hose.cam&lane=unchecked</code></span></div> <div class="mini-item"><strong class="cyan">DID search</strong><span><code>/api/latest/diffs?relay=relay.fire.hose.cam&q=76eyp</code></span></div> <div class="mini-item"><strong class="violet">PDS host</strong><span><code>/api/latest/diffs?relay=relay.fire.hose.cam&pds_host=bracket.us-west.host.bsky.network</code></span></div> </div> </div></section>
<section class="panel"> <h2>Lanes And Classifications</h2> <table> <thead><tr><th>lane</th><th>labels</th><th>how to read it</th></tr></thead> <tbody> <tr><td><span class="pill"><span class="dot" style="background:var(--red)"></span> <code>live</code></span></td><td><code>active_missing</code>, legacy <code>coverage_gap</code></td><td>Checked lower-bound stream gaps. This is the strongest signal for relay coverage debt.</td></tr> <tr><td><span class="pill"><span class="dot" style="background:var(--yellow)"></span> <code>unchecked</code></span></td><td><code>classification_not_attempted</code></td><td>Resolver budget was not spent on this row. It belongs in the full gap set, but not in the live lower bound.</td></tr> <tr><td><span class="pill"><span class="dot" style="background:var(--yellow)"></span> <code>review</code></span></td><td><code>malformed_did</code>, <code>unsupported_did_method</code>, <code>did_resolution_failed</code>, <code>invalid_did_document</code>, <code>unresolvable</code></td><td>Needs operator interpretation. Often resolver/network or DID-document ambiguity.</td></tr> <tr><td><span class="pill"><span class="dot" style="background:var(--muted)"></span> <code>inactive</code></span></td><td><code>no_pds_endpoint</code>, <code>deactivated</code></td><td>Current hosting evidence does not support treating this as live stream debt.</td></tr> <tr><td><span class="pill"><span class="dot" style="background:var(--green)"></span> <code>checked</code></span></td><td>anything except <code>classification_not_attempted</code></td><td>Rows where relay-eval spent classification effort.</td></tr> </tbody> </table></section>
<section class="panel"> <h2>Response Shape</h2> <pre><code>{ "run_id": 4208, "relay": "relay.fire.hose.cam", "limit": 5000, "diffs": [ { "cursor": 527654185, "relay": "relay.fire.hose.cam", "did": "did:plc:...", "classification": "active_missing", "classification_version": 2, "pds": "https://bracket.us-west.host.bsky.network" } ], "next_cursor": 527659184}</code></pre> <p>Use <code>/api/latest/diffs</code> for convenience. Use <code>/api/runs/:run_id/diffs</code> when paging, exporting, or comparing results.</p></section>
<p class="refs"> Semantics are aligned with relay-eval's stream-comparison model and ATP sync concepts: <a href="https://atproto.com/specs/sync">atproto sync</a>, <a href="https://atproto.com/specs/repository">repository</a>, <a href="https://www.ietf.org/archive/id/draft-holmgren-at-synchronization-00.html">IETF sync draft</a>, <a href="https://www.ietf.org/archive/id/draft-holmgren-at-repository-02.html">IETF repository draft</a>.</p>
</div></body></html>