# Multi-Tenancy ## Overview Agent PaaS is scoped by **workspaces**. A workspace is an isolated configuration and deployment environment: it has its own persistence backend (where CR YAML files are stored) and its own deployment target (the Kubernetes cluster or namespace where agents run). All catalog reads/writes and all live resource queries are routed to the active workspace. ## Workspace A workspace has two independent dimensions: ### Persistence Determines where CR YAML files are stored and how mutations are committed. | Type | Fields | Description | | ------- | ---------------------------------------- | ---------------------------------------- | | `local` | `directory`, `authorName`, `authorEmail` | Local filesystem repo via isomorphic-git | | `git` | `repoUrl`, `branch` | Remote Git repository (GitHub) | Both types accept optional directory overrides in `WorkspacePersistence` (for example `modelsDirectory`, `toolsDirectory`, `kbsDirectory`, and `agentsDirectory`). In the current implementation, catalog constructors still use their built-in default directories. The `local` type writes commits directly to the filesystem; mutations are auto-committed and the PR lifecycle is skipped. The `git` type writes via pull request; the PR may be auto-merged based on policy. ### Deployment Determines where live runtime status is read from. | Type | Fields | Description | | ------- | -------------------------------------------------- | -------------------------------------------- | | `k8s` | `namespace`, `apiUrl?`, `token?`, `skipTlsVerify?` | Kubernetes API server (in-cluster or remote) | | `local` | — | No-op; no live status available | ## Server Initialization At startup, `Server.init()` reads the workspace list and constructs one `Catalog` and one `Resources` instance, each internally keyed by workspace ID: ``` Server.init() → Catalog(workspaces) — one GitClient + WorkspaceCatalog per workspace → Resources(workspaces) — one K8sClient + WorkspaceResources per workspace (k8s only) ``` Both are fully initialized before the first request is served. Workspace configuration is managed by `WorkspaceManager` (accessed via `server.ws`); current implementation returns a single local workspace (`default`). ## Workspace Selection ### Cookie The active workspace is stored in the `workspaceId` cookie (`path: '/'`). On each request, `hooks.server.ts` reads this cookie into `event.locals.workspaceId`. ### Default Selection `+layout.server.ts` calls `server.ws.list()` on every page navigation and returns the full list to the frontend. It validates the current `workspaceId` cookie against both the workspace list and the user's permitted workspaces (`user.workspaces`). If invalid or absent, it resets the cookie to the first workspace in the list. ### Workspace Selector UI The workspace selector displays all workspaces the current user has access to and indicates the active one. Selecting a workspace writes the `workspaceId` cookie and reloads the current page. ## Request Flow ``` Browser request → hooks.server.ts reads session cookie → locals.user reads workspaceId cookie → locals.workspaceId → route handler / load function server.catalog.getWorkspaceCatalog(locals.workspace.id) → catalog ops server.resources.getWorkspaceResources(locals.workspace.id) → resource queries ``` If the workspace is absent or does not match any configured workspace, `getWorkspaceCatalog` / `getWorkspaceResources` throw `NotFoundError`.