# Architecture ## Design objective K1412 Agent is a multi-user web coding Agent whose loop can be changed independently of its account system and user interface. The architecture keeps the high-cost experimental surface small: researchers should be able to change context, memory, scheduling, tools, delegation, or completion policy without forking authentication, chat history, Docker lifecycle, or the whole frontend. The system deliberately has one Agent path. The earlier idea of a lightweight Chat loop beside a custom Work loop was removed because it duplicated product behavior, confused model selection, and increased the amount of frontend and backend code that had to remain compatible. ## System context ```mermaid flowchart LR U["Browser user"] --> P["DNS, TLS and Nginx Proxy Manager"] P --> W["Open WebUI
auth, RBAC, history, UI"] W --> R["Agent Runtime
loop, context, memory, model routing"] R --> M["Model providers
K1412 API or DeepSeek"] R --> G["Workspace Gateway
identity and execution policy"] G -->|Tailscale + SSH Docker| X["Per-user workspace container
home-node-itx"] W --> DB["PostgreSQL
users and chats"] R --> DB W --> Q["Redis
coordination"] ``` Only Web is publicly reachable. Runtime, Gateway, PostgreSQL, and Redis live on private Compose networks. Workspace containers run on a separate physical Docker host, receive no application secrets, and expose no host ports. ## Responsibility boundaries | Layer | Owns | Must not own | | --- | --- | --- | | Open WebUI | Registration, login, sessions, admin approval, RBAC, chat persistence, rendering | Agent scheduling, provider secrets, Docker access | | Agent Runtime | Model catalog, context policy, Agent loop, tools, scheduling, delegation, memory, plans, run events, evidence gates | Browser sessions, Docker socket | | Workspace Gateway | Signed identity enforcement, workspace path policy, per-user locks, Docker lifecycle, file delivery | Model calls, public authentication UI | | Workspace container | User code, dependencies, generated files, command execution | Other users' data, service credentials, Docker socket | | PostgreSQL | Open WebUI durable state plus Runtime events, plans, and memories | Execution | | Shared infrastructure | TLS, registry, source hosting, private routing, model serving | Agent behavior | This separation lets the Web UI be upgraded on its own schedule while the Agent loop can iterate frequently. ## Request lifecycle ### Agent request ```mermaid sequenceDiagram participant B as Browser participant W as Open WebUI participant R as Runtime participant M as Model provider participant G as Gateway participant X as User workspace B->>W: Authenticated chat request W->>R: OpenAI-compatible request + service key + signed user identity R->>R: Verify identity, build context, record run.created loop Until evidence-backed completion or budget exhausted R->>M: Recent context + tools + provider policy M-->>R: Assistant content and/or tool calls R->>R: Normalize, validate, limit, and schedule calls R->>G: Tool request + service key + freshly signed identity G->>G: Verify identity and bind user workspace G->>X: Execute with resource and path constraints X-->>G: Exit code, output, or file result G-->>R: Structured tool result R->>R: Persist events and evaluate completion evidence end R-->>W: SSE answer and completed tool detail blocks W-->>B: Rendered Agent response ``` The identity sent by Open WebUI is verified once when Runtime accepts the request. Runtime then signs the already verified identity again for every Gateway call. This prevents a long Agent run from reusing an identity token that expired while the model or a tool was working. ### Workspace file request ```text browser with Open WebUI session -> /api/v1/k1412/workspace/* -> Open WebUI verifies the logged-in user -> Web signs a short-lived user identity -> Gateway verifies service key + signed identity -> Gateway selects only the hashed container and volume for that user -> browse, download, or streamed archive response ``` The browser never receives a Gateway service key and never connects to Docker. ## Runtime internals Runtime is an OpenAI-compatible provider from Open WebUI's point of view, but normal chat completions are implemented by the K1412 loop: - `ModelProvider` translates the internal model spec to a provider request and normalizes streamed completion responses. - `ContextPolicy` removes rendered tool-detail markup, prepends the system contract and durable memories, and keeps the newest visible messages within a per-model character budget. - `ToolRegistry` declares tool schemas and routes workspace tools to Gateway or state tools to `RuntimeStore`. - `AgentLoop` owns iteration, tool normalization, validation, scheduling, recovery, delegation, and evidence-gated completion. - `RuntimeStore` persists run events, per-chat plans, and user memories. More detail is in [agent-loop.md](agent-loop.md). ## Workspace architecture `local-docker` and `ssh-docker` implement the same `ExecutionProvider` contract. Production uses `ssh-docker`: Gateway talks to Docker on `home-node-itx` through a dedicated SSH configuration mounted read-only. Each immutable Open WebUI user ID maps to a SHA-256-derived workspace ID and therefore to exactly one: - container named `k1412-ws-`; - named volume `k1412-ws-data-`; - bridge network `k1412-ws-net-`. Containers run as UID/GID 1000 with a read-only root filesystem, all Linux capabilities dropped, `no-new-privileges`, PID/CPU/memory limits, no Docker socket, and no published ports. `/workspace` is the only persistent writable filesystem. `/tmp` is writable but mounted `noexec`. Python environments live at `/workspace/.venv`. User-level home and package caches live below `/workspace/.agent`. The image puts the virtual environment and user binary directories first on `PATH`. Commands run under Bash with `pipefail`, so a failed install cannot be hidden by a successful `tail` or similar pipeline stage. When `WORKSPACE_IMAGE` changes to a new immutable tag, Gateway replaces a stale container on its next access but reattaches the existing named volume. Image upgrades therefore change the execution environment without deleting user files. ## Networks and deployment Production runs as one Unraid Compose Manager project: - `public`: Web ingress; - `internal`: private service-to-service traffic; - `egress`: Runtime provider access and Gateway SSH access. Only Web publishes NAS port `12004`. Nginx Proxy Manager on the VPS terminates TLS for `agent.k1412.top` and forwards over Tailscale. Images are built for `linux/amd64`, pushed to `docker.k1412.top/wuyang/*` with immutable timestamp-and-commit tags, and deployed by updating only the relevant service. The documentation portal is packaged inside the Web image at `/doc/`. It does not add a public service, port, database, or proxy rule. ## Stable and experimental surfaces Stable platform contracts: - authenticated Web-to-Runtime request; - signed Runtime/Web-to-Gateway identity; - `ExecutionProvider` workspace semantics; - per-user volume isolation; - run-event persistence; - public model IDs; - workspace file API. Expected experiment surfaces: - prompt and context policy; - memory selection; - tool catalog and intent routing; - scheduler and parallelism; - delegation; - recovery and evidence rules; - model budgets and routing. Version experiment-sensitive behavior in `run.created` so results remain comparable after the implementation changes.