> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arkor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# What Studio is

> The local web UI you get from arkor dev: start runs, watch them stream, chat with finished adapters, and publish them at *.arkor.app URLs.

Studio is the local web UI that boots when you run [`arkor dev`](/cli/dev). It lives on your machine, talks to the same CLI process over loopback, and goes away when you stop the dev server. There is no separate signup, no public URL.

Open it at `http://localhost:4000` (configurable with `arkor dev --port`).

## Layout

The header shows the current identity in the form `<mode> · <org>[ / <project>][ · <baseUrl-host>]`, where `mode` is `anonymous` or `auth0`. The cloud-api host suffix is hidden when the CLI is pointing at the production endpoint and shown otherwise.

Four pages, switched via the in-app nav (Overview, Jobs, Playground, Endpoints):

| Route          | Page                             | What you do here                                                                  |
| -------------- | -------------------------------- | --------------------------------------------------------------------------------- |
| `#/`           | Overview (this page)             | Project landing. Trigger a training run from here.                                |
| `#/jobs`       | [Jobs](/studio/jobs)             | Auto-refreshing list of every training run.                                       |
| `#/playground` | [Playground](/studio/playground) | Chat with a completed adapter or the base model.                                  |
| `#/endpoints`  | [Endpoints](/studio/endpoints)   | Publish a `*.arkor.app` URL for an adapter or base model and manage its API keys. |

The Jobs page also opens a per-run detail at `#/jobs/:id` (live status, loss chart, event log). It's a sub-route of Jobs, not its own nav tab.

## Architecture

```
Studio (browser tab, http://localhost:4000)
   │  /api/* on loopback, CSRF-token gated
   ▼
arkor CLI (your machine)
   │  authenticated HTTPS
   ▼
Arkor managed backend
```

Three checks run on every `/api/*` request:

1. **Host header guard.** Only `127.0.0.1` and `localhost` are accepted for every request, including static HTML. A victim navigated to a malicious site that DNS-rebinds onto `127.0.0.1` would still send `Host: evil.com`, which the server rejects with HTTP 403 before serving token-bearing HTML.
2. **Per-launch CSRF token.** `arkor dev` generates a 32-byte token (base64url) on every launch, injects it into `index.html` as `<meta name="arkor-studio-token">`, and requires it on every `/api/*` call as `X-Arkor-Studio-Token`. The job-event stream also accepts `?studioToken=` because `EventSource` cannot send custom headers; mutation routes do not accept query-string tokens. Cross-origin tabs cannot read the meta, so a "simple" cross-origin POST that skips preflight is still rejected. Comparison is `timingSafeEqual`.
3. **No CORS.** The SPA is same-origin so CORS adds no value. Reflecting `*` would let "simple" cross-origin POSTs (`text/plain`, `urlencoded`) through; the token check is what rejects them.

The token is rotated on every `arkor dev` launch, so a stale tab from a previous run will fail with HTTP 403 until you reload it.

## What works today

| Feature          | Notes                                                                                                                                                                                                                                     |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Run training** | A button on the Overview page (`#/`). It calls `POST /api/train`, which spawns `arkor start` and streams stdout/stderr back into the page as raw text.                                                                                    |
| **Jobs list**    | `#/jobs`. Auto-polled every 5 seconds with a manual Refresh button, search by name / ID, and a status filter (`All` / `Running` / `Completed` / `Queued` / `Failed` / `Cancelled`). Columns: Status, Name (links to detail), Created, ID. |
| **Job detail**   | `#/jobs/:id`. Live status badge, an SVG loss chart, and a raw event log (last 50 lines). Streams from `/api/jobs/:id/events` via Server-Sent Events.                                                                                      |
| **Playground**   | Chat UI on `#/playground`. Two modes: a single supported base model, or the final adapter from any completed job.                                                                                                                         |
| **Endpoints**    | `*.arkor.app` URL management on `#/endpoints`. Create deployments, toggle enabled / auth mode, issue and revoke API keys. The UI mirrors the SDK's [`CloudApiClient`](/sdk/deployments); every action has a programmatic equivalent.      |

## Not yet

These exist either at the SDK or HTTP-API level, but not as Studio UI today:

| Missing UI                                                 | Workaround                                                                                                                                                                                                      |
| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Cancel or pause a running job                              | Call [`trainer.cancel()`](/sdk/trainer-control#cancel) from your own code.                                                                                                                                      |
| Pick an intermediate checkpoint adapter in the Playground  | Use [`onCheckpoint({ infer })`](/sdk/callbacks) inside your trainer. The SDK's `infer` is bound to the just-saved checkpoint.                                                                                   |
| Filter / search / paginate the jobs list                   | Out of scope for the polling list view today.                                                                                                                                                                   |
| Multiple trainers per project                              | `/api/manifest` returns a single `trainer`. The SDK [`createArkor`](/sdk/create-arkor) only accepts one.                                                                                                        |
| Tweak `temperature` / `topP` / `maxTokens` from Playground | The HTTP API ([`InferArgs`](/sdk/infer)) accepts these; pass them when calling `infer` from the SDK.                                                                                                            |
| Loss chart zoom, export, tooltip                           | The chart is a static SVG path.                                                                                                                                                                                 |
| Edit a deployment's target after creation                  | The cloud API supports `PATCH /v1/endpoints/:id` with `target`; the [Endpoints](/studio/endpoints) UI exposes only the auth-mode and enabled toggles. Call [`updateDeployment`](/sdk/deployments) from the SDK. |

## When not to use Studio

Studio is a development tool. It only listens on loopback, only while `arkor dev` is up, and rotates its CSRF token every launch. For production usage, call [`infer`](/sdk/infer) from your own application code (or whatever serving layer you ship) rather than pointing users at Studio.
