A dataroom for US uranium exploration. It ingests public geoscience data with full provenance, pulls structured facts out of technical documents with an LLM agent, normalizes everything to a single coordinate system, ranks prospects, and serves the result. Live at uranium.shawnz.info.
The organizing constraint is traceability: a value that cannot be traced to a primary artifact is worthless here. Most of the engineering is guardrails, and that is the point.

The problem
Uranium exploration data is public but hostile. It lives in 1970s government reconnaissance surveys, scanned drill logs, state geological survey archives, and SEC filings. It arrives in incompatible projections, with inconsistent units, and with measurement methods that look interchangeable and are not.
The failure mode is not missing data. It is confidently wrong data — a radiometric reading quietly treated as a chemical assay, an inferred resource averaged with a measured one, a mining claim read as evidence of a deposit. An exploration dataroom that is 95% right is not 95% useful.
The flow
1. Acquire
Every source has a connector, and every connector funnels through one choke point. Nothing reaches the raw layer any other way. Each landing atomically writes the payload, a manifest (source URL, retrieval timestamp, SHA-256, HTTP headers, license, dataset version, run ID), and a catalog row.
Access is official channels only: public downloads and documented APIs with our own keys. No CAPTCHA solving, no login walls, no terms-of-service violations. A gated source is recorded as inaccessible rather than worked around — which is itself a finding worth publishing.
Currently 25,100 catalogued artifacts across 43 sources.
2. Normalize
Everything reprojects to EPSG:5070 (CONUS Albers), with the original coordinate system recorded before the transform. Output is GeoParquet, validated by schema contracts.
Grades and geochemistry always carry method, units, and category — radiometric equivalent uranium versus chemical assay versus prompt fission neutron; ppm versus percent U₃O₈; measured/indicated/inferred. Never a bare number, never merged across methods without a flag. Radioactive disequilibrium means equivalent uranium is not chemical uranium, in either direction.

3. Extract
An LLM agent reads technical documents and proposes structured facts. The proposals are not trusted. Each one passes three gates:
- Schema validation — shape and types.
- Snippet containment — the extracted value must appear literally in the verbatim source snippet the agent cited. This is enforced by deterministic code, not by asking the model nicely. It is the single most load-bearing check in the system.
- Plausibility — range and unit sanity.
Only what survives all three becomes a fact. Everything else goes to a human review queue — never silently into the fact table, never silently dropped. Every promoted fact links back to the primary host it came from, not to our copy.
Currently 10,234 promoted facts across 30,463 documents, with 7,264 items queued for review.
4. Rank
A weighted multi-criteria score per grid cell. Not a black box: each cell carries a per-layer breakdown of its normalized value and the points that layer contributed, which reconstruct the total exactly. Beside it sits a confidence figure, explicitly labelled as a data-coverage heuristic and not a probability.
The caveats travel with the number. Normalization is relative to the area of interest, not absolute. Reconnaissance-era anomalies are scored relative to their own survey, never against national thresholds. Claim activity is treated as lagging and noisy.

5. Serve
A Cloudflare Worker is the only read boundary — no database or object-store credential ever reaches the browser. It fronts Postgres for serving vectors and the provenance spine, and object storage for parquet, cloud-optimized GeoTIFFs, and vector tiles. Bulk analytics run in DuckDB reading parquet directly off object storage; the transactional database deliberately stays small.
The interface is a basin workspace: 22 evidence layers in five groups, each with its own state, count, and limitations rendered next to its toggle. Four of the layers are mutually exclusive scientific surfaces — turning one on turns the others off, because stacking four choropleths is not a reading of anything.


6. Prove it still works
An orchestrator runs the assets with asset checks, freshness policies, and schema validation. A golden-set evaluation harness gates every change to the agent’s prompt, tools, or guardrails — a regression against the baseline blocks the change. Data inventory and pipeline health publish as snapshots rendered on public pages, so the observability is visible rather than buried in a log.
1,183 tests. 29 sources normalized, 9 fully processed, 102 identified but requiring a human download, 6 recorded as inaccessible.
Terrain
Evidence drapes over USGS 3DEP elevation, served as a content-addressed terrain tile pyramid. The view carries its own label — vertical exaggeration is stated on the map rather than left for the viewer to assume.

What I would defend
The containment check. It is the cheapest possible guardrail and it eliminates the entire class of plausible-sounding fabrication that makes LLM extraction unusable for diligence. A value either appears in the cited text or it does not exist.
Labelling over dropping. Unverified data is marked, not deleted. Conflicting production and resource figures are reported side by side with their vintage and definition rather than averaged into a single wrong number. Null plus a note beats a guess.
Saying what the software cannot do. Every layer renders its own limitations. The screening score says on its face that it is a review priority and not a resource estimate. Confidence is labelled a coverage heuristic. This costs surface polish and buys the only thing that matters in a dataroom, which is that a reader can tell what they are looking at.
Cost
It runs at zero dollars a month. Object-store egress is free by design, and the compute sits in free tiers. The bulk raster — hundreds of gigabytes of aerial imagery and elevation — was evicted from hot storage once the derived products were built, keeping every provenance manifest. Because each manifest retains the source URL and SHA-256, any evicted artifact is re-fetchable from its primary source and verifiable byte-for-byte against the recorded hash. The catalog records the eviction explicitly; the dataroom does not claim to hold bytes it deleted.