Free-Tier Infrastructure

JobCache is a peer product, not a hosted SaaS. It pays for itself by operating inside the free tier of every vendor it uses. That constraint shapes a lot of decisions that look strange in isolation.

The free-tier inventory

Layer Vendor Free-tier ceiling
Hot DB CrateDB Cloud (CRFREE) 2 vCPU / 2 GiB / 8 GiB disk, single node, no request meter. Replaced CockroachDB Serverless in the 2026-06 cutover; the Cockroach cluster is dormant, rollback only.
Service hosting Render Hobby Small monthly compute budget, sleep when idle
Edge workers Cloudflare Workers (free) 100k requests/day account-wide
Cold evidence Backblaze B2 10 GB stored, 1 GB/day download free
Scheduler CircleCI self-hosted runner resource_class: corbet/unraid; work runs on owned k3s/Unraid capacity
Planner tick Hourly CircleCI jobcache_planner → Render interface Bearer-token gated; queues device work only

Pulumi tracks the inventory in jobcache/infra/ so the stack can be torn down and rebuilt without rediscovering which vendor holds what.

Peer-product, not sub-product

CareerVector and JobCache share the objective public ad boundary: ad_id, shared field vocabulary, cells, evidence keys, chunks, embeddings, and current-cell rules. They do not share a deploy unit or an auth surface. JobCache can be rebranded, sold, or operated for a third party without dragging CareerVector along; CareerVector can switch shared-ad providers without JobCache going dark.

The CareerVector ↔ JobCache contract is the shared ad/field/cell model in CrateDB plus operational read/control APIs around it. CareerVector's bottom-up workspace lookup/write and JobCache's top-down distributed ingest both use that boundary; private workspace jobs are not part of the submitted observation envelope. This split is by design — see JOBCACHE-CAREERVECTOR-RELATIONSHIP.md for the broader peer-product reasoning.

MCP consolidation

JobCache hosts multiple MCP surfaces (mcp.jobcache.corbet.ch, mcp.ops.jobcache.corbet.ch, mcp.status.jobcache.corbet.ch, mcp.qa.jobcache.corbet.ch). They are deliberately not one mega-worker:

  • Each MCP wraps exactly one API. Auth posture lines up with the API it fronts (public, admin, public, allowlisted).
  • A leak in one cannot reach the data plane of another.
  • The free-tier Worker request budget is shared, but each MCP's surface is small and operator-only or agent-only — total traffic is tiny.

What is consolidated is the secret: all admin MCPs use the single rotated ADMIN_TOKEN. See admin-auth.md.

The 100k-account-wide footgun

Cloudflare Workers free tier is 100k requests/day across every Worker on the account. JobCache shares this bucket with CareerVector and dotkeeper. CLAUDE.md §22 governs the rule: no push-triggered CI that hits deployed Workers, no cron loops, no high-fanout poll. Audit the daily total with ~/.agent/tools/cf-quota/check.mjs before adding any new automation.

This is why the JobCache planner job lives in CircleCI on corbet/unraid and hits the Render-hosted interface, not a Cloudflare Worker. The job queues bounded device work; it does not scrape, chunk, embed, geocode, or run LLMs centrally.

See also

Source: jobcache/wiki/content/architecture/free-tier-infra.md