ASK Platform — Service Flows¶
How each service participates in the four main platform flows.
All flows assume the user has already logged in and holds a valid sessionToken.
Services at a Glance¶
| Service | Role | Internal address |
|---|---|---|
| Frontend | Next.js web UI — chat, admin, document upload | ask.mod.auh1.dev.dir |
| Core Service | Auth proxy, org/user/model CRUD, JWT issuer | core-service.ask.svc.cluster.local |
| Zitadel | IAM / SSO — credential validation, org membership | zitadel.ask.svc.cluster.local |
| Assistant Service | Chat orchestration, RAG coordination, streaming | assistant-service-intelligence.ask.svc.cluster.local |
| KMS | Document ingestion API, retrieval, presigned URLs | knowledge-management-service.ask.svc.cluster.local |
| Hatchet | Async workflow engine — dispatches ingestion jobs | hatchet-engine.ask.svc.cluster.local:7070 (gRPC) |
| Ingestion Worker | Pulls file from S3, chunks, embeds, upserts to Weaviate | knowledge-management-service-ingestion-worker |
| Foundry / LiteLLM | LLM proxy — chat model + embedding model | foundry-litellm.foundry.svc.cluster.local:4000 |
| Weaviate | Vector store — cosine similarity search | weaviate.weaviate.svc.cluster.local |
| Ceph S3 (RGW) | Object storage — raw documents in ask-knowledge-management bucket |
rook-ceph-rgw-ceph-objectstore.rook-ceph.svc.cluster.local |
| Vault + ESO | Secrets injection at pod startup | vault.vault.svc.cluster.local |
| CNPG | PostgreSQL clusters — core-pg, knowledge-pg, hatchet-pg |
in-cluster |
Flow 1 — Authentication / Login¶
sequenceDiagram
actor User
participant FE as Frontend
participant CS as Core Service
participant Z as Zitadel
participant DB as core-pg (CNPG)
User->>FE: email + password
FE->>CS: POST /api/v1/auth/login
CS->>Z: gRPC — verify credentials + org membership
Z->>DB: lookup projections.instance_members4 / org_members4
DB-->>Z: membership record
Z-->>CS: ✓ valid session
CS->>DB: fetch org_id, role (ask71_admin / superadmin)
CS-->>FE: sessionToken (JWT)
FE-->>User: logged in — token stored in browser
Key points:
sessionTokenis a JWT issued by Core Service, not Zitadel. All subsequent API calls send it asAuthorization: Bearer <token>.- Core Service calls Zitadel using the
IAM_SERVICE__PAT(from Vault via ESO at startup) — theCORE_SERVICEmachine user must hold IAM Owner in Zitadel. Without this membership, all logins return404 "membership not found". - Roles:
ask71_admin→ org-scoped access.superadmin→ full platform + IAM access.
Flow 2 — Chat (no Knowledge Base)¶
sequenceDiagram
actor User
participant FE as Frontend
participant CS as Core Service
participant AS as Assistant Service
participant F as Foundry (LiteLLM)
participant H as Hatchet
User->>FE: send message
FE->>CS: POST /chat (Authorization: Bearer token)
CS->>CS: validate JWT, attach org + user context
CS->>AS: forward chat request + context
AS->>AS: build system prompt (persona, tools, model config)
AS->>F: POST /v1/chat/completions (qwen3-5-122b)
F-->>AS: stream tokens
AS-->>CS: stream tokens
CS-->>FE: stream tokens (SSE)
FE-->>User: response rendered in UI
AS-)H: async — usage metering event
Key points:
- The model
qwen/qwen3-5-122b-a10bis served in-cluster via Foundry/LiteLLM — no external LLM call. - Responses are streamed end-to-end using Server-Sent Events (SSE).
- Hatchet receives a fire-and-forget metering event after the response completes — does not block streaming.
Flow 3 — Chat with RAG (Knowledge Base enabled)¶
sequenceDiagram
actor User
participant FE as Frontend
participant CS as Core Service
participant AS as Assistant Service
participant KMS as KMS
participant W as Weaviate
participant F as Foundry
User->>FE: message + collection selected
FE->>CS: POST /chat (with collection_ids)
CS->>AS: forward + context
AS->>KMS: POST /retrieve/?query=<user message>
note over KMS: body: {retriever_parameters, collection_ids}
KMS->>F: embed query → qwen3-embedding-0.6b (1024-dim)
F-->>KMS: query vector
KMS->>W: vector similarity search (cosine, top_k=25)
W-->>KMS: top-k chunks with scores
KMS->>KMS: rerank, apply similarity_threshold, character_limit
KMS-->>AS: {documents: [{document_id, chunks: [{content, id, order}]}]}
AS->>AS: inject chunks into system prompt as context
AS->>F: POST /v1/chat/completions (qwen3-5-122b + context)
F-->>AS: stream tokens (with citations referencing document_id)
AS-->>FE: streamed response + chunk metadata
FE-->>User: response with citation links
Key points:
- The retrieve call is
POST /retrieve/?query=<text>—queryis a URL query parameter, not in the JSON body. - Response structure is
documents[].chunks[]— not a flatchunks[]array. similarity_thresholddefault is0.7071(√2/2). Set to0.0to disable and return all results.- Citations in the UI reference
document_id+chunk.idfrom the retrieve response.
Flow 4 — Document Upload & Ingestion¶
This flow has two phases: a synchronous user-facing phase and an asynchronous background phase.
Phase A — Sync (user waits for upload confirmation)¶
sequenceDiagram
actor User
participant FE as Frontend
participant CS as Core Service
participant KMS as KMS
participant S3 as Ceph S3 (RGW)
User->>FE: select file + collection
FE->>CS: POST /documents/upload/initiate
CS->>KMS: POST /documents/upload/initiate {file_name, file_hash, collection_id}
KMS->>S3: generate presigned PUT URL
S3-->>KMS: presigned URL (short TTL)
KMS-->>CS: {document_id, presigned_url, ...}
CS-->>FE: document_id + presigned_url
FE->>S3: PUT file directly (presigned URL) ← bypasses all pods
S3-->>FE: 200 OK
FE->>CS: PATCH /documents/upload/{doc_id}/update-status?upload_status=queued
CS->>KMS: PATCH update-status
KMS->>KMS: DB: set processing_status = queued
KMS-)KMS: fire FastAPI background task → trigger Hatchet
KMS-->>CS: 200 {processing_status: queued}
CS-->>FE: 200
FE-->>User: "processing..." + poll for ready
Phase B — Async (background ingestion worker)¶
sequenceDiagram
participant KMS as KMS (background task)
participant HA as Hatchet API
participant HE as Hatchet Engine
participant W as Ingestion Worker
participant S3 as Ceph S3 (RGW)
participant F as Foundry (embedding)
participant WV as Weaviate
participant DB as knowledge-pg
KMS->>HA: POST /workflow-runs/trigger (gRPC → hatchet-engine:7070)
HA->>HE: gRPC dispatch
HE->>W: AMQP via RabbitMQ — run ingestion job
W->>S3: GET raw file from bucket ask-knowledge-management
S3-->>W: file bytes
W->>W: parse + chunk (hierarchical strategy)
W->>F: POST /v1/embeddings (qwen3-embedding-0.6b)
F-->>W: 1024-dim vectors per chunk
W->>WV: upsert chunks + vectors
WV-->>W: ✓ indexed
W->>DB: UPDATE documents SET processing_status='ready', workflow_run_id=<id>
DB-->>W: ✓
Key points:
- The file goes directly from the browser to Ceph S3 using the presigned URL — it never transits through any application pod. This is by design for large files.
- The
update-statusendpoint only triggers the Hatchet workflow on theuploading → queuedtransition. Calling it again on an already-queueddoc is a no-op. - Hatchet API must dial the engine on
hatchet-engine.ask.svc.cluster.local:7070(internal plaintext gRPC). The sharedSERVER_GRPC_BROADCAST_ADDRESSpoints to the Nginx ingress (TLS), which causes a protocol mismatch — see Troubleshooting. processing_statuslifecycle:uploading → queued → processing → ready(orfailed).
Flow 5 — Admin: Org Onboarding & Model Config¶
sequenceDiagram
actor Admin
participant FE as Frontend (admin UI)
participant CS as Core Service
participant Z as Zitadel
participant KMS as KMS
participant F as Foundry
participant V as Vault + ESO
Note over Admin,V: Org Onboarding
Admin->>FE: create new department org
FE->>CS: POST /organizations/onboard
CS->>Z: create org + invited users (gRPC management API)
Z-->>CS: org_id
CS->>KMS: POST /collections/ (collection scoped to org)
KMS-->>CS: collection_id
CS-->>FE: org onboarded
Note over Admin,V: Model Catalog
Admin->>FE: view/enable models
FE->>CS: GET /models
CS->>F: GET /model/info (LiteLLM)
F-->>CS: available models + aliases
CS-->>FE: model list with org-scoped availability
Note over Admin,V: Secrets (startup-time only)
V->>CS: IAM_SERVICE__PAT (ESO VaultStaticSecret)
V->>F: LLM API keys per model
Key points:
- Each department gets its own KMS collection. Collection actors (
/collections/{id}/actors) are set to the department's Zitadel group — users outside the group get403on collection reads. - Model API keys are injected at pod startup via Vault Secrets Operator (ESO VaultStaticSecret) — never in env files or ConfigMaps.
- SAML/LDAP for MoD AD integration is not yet configured — pending Zitadel SAML connector setup.
Cross-Cutting Concerns¶
| Concern | How it's handled |
|---|---|
| TLS | All ingress traffic terminates TLS at Nginx. Internal pod-to-pod traffic is HTTP/plaintext. |
| Secrets | Vault KV v2 + ESO VaultStaticSecret. Secrets injected as k8s Secrets at pod startup. Never in ConfigMaps or env files. |
| Config | ArgoCD ApplicationSet syncs all Helm values from mod-ask-devops Git repo. No manual kubectl apply in steady state. |
| Cert rotation | cert-manager issues internal TLS certs. Stakater Reloader watches cert secrets and restarts affected pods. |
| Observability | Resource limits/requests on all workloads. Hatchet dashboard at hatchet.ask.mod.auh1.dev.dir. |