Orchestrator / tensor — corrected final state
Canonical target architecture: [[jobcache-control-plane]] (architecture/CONTROL-PLANE.md,
locked 2026-06-08) — the THIN orchestrator: tensor in RAM + a NEVER-ENDING priority queue
built from the DB; hands every device its tensor + lease in one call; one-row-per-URL
last-writer-wins; NO device is ever cut off — trust only WEIGHTS queue priority (recency,
observer trust, page volatility, …), never a gate; a low-trust result just raises that
target's re-check priority so another actor corroborates; Render demoted to pure frontend.
This file is STATUS toward that target.
Principles live in sporewright (docs/ROUTING-MODEL.md, MODEL.md). This file is the
status, not the philosophy.
Lease engine — BUILDING LIVE (2026-06-08), brick by brick toward CONTROL-PLANE.md
- Orchestrator owns its priority queue —
queue.rsbuilds it straight from CrateDB (HTTP:4200/_sqlvia curl), ranked staleness × trust-deficit; excludes targets leased within LEASE_TTL (600s). The M2 budget loop still runs alongside. - RabbitMQ work queue (enqueue) — a refill loop (
QUEUE_REFILL_SECS=120) purges + publishes the top-50 due targets tocv.workwith bucketed priority. RabbitMQ = the lease-delivery engine, PRIVATE to the orchestrator (lapin, plain AMQP 5672, co-located on alwaysdata; devices NEVER hold AMQP creds — untrusted fleet). - Device lease endpoint (consume) —
POST /lease: pop highest-priority target (atomic dequeue) → stampleased_at→ mint primary Corbet + B2 overflow deposit URLs → return{ lease, deposit_url, deposit_overflow_url, deposit_key, tensor }— ONE call. Devices only ever see HTTP. - Device deposits to object storage; orchestrator folds into CrateDB — the device PUTs its one
observation to the orchestrator-minted presigned URL (
deposits/<ad_id>/<token>.json); the URL is object-scoped, so it IS the lease write key (no DB/queue/store credential in the device). Corbet S3 is primary; B2 accepts the same logical deposit key only after a primary failure.ingest.rsis the ONLY corpus writer and tries both inboxes before charging a poison attempt.object_store.rsis pure-Rust SigV4 (PUT/GET/LIST/DELETE)./observe+ the DB lease-key write-through REMOVED (CrateDB has no per-lease RLS, so the presigned URL is the credential instead). lease_key column deprecated (kept nullable). - Axiom ops tap —
axiom.rs: fire-and-forget lease/queue_refill/ingest/planner_tick telemetry tojobcache_telemetry(EU edge); never read back for a control decision. Proven live. - Device client — TS, orchestrator-backed —
orchestrator-device-client.ts: aJobcacheDeviceClientthat leases from the orchestrator (/lease), resolves the adapter from the url HOST (adapterMetaForUrl— the orchestrator stays dumb), and on submit RS03-sealsfields['raw']to the master pubkey,/archive-grant+ PUTs it toraw/<hash>, then PUTs the structuredvalue(raw excluded) + the archive pointer to the presigned deposit url. The EXISTING cycle + adapters + method-chain + method-learning are reused UNCHANGED behind the client seam (zero change to the broker path or the scraping/learning code).runConfiguredOrchestratorDeviceCyclewires it. Four focused tests + typecheck clean. - Corbet-primary/B2-overflow store-and-forward — Rust + TS devices try each primary
object-scoped PUT once, then the paired B2 grant. Deposit notifications identify the successful
inbox as a latency hint; the ledger sweep still tries both. Raw B2 objects use
overflow/raw/;raw_overflow.rsstreams their encrypted bytes to canonicalraw/, reads the Corbet object back, verifies byte length + SHA-256, and only then deletes the overflow copy. - Corpus archive pointer —
ad_observation_state.raw_object_key+raw_wrapped_dk; the CrateDB self-ingest staging + upsert carry the pointer from the deposit into the row, so the corpus links to its RS03-encrypted job ad (serve / re-extraction via araw_grant). - Rust RS03 encoder — PROVEN (
crates/jobcache-device/src/rs03.rs): the Rust device seals the raw job ad to the master pubkey (X25519 sealed box + AES-GCM + brotli), all pure-Rust / musl-clean (x25519-dalek, brotli, hkdf, getrandom). Cross-language interop proven with the PRODUCTION keypair: Rust seals → TSdecryptRs03decrypts → exact match. brotli is the ONE fleet codec (pure-Rust encoder exists; Node has brotli built-in; beats zstd on HTML/text). - Rust orchestrator transport (mechanical remainder) — mirror the TS orchestrator client
in the Rust cycle (
main.rs): anorchestrator.rs(curl lease / archive-grant / deposit, likebroker.rs),adapter_for_url(host→adapter), and the deposit path (extractfields['raw']→encrypt_rs03→ archive-grant → PUT → deposit structured value + pointer). The crypto + the flow are proven; this is the same flow in Rust. Then the fleet runs the orchestrator client. - Generic model hoisted to sporewright — priority, buckets, lease lifecycle, budget band
live in
sporewright::schedule(public); the server is thin I/O (CrateDB SQL, RabbitMQ, B2, HTTP).
The model (the bit I kept getting wrong)
- The option axis is the candidates chosen among — API providers / services / execution archetypes / P2P peers offering a service. NEVER a roster of individual devices.
- The
deviceLEVEL is learning at the device — the finest context tier where one device applies a LOCAL DELTA to the global consensus for its own conditions (the Australian device for whom the European mean isn't best). Not a list of devices. - Weights flow DOWN, measurements flow UP. Orchestrator/workspace seed gates (hard gate = no key / missing capability) and meddle with weights (policy); devices mostly measure + report.
- Knowing which devices are connected is the OPS story (telemetry → Axiom → dashboard;
cockpit
/devicesalready does it), NOT a routing input. - CareerVector is where the tensor earns its keep (the many LLMs competing on the option
axis). JobCache is trivial: a device declares its capability and gets the one kind of
work it can do — a hard capability gate — which the broker's
selectReadyTaskslease already does.
What's DONE (the bulk — most of it already runs)
- Tensor + budget substrate — clock-free
Σ value·weight, gates, normalizer, per-pool shadow-priceλ+Budget::tick+reduce_sum+TokenBucket. Rust+TS, golden-pinned (sporewright). - CareerVector options routing — the LLM cascade resolves the real option axis
(
resolveCascadeTensor, live; gates on missing key, orders by quality/financial/route). - JobCache device → work — trivial capability gate; the broker leases capability-matched
work (
selectReadyTasks, live). The orchestrator owns it outright; nothing to build here. - The bargain (JobCache) — device declares capabilities (
upsertCapability) → gets fitting work. Live. - The trust loop, tactically — pairwise agree→+ / disagree→− (
updatePairTrust, devices at 0.96), verify-dispatch on disagreement, freshness/time-mediation. Live in the broker. - SOFT trust principle recorded — trust is a soft modulation + verify pressure, never a
hard gate (sporewright §3C/Story 6). The
+∞min_trustgate inrouter.rsis superseded. - Device observability — cockpit
/devices(online/offline, trust, capabilities) + ingestion-health + source-matrix. The "who's connected / what's going on" story. - Planner heartbeat — the orchestrator slow tick drives
POST /api/planner/runhourly.
JobCache coverage-budget loop — BUILT 2026-06-08 (the M2 fold)
The orchestrator's documented job: "it decides when and with what budget to ask" (tick.rs). The budget DECISION lives in the orchestrator (alwaysdata), NOT the broker.
- Broker exposes RAW coverage —
GET /api/coverage(per-source pending/failed/done24h + totals). Measurement only; control-gated; on Render because that is where the DB is. - Orchestrator folds coverage → budget —
tick.rs::derive_budget: pending BACKLOG is the lever — idle queue ⇒ INVEST (max_tasks 100, refetch 40), saturated ⇒ BACK OFF (10, 10), linear between. Each tick GETs/api/coverage, derives, drivesPOST /api/planner/run. Coverage-read hiccup ⇒ conservative fixed fallback (heartbeat never stops). 4 fold unit tests. - Deploy path is NOT blocked —
infra/jobcache/redeploy-orchestrator.sh(drüben build on build-brain k3s →deploy-orchestrator.shships the static ELF to alwaysdata over KEYED ssh → restart user_program 1047719 via the alwaysdata API → verify /healthz). The old fleet-build "BLOCKED (needs human) / 502" note was STALE — orchestrator is LIVE (healthz 200). - ⚠️ WRONG TURN avoided — first cut put the budget BALANCE in the broker's planner (Render) to dodge the alwaysdata deploy. Reverted: balancing belongs in the orchestrator. The planner keeps only TACTICAL per-source distribution (staleness/fairness) next to the DB.
Genuine remainder — small + DEFERRED (not blocking; sensibly waits)
- F2 (CareerVector). The full λ price-loop — per-pool usage → shadow-price
λ(Lagrangian dual ascent) → publish;resolveauto-reshards off saturated channels. Primitives BUILT (budget.rs/budget.ts). The JobCache loop above is the simpler backlog-envelope cousin; the full λ loop is a CareerVector LLM-quota concern — defer until real quota pressure exists. - Device-level local deltas — a device re-scores its chain from its Δ vs the global mean (geo/connection). A refinement; only matters once there's a non-trivial option set per device.
- Rich direct device↔orchestrator channel (the bijective wire) — only needed when a product grows a real options competition the broker's lease doesn't already cover. Not now.
Bottom line
The orchestrator/tensor is largely real: the tensor + budget substrate is built, CareerVector routes its LLM options over it, JobCache's device→work is the trivial capability gate the broker already serves, device connectivity is the cockpit's ops view, and the trust loop runs. The misframing (devices-as-options) made this look like a big build; corrected, the genuine remainder is the budget price-loop wiring (deferred until real quota pressure) + a couple of refinements. This thread is closed — ready to advance.
Superseded — do NOT build
Devices on the option axis (they're not options). Hard +∞ trust gate (→ soft cost + verify).
Borda seed-consensus (→ declared-presence quorum). Matryoshka stack (→ one sparse tensor + tier weight).