Skip to main content
r5d.chat has two moving parts: a control plane that owns identity and state, and one workspace per user that does the actual work.

The control plane

The control plane is a TanStack Start application running on Bun. It is the only component your browser talks to, and the only component that holds secrets. It owns:
  • Identity: accounts, sessions, invitations, and optional OIDC.
  • Configuration: encrypted provider credentials, models, and MCP servers.
  • Conversations: the durable message history in PostgreSQL.
  • Model streaming: it calls the model provider and streams the response.
  • Reconciliation: it creates and scales the Kubernetes objects per user.
PostgreSQL is authoritative for users, chats, messages, workspace intent, and audit history.

Workspaces

Every user maps to a deterministic set of Kubernetes objects, named from a hash of the user ID: The complete PVC mounts at /home/r5d. The chart never uses subPath, which keeps it compatible with runtimes like Kata. Inside the pod, r5dchat daemon exposes a private authenticated HTTP API for file, search, and process operations. The control plane calls it with the workspace’s bearer token. The browser never receives that token, and all file traffic is proxied through the control plane.
Tenant pods run as root on purpose so agents can install packages, but they receive no service-account token, host path, runtime socket, or Linux capability. See Security for the full model and its limits.

The idle lifecycle

Workspaces are expensive to keep running and cheap to restart, so they scale to zero automatically.
1

Activity starts the workspace

A chat run, file operation, upload, preview, or command scales the Deployment to one and waits for the daemon’s readiness probe. A chat response starts answering meanwhile; only its workspace tools wait.
2

Leases track in-flight work

Long-running operations such as shell commands take an active lease so a busy workspace is never reaped mid-task.
3

Idle workspaces scale to zero

When every lease has ended and WORKSPACE_IDLE_SECONDS has elapsed, a reconciler running every 60 seconds scales compute to zero.
4

The data stays

The Service, identity Secret, database records, and PVC all remain. The next request scales the same Deployment back up and remounts the same home directory.

Providers and models

A provider is an OpenAI-compatible endpoint: a base URL, an API key, and optional custom headers. A model belongs to a provider and adds a provider model ID, a display name, a context-window limit, and a maximum output size. Each user configures their own providers. Credentials are encrypted with SETTINGS_ENCRYPTION_KEY and never leave the control plane. Exactly one model per user is the default, enforced by a partial unique index in the database. Deleting or disabling the default promotes the oldest remaining enabled model, so a user is never left without one.

Configure providers

Base URLs, custom headers, the outbound URL guard, and default-model rules.

Projects and collaborative conversations

A project groups multiple conversations under one membership list and one shared artifact namespace. Owners and admins manage project access; members can use its conversations and artifacts. A standalone conversation can also have multiple participants without belonging to a project, in which case it owns a separate chat artifact namespace. Project conversations inherit access from project membership. Standalone conversations use their participant list. Message history and artifacts are shared in either case, while each participant still runs tools in their own private workspace with their own authorized model and MCP preferences.

Moving and archiving

A conversation can move into a project, out of one, or between projects from its menu in the sidebar. A standalone conversation’s files move with it into the project’s namespace and keep their paths, unless one is already taken there, in which case they all move into one folder named after the conversation. A project conversation’s files belong to the project and stay there when the conversation leaves. artifact:// links keep working either way, because they name the artifact rather than its path. Archiving tidies a conversation or project away without deleting anything. Archived items are listed under Archived in the sidebar, where they can be opened, restored, or deleted. An archived conversation can be read but not continued until it is unarchived. Archiving a project applies to every member and pauses new responses in its conversations until an owner or admin restores it.

Conversations and fresh context

A conversation owns one ordered message stream. Starting a fresh context does not create a new conversation: it appends a flagged system prompt to the same stream. Provider context begins at the most recent flag, while the durable transcript stays complete. This lets the agent recover earlier detail on demand from inside the workspace:
The CLI authenticates with workspace identity, and the server re-checks that the requesting workspace’s user is authorized for the project or standalone conversation.

Optional session memory and handover

The agent is told about a private working-memory folder at /home/r5d/.r5dchat/memory/<conversation-id>/. It can keep concise checkpoints, decisions, file references and next steps there using ordinary file tools. Reads and writes are voluntary; the first write creates the folder. Notes persist in the workspace across context resets and restarts. They are not published artifacts, not automatically read into every request, and not automatically deleted with a conversation. Do not put secrets in memory notes. For automatic context rollover in durable interactive chats, the control plane makes a separate, tool-free request to the same model for a concise handover at a settled tool boundary. A valid handover is embedded in the new context as working notes, alongside the exact current request and steering inputs. The request has a token/size bound and a 60-second timeout, makes no tool calls, and is not retried. If it fails, returns invalid output, or does not fit, the agent continues using memory and targeted history instead. Stop still cancels the run. This auxiliary request uses the configured upstream provider and may incur its normal inference usage.

Artifacts

Artifacts are remote objects owned by the control plane, not files under /home/r5d in a workspace. Project chats resolve to project/<id> and share one namespace across the project; standalone chats resolve to chat/<id> and share it only with their participants. Browser uploads and agent deliverables live on the dedicated artifact PVC. The runtime receives the active scope as ARTIFACT_SCOPE. Users and agents use canonical Markdown links such as [filename](artifact://<artifact-id>); the stable root ID keeps references valid if an artifact is moved, renamed, or amended. Each root points at a current head and retains immutable revisions for change control. Agents pass the artifact:// target to r5dchat artifacts read or pull, publish amendments with push --force, inspect history, and create a new head from an earlier revision with rollback. Revised content is stored as a raw snapshot or, when smaller, a Zstandard dictionary-compressed binary delta against the root snapshot. The browser resolves links to the current head through authenticated previews and downloads. In private chat transcripts, canonical links labelled with a .md or .markdown filename (case-insensitive) open a rendered Markdown dialog, just as raster image links open an image preview. The Markdown dialog has an explicit Download link and an accessible Close control; Escape closes it and returns focus to the link. Headings, lists, tables and code are rendered with the chat’s sanitizing renderer, with raw HTML disabled. Each opening fetches the current head again, so reopening the same canonical link after an amendment shows the latest revision. Previews are limited to 1 MiB; empty, failed or oversized previews still leave Download available. Links labelled with other filenames remain downloads. Explicit /api/artifacts?id=<artifact-id>&operation=download links always download, including Markdown; neither preview nor download bypasses artifact namespace authorization. Public snapshots do not expose private artifact previews.

How a message flows

1

The browser posts to the control plane

POST /api/chat with the conversation ID and the new message.
2

The control plane prepares the run

It authenticates the session, resolves the model (one picked for the conversation, otherwise the current default), decrypts the provider credentials, and ensures the workspace is running.
3

The model streams back

Text, reasoning, and tool calls stream to the browser as they arrive.
4

Tool calls execute in the workspace

Each tool call becomes an authenticated request to the workspace daemon, refreshing the activity timestamp.
5

Everything is persisted

Messages and tool results are written to PostgreSQL, so a reload or a new device shows the same conversation.

Full architecture

Component boundaries, trust boundaries, and the threat model.