feat: add remote workspaces and verified deliverables
This commit is contained in:
@@ -13,6 +13,7 @@ workspace, but not the same Agent loop.
|
||||
| Visible history | Open WebUI | Open WebUI |
|
||||
| Run events | Open WebUI | Runtime event store |
|
||||
| Context and memory | Open WebUI defaults are disabled by policy | Runtime policy and store |
|
||||
| Workspace files | Authenticated K1412 file browser | Same file browser and volume |
|
||||
|
||||
Selecting Work atomically upgrades `(user_id, chat_id)` in the Runtime store.
|
||||
Subsequent Chat requests for that conversation are rejected even if a client
|
||||
@@ -33,6 +34,13 @@ Every run records its model tier, strategy version, scheduler version, context
|
||||
policy, model/tool events, failures, and completion. This event stream is the
|
||||
foundation for replay, evaluation, and future experiment assignment.
|
||||
|
||||
Work completion is evidence-gated. Requests for scripts, reports, code, or
|
||||
other concrete artifacts are not accepted as complete until a file mutation
|
||||
has succeeded and a later read, diff, or command has verified the latest
|
||||
mutation. Bare interactive shell commands are rejected. If the iteration
|
||||
budget is exhausted without evidence, Runtime returns an explicit incomplete
|
||||
checkpoint instead of repeating an unverified model claim.
|
||||
|
||||
Read-only tools and read-only child Agents may run concurrently. Workspace
|
||||
mutations are serialized. A child Agent cannot delegate again, and read-only
|
||||
children receive only read-only tools.
|
||||
@@ -54,3 +62,18 @@ Only Open WebUI publishes a host port. Runtime, Gateway, Postgres, and Redis
|
||||
remain unpublishing services. Runtime and Gateway also join an egress-only
|
||||
bridge so the model API and a future SSH Docker host are reachable without
|
||||
exposing either service on the host.
|
||||
|
||||
## Workspace file path
|
||||
|
||||
```text
|
||||
browser with Open WebUI session
|
||||
-> /api/v1/k1412/workspace/*
|
||||
-> Open WebUI backend verifies the user
|
||||
-> backend mints a short-lived signed identity JWT
|
||||
-> Workspace Gateway verifies service key + user JWT
|
||||
-> provider selects only that user's hashed container and volume
|
||||
```
|
||||
|
||||
The file API exposes read-only browse, file download, and streamed directory
|
||||
archive operations. Paths pass through the same `/workspace` normalization as
|
||||
Agent tools. The browser never talks to Gateway or Docker directly.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Infrastructure map
|
||||
|
||||
This document separates shared infrastructure from the parts that define
|
||||
K1412 Agent behavior.
|
||||
|
||||
## Request path
|
||||
|
||||
```text
|
||||
Internet
|
||||
-> DNS / TLS
|
||||
-> Nginx Proxy Manager
|
||||
-> Unraid NAS : K1412 web stack
|
||||
-> Tailscale + SSH
|
||||
-> home-node-itx : per-user Docker workspaces
|
||||
```
|
||||
|
||||
## Shared infrastructure
|
||||
|
||||
These services make the application reachable or deployable, but changing
|
||||
them does not change the Agent loop:
|
||||
|
||||
| Service | Responsibility |
|
||||
| --- | --- |
|
||||
| `git.k1412.top` | Public source repository |
|
||||
| `docker.k1412.top` | Private immutable container images |
|
||||
| Nginx Proxy Manager | HTTPS entry and reverse proxy |
|
||||
| k1412 homepage | Service discovery entry |
|
||||
| Tailscale | Private NAS-to-execution-host network |
|
||||
| Unraid Compose Manager | Application lifecycle on the NAS |
|
||||
|
||||
Other NAS applications are independent tenants. They are not dependencies of
|
||||
K1412 Agent merely because they share the host.
|
||||
|
||||
## K1412 platform services on the NAS
|
||||
|
||||
| Container | Responsibility | Agent core? |
|
||||
| --- | --- | --- |
|
||||
| `k1412-agent-web` | Open WebUI auth, RBAC, history, UI, Chat loop | Product shell |
|
||||
| `k1412-agent-runtime` | Work loop and provider gateway | Yes |
|
||||
| `k1412-agent-gateway` | Identity, execution policy, workspace lifecycle | Yes |
|
||||
| `k1412-agent-postgres` | Open WebUI and Runtime durable state | State |
|
||||
| `k1412-agent-redis` | Open WebUI coordination/cache | State |
|
||||
| `k1412-agent-bootstrap` | Idempotent admin/tool bootstrap job | Operations |
|
||||
|
||||
Only Web publishes a host port. Runtime, Gateway, PostgreSQL, and Redis stay on
|
||||
private Compose networks.
|
||||
|
||||
## Physical execution host
|
||||
|
||||
`home-node-itx` is a dedicated, replaceable Docker execution node:
|
||||
|
||||
- Intel Core i3-12100F, 4 cores / 8 threads;
|
||||
- approximately 14 GiB usable RAM;
|
||||
- 120 GB NVMe system disk;
|
||||
- 500 GB SSD formatted ext4 and mounted at `/srv/k1412-data`;
|
||||
- Docker root at `/srv/k1412-data/docker`;
|
||||
- user workspace volumes stored on the same data disk.
|
||||
|
||||
The NAS Gateway connects with a dedicated Ed25519 key and strict host-key
|
||||
checking. The execution host stores no web/database secrets. Replacing it
|
||||
requires migrating Docker volumes and changing SSH configuration, not changing
|
||||
the Agent loop.
|
||||
|
||||
## Agent core
|
||||
|
||||
The code intended for continuous experimentation is:
|
||||
|
||||
- `agent_platform/runtime/loop.py`: loop and evidence policy;
|
||||
- `agent_platform/runtime/context.py`: context policy;
|
||||
- `agent_platform/runtime/tools.py`: tools and scheduling metadata;
|
||||
- `agent_platform/store.py`: events, plans, mode state, memory;
|
||||
- `agent_platform/gateway/`: isolated execution providers.
|
||||
|
||||
Frontend branding, reverse proxy configuration, and registry automation are
|
||||
important product/operations work, but they are not Agent intelligence.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Open WebUI integration
|
||||
|
||||
## Responsibility split
|
||||
|
||||
Open WebUI is the product shell. It owns account registration, login sessions,
|
||||
RBAC, administrator approval, conversation history, rendering, and the Chat
|
||||
mode Agent loop. K1412 does not fork those responsibilities into a second
|
||||
authentication or chat database.
|
||||
|
||||
K1412 Runtime owns Work mode: context selection, durable Work memory, planning,
|
||||
tool schemas, scheduling, parallel reads, child Agents, evidence gating,
|
||||
experiments, and run-event persistence. Workspace Gateway owns identity-bound
|
||||
execution policy and Docker lifecycle.
|
||||
|
||||
This split makes the expensive experimental surface small. A new scheduler or
|
||||
context policy changes Runtime, not the account system or entire frontend.
|
||||
|
||||
## Tool and progress rendering
|
||||
|
||||
Open WebUI natively understands tool-call and reasoning detail blocks. It also
|
||||
has a richer live status mechanism for loops that execute inside its own
|
||||
backend. Work is an external OpenAI-compatible provider, so it cannot mutate
|
||||
Open WebUI's internal socket status objects directly.
|
||||
|
||||
K1412 therefore emits Open WebUI-compatible completed tool detail blocks. Each
|
||||
tool appears once with:
|
||||
|
||||
- a localized action name;
|
||||
- sanitized arguments (file contents and patches are omitted);
|
||||
- success or failure state;
|
||||
- concise plain output instead of the internal JSON envelope.
|
||||
|
||||
K1412 deliberately does not expose hidden chain-of-thought. The UI shows
|
||||
execution progress, tool evidence, completion checks, and concise Agent
|
||||
summaries. This is enough to inspect behavior without treating private model
|
||||
reasoning as a product API.
|
||||
|
||||
The earlier implementation appended one unfinished and one completed
|
||||
`<details>` element for every tool call. Since standard provider streaming is
|
||||
append-only, Open WebUI rendered them as duplicate cards. Runtime now emits
|
||||
only the completed card; the normal generation indicator covers the active
|
||||
wait.
|
||||
|
||||
## Patches and upgrade cost
|
||||
|
||||
Open WebUI is pinned to v0.9.6 and an exact commit. The web image applies a
|
||||
small patch set during a reproducible build. Product-specific Svelte
|
||||
components and the authenticated workspace router live under `docker/web/`.
|
||||
|
||||
Upgrading Open WebUI requires:
|
||||
|
||||
1. updating the tag and commit pin;
|
||||
2. replaying every patch with `git apply`;
|
||||
3. building the Svelte production bundle;
|
||||
4. running authentication, Chat, Work, and file-browser E2E tests;
|
||||
5. reviewing upstream license changes.
|
||||
|
||||
The K1412 Agent loop is not embedded into Open WebUI, so most Work experiments
|
||||
do not incur this upgrade cost.
|
||||
@@ -0,0 +1,89 @@
|
||||
# Operations runbook
|
||||
|
||||
## Production layout
|
||||
|
||||
- Public URL: `https://agent.k1412.top`
|
||||
- NAS project: `/boot/config/plugins/compose.manager/projects/k1412-agent`
|
||||
- Public host port: `12004`
|
||||
- Execution provider: `ssh-docker`
|
||||
- Execution host Docker root: `/srv/k1412-data/docker`
|
||||
- Execution host workspace volumes: Docker volumes named
|
||||
`k1412-ws-data-<hashed-user-id>`
|
||||
|
||||
No credential belongs in this document or repository.
|
||||
|
||||
## Deployment
|
||||
|
||||
Build and push immutable `linux/amd64` images:
|
||||
|
||||
```bash
|
||||
docker buildx build --platform linux/amd64 -f docker/web.Dockerfile \
|
||||
-t docker.k1412.top/wuyang/k1412-agent-web:<release> --push .
|
||||
docker buildx build --platform linux/amd64 -f docker/runtime.Dockerfile \
|
||||
-t docker.k1412.top/wuyang/k1412-agent-runtime:<release> --push .
|
||||
docker buildx build --platform linux/amd64 -f docker/gateway.Dockerfile \
|
||||
-t docker.k1412.top/wuyang/k1412-agent-gateway:<release> --push .
|
||||
docker buildx build --platform linux/amd64 -f docker/workspace.Dockerfile \
|
||||
-t docker.k1412.top/wuyang/k1412-agent-workspace:<release> --push .
|
||||
```
|
||||
|
||||
Copy `deploy/docker-compose.yml`, `deploy/docker-compose.override.yml`, and
|
||||
`deploy/docker-compose.ssh.yml` to the NAS project. Update only immutable image
|
||||
tags and non-secret settings in the protected `.env`, validate with
|
||||
`docker compose config`, pull, and recreate.
|
||||
|
||||
The SSH override removes `/var/run/docker.sock` from Gateway and mounts the
|
||||
dedicated SSH directory read-only at `/root/.ssh`.
|
||||
|
||||
## Workspace migration
|
||||
|
||||
Before changing execution hosts:
|
||||
|
||||
1. stop Gateway so no new workspace mutation can begin;
|
||||
2. enumerate containers and volumes with the
|
||||
`app.k1412.component=user-workspace` label/name prefix;
|
||||
3. stop each workspace container;
|
||||
4. stream a tar archive of each named volume to an identically named volume on
|
||||
the new Docker host;
|
||||
5. pull the exact workspace image on the new host;
|
||||
6. start Gateway with the new SSH target;
|
||||
7. verify file listing and one read for every migrated volume;
|
||||
8. retain the old volumes until the verification window ends.
|
||||
|
||||
Volume names contain only a user-ID hash, not email or display name.
|
||||
|
||||
## Verification
|
||||
|
||||
For every release:
|
||||
|
||||
```bash
|
||||
.venv/bin/pytest
|
||||
RUN_DOCKER_INTEGRATION=1 .venv/bin/pytest tests/test_docker_workspace.py
|
||||
./scripts/verify-e2e.sh
|
||||
./scripts/audit-images.sh
|
||||
```
|
||||
|
||||
Production smoke checks must cover:
|
||||
|
||||
- anonymous requests are redirected/rejected;
|
||||
- an approved user can use Chat;
|
||||
- Chat can upgrade to Work but cannot downgrade;
|
||||
- Work creates and verifies a real file;
|
||||
- the file browser lists and downloads that file;
|
||||
- a second user cannot see it;
|
||||
- Gateway health reports `ssh-docker`;
|
||||
- the workspace container exists on the physical execution host, not the NAS.
|
||||
|
||||
## Backup priorities
|
||||
|
||||
Back up:
|
||||
|
||||
1. PostgreSQL;
|
||||
2. the Open WebUI data volume;
|
||||
3. Runtime state in PostgreSQL;
|
||||
4. per-user workspace volumes on the execution host;
|
||||
5. the protected deployment `.env` and SSH directory through the private
|
||||
infrastructure backup process.
|
||||
|
||||
Redis is reconstructible coordination state and is lower priority than the
|
||||
database and workspace volumes.
|
||||
+9
-7
@@ -38,12 +38,14 @@ chosen SSH configuration read-only, and moves Docker access to an SSH-connected
|
||||
execution host.
|
||||
|
||||
Workspace paths are normalized and rejected if they escape `/workspace`.
|
||||
File browsing and downloads use the same identity boundary. Single-file
|
||||
downloads are bounded to 128 MiB by default; directory archives are streamed.
|
||||
|
||||
## Deployment secrets
|
||||
|
||||
Secrets live only in a mode-600 deployment `.env` outside Git. Images contain
|
||||
no provider or infrastructure credentials. Any API key pasted into a chat
|
||||
should be rotated before deployment and the replacement placed in `.env`.
|
||||
no provider or infrastructure credentials. Provider credentials are never
|
||||
returned to the browser, logged by application code, or committed to Git.
|
||||
|
||||
The Web image is rebuilt from a commit-pinned Open WebUI release on
|
||||
digest-pinned Node and Python bases. RAG/vector, cloud storage, media model,
|
||||
@@ -64,8 +66,8 @@ remediate them.
|
||||
|
||||
## Remaining hardening before hostile public use
|
||||
|
||||
Local Docker provides process and filesystem isolation, but the Docker daemon
|
||||
remains a high-value boundary. Before treating the service as hostile
|
||||
multi-tenant infrastructure, move workspaces to the dedicated physical
|
||||
execution host, add egress policy, image signing/scanning, central audit
|
||||
retention, backup/restore tests, and resource-abuse alerts.
|
||||
Workspaces run on a dedicated physical Docker host, separated from the public
|
||||
web and database services. The Docker daemon remains a high-value boundary.
|
||||
Before treating the service as hostile multi-tenant infrastructure, add
|
||||
per-workspace egress policy, image signing, central audit retention,
|
||||
backup/restore drills, quotas, and resource-abuse alerts.
|
||||
|
||||
Reference in New Issue
Block a user