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.
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:
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, repeated here for the persistence story:
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
/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:
- Snapshot the mounted volume (Daytona-provider native).
- Export sessions via
POST /api/v1/sessions/{id}/export.
- 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