polls on atproto pollz.waow.tech
README.md

Recovering the public AppView index #

Before replacing the existing AppView, export its public rows and discovery identities from a consistent SQLite snapshot or directly from the source DB:

uv run scripts/bootstrap.py export \
  --source /path/to/old-pollz.db --output /safe/public-seed.json

The exporter opens SQLite read-only and selects only the public polls, votes, and profiles columns in one read transaction. It never selects authentication tables. The immutable seed retains every indexed public row, including records subsequently confirmed absent from a PDS. Its actor set includes poll authors, voters, poll-subject authors, and cached profile DIDs. Invalid historical references are preserved and reported as discovery warnings. Existing output files are refused.

Hydrate both collections from the current PDS of each known actor into a new public-only database:

uv run scripts/bootstrap.py rebuild \
  --seed /safe/public-seed.json --database /safe/pollz-public.db \
  --report /safe/pollz-recovery.json

DID resolution verifies the DID document identity and its PDS service. PDS reads use unauthenticated com.atproto.repo.listRecords with pagination. Votes can identify additional poll authors, whose repositories are also scanned. No PDS mutation endpoints are called.

A complete successful collection enumeration can confirm a seeded URI absent; that URI is removed from the rebuilt current index and listed in the report. The historical seed remains unchanged. HTTP 404, 429, 5xx, timeouts, malformed responses, repeated pagination cursors, and identity mismatches are failures, not evidence of deletion. A collection is applied only after every page has arrived successfully; failures retain its seeded data. Invalid current records are reported without pretending they were deleted. Exit status 2 means an incomplete scan; do not serve that database as a completed rebuild.

The report's complete covers only the discovered actors and collection scan. It does not establish global discovery, historical event coverage, or a safe stream cutover. The current backend does not persist a stream cursor. Before serving a rebuilt database, implement and replay a durable stream overlap from seedExportedAt through the rebuild and join live ingestion without a gap. Forward-only Jetstream cannot discover dormant historical actors or recover writes made during bootstrap.

This is a projection of currently available records, using the existing latest-createdAt vote rule. Older vote records can remain on a PDS after a newer one was deleted. Rebuilding can therefore revive the older remaining vote; exact past delete-event behavior needs an event archive or tombstones that the old index did not retain. The seed preserves the previous indexed result for comparison. Actors never captured in the old index or profiles are outside the seed's discovery coverage.

Use a fresh DATA_PATH for the new AppView. Its startup deliberately refuses legacy OAuth/session tables. Retain the old database securely until the public export and recovery checks are verified; it may still contain credentials. The bootstrap tool neither deletes nor rotates those credentials.

Run the offline tests:

uv run python -m unittest discover -s scripts -p 'test_*.py' -v

They use real SQLite databases and an actual local HTTP record provider. --pds-map accepts a JSON mapping of DIDs to PDS origins for controlled recovery or testing. --allow-http-loopback permits HTTP only for a local test provider; production resolution requires HTTPS.

Protocol references: listRecords lexicon, DID service discovery.