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.