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

# Implement a custom Store

Use your own Store when session history must live in your database or service.
Inherit `SyncStore` or `AsyncStore` from `contree_sdk.store`, implement its methods,
and pass an instance through `store=`. The session uses the same interface to save
history and resume work, regardless of the storage backend.

Start with [Save and resume a session](https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/sessions.md) for memory and SQLite examples. This page covers
extension points and the guarantees a custom implementation must preserve.

## Extend an existing implementation

If you only need different defaults, subclass a supplied Store. This example
gives newly created trees a seven-day lifetime unless the caller supplies a TTL.
SQLite still handles persistence, atomic writes, and pagination.

The program saves a file, closes the Store, and opens the same session through a
new Store instance. It prints `saved`. Configure a connection as described in
[Connection sources](https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/getting-started.md#sandbox-connection).

> [tab] Sync
>
> ```python theme={null}
> from contree_sdk import create_sandbox_session
> from contree_sdk.store import SessionInfo, SyncSQLiteStore
>
>
> class RetainedSQLiteStore(SyncSQLiteStore):
>     def create_session(self, session_id: str, *, kind: str = "session", ttl: float | None = None) -> SessionInfo:
>         lifetime = 7 * 24 * 60 * 60 if ttl is None else ttl
>         return super().create_session(session_id, kind=kind, ttl=lifetime)
>
>
> with RetainedSQLiteStore("history.db") as store:
>     with create_sandbox_session(image="tag:alpine:latest", store=store, session_id="my-project") as session:
>         session.run(shell="echo saved > /saved.txt")
>
> with RetainedSQLiteStore("history.db") as store:
>     with create_sandbox_session(store=store, session_id="my-project") as session:
>         content = session.read_file("/saved.txt")
>         print(content.decode())
> ```

> [tab] Async
>
> ```python theme={null}
> from contree_sdk import create_sandbox_session
> from contree_sdk.store import AsyncSQLiteStore, SessionInfo
>
>
> class RetainedSQLiteStore(AsyncSQLiteStore):
>     async def create_session(self, session_id: str, *, kind: str = "session", ttl: float | None = None) -> SessionInfo:
>         lifetime = 7 * 24 * 60 * 60 if ttl is None else ttl
>         return await super().create_session(session_id, kind=kind, ttl=lifetime)
>
>
> async with RetainedSQLiteStore("history.db") as store:
>     async with create_sandbox_session(image="tag:alpine:latest", store=store, session_id="my-project") as session:
>         await session.run(shell="echo saved > /saved.txt")
>
> async with RetainedSQLiteStore("history.db") as store:
>     async with create_sandbox_session(store=store, session_id="my-project") as session:
>         content = await session.read_file("/saved.txt")
>         print(content.decode())
> ```

Run the async example inside an async function or a notebook that supports
top-level `await`. The supplied Store stays open after the session context exits.
The outer Store context closes it.

Reopening a tree does not renew its expiry. Use `set_session_ttl()` to change an
existing tree’s lifetime. Expiry makes its history unavailable; it does not delete
server images or stop running operations. The example changes the default for
tree creation only. Other methods retain the supplied SQLite behavior.

## Implement storage in another database

For a different backend, inherit `SyncStore` or `AsyncStore` directly. Implement
every abstract method below with your database client. PostgreSQL and Redis are
possible application backends; the SDK does not include implementations for them.

| Methods | Responsibility |
| - | - |
| `create_session()`, `get_session()` | Create or read a tree’s identity, kind, and expiry. |
| `set_session_ttl()`, `delete_session()` | Change retention or remove the whole tree. |
| `list_sessions()` | Filter and page through stored trees. |
| `append()` | Atomically add an immutable node with its explicit parent. |
| `get_node()`, `get_nodes()` | Read one node or an atomic batch of known identities. |
| `list_nodes()`, `list_children()` | Filter and page through nodes or direct children. |
| `list_history()` | Follow parents from a selected node toward the root. |
| `close()` | Release connections and resources owned by the Store. |

The async interface has the same methods and results, with `async def` and
`await`. Use an async database client or explicitly offload blocking I/O. The base
classes supply the matching context-manager methods through `close()`.

Store preserves `Node.id`, `parent_id`, `kind`, and JSON-object `data`. A tree also
has a `kind`: sessions, builds, and upload reuse can share a backend. Your Store
must preserve their data without interpreting session commands or Docker hashes.
Use [Tree stores](https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/store.md) for exact signatures, limits, and error behavior.

## Preserve the storage guarantees

* Make each call atomic across all clients, including separate processes and
  database connections. A lock in one Python object is insufficient.
* Require a live tree and an existing parent in that tree for `append()`. Allow
  only one root. A late append must not recreate a deleted or expired tree.
* Accept an identical repeated node without duplication. Raise
  `NodeConflictError` if the same identity has different contents or a second root
  is inserted. Do not overwrite saved nodes.
* Return independent JSON values. Changes to a caller’s dictionary must not
  change stored data. Preserve atomic batch reads in `get_nodes()`.
* Apply filters before pagination. Order identity-based lists by ID and history
  by ancestry. Return opaque continuation cursors; pages need not share a snapshot.
* Apply TTL to the whole tree. Reads and appends must not extend its lifetime.
  Define the backend policy for `ttl=None` and preserve the contract’s expiry errors.

Test retries, conflicting inserts, pagination, expiry, and deletion through
separate connections before using a new backend. Also test saving a session and
resuming it through a new Store instance. The [Store contract](https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/store.md#contract)
defines the complete requirements.

Store saves history and references to remote images. Session controls execution,
the selected node, and local branch names. A custom Store does not need to run
commands or choose a shared current position. Pass it through `store=` to sessions
or helpers, and close it after all users finish.
