commits
- server-rendered stats page: actor counts, sparkline (7d searches/hour),
avg latency, handle/avatar coverage with CSS tooltips
- metrics table + fire-and-forget hourly recording via ctx.waitUntil
- move handle != '' filter into SQL WHERE (before LIMIT) so results
aren't short-changed by empty-handle rows consuming limit slots
- smoke test for /stats endpoint
- stats link in homepage footer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- bind empty string instead of null for missing handles (fixes batch failures)
- use COALESCE(NULLIF(...)) to preserve existing handles on partial updates
- filter empty-handle actors from search results
- reject limit <= 0, NaN, and non-numeric values with 400
- restore SLINGSHOT_URL constant for /request-indexing
- trim readme to match project style
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
unhandled KV quota errors (free tier: 1000 writes/day) were causing
the entire /admin/ingest endpoint to return 500, which made it look
like D1 was rejecting writes. the actual ingest was never reached.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
500 upserts with FTS5 triggers = ~1500 write ops per batch, which
trips D1's write rate limit (error 1101). 100 is well within limits.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the previous retry logic accumulated all events into a single growing
buffer and re-sent the entire thing on every incoming event. D1 rejects
large batches (1101), so this created a death spiral: bigger batch →
rejection → buffer grows → even bigger batch.
now flush sends at most MAX_BATCH (500) items per attempt, shifts
remaining items forward, and waits 5 seconds before retrying after
a failure. during catch-up this drains the backlog incrementally
instead of choking.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ingester: retain failed buffers for retry instead of dropping them.
cursor only advances after both ingest and delete batches succeed.
backlog overflow cap (5000 events) prevents unbounded memory growth
when D1 is persistently unavailable.
cursor fetch retries 3 times with backoff (1s, 3s, 10s) before
falling back to live.
smoke tests: empty query now expects 400 (not 200), added test for
limit>100 returning 400.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
documents the response shape gap (missing labels, associated, createdAt)
and lack of moderation filtering as explicit known limitations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the ingester now fetches the last cursor from the worker on startup
and passes it to jetstream, so restarts resume where they left off
instead of starting from live.
removed synchronous slingshot call from profile commit handling —
identity events already carry handles, and the backfill/request-indexing
paths cover any gaps. this eliminates an external dependency from the
hot path and unblocks processing during slingshot downtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- schema.sql: avatar_cid → avatar_url to match production
- sanitize(): use unicode-aware \p{L}\p{N} instead of ASCII-only \w
- return 400 for empty query and limit>100 to match Bluesky's behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
KV key "backfill" = "off" disables without redeploy. global rate
limit (10/min) caps total backfill writes regardless of user count.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the /request-indexing endpoint interpolated user input and slingshot
response data directly into html. added escHtml() for entity encoding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cloudflare worker + zig ingester for ATProto actor search.
includes rate limiting, admin auth, bluesky backfill, and smoke tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- server-rendered stats page: actor counts, sparkline (7d searches/hour),
avg latency, handle/avatar coverage with CSS tooltips
- metrics table + fire-and-forget hourly recording via ctx.waitUntil
- move handle != '' filter into SQL WHERE (before LIMIT) so results
aren't short-changed by empty-handle rows consuming limit slots
- smoke test for /stats endpoint
- stats link in homepage footer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- bind empty string instead of null for missing handles (fixes batch failures)
- use COALESCE(NULLIF(...)) to preserve existing handles on partial updates
- filter empty-handle actors from search results
- reject limit <= 0, NaN, and non-numeric values with 400
- restore SLINGSHOT_URL constant for /request-indexing
- trim readme to match project style
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the previous retry logic accumulated all events into a single growing
buffer and re-sent the entire thing on every incoming event. D1 rejects
large batches (1101), so this created a death spiral: bigger batch →
rejection → buffer grows → even bigger batch.
now flush sends at most MAX_BATCH (500) items per attempt, shifts
remaining items forward, and waits 5 seconds before retrying after
a failure. during catch-up this drains the backlog incrementally
instead of choking.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ingester: retain failed buffers for retry instead of dropping them.
cursor only advances after both ingest and delete batches succeed.
backlog overflow cap (5000 events) prevents unbounded memory growth
when D1 is persistently unavailable.
cursor fetch retries 3 times with backoff (1s, 3s, 10s) before
falling back to live.
smoke tests: empty query now expects 400 (not 200), added test for
limit>100 returning 400.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the ingester now fetches the last cursor from the worker on startup
and passes it to jetstream, so restarts resume where they left off
instead of starting from live.
removed synchronous slingshot call from profile commit handling —
identity events already carry handles, and the backfill/request-indexing
paths cover any gaps. this eliminates an external dependency from the
hot path and unblocks processing during slingshot downtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>