| R1 |
The Docker socket is root on the host. The serving manager and the pipeline runner run as root with /var/run/docker.sock mounted. A user who can register a model with a serving_image and deploy it chooses the image that runs on the host. |
deploy/compose.yaml; serving/internal/serving/manager.go |
Keep Compose a single-operator profile, as documented. Multi-user installations use Kubernetes, where neither service runs. |
| R2 |
Internal services accept unauthenticated calls when no token is set. The storage proxy, feature gateway and serving manager check MLAIOPS_INTERNAL_TOKEN only if it is set, and Compose does not set it for these three. Their ports are published on loopback, so any local process can call them, including the serving manager's POST /deployments. |
authorized in platform/go/cmd/storage-proxy/main.go and serving/cmd/serving-manager/main.go; service environments in deploy/compose.yaml |
Set the token on these services in Compose, or stop publishing their ports. |
| R3 |
One shared service token. Every in-platform service authenticates with the same static bearer token; there is no per-workload identity or mutual TLS. A leaked token grants the service role everywhere. |
servicePrincipal in platform/go/internal/auth/auth.go |
Per-service credentials, or mesh identity on Kubernetes (deploy/config/network-istio/ holds a strict mTLS policy). |
| R4 |
Row-level security depends on the database role. Tenant isolation relies on forced row-level security, which superusers and BYPASSRLS roles skip. The Compose database user is the image's superuser. |
platform/go/internal/store/migrations/001_initial.sql; POSTGRES_USER in deploy/compose.yaml |
Document and check that production connects as an ordinary role; add a startup check. |
| R5 |
Long calls outlive the server's write timeout (from code reading). The gateway's HTTP server has a 30-second WriteTimeout, but agent turns may take up to 120 seconds and serving deployments up to 90 seconds; a response that completes after 30 seconds cannot be written back. GET /api/v1/events does not lift the deadline (the log tail does), so the live stream ends about every 30 seconds and the browser reconnects. |
platform/go/cmd/gateway/main.go; invokeAgent, requestServing and events in platform/go/internal/httpapi/server.go; platform/go/internal/httpapi/logs.go |
Clear the write deadline with http.ResponseController on long-running and streaming handlers, as the log tail does. |
| R6 |
Outbox duplicates and stalls. The outbox query uses FOR UPDATE SKIP LOCKED outside an explicit transaction, so the row lock ends before publishing and several gateway replicas (the chart runs three) can publish the same row. A row that always fails is retried every second with no limit and no dead-letter state. |
PendingOutbox in platform/go/internal/store/postgres.go; platform/go/internal/store/outbox.go |
Claim rows inside a transaction or with a lease, cap attempts, and expose the backlog as a metric. Consumers are already idempotent. |
| R7 |
Local sessions are per process. Local sign-in sessions and the sign-in throttle live in gateway memory. They end on restart and are not shared between replicas, so break-glass sign-in behind several replicas only works on the replica that issued the cookie. |
LocalSessionManager in platform/go/internal/auth/local_session.go |
Store local sessions in PostgreSQL like workspace sessions, or pin break-glass access to one replica. |
| R8 |
Single sign-on sessions cannot be revoked server-side. The kionga_session cookie holds the raw ID token; the gateway verifies it on each request. Logout clears the cookie, but a copied cookie stays valid until the token expires. |
callback in platform/go/internal/auth/session.go; identify in platform/go/internal/auth/authenticator.go |
Opaque server-side session IDs with a revocation list. |
| R9 |
Role claims and group names are trusted directly. A token's roles claim is used as-is, and an identity-provider group named admin, operator, engineer, user or viewer grants that role without an entry in KIONGA_SSO_GROUP_ROLES. |
rolesFromClaims in platform/go/internal/auth/auth.go |
Filter claimed roles against the known human roles, and make implicit group-name mapping opt-in. |
| R10 |
Fallback principal in RBAC. When a request reaches the RBAC middleware without a principal, it acts as local-dev with MLAIOPS_LOCAL_ROLE (default admin). Today only the public blog reads arrive that way and their handlers ignore the principal, but a new public route would inherit administrator rights. |
RBACWithResolver in platform/go/internal/auth/auth.go; publicPath |
Treat a missing principal as anonymous and deny by default. |
| R11 |
Function pipelines run in a gateway goroutine. A function-mode run executes from a goroutine started by the submit request, so it depends on that gateway replica staying up. |
dispatchPipeline in platform/go/internal/httpapi/pipeline_dispatch.go |
Move execution to a worker that resumes from persisted step state. |
| R12 |
Unverified integrations. The Kubernetes pipeline executor, log export to Elasticsearch and OpenSearch, and external S3 connections are tested against fakes only; the Kubernetes executor is off by default. |
Implementation status |
Qualify each against a live system in staging before promising it. |
| R13 |
External dependencies on the scale path. The Helm chart does not install RustFS, Kafka, the Kafka REST proxy, MLflow, Prefect, Langfuse or an OIDC provider. Each needs an owner, security configuration and backups. |
deploy/helm/kionga/values.yaml; the deployment plan |
Name owners per installation; keep the acceptance checklist current. |