System Architecture

There are two products and three privacy/runtime zones. JobCache App is the observer-facing product edge. JobCache Core is JobCache's shared corpus, search, recommendation and market-intelligence substrate. CareerVector is the person-facing private workspace product. JobCache Core belongs operationally and conceptually to JobCache, while owning neither product's local identities.

Status (2026-08-18): the three-zone boundary is live. The processing plane shown below is the target evolution: a small, rebuildable jobcache-pipeline D1 coordinates content-addressed enrichment and batched publication without becoming a fourth fact-tree rung or another source of public truth. See The Shared Seam Package for the exact as-built privacy boundary.

[!important] Whole-system blueprint Open the code-rendered Blueprint for the canonical 5x4 operating map. Every region links to its own Markdown contract in the repository-level architecture/ folder. The diagram below remains the detailed ingestion and privacy projection.

Whole-system target architecture: JobCache App above, CareerVector below, ingestion entering JobCache Core from the left, CrateDB at the centre, and management and observability on the right.

The main public-data lifecycle runs left to right. Product queries and immutable result delivery attach vertically through the Core APIs. The dashed management plane is deliberately the next design pass; it observes the system sideways and emits declared control intent, but it must not become a second scheduler or corpus writer.

Detailed privacy-boundary diagram

an operator · an advertiser an LLM · an API client JOBCACHE APP observer-facing · session-private website MCP server public API stateless OR one session per request · link = identity · cannot enumerate D1 — sessions saved views · dashboards · by session_id reduction pipeline observer_key OR anonymous cohort · purpose-scoped collector FAST · live search, metrics collector SLOW · saved views internal API + worker bounded outbox work · authenticated · never public FAST lane · not via the store PRIVACY SEAM — no local identity crosses this line session_id stays above · pseudonymous observer_key OR anonymous aggregates cross by purpose JOBCACHE CORE shared corpus · owns neither local identity internal API + worker authenticated JobCache internal API — faces JobCache App search executor FAST · the public product metrics builder FAST · ad performance CrateDB doc.ads · job_vec · person_vectors · observer_vectors · graph_edges corpus pipeline normalise · disambiguate ad→role embed · enrich · index search executor FAST · a person is waiting recommendation engine SLOW · nobody is waiting internal API + worker authenticated JobCache internal API — faces CareerVector PRIVACY SEAM — no local identity crosses this line workspace_id stays below · pseudonymous person_key OR anonymous aggregates cross by purpose CAREERVECTOR the product · workspaces website · RADAR modal MCP server public API exactly ONE workspace per request · link = identity · cannot enumerate D1 — workspaces profiles · snapshots · by workspace_id reduction pipeline person_key OR anonymous cohort · purpose-scoped collector FAST · counters, live query collector SLOW · vectors, edges internal API + worker sees ALL workspaces · authenticated · never public FAST lane · not via the store a job seeker an LLM · an API client dashed = FAST lane · stored snapshots are the fallback floor. Slow lanes are PULLED by JobCache Core through scoped internal APIs.

Reading the detailed privacy diagram

Mark Means
Teal outline transforms data — pipelines and boundary modules
Red outline privacy-critical — authenticated internal product protocol, never a public product route
Heavy outline must be reachable — the public API
Thin outline a perspective — a facade on the public API
Grey fill a store — it holds, it does not transform
Solid arrow slow lane — batch, pulled by JobCache Core, nobody is waiting
Dashed arrow fast lane — synchronous; never routes the response through a store
Red dashed rule a privacy seam. No product-local identity crosses it

The claims it makes

Two products, three zones

CareerVector is the private applicant domain. JobCache contains two runtime zones: JobCache App is its observer-facing private edge, while JobCache Core owns the shared market corpus and intelligence. “Core” describes a concern boundary inside JobCache, not a third product, brand or neutral owner.

Two privacy seams, with two permitted privacy classes

Each product edge keeps the only map from its local identity: workspace_id ↔ person_key in CareerVector and session_id ↔ observer_key in JobCache App. A fact crossing a seam declares exactly one privacy class:

Class Used for Crossing rule
pseudonymous individual recommendation, individualized saved state a versioned person_key or observer_key; never the local ID
anonymous aggregate market metrics and cohort analytics no participant key; thresholded before emission

Pseudonyms remain personal, linkable data and receive retention, erasure and key-rotation handling. Anonymous is a property of the emitted aggregate, not a synonym for “hashed.” Only person facts may train person recommendations; observer activity must never be read as applicant demand.

The mirror is implementation, not duplicated design

The outer zones have the same store/reduction, collector and internal-API mechanism. The participant noun selects a coherent profile (person or observer); product public APIs and perspectives remain distinct.

Row Shared implementation?
perspectives no — RADAR and JobCache browse/dashboard answer different questions
public API no — product-specific stateless reads and workspace/session-capability operations
local store + reduction yes
fast and slow collectors yes
seam-facing internal API yes

The exact package boundary and current extraction status live in The Shared Seam Package.

Fast and slow are different reliability contracts

Stateless JobCache search/evidence returns the verified JobCache Core answer without creating local identity or state. A fast operation that promises reloadable workspace/session state also returns Core's answer directly, but commits the immutable result snapshot and local pointer atomically before reporting success. A stored snapshot is the fallback floor. On first use, or where no compatible snapshot exists, an outage is honestly unavailable rather than silently described as stale.

The slow lane is claim/ack work pulled by JobCache Core. A cursor advances only for facts durably accepted by Core; rejected facts remain visible in a quarantine path. Delivery back to a product edge is idempotent and commits its snapshot and pointer atomically. Each worker is authenticated by name and receives only its declared operation capabilities; the general cross-product control token cannot enter this all-workspace protocol.

JobCache Core owns corpus state, not edge-operational churn

CrateDB owns shared ads, roles, evidence, embeddings and graph projections. Session or workspace state remains at the relevant product edge. Leases, retries, delivery receipts and quarantine are operational state beside the JobCache internal API, not search-index tenants in the corpus.

Result identity is immutable

A request-equivalence key may deduplicate equal work, but it is not a result ID. A result ID identifies canonical ordered bytes plus the corpus snapshot, retrieval/ranking version and other result-changing inputs. The same result ID can never name different bytes. Provenance, persistence, age and health are separate fields; a stored result is not automatically stale.

Current implementation state

Zone As built Gap to this graph
CareerVector D1-backed RADAR routes, verified role search/delivery, composite slow-lane cursors and atomic participant-erasure outbox deploy the additive migration/credentials, run the Core erasure worker and finish the remaining recommendation/metrics cutover
JobCache Core live corpus/extraction, strict role search and fail-closed participant tombstones with deterministic receipts deploy the lifecycle migration/credential; keep reducers on active views and finish recommendation/metrics APIs
JobCache App jobcache/api/ D1 API with opaque sessions, views, immutable snapshot grants, explicit reductions and deletion outbox; jobcache/mcp/ thin peer provision App D1 and Workers in canonical IaC, coordinate domain/service binding cutover, then build UI as another API client

The direct-CrateDB and JobCache-public search gateways in CareerVector are compatibility scaffolds, not the prescribed boundary. The target state is CareerVector calling only the authenticated JobCache internal API.

Vocabulary

Term Means
Ad one posting on one board — evidence for a role
Role one hiring intent backed by one or more ads — the user-facing unit
Person someone in the market; pseudonymized as person_key when individual context is required
Observer someone looking at the market; pseudonymized as observer_key when individual context is required
Profile the person a CareerVector workspace serves; never a list
Search results roles returned for an active query
Recommendations roles materialized from a person's reduced preference state

“Candidates” is not used: in a hiring product it means applicants.

Deliberately off this map

Ad discovery and extraction are separate JobCache Core pipelines. CareerVector URL import and JobCache submission may both contribute public ad evidence, but neither changes the privacy rules for individual or aggregate participant facts. Ad ownership is likewise not implied: an observer may analyze any public ad.

CareerVector's private LLM routing is an independent Sporewright application documented in LLM-TENSOR-ROUTING. Sporewright owns generic routing mathematics; it does not own this seam, either product's identities, or JobCache corpus persistence.

Source: wiki/content/architecture/SYSTEM-ARCHITECTURE.md