> ## Documentation Index
> Fetch the complete documentation index at: https://arkor-92aeef0e-eng-615.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Project structure

> Where Arkor expects your code, and what lives in .arkor/ and ~/.arkor/.

A scaffolded Arkor project looks like this:

```
my-arkor-app/
├── src/arkor/
│   ├── index.ts        # createArkor({ trainer })
│   └── trainer.ts      # createTrainer({ ... })
├── arkor.config.ts
├── AGENTS.md           # AI-agent rules (skip with --no-agents-md on create-arkor / arkor init)
├── CLAUDE.md           # generated default: a one-line `@AGENTS.md` re-export
├── .arkor/             # per-project state (gitignored)
└── package.json        # dev / build / start
```

`AGENTS.md` is the source of truth for AI-agent instructions. `CLAUDE.md`
exists because Claude Code auto-loads `CLAUDE.md` from the project root,
and its `@<path>` directive is a built-in import. **When the scaffolder
creates `CLAUDE.md` itself**, the default contents are just the single
line `@AGENTS.md`, which inlines `AGENTS.md` into Claude's context and
keeps both files in sync without duplication. If a project-specific
`CLAUDE.md` already exists, the scaffolder leaves it alone and your
existing instructions stay authoritative.

Two layers of state matter beyond your source: `.arkor/` (per-project, in the repo) and `~/.arkor/` (per-user, in your home directory).

## `src/arkor/`

Arkor discovers your project by looking for `src/arkor/index.ts`. That file should expose the result of `createArkor()`. The CLI accepts three export shapes (in priority order):

1. **`export const arkor = createArkor({ trainer })`** (preferred, what the templates produce).
2. **`export const trainer = createTrainer({ ... })`** as a power-user shortcut when you do not need the umbrella.
3. **A default export** holding either an `Arkor` manifest or a `Trainer`.

```ts theme={null}
// src/arkor/index.ts (preferred shape)
import { createArkor } from "arkor";
import { trainer } from "./trainer";

export const arkor = createArkor({ trainer });
```

The trainer itself lives in a sibling file by convention so `index.ts` stays thin:

```ts theme={null}
// src/arkor/trainer.ts
import { createTrainer } from "arkor";

export const trainer = createTrainer({
  name: "support-bot-v1",
  model: "unsloth/gemma-4-E4B-it",
  dataset: { type: "huggingface", name: "arkorlab/triage-demo" },
  lora: { r: 16, alpha: 16 },
  maxSteps: 100,
});
```

You can split the trainer further (helpers, prompt builders) however you want; only `index.ts` needs to be at the canonical path.

To register additional trainers in the future, drop more files and pass them through `createArkor`. Today the API accepts a single `trainer`; `deploy` and `eval` slots are reserved on the type but not yet implemented.

## `arkor.config.ts`

The scaffolder generates a default `arkor.config.ts`:

```ts theme={null}
// arkor.config.ts
export default {};
```

It is currently a placeholder. The runtime does not read fields from it yet. All training defaults live on the `Trainer` itself (`maxSteps`, `learningRate`, `lora`, etc.) so you control them per-trainer rather than at the project level. Leave the file alone unless you have a reason to delete it.

## `.arkor/` (per-project, gitignored)

You should not commit this directory.

* **`.arkor/state.json`**. Project routing: `orgSlug`, `projectSlug`, `projectId`. This is how the runtime maps your local repo to a workspace on the managed backend. The file is created by `ensureProjectState()`, which runs the first time a CLI or Studio action needs a scope: starting training, hitting base-model inference, or creating the first deployment from Studio's [Endpoints](/studio/endpoints) page. Pure read paths (the Endpoints / Jobs list views, for instance) deliberately do **not** trigger bootstrap, so opening Studio on a fresh checkout shows empty lists without provisioning a remote project as a side effect. For **anonymous** workspaces the file is auto-created on that first write call. For **OAuth** workspaces the runtime errors out instead: today, neither `arkor login` nor `arkor init` populates the file, so you create it by hand with `{ orgSlug, projectSlug, projectId }` and the runtime takes over from there. Once it exists, do not edit it by hand.

  One caveat after `arkor logout`: logout removes only `credentials.json`, so a `.arkor/state.json` left by the previous anonymous identity survives. The next anonymous session gets a new organization and **ignores** the stale file on read paths (lists stay empty, Studio still opens), but the write paths above refuse to reuse or overwrite it (it may be a hand-maintained OAuth scope) and fail with a 409 / error telling you to delete `.arkor/state.json` to start a fresh anonymous project, or `arkor login` with the account that owns it.
* **`.arkor/build/index.mjs`**. Output of `arkor build`: an esbuild bundle of `src/arkor/index.ts` targeting Node 22.22 with bare specifiers kept external. `arkor start` runs this.

## `~/.arkor/` (per-user)

Lives in your home directory, shared across all Arkor projects on the machine.

* **`~/.arkor/credentials.json`**. Auth state. Either an Arkor Cloud OAuth token (written by `arkor login --oauth`, or by choosing `OAuth (browser)` in the interactive `arkor login` picker) or an anonymous token (created on first use if you never logged in). The file is tagged with a `mode` field of `"oauth"` or `"anon"` so the CLI knows which path you are on. `arkor login` writes only this file; it does **not** create `.arkor/state.json`.
* **`~/.arkor/studio-token`** (transient). A per-launch CSRF token written by `arkor dev` (mode `0600`). Used by Studio to authorize calls back into the CLI's local server. Rotated on every `arkor dev` run.

`arkor logout` deletes `credentials.json` only after confirmation (or with `--force`). If the file holds anonymous credentials, deleting it permanently loses access to that anonymous identity unless you backed up the file yourself. It does **not** touch the project-local `.arkor/state.json`; if you then start a fresh anonymous session, that leftover file blocks the write paths until you delete it or sign in (see the `.arkor/state.json` note above). The studio token is removed on process exit on a best-effort basis (it may linger if `arkor dev` crashes) and is rotated on the next launch.

## What the CLI looks for

* `arkor dev` starts the Studio web server at `127.0.0.1:4000` and writes `~/.arkor/studio-token`. It does not file-watch `src/arkor/`. Studio's `/api/manifest` endpoint does call `runBuild` and re-imports through a content-addressed copy of the bundle (so unedited reloads reuse Node's module cache and edits bust it), but only when the Studio UI fetches it (currently on Run training page mount). If you stay on that page across multiple runs the build artifact is reused; refresh the Run training page (or run `arkor build` from the terminal) between edits to pick up changes. `arkor dev` does not write `.arkor/state.json` on its own.
* `arkor build` reads `src/arkor/index.ts` (or the entry you pass) and writes `.arkor/build/index.mjs`. You only need to call this directly outside the Studio dev loop (e.g. CI, or right before `arkor start` from a script).
* `arkor start` resolves the entry through the runner, rebuilds `.arkor/build/index.mjs` if it is missing or you passed an explicit entry, and runs it (which calls `trainer.start()` and `trainer.wait()`). Triggering training from Studio internally spawns `arkor start`.

Knowing where state lives makes the CLI predictable: if something looks wrong, peek at `.arkor/state.json` and `~/.arkor/credentials.json` before reaching for support.
