Satellites — the load-bearing pieces that do not run in oper

Two Modal GPU endpoints, one Cloudflare edge worker, one docs site · ← platform hub · no scan contract — none of these is a Kubernetes service

1. What it is

Four things in this repository are load-bearing but do not run in the oper Kubernetes namespace, so Argo CD never sees them and kubectl cannot tell you whether they are healthy: two GPU endpoints on Modal, one Cloudflare edge worker, and the public documentation site. Three of the four are genuinely live; the fourth is deployed but currently documents nothing.

Who uses themmicro-learning's TTS worker (Modal Chatterbox) · RagFlow, driven by rag-context-worker (Modal MinerU) · anyone clicking an OTP or invitation link in an email (Cloudflare) · engineers and integrators (docs portal)
RuntimeModal serverless GPU containers · Cloudflare Workers edge runtime · one in-cluster Docusaurus static pod (docs-portal, the only one with a Deployment)
DatabaseNone. The Modal apps keep model weights in Modal Volumes (chatterbox-weights, mineru-model-cache); the edge worker is stateless.
RedisNone.
NATS streamsNone. Every satellite is reached over HTTPS.
External APIsThey are the external APIs — Modal and Cloudflare are third-party platforms holding Oper data in transit.
Entry points1 Modal HTTPS endpoint (TTS) · 1 Modal HTTPS endpoint (PDF parse) · 3 edge paths · 1 static site
How they are deployedBy hand. There is no GitHub workflow for any of them except docs-portal (.github/workflows/docs-portal-ci.yaml): the Modal apps ship with modal deploy and the edge worker with wrangler deploy. Nothing reconciles them against this repository.

Why this page exists

Everything here can break without a single Kubernetes signal firing. A Modal deploy that never happened, a Cloudflare route that 404s, a spec refresh that never ran — none of it shows up in Argo, in a pod restart count, or in a readiness probe. These are the blind spots, so each entry below states who calls it, how it gets deployed, and what proves it is live.

2. Feature map

flowchart LR
  ML["micro-learning TTS worker"] --> CB["chatterbox-modal (Modal A10G)"]
  CB --> WAV["narration audio → asset-manager"]
  RC["rag-context-worker"] --> RF["RagFlow (ragflow ns)"]
  RC -->|"warm-up ping"| MU["miner-u on Modal (L4)"]
  RF -->|"MINERU_APISERVER"| MU
  OTP["otp-worker email"] --> CF["link-otp-worker (links.useoper.com)"]
  CF --> APP["mobile app deep link"]
  DEV["Engineers"] --> DP["docs-portal (docs.useoper.com)"]

3. The satellites

Internal (other services)

chatterbox-modal — the voice behind every narrated lesson live

internal

When a lesson screen needs spoken narration, the TTS worker sends the text and a reference voice clip here and gets back a WAV. It runs Chatterbox Multilingual on a Modal GPU that only exists while a sentence is being synthesised, which is why audio costs fractions of a cent per screen instead of the per-request minimums the previous provider charged. One multilingual model covers both English and Spanish.

What calls it
micro-learning-service-v2's TTS worker, at the URL in TTS_ENDPOINT; a per-voice endpoint entry in the voice map can override it for A/B testing a second engine.
Contract
POST <endpoint> with {text, audio_url, language, temperature, exaggeration, cfg, seed} → raw audio/wav. 422 on missing text or reference clip (permanent failure for the worker); 5xx and timeouts are retried inside the worker's delivery budget.
Deployment
modal deploy app.py, by hand. Modal app chatterbox-tts, GPU A10G, 120 s scale-down window, weights in the chatterbox-weights Volume. Auth is Modal proxy auth: MODAL_KEY / MODAL_SECRET from tts-worker-v2-secrets.
Evidence of life
Endpoint pinned in cluster config: micro-learning-service-v2/k8s/tts-worker-v2-configmap.yaml:11https://fiott248--chatterbox-tts-multilingual-tts.modal.run; consumed at micro-learning-service-v2/app/tts_worker/pipeline.py:793 (raises tts_endpoint_missing if unset, so the audio pipeline cannot run without it). Credentials wired at micro-learning-service-v2/k8s/tts-worker-v2-deployment.yaml:96 and k8s/es-tts-worker-v2.yaml:29-31. Listed as live external egress in ARCHITECTURE.md §3.6 and as "load-bearing for tts-worker-v2" in §2. Source: chatterbox-modal/app.py:29 (app name), :43 (GPU), :135 (the proxy-auth POST endpoint).

miner-u on Modal — the PDF parser behind document ingest live

internal

When an author uploads a manual or a policy PDF, RagFlow has to turn it into clean text and tables before anything can be generated from it. MinerU does that parsing on a GPU that scales to zero between documents — a 79-page manual takes one to two minutes instead of four, and idle time costs nothing. rag-context-worker pings it awake before queueing a parse so the author does not pay for the cold start.

What calls it
RagFlow (in the ragflow namespace, whose manifests live outside this repo) via MINERU_APISERVER. rag-context-worker selects the backend and sends the warm-up ping, but never parses directly.
Contract
MinerU's own /file_parse HTTP API. Because RagFlow's client cannot send custom headers, auth is a secret path prefix baked into the URL; the bare root returns only an anonymous {"status":"ok"}.
Deployment
modal deploy miner-u/modal/modal_app.py, by hand. Modal app mineru-parser, GPU L4 by default, 20-minute warm window, min_containers=0, weights in the mineru-model-cache Volume.
Evidence of life
The backend is selected in cluster config — rag-context-worker/k8s/configmap.yaml:19 RAGFLOW_LAYOUT_RECOGNIZER: "mineru", read by rag-context-worker/app/ragflow/datasets.py:70-73 — and the deployed Modal URL is pinned for warm-up at rag-context-worker/k8s/configmap.yaml:25 (https://fiott248--mineru.modal.run/<prefix>/openapi.json), used at rag-context-worker/app/consumers/indexing_consumer.py:405. Source: miner-u/modal/modal_app.py:21-33.
Caveats worth knowing
Tenant PDFs leave the cluster to Modal's US infrastructure — a deliberate compliance call is required before routing regulated content (miner-u/modal/README.md:54-59). And the secret path prefix that serves as the endpoint's only authentication is committed in a plaintext ConfigMap (rag-context-worker/k8s/configmap.yaml:25); rotating it means redeploying the Modal app and updating both that URL and MINERU_APISERVER on the RagFlow pods.

Employee (mobile)

link-otp-worker — where an emailed sign-in link lands live

mobile

An employee gets a sign-in email, taps the link, and should end up signed in inside the Oper app. This Cloudflare worker is what that link points at. It serves the two association files iOS and Android require before they will let a website open an app, and it answers the link itself with a small page that hands the code to the app — never a redirect and never an empty response, both of which break Universal Links and silently strand the user in a browser.

What calls it
Employee OTP emails sent by the otp-worker, which build https://links.useoper.com/sign-in?code=…. Admin OTP emails deliberately point at app.useoper.com instead, so they never reach this worker.
Paths
/.well-known/apple-app-site-association · /.well-known/assetlinks.json · /sign-in?code= (mobile bridge page or desktop fallback page; an error page when the code is missing). Everything else returns 404.
Deployment
wrangler deploy, by hand — there is no workflow for it in .github/workflows/. Bound to links.useoper.com as a Cloudflare custom domain, so TLS is Cloudflare-managed.
Evidence of life
Producer: users-auth-service/app/otp_worker/processor.py:195-199 selects links.useoper.com for employee OTP emails; the template renders https://{domain}/sign-in?code={code} (users-auth-service/app/otp_worker/templates/otp_email.html:91). Handler: cloudflare-workers/link-otp-worker/src/index.js:53. Domain binding: wrangler.jsonc:19-21. Also evidenced independently by the compliance audit, which treats the live OTP-in-URL flow as a real data-transfer path (compliance/shared/vendor-list.md:33).

Admin

docs-portal — the public API documentation site suspect

admin

A Docusaurus site at docs.useoper.com intended to publish the OpenAPI reference for every backend service, so an integrator or a new engineer can see the platform's HTTP surface without reading the code. The site is deployed and serving. It currently documents no endpoints at all.

What calls it
Humans, over the public internet. Nothing in the platform depends on it at runtime.
Deployment
The one satellite with real CI: .github/workflows/docs-portal-ci.yaml builds the image, and it runs as an ordinary in-cluster Deployment (docs-portal/k8s/deployment.yaml:4, replicas: 1) behind an ingress for docs.useoper.com (docs-portal/k8s/ingress.yaml:16). It is on this page because it is the documentation surface for everything else here, and because its content pipeline — not its pod — is the part that is broken.
The verifiable problem
All eight specs under docs-portal/specs/ are placeholders. Every one parses to "paths": {} with "version": "0.0.0-placeholder" and a description that says it "will be replaced by the docs-portal-refresh-specs workflow once it runs against the cluster" (e.g. docs-portal/specs/feed-service.json:5-7). The portal therefore renders a service list and zero endpoints.
Evidence
Parsed all of docs-portal/specs/*.json: asset-manager, assignment, authoring-image-worker, feed, micro-learning-v2, rag-context-worker, suggest-pipeline-worker, users-auth — len(paths) == 0 for all eight. The refresh workflow (.github/workflows/docs-portal-refresh-specs.yaml) is workflow_dispatch plus a 06:00 UTC cron on a k8s-runner; it port-forwards eight services and opens a PR with the diff, and no such PR has landed. Its service list at :57-65 no longer contains the dead authoring-service that ARCHITECTURE.md §4.2 recorded as making it hang — that half is fixed; the empty specs are not.

Background

None. Both Modal apps are request-driven and scale to zero; the edge worker runs per request; the docs pod serves static files. No satellite runs a scheduled job of its own — the only schedule involved is the spec-refresh cron, which runs in GitHub Actions, not here.

4. API reference

No Kubernetes Service and no in-cluster route: these are external endpoints and edge paths. "Callers" is the in-repo code or configuration that points at them.

MethodPathAuthFeatureCallersVerdict
POSThttps://fiott248--chatterbox-tts-multilingual-tts.modal.runModal proxy auth (Modal-Key / Modal-Secret)Narration synthesismicro-learning-service-v2/app/tts_worker/pipeline.py:793 via TTS_ENDPOINTlive
POSThttps://fiott248--mineru.modal.run/<secret-prefix>/file_parseSecret path prefixPDF parsing for RagFlowRagFlow pods via MINERU_APISERVER (manifests outside this repo); warmed by rag-context-worker/app/consumers/indexing_consumer.py:405live
GEThttps://links.useoper.com/sign-in?code=none — the code in the URL is the credentialOTP deep linkusers-auth-service/app/otp_worker/templates/otp_email.html:91live
GEThttps://links.useoper.com/.well-known/apple-app-site-associationnone (public by design)iOS app-link associationiOS, before it will open the app for a linklive
GEThttps://links.useoper.com/.well-known/assetlinks.jsonnone (public by design)Android app-link associationAndroid, samelive
GEThttps://links.useoper.com/i/<tenant-slug>n/aInvitation landingusers-auth-service/app/otp_worker/processor.py:378 — the worker has no handler for itdead
GEThttps://docs.useoper.com/noneAPI documentationhumanssuspect

5. Async contracts

Consumes

SubjectStreamDurablePublished byFeatureVerdict
None. No satellite has a NATS client; every one of them is reached over HTTPS.

Publishes

SubjectConsumed byFeatureVerdict
None.

Background jobs

JobScheduleWhat it doesVerdict
docs-portal-refresh-specs
.github/workflows/docs-portal-refresh-specs.yaml
Daily 06:00 UTC + manualPort-forwards eight services, pulls their openapi.json, opens a PR with the diffsuspect — the specs it should be replacing are still placeholders, so either it is not completing or its PRs are not being merged

6. Data it owns

No database and no tables anywhere in this page's scope. What each satellite holds:

StoreWhat it holdsWritten by
Modal Volume chatterbox-weightsChatterbox Multilingual model weights, downloaded once on first run so later cold starts only pay model load.chatterbox-modal on first execution
chatterbox-modal/app.py:33
Modal Volume mineru-model-cacheMinerU model weights, same pattern.miner-u on first parse
miner-u/modal/modal_app.py:35
Cloudflare Worker varsThe Apple and Android app-association documents. Public by design — they are served verbatim to anyone; no secret belongs here.Set at deploy time from wrangler.jsonc
docs-portal imageStatic HTML plus the eight (currently empty) OpenAPI specs, baked in at build time.CI
docs-portal/Dockerfile, .github/workflows/docs-portal-ci.yaml

Nothing here writes an Oper database. The data that matters is data in transit: narration text and reference clips to Modal, tenant PDFs to Modal, and an OTP code through Cloudflare's edge — all three recorded as cross-border transfers in compliance/shared/vendor-list.md.

7. Dependencies

flowchart LR
  MLW["micro-learning TTS worker"] --> CB["chatterbox-modal"]
  RCW["rag-context-worker"] --> MU["miner-u on Modal"]
  RFLOW["RagFlow (ragflow ns)"] --> MU
  OTPW["otp-worker (users-auth image)"] --> MAIL["Mailgun"]
  MAIL --> USER["Employee inbox"]
  USER --> CFW["link-otp-worker"]
  CFW --> MAPP["Oper mobile app"]
  CI["GitHub Actions"] --> DP["docs-portal pod"]
  CB --> AM["asset-manager (via the TTS worker)"]

Note the asymmetry: rag-context-worker only selects and warms MinerU; the actual parse call is made by RagFlow, whose configuration lives outside this repository. That is the one edge on this page that cannot be fully verified from here.

8. Dead-code verdicts

Every satellite entry point that is not plainly live. Deleting is a separate decision — see the hub roll-up.

Entry pointKindVerdictEvidence
https://links.useoper.com/i/{tenant_slug} — invitation links Edge route dead The link is really sent: users-auth-service/app/otp_worker/processor.py:375-379 builds https://{invite_link_domain}/i/{tenant_slug} (or bare /i with no slug), with invite_link_domain defaulting to links.useoper.com (app/otp_worker/config.py:55-57); it is rendered into the invitation email at app/otp_worker/processor.py:419 / templates/invitation_email.html.
And the worker cannot answer it: cloudflare-workers/link-otp-worker/src/index.js matches exactly three paths — /.well-known/apple-app-site-association (:27), /.well-known/assetlinks.json (:36) and /sign-in (:44) — and every other request hits return new Response("Not Found", { status: 404 }) at :81. There is no route, no redirect and no catch-all. An invited employee lands on a 404. Confirms ARCHITECTURE.md §4.1(8).
cloudflare-workers/link-otp-worker/wrangler.toml Config duplicate dead Duplicate: the worker ships both wrangler.jsonc and wrangler.toml for the same worker name and entry point.
And divergent, in a way that would break production: the TOML carries a placeholder Apple team id (ABCDE12345.com.useoper.training) where the JSONC has the real one (U4KQGWA75N.com.useoper.training), and it omits the custom_domains: ["links.useoper.com"] binding entirely. A deploy that picked up the TOML would publish an association file that iOS rejects — silently breaking every Universal Link — and would not bind the domain. Since there is no CI for this worker, which file a given wrangler deploy reads is decided by whoever runs it. Confirms ARCHITECTURE.md §4.1(8).
cloudflare-workers/link-otp-worker/test/index.spec.js Test dead Untouched create-cloudflare scaffolding: it fetches http://example.com and asserts the response body is "Hello World!" (:5-19), which this worker has never returned — that path hits the 404 branch. It asserts nothing about the three real routes and would fail if run.
miner-u/k8s/ — the in-cluster CPU fallback Manifest dead Referenced in three places: miner-u/README.md:20 (kubectl apply -f miner-u/k8s/deployment.yaml), miner-u/modal/README.md:3 ("the in-cluster CPU pod (miner-u/k8s/)"), and rag-context-worker/k8s/configmap.yaml:17 ("needs the GPU mineru-api workload … k8s/mineru-api.yaml").
And it does not exist: git ls-files miner-u returns exactly four files — Dockerfile, README.md, modal/README.md, modal/modal_app.py — and a repo-wide search for *mineru* filenames finds no manifest anywhere. This also corrects ARCHITECTURE.md §2, which describes the fallback as "a fallback with a placeholder image": there is no manifest and no placeholder image left. The documented rollback path from Modal back to http://mineru-api.ragflow.svc.cluster.local:8000 (miner-u/modal/README.md:38-40) therefore has nothing to roll back to — the CPU Dockerfile survives, but the deployment that used it must be re-authored.
docs-portal/specs/*.json (all eight) Content dead Nothing to serve: every file parses to "paths": {} — verified by loading all eight with a JSON parser — and each declares itself a placeholder in info.version (0.0.0-placeholder) and info.description.
And the generator has not delivered: the only thing that can replace them is .github/workflows/docs-portal-refresh-specs.yaml, whose whole job is to port-forward the services, fetch their live openapi.json and open a PR; no such PR has landed, because the committed specs are still the originals. The pod at docs.useoper.com is up and serving — it is the content that is empty.
docs.useoper.com (the site itself) Deployment suspect Deployed and reachable (docs-portal/k8s/deployment.yaml, ingress.yaml:16) with working CI, but no in-repo consumer and, given the empty specs, no demonstrated reader value today. Whether anyone actually visits it is not answerable from this repository — ingress access logs would resolve it.

9. Sources