FactoryWager Wiki

Registry Production Readiness

Canonical topology

FactoryWager uses two deliberately separate registry planes:

Do not make the R2 bucket public and do not copy R2_ACCESS_KEY_ID or R2_SECRET_ACCESS_KEY into Pages. The Pages binding is the read authority.

Health

The edge probe is:

curl --fail https://registry.factory-wager.com/api/registry/health

It checks that the R2 binding can read and structurally parse registry.json. It returns package and version counts without exposing credentials or storage errors. The Bun host also exposes /health and /-/ping; factory health performs the signed R2 probe used by operators.

Integrity monitoring

Run a one-shot size and SHA-256 audit:

bun run factory:integrity

The result is written to reports/registry-integrity.json. A failed cycle sends a redacted alert when SLACK_WEBHOOK_URL, or both TELEGRAM_BOT_TOKEN and TELEGRAM_OPS_CHAT_ID, are configured.

The registry-integrity spine tenant runs daily at 03:00 UTC. The long-lived Bun host can also enable the in-process complement with REGISTRY_MONITOR=1 bun run factory:serve. Spine remains the scheduling SSOT; do not register both on the same host.

Client SDK

The runtime-neutral SDK lives at packages/registry-client. It uses Web APIs only and works in Bun, browsers, and Cloudflare Workers.

Full usage, URL format, and verification: docs/registry-client.md · proof JSON at /registry/registry-client-proof.json · bun run verify:registry-client:save.

cd packages/registry-client
bun run build
bun publish --dry-run --access public

After inspecting the dry-run archive, publish through the authenticated FactoryWager registry:

bun publish --registry https://registry.factory-wager.com --access public

The SDK resolves dist-tags, produces allowlisted asset URLs, and verifies both byte length and SHA-256 on download. Its publish() method targets the private multipart endpoint; the Pages/R2 read plane rejects writes.

Lane note (ADR-0002): bun publish --registry https://registry.factory-wager.com does not work against the Pages deployment today (read-only, 405 on non-GET). Publish via RegistryClient.publish (R2) or the loopback serve-public lane, then refresh the snapshot.

Configure the two origins independently so the bearer token is never sent to the public read plane:

const client = new RegistryClient({
  baseUrl: 'https://registry.factory-wager.com',
  publishUrl: 'https://registry-write.internal.factory-wager.com',
  apiKey: runtimeConfig.registryToken,
});

The Bun gateway rejects writes when no FACTORY_WAGER_TOKEN or REGISTRY_SECRET is configured, compares bearer tokens by fixed-length digests, and enforces REGISTRY_MAX_PUBLISH_BYTES (50 MiB by default). Run one publish gateway instance; the R2 index update model has a single-writer authority while Cloudflare scales the read plane independently.

Dynamic integrations

Routing algorithms and DOD model releases are registry artifacts. Consumers must download verified bytes, extract them into a controlled directory, and load the declared local entry point through their normal sandbox boundary. Remote import() and eval() of registry URLs are intentionally unsupported. DOD snapshots stamp modelVersion so audit evidence remains attributable to the verifier release.

Deployment checklist

Recovery

When integrity fails, inspect reports/registry-integrity.json, restore or re-publish the named object, and rerun:

bun run spine:schedule:once -- --tenant=registry-integrity

Never repair an integrity failure by editing the recorded checksum to match an untrusted object.