> ## 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-rlm — Daytona session manifests, the volatile code-interpreter context, and the canonical Neon Postgres multi-tenant store.

`fleet-rlm` persistence spans three layers: session manifests on the Daytona mounted volume, the volatile code-interpreter context, and the canonical Neon/Postgres multi-tenant store. Each has a distinct lifetime and authority.

## What gets persisted, where

| Layer                    | Backed by                     | Lifetime                            | Authority for                    |
| ------------------------ | ----------------------------- | ----------------------------------- | -------------------------------- |
| Session manifest         | Daytona mounted volume        | Across sandbox restarts and resumes | Restart-restore, agent state     |
| Code-interpreter context | Daytona sandbox memory        | Warm turns in one sandbox           | Live Python state                |
| Neon/Postgres            | External Postgres / Neon      | Canonical multi-tenant state        | Identity, tenants, history index |
| Local sidecar            | `integrations/local_store.py` | Per-process                         | Local development convenience    |

## Session manifest

Session manifests are the **authoritative local restart-restore source**. They live on the mounted Daytona volume at:

```text theme={null}
meta/workspaces/<workspace_id>/users/<user_id>/react-session-<session_id>.json
```

A best-effort legacy fallback under `workspaces/...` exists for migration compatibility only.

The manifest's `state` payload restores:

| Field               | What it restores                                                                                                 |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `history`           | `dspy.History` conversation turns                                                                                |
| `core_memory`       | `AgentRuntime` core memory — default core memory plus persisted keys                                             |
| `document_paths`    | Session-local loaded document paths                                                                              |
| `interpreter_state` | Daytona interpreter state — sandbox ID, workspace path, repo URL/ref, context paths, volume name, volume subpath |

### Import semantics: replace, not merge

Importing a session **replaces** session-local memory and document state instead of merging into the currently active runtime. This is intentional — merging would silently leak state across sessions.

Empty or missing state resets:

* Conversation history.
* Core memory.
* Loaded documents.
* Sandbox buffers.

Switching sessions therefore cannot leak stale agent context. If you want to carry state forward, do it explicitly via document attachments or core-memory writes, not by relying on residual runtime state.

## Three storage layers (recap)

This is the same three-layer model from [Daytona runtime](/fleet-rlm/concepts/daytona-runtime), repeated here for the persistence story:

```mermaid theme={null}
graph TB
    subgraph "Across sandboxes"
      TEMPLATE["fleet-rlm-base snapshot<br/>Reusable environment template"]
    end
    subgraph "Across warm turns (one sandbox)"
      CONTEXT["code_interpreter.create_context()<br/>Python globals, imports, helpers"]
    end
    subgraph "Across sandbox restarts"
      VOLUME["/home/daytona/memory/<br/>memory/ artifacts/ buffers/ meta/"]
    end
```

Repos, staged context inputs, package installs, caches, and scratch files **are not durable by default**. They live in the workspace root, not the volume root. Files survive context reset, sandbox restart, or session resume only when they are explicitly written to one of the four canonical durable directories.

## Durable volume layout

```text theme={null}
/home/daytona/memory/
├── memory/        # agent durable memory (writes via core memory APIs)
├── artifacts/     # generated artifacts (reports, exports, exports)
├── buffers/       # streaming buffers and intermediate large files
└── meta/
    └── workspaces/<workspace_id>/users/<user_id>/
        └── react-session-<session_id>.json
```

Workspace-aware tools target the live sandbox workspace; volume-aware tools target the durable directories. There is no automatic sync between the two.

## Neon-backed multi-tenant state

When `DATABASE_REQUIRED=true` (production default), canonical state lives in **Neon/Postgres** via SQLAlchemy models under `src/fleet_rlm/integrations/database/`:

| Module                   | Owns                                        |
| ------------------------ | ------------------------------------------- |
| `models_identity.py`     | Tenants, users, workspace identities        |
| `models_jobs.py`         | Background job tracking                     |
| `models_memory.py`       | Persisted memory items                      |
| `models_optimization.py` | Optimization runs, datasets, results        |
| `models_runs.py`         | Execution run steps                         |
| `models_sandbox.py`      | Sandbox metadata snapshots                  |
| `repository_*.py`        | Repository-pattern access to each table set |

Row-level security (RLS) is enforced — see ADR-003 in the upstream repo for the policy. Multi-tenant admission happens at the auth boundary: in `AUTH_MODE=entra`, an Entra bearer token is exchanged for an internal `tenant_id` + `user_id`, and every query is scoped to that tenant.

When `DATABASE_REQUIRED=false` (local dev), the runtime falls back to `integrations/local_store.py` for session history and turn transcripts. This is convenience-only — not a production persistence path.

## Session lifecycle endpoints

| Endpoint                       | Method   | Purpose                 |
| ------------------------------ | -------- | ----------------------- |
| `/api/v1/sessions/state`       | `GET`    | Active session envelope |
| `/api/v1/sessions`             | `GET`    | List sessions           |
| `/api/v1/sessions/{id}`        | `GET`    | Session detail          |
| `/api/v1/sessions/{id}`        | `DELETE` | Delete a session        |
| `/api/v1/sessions/{id}/turns`  | `GET`    | Conversation turns      |
| `/api/v1/sessions/{id}/export` | `POST`   | Export session manifest |

Sessions are scoped to the authenticated `tenant_id` + `user_id` in `AUTH_MODE=entra`. Export produces a manifest that round-trips with the local `meta/workspaces/.../react-session-*.json` shape.

## Reset and force-new-session

The runtime forces sandbox recreation only when continuity would be unsafe:

* Explicit session reset (`force_new_session=true`).
* Mounted volume incompatibility.
* Unrecoverable sandbox or reconcile failure.
* Resume failure for a persisted sandbox/context snapshot.

WebSocket session switching uses the async reset path (`agent.areset(...)`) when clearing Daytona sandbox buffers for a fresh or restored session without saved state. This guarantees the live interpreter context is torn down before a new session attaches.

## Backup and recovery

The Daytona mounted volume is the durable target. To back up agent state:

1. Snapshot the mounted volume (Daytona-provider native).
2. Export sessions via `POST /api/v1/sessions/{id}/export`.
3. For Neon/Postgres, use the provider's native point-in-time recovery.

There is no first-class "export everything" command. The volume + Postgres are the source of truth.

## See also

* [Daytona runtime](/fleet-rlm/concepts/daytona-runtime) — sandbox lifecycle and volume semantics.
* [Architecture](/fleet-rlm/concepts/architecture) — where persistence sits in the layering.
* [Configuration](/fleet-rlm/reference/configuration) — auth and database env vars.
