# 10xgraph.json configuration reference

> Every key the 10xgraph.json config file accepts, with defaults, types, purposes, and complete examples for rate limiting, auth, and servers.

Source: https://10xgraph.com/docs/reference/api-cli/configuration
Last updated: 2026-10-08

`10xgraph.json` is the configuration file for the 10xGraph API server and CLI. It specifies which graph to serve, how to authenticate, where state lives, and server behavior. Only the `agent` key is required; every other key is optional. The file is plain JSON. Edit it by hand or with `10xgraph config` (a browser editor).

Projects created before 0.7.0 use `agentflow.json`. It still works until 2.0: the CLI and the server read it when `10xgraph.json` is missing from the same directory. The keys are identical, so migration is a rename (`mv agentflow.json 10xgraph.json`) or a save from `10xgraph config`.

## Which keys does it accept?

| Key | Type | Default | Purpose |
|---|---|---|---|
| `agent` | string | required | Compiled graph as `module:attribute`, for example `graph.agent:app` |
| `env` | string | none | Dotenv file to load before the graph starts, for example `.env` |
| `auth` | string, object or null | `null` | Authentication: `"jwt"`, or `{"method": "custom", "path": "module:Class"}` |
| `authorization` | string, object or null | by `MODE` | Access control backend |
| `injectq` | string or null | none | Import path of an InjectQ container |
| `store` | string or null | none | Import path of a `BaseStore` for long-term memory |
| `checkpointer` | string or null | none | Not applied by the API server. Pass the checkpointer to `compile()` |
| `thread_name_generator` | string or null | none | Import path of a thread name generator |
| `redis` | string or null | none | Redis URL for the shared ownership cache. Falls back to `REDIS_URL` |
| `remote_tools` | array | `[]` | Schemas of tools that run in the client |
| `rate_limit` | object | absent (off) | Request rate limiting |
| `websocket` | object | built-in caps | WebSocket connection limits |
| `ag_ui` | object | disabled | AG-UI protocol endpoint |
| `observability` | object | absent | Logfire and LangSmith tracing |
| `test` | object | absent | Defaults for `10xgraph test` |
| `evaluation` | object | absent | Defaults for `10xgraph eval` |

Import paths use `module:attribute`. Unknown top-level keys are kept in the file but ignored.

### auth

| Value | Meaning |
|---|---|
| `null` or absent | No authentication. Every request runs as `anonymous`, and the server logs a warning |
| `"jwt"` | Verify bearer JWTs. Needs `JWT_SECRET_KEY` and `JWT_ALGORITHM` |
| `{"method": "custom", "path": "auth.agent_auth:AgentAuth"}` | Load a `BaseAuth` subclass. Both `method` and `path` are required |

See [Server authentication](/docs/server/auth) for the full guide.

### authorization

| Value | Meaning |
|---|---|
| `"ownership"` | A thread is accessible only to the user who owns it |
| `"allow_all"`, `"default"`, `"none"` | Any authenticated user may do anything |
| `"module:attribute"` | Custom `AuthorizationBackend` |
| `{"backend": "rbac", ...}` | Role-based scopes plus owner isolation |
| absent | `ownership` when `MODE=production`, otherwise allow-all |

RBAC object keys:

| Key | Type | Default | Purpose |
|---|---|---|---|
| `backend` | string | required | `rbac`, `role_based` or `roles` (`type` is also accepted) |
| `roles` | object | `{}` | Role name to a list of scopes. `"*"` grants every scope. `role_scopes` is also accepted |
| `default_scopes` | array | `[]` | Scopes granted to everyone |
| `isolation` | string | `owner` | `owner` or `none`: whether storage is scoped per user |

Scopes are `checkpointer:read|write|delete`, `config:read`, `files:read|upload`, `graph:fix|invoke|read|setup|stop|stream` and `store:read|write|delete`.

## rate_limit

Rate limiting applies to REST requests. `/ping` is always excluded. Set `"enabled": false` to keep the block without enforcing it.

| Key | Type | Default | Purpose |
|---|---|---|---|
| `enabled` | boolean | `true` | Turn limiting on or off |
| `backend` | string | `memory` | `memory`, `redis` or `custom` (bind a `BaseRateLimitBackend` in InjectQ) |
| `requests` | integer | `100` | Requests allowed per window. Must be above 0 |
| `window` | integer | `60` | Window length in seconds. Must be above 0 |
| `by` | string | `ip` | Count per `ip`, `user` or `global` |
| `exclude_paths` | array | `[]` | Extra paths to skip |
| `redis` | object or string | none | `{"url": "...", "prefix": "..."}` or a URL string. `$VAR` and `${VAR}` are expanded |
| `redis.prefix` | string | `10xgraph:rate-limit` | Redis key prefix |
| `fail_open` | boolean | `true` | Allow requests when the backend errors. `false` denies them |
| `trusted_proxy_headers` | boolean | `false` | Honour `X-Forwarded-For`. Only behind a proxy you control |
| `trusted_proxy_hops` | integer | `1` | Proxies you control, counted from the right of `X-Forwarded-For`. At least 1 |
| `trusted_proxies` | array | `[]` | IPs or CIDR ranges your proxies connect from. Others cannot spoof the header |

The `memory` backend counts per process, so with N workers the real limit is `requests` times N and it resets on restart. Use `redis` for any multi-worker deployment.

## websocket, ag_ui and observability

| Key | Type | Default | Purpose |
|---|---|---|---|
| `websocket.max_connections` | integer or null | `1000` | Concurrent connections per process. `0` or `null` means unlimited |
| `websocket.max_connections_per_user` | integer or null | `10` | Per verified user. `0` or `null` means unlimited |
| `websocket.realtime_models` | array of strings | `[]` | Models a `/v1/graph/live` client may request |
| `ag_ui.enabled` | boolean | `false` | Mounts `POST /v1/ag-ui`. Needs the `ag-ui` extra |
| `ag_ui.allow_client_tools` | boolean | `true` | Offer tools sent by the AG-UI client to the model |
| `observability.level` | string | `standard` | `spans`, `standard` or `full` (`full` may record PII) |
| `observability.logfire` | object | none | Keys include `enabled`, `service_name`, `send_to_logfire`, `console` |
| `observability.langsmith` | object | none | Keys include `enabled`, `project`, `endpoint` |

Tokens stay in the environment: `LOGFIRE_TOKEN` and `LANGSMITH_API_KEY` are never read from the file.

## remote_tools

Each entry declares a tool the model can call that runs in the client. Unknown fields are rejected and tool names must be unique.

| Key | Type | Required | Purpose |
|---|---|---|---|
| `node` | string | yes | Graph node the tool belongs to |
| `name` | string | yes | Tool name |
| `description` | string | yes | Description shown to the model |
| `parameters` | object | no | JSON Schema with `type: "object"`. Defaults to no properties |

## test and evaluation

| Key | Type | Default | Purpose |
|---|---|---|---|
| `test.path` | string | pytest discovery | Test directory or file |
| `test.coverage` | boolean | `false` | Run with coverage |
| `test.coverage_threshold` | integer | none | Fail below this percentage |
| `evaluation.directory` | string | `evals` | Where eval files live |
| `evaluation.output_dir` | string | `eval_reports` | Report directory |
| `evaluation.threshold` | number | none | Minimum pass rate, 0 to 1 |
| `evaluation.parallel` | boolean | `false` | Run cases concurrently |
| `evaluation.max_concurrency` | integer | `4` | Concurrent cases when parallel |

## Environment variables

The server reads configuration from environment variables, which are listed in full in the [environment variables reference](/docs/reference/api-cli/environment). Common ones include `MODE`, `IS_DEBUG`, `JWT_SECRET_KEY`, `JWT_ALGORITHM`, `ORIGINS`, `MAX_REQUEST_SIZE`, `REDIS_URL`, `SENTRY_DSN`, and OTEL/observability settings. Set them as process environment variables or in a `.env` file referenced by the `env` key in `10xgraph.json`.

## What does a complete config look like?

```json title="10xgraph.json"
{
  "agent": "graph.agent:app",
  "env": ".env",
  "auth": "jwt",
  "authorization": "ownership",
  "injectq": "graph.agent:container",
  "store": null,
  "redis": "redis://localhost:6379/0",
  "thread_name_generator": "graph.thread_name_generator:MyNameGenerator",
  "rate_limit": {
    "enabled": true,
    "backend": "redis",
    "requests": 100,
    "window": 60,
    "by": "user",
    "trusted_proxy_headers": true,
    "trusted_proxy_hops": 1,
    "exclude_paths": ["/docs"],
    "redis": { "url": "${REDIS_URL}", "prefix": "myapp:rate-limit" },
    "fail_open": true
  },
  "websocket": { "max_connections": 500, "max_connections_per_user": 5 },
  "ag_ui": { "enabled": false },
  "observability": { "level": "standard", "logfire": { "enabled": false } },
  "test": { "path": "tests", "coverage": true, "coverage_threshold": 80 },
  "evaluation": { "directory": "evals", "output_dir": "eval_reports", "threshold": 0.75 }
}
```

## Frequently asked questions

### Which key is required in 10xgraph.json?

Only agent, a string in module:attribute form that points at your compiled graph. Every other key is optional.

### Where does the CLI look for the config file?

It searches the working directory and its parents for the path you pass with --config (default 10xgraph.json). With the default name, each directory is checked for 10xgraph.json first and the older agentflow.json second. Without a path, test and eval also try .agentflow.json and agentflow.config.json.

### Does agentflow.json still work?

Yes, until 2.0. When 10xgraph.json is missing, the CLI and the server read agentflow.json from the same directory. Rename the file to 10xgraph.json when convenient; the keys are identical.
