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

# Docker Sandboxes

> Give any sandboxed coding agent persistent memory with the cognee-memory Docker Sandboxes kit.

The **cognee-memory** kit is a [Docker Sandboxes](https://docs.docker.com/ai/sandboxes/customize/kits/) mixin that gives any sandboxed coding agent persistent, self-improving memory backed by cognee. Everything runs embedded inside the sandbox — no external services, no database to provision.

```bash theme={null}
sbx run claude --kit ./cognee-memory
```

The kit lives in the cognee repository at [`examples/integrations/docker-sandbox-kit`](https://github.com/topoteretes/cognee/tree/main/examples/integrations/docker-sandbox-kit). Clone it (or copy the `cognee-memory/` directory) and point `--kit` at it.

<Info>
  Verified end-to-end with **sbx v0.39.0** under a `deny-all` network policy. The kit does not declare a minimum `sbx` version.
</Info>

## What the kit does

`cognee-memory/spec.yaml` is a stackable mixin kit (`schemaVersion: "2"`, `kind: mixin`) that:

* installs the CLI with `uv tool install cognee` at sandbox creation — this assumes `uv` is present in the base image, which Docker's default sandbox images ship;
* pins all memory state to `/home/agent/.cognee` (`DATA_ROOT_DIRECTORY=/home/agent/.cognee/data`, `SYSTEM_ROOT_DIRECTORY=/home/agent/.cognee/system`), so memory survives sandbox restarts and is easy to inspect or back up;
* defaults to `LLM_MODEL=openai/gpt-5-mini` and sets `TELEMETRY_DISABLED=1`;
* pins `ENABLE_BACKEND_ACCESS_CONTROL=true` — cognee's default, made explicit so the kit's multi-agent behavior is unambiguous: multi-tenant ACLs and per-user+dataset database isolation;
* declares a proxy-managed OpenAI credential so the real key never enters the sandbox VM;
* allowlists only the domains cognee actually needs under `deny-all`;
* appends usage instructions to the agent's memory file (`kits-memory/cognee-memory.md`): recall at task start → work → remember durable learnings, plus the multi-agent handover pattern.

Because it is a mixin, it stacks onto any agent sandbox — `claude`, `shell`, `opencode`, and so on.

## Prerequisites

Install and start `sbx`, then set the baseline policy and the OpenAI secret. The install line below is macOS/Homebrew; use whatever install path `sbx` documents for your platform.

```bash theme={null}
brew trust docker/tap && brew install docker/tap/sbx
sbx daemon start          # own terminal, or: nohup sbx daemon start &
sbx login                 # browser OAuth
sbx policy init deny-all  # strictest baseline; the kit's allowlist is the only egress
sbx secret set-custom --host api.openai.com --env LLM_API_KEY --value "$LLM_API_KEY"
```

`set-custom` prints a placeholder (`sbx-cs-…`, retrievable later with `sbx secret ls`). Sandboxes only ever see the placeholder; the proxy substitutes the real key on requests to `api.openai.com`.

### The `cognee-openai` credential

The kit declares its credential service as **`cognee-openai`**, not `openai`:

```yaml theme={null}
credentials:
  - service: cognee-openai
    description: OpenAI API key used by cognee for entity extraction and embeddings
    required: false
    apiKey:
      name: LLM_API_KEY
      proxyManaged: true
      inject:
        - domain: api.openai.com
          scheme: bearer
```

The rename is deliberate: built-in agent kits (`shell`, `claude`, …) already declare the common LLM services, and composition fails when two kits define the same service. `required: false` means sandbox creation is never blocked when no key is bound.

There are two ways to supply the key, both proxy-side:

1. **Bind the service** — interactively at run time, or via `~/.config/sbx/credentials.yaml`. The agent sees `LLM_API_KEY=proxy-managed` and the `inject` rule rewrites the `Authorization` header for `api.openai.com`.
2. **Headless** — `sbx secret set-custom --host api.openai.com --env LLM_API_KEY --value <key>`, as in the prerequisites above.

<Warning>
  The kit's `proxy-managed` environment value wins over the custom secret's placeholder. For headless `sbx exec` commands, export the printed placeholder explicitly:

  ```bash theme={null}
  sbx exec <sandbox> -- sh -lc 'export LLM_API_KEY=<placeholder> LOG_LEVEL=ERROR; \
      cognee-cli remember "fact worth keeping"'
  ```
</Warning>

### Network allowlist

Under `deny-all`, the kit's allowlist is the sandbox's only egress. Each domain was discovered by running under the deny-all policy and reading `sbx policy log` — the recommended way to derive a kit allowlist:

| Domain                               | Why cognee needs it                                                      |
| ------------------------------------ | ------------------------------------------------------------------------ |
| `api.openai.com`                     | LLM and embedding calls                                                  |
| `pypi.org`, `files.pythonhosted.org` | `uv tool install cognee`                                                 |
| `extension.ladybugdb.com`            | Ladybug, cognee's embedded graph DB, fetches its extensions on first use |
| `raw.githubusercontent.com`          | litellm's model-cost map                                                 |

## Single-agent usage

```bash theme={null}
sbx run claude --kit ./cognee-memory    # agent with persistent memory
sbx run shell  --kit ./cognee-memory    # or a plain shell sandbox
```

Inside the sandbox the agent has the full [memory CLI](/cognee-cli/overview):

| Command                                             | Purpose                                    |
| --------------------------------------------------- | ------------------------------------------ |
| `cognee-cli remember "text, a file path, or a URL"` | Store knowledge                            |
| `cognee-cli recall "your question"`                 | Query memory                               |
| `cognee-cli improve`                                | Enrich/index the graph                     |
| `cognee-cli forget --all`                           | Delete everything (no confirmation prompt) |

The first `remember` builds a knowledge graph with a few LLM calls, so it takes noticeably longer than a plain key-value write; `recall` then answers from the graph.

To use a provider other than OpenAI, edit `environment.variables`, the `credentials` and `permissions.network` blocks, and the stored secret to match — see [LLM providers](/setup-configuration/llm-providers).

## Multi-agent demo: supervisor to worker handover

[`demo/handover.sh`](https://github.com/topoteretes/cognee/blob/main/examples/integrations/docker-sandbox-kit/demo/handover.sh) runs a round-trip memory handover between **two real sandboxes**, `cognee-supervisor` and `cognee-worker`, both created from this kit and sharing the `demo/` directory as their workspace. The supervisor and worker are separate cognee **users**, so ACLs gate what each one can read and write.

```bash theme={null}
export LLM_API_KEY=sk-...   # only needed the first time, for the secret
./demo/handover.sh
sbx policy log              # the audit trail: per-domain allow/deny
```

The three phases run sequentially:

<Steps>
  <Step title="brief — supervisor sandbox">
    Stores a private note (dataset `supervisor_private`) and a handover briefing (dataset `handover`) in its own datasets, grants the worker `read` and `write` on the briefing, and writes a handover token to `demo/handover-out/handover_token.json` carrying the briefing's dataset **UUID**.

    ```python theme={null}
    for permission in ("read", "write"):
        await authorized_give_permission_on_datasets(
            principal_id=worker.id,
            dataset_ids=[handover_id],
            permission_name=permission,
            owner_id=supervisor.id,
        )
    ```

    The creator automatically holds `share`, so no extra grant is needed to hand access over.
  </Step>

  <Step title="work — worker sandbox">
    Redeems the token by UUID, proves the two boundaries, then writes its completion report back into the shared dataset:

    ```python theme={null}
    await cognee.recall("What is my task and how do I deploy?",
                        dataset_ids=[dataset_id], user=worker)
    ...
    await cognee.remember(WORKER_REPORT, dataset_id=dataset_id, user=worker)
    ```

    Negative checks: recalling the supervisor's private dataset raises `PermissionDeniedError`, and recalling the shared dataset **by name** (`datasets=["handover"]`) fails to resolve.
  </Step>

  <Step title="review — supervisor sandbox">
    Recalls the worker's report from the shared dataset.
  </Step>
</Steps>

<Note>
  **Share by UUID, never by name.** Dataset names are namespaced per user — a name maps to `uuid5(name + user.id + tenant_id)` — so a name never crosses a user boundary. The dataset UUID is the only cross-user address, for reads and for cross-owner writes alike.
</Note>

Permission management is Python-SDK/REST-only: `cognee-cli` has no user or permission commands, so the multi-agent pattern requires the SDK (`create_user`, `get_datasets`, `authorized_give_permission_on_datasets`). On a fresh install, call `create_db_and_tables()` before touching users or ACLs — the CLI does this implicitly, the raw SDK path does not.

The payload, [`demo/supervisor_worker_handover.py`](https://github.com/topoteretes/cognee/blob/main/examples/integrations/docker-sandbox-kit/demo/supervisor_worker_handover.py), is self-contained: drop it into any repository with cognee installed and run `python supervisor_worker_handover.py` to execute all phases in one process, or `--phase brief|work|review` to split them across environments.

Clean up with:

```bash theme={null}
sbx rm -f cognee-supervisor cognee-worker && rm -rf demo/cognee-state demo/handover-out
```

### How the memory snapshot moves

Embedded LanceDB cannot operate on the shared virtiofs workspace mount. The demo works around this by running cognee state on each VM's **local disk** during a phase and handing it between sandboxes as a snapshot with `sbx cp` — the host keeps the canonical copy in `demo/cognee-state/` between phases:

```bash theme={null}
sbx exec "$1" -- sudo rm -rf /home/agent/cognee-state
sbx cp cognee-state "$1":/home/agent/
sbx exec "$1" -- sudo chown -R agent:agent /home/agent/cognee-state
# ... run the phase against DATA_ROOT_DIRECTORY / SYSTEM_ROOT_DIRECTORY on local disk ...
sbx cp "$1":/home/agent/cognee-state .
```

The `chown` step is required because `sbx cp` preserves the host uid. Phases run sequentially — the snapshot moves, it is never shared live.

<Tip>
  For always-on cross-sandbox memory (concurrent agents, no shared workspace), run a central [cognee API server](/how-to-guides/cognee-sdk/deployment/index) and point sandboxes at it over the network allowlist instead of sharing embedded storage.
</Tip>

### Backends that support user permissioning

Permissions are `read` / `write` / `delete` / `share` per dataset. The kit's defaults — Ladybug/Kuzu for the graph, LanceDB for vectors — support per-user+dataset database isolation, as do Neo4j (including the `neo4j_community` container-per-dataset handler), Postgres (demo), Turso, and PGVector. Neptune, Neptune Analytics, `ladybug-remote`, and community vector adapters that do not register a dataset-database handler are **not** supported — see the [permissions system](/core-concepts/multi-user-mode/permissions-system/overview) for how users, datasets, and ACLs fit together.

## Inspecting memory and security

```bash theme={null}
sbx exec cognee-supervisor -- sh -lc 'echo $LLM_API_KEY'   # "proxy-managed" — never a real key
sbx exec cognee-supervisor -- curl -s -o /dev/null -w "%{http_code}" https://example.com   # 403: deny-all
sbx policy log                                             # every allow/deny decision
ls demo/cognee-state/system/databases/<owner-user-uuid>/   # <dataset-uuid>.lbug + .lance.db per dataset
```

The relational database (`demo/cognee-state/system/databases/cognee_db`, SQLite) holds users, datasets, and the ACL rows. After the demo, the worker holds exactly two grants — `read` and `write` — on the shared dataset, and nothing on the private one.

***

<CardGroup cols={2}>
  <Card title="Kit source" icon="github" href="https://github.com/topoteretes/cognee/tree/main/examples/integrations/docker-sandbox-kit">
    `spec.yaml`, the handover demo, and the README
  </Card>

  <Card title="Docker Sandboxes kits" icon="container" href="https://docs.docker.com/ai/sandboxes/customize/kits/">
    Docker's documentation for authoring kits
  </Card>
</CardGroup>
