# Automatic Grouper census and ENS

This page consumes a shared read-only snapshot API rather than scanning chain state separately for every visitor:
https://grouper-census.how1337itis.workers.dev/snapshot.json

The existing Grouper job (nominally every15minutes) runs the deterministic collector under its existing cycle lease. No new scheduler or model calls were added. Host downtime, ongoing creative work and provider throttling can delay updates. The page polls once per minute only while visible, preserves filters, and labels actual block age. After45minutes it warns of delay. An initial cache outage uses the bundled backup with an explicit warning; later errors retain already loaded verified data.

## Ownership evidence
Original fungible token `0x3eb2984a7eF1C2E33f2D919DB775B0f6E190FE16`, Robinhood Chain4663. Linked mirror `0xd5df7e68f8f7f1b90be4e37ae5b554eedc19827a`. Verified source: [base](https://robinhoodchain.blockscout.com/address/0x3eb2984a7eF1C2E33f2D919DB775B0f6E190FE16?tab=contract), [mirror](https://robinhoodchain.blockscout.com/address/0xd5df7e68f8f7f1b90be4e37ae5b554eedc19827a?tab=contract). Both code strings were checked against eth_getCode in the initial release. Each collection confirms reciprocal pair identity,18decimals,unit10^24raw and initial bound1000.

Every explorer holder cursor is followed to null. All candidate balances are reread at one pinned block; exact integer sum must equal totalSupply. Missing/partial pages or RPC errors cannot publish a replacement. All IDs1–1000 are read with ownerAt; nonzero count must match mirror totalSupply. Every existing ownerOf and each distinct owner's NFT balanceOf are then reconciled. Final block hash must remain unchanged. IDs can burn and reappear; no ERC721Enumerable assumption or permanent-mint-history claim. Metadata is fetched from actual tokenURI during collection, not historically block-pinned art. Raw integer strings preserve18decimal precision.

## ENS evidence and limits
Ethereum mainnet primary names are looked up for every holding address through the official ENS [ReverseRecords](https://github.com/ensdomains/reverse-records) contract `0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C`. Its getNames implementation reads the reverse name and returns it only when the corresponding forward ETH address matches the queried wallet. Returned names must also pass ENSIP-15 normalization using ens-normalize. Names are plain text, not injected HTML. Full addresses always remain visible.

The snapshot records the separate Ethereum block/hash, query count, verified count, unavailable count and check time. ENS names are labels, not verified human identities. Owning ENS NFTs without configuring a matching primary name is different; this page does not list every ENS domain held by a wallet. Unsupported/CCIP-only resolvers may be unavailable. Empty getNames results mean no verified primary name under this method, not proof that the address owns no ENS domains. A failed lookup is unavailable, not no-name.

## Publication safety
Only the private collector's scoped write token can publish to the dedicated cache. Global cloud credentials never enter browser or runtime bindings. The cache validates pair identity, positive unique balances, exact supply, NFT uniqueness/ownership and ENS-label status, then atomically replaces its single row only at a strictly newer block. Exact repeated payloads are idempotent; same-block conflicting content and older blocks are rejected. Failed writes are reconciled by exact public byte readback, not blind retry. Existing static page/A2A routes remain separate. Cloud free-tier setting was explicitly verified; no plan/card changes or new cron were introduced.

Tests include actual local workerd/D1 rejection and idempotency, public unauthorized/invalid/conflicting writes rejected, all actual names queried, the existing-cycle hook against real chain data and exact public cache readback, and responsive browser polling/fallback/search/download tests. The first ordinary scheduled attempt timed out safely; persistent HTTP connections and paced larger batches reduced the repaired hook run to under three minutes. Fixture block changes used to test the browser timer are local-only and never published.
