# Workspaces A workspace is a named draft of the store design (ADR 0027 §3). The builder's own draft is the workspace `main`; every agent and developer workspace is a separate row beside it — same bundle format, same validation, different identity. Design work by agents happens **only** in their own workspace: an Ajan token is bound to exactly one, and a write to `main` is refused with 403 `agent_workspace_only`. ## The draft model - [`workspaces.list`](/reference/routes/workspaces.list) — every workspace with its kind, status, draft rev and sandbox state. - [`workspaces.create`](/reference/routes/workspaces.create) — creates one; for kind `agent` it also mints the bound Ajan token (returned once). At most [`workspacesPerStore`](/reference/limits) per store; idle workspaces archive after [`workspaceIdleArchiveDays`](/reference/limits) days. - Every draft route takes `?workspace=` (default `main`): [`design.draft.get`](/reference/routes/design.draft.get), [`design.draft.put`](/reference/routes/design.draft.put), [`design.draft.delete`](/reference/routes/design.draft.delete), [`design.draftFromTemplate`](/reference/routes/design.draftFromTemplate). Draft writes are **whole-draft compare-and-swap**: `PUT` carries `expectedRev`, and a mismatch is HTTP 409 with the current state in the body — `{rev, hash, updatedAt, updatedBy}`. On 409, re-read the draft, redo your edit on top of the new body, and PUT again with the new rev. **The server never merges** — there is no three-way merge endpoint, no "force" flag, and a lost race is always resolved client-side. Through the CLI the same contract surfaces as [`push`](/reference/cli) exiting 4 on conflict. Workspace writes carry the `workspace` agent policy: they execute directly for every token kind — no proposal is recorded — because a workspace draft changes nothing live. The proposal boundary sits at publish, not at draft edits. ## Stable preview URL [`design.previewUrl.get`](/reference/routes/design.previewUrl.get) with `?workspace=ws_…` returns a signed link that **follows the workspace's current draft** — push again and the same URL shows the new state (a lightweight poll reloads the open tab). With `?hash=` instead, the link freezes one snapshot. Hand the workspace link to the owner once; it stays valid as you iterate. CLI: [`preview`](/reference/cli), or [`dev`](/reference/cli) to watch, push on change and print it. ## Builder always wins The acceptance rule is literal: *builder open + agent edit + builder save = no data loss.* It holds structurally on the agent path — your workspace and the builder's `main` are different rows, so you can never overwrite an owner's unsaved work, and the owner's edits never appear in your draft mid-flight. Developer tokens writing `main` share the builder's CAS: on a 409 the builder rebases its own unsaved edits onto the new rev client-side; your job on a 409 is the same — rebase your edit, never expect the server to reconcile. ## Adopt is owner-side ("Taslağa al") Getting workspace work into the builder's draft is the owner's click, not your call: [`workspaces.adopt`](/reference/routes/workspaces.adopt) (`POST /v1/workspaces/{ws}/adopt`) copies the workspace body into `main` under `expectedMainRev` CAS — a copy, never a merge; the builder's unsaved state guards itself with the rev check. The owner does this from the panel after looking at your preview. From there, publishing to the live store is a separate owner decision ([`design.publish`](/reference/commands/design.publish) — owner policy; an agent's publish intent travels as a proposal design item, which re-checks the workspace hash and the live sequence before running). Deleting your draft ([`design.draft.delete`](/reference/routes/design.draft.delete)) also takes `expectedRev` — the same CAS discipline applies to discarding as to writing. ## Hosted sandboxes (P1.5) A workspace created with **Barındırılan sandbox** gets an isolated container Mozaik prepares: the store opened as a project tree under `/workspace/store`, the **pinned `mozaik` CLI of the exact platform build**, AGENTS.md and `.mcp.json` — nothing to install, nothing to version-match. No agent process runs inside; you drive it remotely: - [`workspaces.exec`](/reference/routes/workspaces.exec) — run one command (cwd defaults to `/workspace/store`; timeout up to [`execTimeoutMaxMs`](/reference/limits) ms, default [`execTimeoutDefaultMs`](/reference/limits); stdout/stderr capped at [`execOutputMaxBytes`](/reference/limits) bytes). The pinned CLI is on PATH: `mozaik status`, `mozaik push`, `mozaik propose`. - [`workspaces.files.list`](/reference/routes/workspaces.files.list) / [`workspaces.files.read`](/reference/routes/workspaces.files.read) / [`workspaces.files.write`](/reference/routes/workspaces.files.write) — file access confined to `/workspace` (≤ [`workspaceFileMaxBytes`](/reference/limits) bytes per file; base64 for binary). - [`workspaces.backup`](/reference/routes/workspaces.backup) — snapshot `/workspace/store` to storage ([`backupTtlDays`](/reference/limits)-day TTL). Backups also run automatically (debounced [`backupDebounceSec`](/reference/limits) s after activity) and restore on the next cold start. - [`workspaces.activity.list`](/reference/routes/workspaces.activity.list) — the append-only feed of everything that ran (op, summary, exit code, duration). The store owner sees the same feed in the panel. - [`workspaces.preview`](/reference/routes/workspaces.preview) — the workspace's stable draft preview URL (never wakes the container). **Lifecycle.** The container sleeps after [`sandboxSleepAfterSec`](/reference/limits) s idle and its filesystem is wiped on sleep — the platform is the source of truth; on the next call the sandbox restores your scratch backup and re-opens the store project. At most [`hostedAwakePerStore`](/reference/limits) sandboxes per store run at once, with a [`awakeBudgetPerDaySec`](/reference/limits)-second daily awake budget (HTTP 429 beyond either). Idle workspaces archive after [`workspaceIdleArchiveDays`](/reference/limits) days. **Credentials.** The container never stores a token: each call injects a propose-only, workspace-bound, 24-hour token as environment. Everything you run inside obeys the same rules as outside — mutations become proposals; publishing stays with the owner. **Internet access** is chosen by the owner at creation: *Kısıtlı* (default — the sandbox reaches only the Mozaik API and docs) or *Açık* (unrestricted; separately priced in the future). **Staying current.** Exec responses carry `imageStale: true` when the sandbox's image is older than the platform (the panel shows "Güncel değil"); the owner's **Yenile** rebuilds it on the current image — your files come back from the backup. An image below the platform's supported CLI floor is refused outright with HTTP 412 `workspace_stale` until refreshed. CLI shorthands: `mozaik workspace exec -- `, `mozaik workspace logs`, `mozaik workspace preview`.