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

# Sessions and persistence

> Three persistence layers in Fleet — Alembic-managed Postgres for committed history, the Daytona Volume Scope for durable workspace state, and volatile interpreter context.

Fleet separates persistence into three layers with distinct lifetimes and authorities. The commit boundary is a single durable event: `POST /api/sessions/{session_id}/turns` streams a Turn attempt, and only a successful commit advances Session history, publishes Artifact identity, and writes the `Turn`/`Run`/`Checkpoint`/`Artifact` rows atomically.

For the sandbox and volume mechanics that sit under this model, see [Daytona runtime](/fleet-rlm/concepts/daytona-runtime).

## The three persistence layers

| Layer                          | Backed by                                                  | Lifetime                   | Authority for                                                                                                                                  |
| ------------------------------ | ---------------------------------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Alembic-managed Postgres       | `src/fleet_rlm/persistence/` repositories over Postgres    | Canonical, across restarts | Sessions, committed Turns, Runs, Attachments, Artifacts, sandbox bindings                                                                      |
| Daytona Workspace Volume Scope | Daytona persistent volume mounted at `/home/daytona/fleet` | Across sandbox replacement | Workspace Memory, browsable projects, Session Workspace files, Run attachments and candidates, committed Artifact bytes, private `result.json` |
| Interpreter context            | Daytona code-interpreter context inside one Sandbox        | One Run                    | Live Python state across RLM iterations within a Run                                                                                           |

Every later Run receives a fresh interpreter context. Workspace files and Postgres rows outlive Sandbox replacement independently.

## Alembic-managed Postgres

The relational store is the canonical record. Domain repository interfaces live under `src/fleet_rlm/persistence/`, and Alembic owns the live schema. Runtime startup never applies migrations.

Apply the head schema explicitly before serving traffic:

```bash theme={null}
uv run python scripts/db_init.py
# or, for a targeted deploy step
uv run alembic upgrade head
uv run alembic check
```

The managed profile requires `FLEET_DATABASE_URL`. Local SQLite is suitable for development only. Fleet runs under one deterministic local `User` + `Workspace` scope, so requests carry no `Authorization` header and there is no row-level security scoping to configure.

## Committed Turn history

A Turn attempt is streamed through:

```
POST /api/sessions/{session_id}/turns
Idempotency-Key: <caller-generated key>
```

The response is the AI SDK UI message stream. On a successful commit, the Turn atomically writes to `Turn`, `Run`, `Checkpoint`, and `Artifact` tables in one transaction. A failed commit advances no Session history, publishes no Artifact identity, and still releases owned resources.

Cancelled attempts persist a bounded tombstone:

* The original user input.
* One assistant message carrying only a `cancelled` `data-status` part.
* Observed usage counters.
* The closed text `Turn cancelled`.

Tombstones never carry reasoning, code, output, or Tool evidence.

The full committed Session history stays host-side. The RLM reads it through the bounded `read_session_history` Tool rather than by loading transcripts into a prompt.

## Session catalog

Sessions are owned by the local scope and addressed under `/api/sessions` (no `/v1` prefix).

| Endpoint                   | Method  | Purpose                                            |
| -------------------------- | ------- | -------------------------------------------------- |
| `/api/sessions`            | `GET`   | List owned Sessions                                |
| `/api/sessions`            | `POST`  | Create a Session                                   |
| `/api/sessions/{id}`       | `GET`   | Read Session metadata                              |
| `/api/sessions/{id}`       | `PATCH` | Rename or archive                                  |
| `/api/sessions/{id}/turns` | `GET`   | Ordered committed Turn history                     |
| `/api/sessions/{id}/turns` | `POST`  | Stream a Turn attempt (requires `Idempotency-Key`) |

There is no export endpoint. Backup is handled at the Postgres and Volume layers, not through an application-level dump.

## Daytona Workspace Volume Scope

The Volume mount is fixed at `/home/daytona/fleet`, configured through `[defaults.daytona] volume_mount_path`. Fleet provisions only the namespaces it owns and leaves bundled Skills host-owned — they are not copied into the Volume.

```
/home/daytona/fleet/                          # volume_mount_path
├── workspaces/<workspace-id>/
│   ├── memory/MEMORIES.md                    # Workspace Memory
│   ├── projects/<slug>/                      # Browsable project state
│   ├── sessions/<session-id>/                # Session Workspace
│   └── runs/<run-id>/                        # Run attachments, candidates, result.json
├── attachments/                              # Shared durable Attachment bytes
├── artifacts/                                # Committed Artifact bytes
└── recursive/<workspace-id>/<run-id>/<call-index>/  # Recursive child scope
```

## Attachments and Artifacts

Attachments and Artifacts share the Volume Scope but move through very different lifecycles.

**Attachments.** `POST /api/attachments` uploads durable bytes to Workspace Volume Scope before writing metadata, then stages the file for the referenced Runs. Attachment identity is public at upload time.

**Artifacts.** A candidate lives privately in the Run scope until Turn Commit. The path is:

1. The host-mediated `create_artifact` produces a private Run candidate.
2. Verified bytes reach a UUID-unique durable path under `artifacts/`.
3. Turn Commit atomically writes the Artifact row and publishes identity.

There is no `POST /api/artifacts`. Read paths are:

* `GET /api/artifacts/{artifact_id}` — metadata.
* `GET /api/artifacts/{artifact_id}/content` — verified bytes.

Failed metadata commits may leave GC-eligible orphan bytes behind, but never public rows.

## Session Workspace files

Session Workspace files are immediate private state under the Session Volume path. Daytona exposes bounded operations against them:

* List and read with pagination.
* Append, in-place unique fragment edit, and whole-file replacement.
* Strict delete for files and empty directories only. No recursion, no force flag.

Writes, appends, edits, and deletes accept optional SHA-256 preconditions and never follow symlinks. For write and append, checksum comparison and mutation execute inside one mounted Workspace agent operation with target locking and inode revalidation across I/O Sandboxes.

Existing Workspace documents can be staged as private Artifact Candidates without resending bytes. Turn Commit remains the only publication boundary. Workspace files survive failed Runs and Sandbox replacement independently of the commit-gated `result.json` snapshot and the Artifact lifecycle.

### The Files API surface

The Files API (`/api/files*`) always resolves the process-local Workspace. Callers cannot select a Workspace or address Daytona Volume, mount, Sandbox, Attachment, Artifact, Session, or Run identifiers.

* No rename operation.
* `DELETE /api/files/content` removes one file or one empty directory. Non-empty targets return `409`.
* `PATCH /api/files/content` requires the `old` text to occur exactly once. Absent or ambiguous matches return `409`. The response returns the fresh checksum so callers can chain preconditions.

See [HTTP API](/fleet-rlm/reference/http-api) for the full request and response shapes.

## Workspace Memory

Workspace Memory is separate workspace-wide immediate state, not Session history and not a Turn-commit record. It lives at a fixed path:

```
workspaces/<workspace-id>/memory/MEMORIES.md
```

A pre-existing root `MEMORIES.md` migrates on first open without losing content. Session and Run state retain their nested paths below that root.

The RLM accesses memory through a bounded Tool set:

* `read_workspace_memory`
* `remember`
* `list_memories`
* `search_memories`
* `edit_memory`
* `forget`
* `update_workspace_memory` (back-compat alias)

Fleet injects a bounded `workspace_memory tail` digest of relevant plus newest records into each Turn's `session_context`. The digest is capped at 4 KiB, so the RLM has recent context without needing a Tool call.

The RLM may append a record only when the user explicitly asks to remember something. `remember` writes v3 records with a fresh id and up to 4 KiB of formatted UTF-8. Records are durable immediately. Reads return the newest complete records within a fixed 256 KiB byte budget, and the configured `max_upload_bytes` caps the whole memory file.

Memory survives failed Runs and Sandbox replacement.

## Interpreter context

Interpreter calls within one Run reuse a single context, so Python globals, imports, and helper functions persist across RLM iterations inside that Run. The context is volatile:

* It never crosses Run boundaries.
* Every later Run receives a fresh context.
* It carries no durable state — anything meant to outlive the Run must reach the Volume or Postgres.

## Backup and recovery

* **Postgres.** Alembic head is the durable schema baseline. Use `uv run alembic upgrade head` for explicit deployment and `uv run alembic check` to verify.
* **Volume.** Daytona Volume snapshots taken through the provider are the durable-state backup path for Workspace Memory, Session Workspace files, Attachments, and Artifact bytes.
* **No first-class export.** Fleet does not ship an "export everything" command. Postgres and the Volume are the source of truth, backed up in place.

## See also

<CardGroup cols={2}>
  <Card title="Daytona runtime" href="/fleet-rlm/concepts/daytona-runtime">
    Sandbox lifecycle, volume mount, and the host-callback bridge under this persistence model.
  </Card>

  <Card title="HTTP API" href="/fleet-rlm/reference/http-api">
    Session, Turn, Attachment, Artifact, and Files endpoint reference.
  </Card>

  <Card title="Configuration" href="/fleet-rlm/reference/configuration">
    `FLEET_DATABASE_URL`, `[defaults.daytona] volume_mount_path`, and related policy in `config/fleet.toml`.
  </Card>

  <Card title="Architecture" href="/fleet-rlm/concepts/architecture">
    Where the three persistence layers sit inside the overall Fleet layering.
  </Card>
</CardGroup>
