← Statpro dev log

2026-08-14

A fail-closed gate for taking Ask public

infrawebproductsolo-founder

I've been getting Statpro's Ask feature ready to go fully public, somewhere around 10,000 indexed pages across MLB, NBA, and NFL. The hard part was never the volume. It was making sure nothing could ship to that public surface unless it had actually passed the full release evidence.

The problem: a rollout with no off switch

The earlier Ask shadow release ran in production but stayed invisible. Flipping it public meant the same stack now serves real readers, and there was no clean way to say "this exact build, this exact schema, this exact contract is what's allowed to be public." If a file changed under me between the audit and the cutover, the public stack could synthesize against a different reality than the one I'd verified.

So I built a signed cutover receipt. The deployment can't synthesize either public stack unless a canonical JSON file exists, carries a valid Ed25519 signature from an offline launch key, and names the exact SHA-256 of the current Ask application contract. That contract covers the web Ask routes, discovery, sitemaps, the evaluator and publisher, migration 0029, the Docker runtime, and the CDK gate itself. Touch any covered production file and the receipt is invalid; you re-sign or you don't ship.

What the receipt demands before it signs

The signer won't sign unless the evidence proves the release is actually ready, not just that someone asked:

  • Indexed inventory: at least 10,000 current indexed canonical publications across the three leagues, with no stale or overdue rows and no collisions.
  • Stratified review: a passing 100-question stratified sample, plus passing route, SEO, and sitemap contracts.
  • Healthy scheduler: bounded p95 latency, tested pause/withdraw/restore controls, and an exact replay that performs zero database writes.
  • Guardrails: public Musings stays disabled and mobile is recorded as excluded, so the receipt can't quietly widen scope.

The private key never lives in the repo. The signer is a separate node:crypto tool that reads a private audit input and writes only the signed receipt; it performs no production mutation. Deployment code carries only the public verification key.

Migration 0029: a role that can only publish

Public Ask runs off a dedicated database principal. Migration 0029 creates sports_publisher, a role the scheduled publisher runs as, and asserts that the legacy admin can administer the login but cannot inherit or SET ROLE into its runtime privileges. The postflight check reads pg_auth_members and fails the migration unless exactly one creator-admin edge exists, with inherit_option = false and set_option = false. If a future grant widens that, the migration no longer applies cleanly and the release can't advance.

// PostgreSQL 17 records one creator-admin edge for an RDS-managed role.
// The admin may administer the login, but cannot inherit or SET ROLE into it.
rows[0]?.admin_option === true &&
rows[0]?.inherit_option === false &&
rows[0]?.set_option === false

This is the shape I want for every public cutover from here on: a signed receipt over a content-addressed contract, with the contract covering the exact files that can change the public surface. The rollout is staged behind statproMuseRolloutStage, so the same gate governs each step from ask through publishing to published.

this is the build log of statpro · statpro.io · all entries · essays