# 10xgraph.json reference

> Every 10xgraph.json key the server and CLI read, with types, defaults and examples, the rate_limit sub-keys, and the environment variables the API server uses.

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

`10xgraph.json` tells the 10xGraph API server which graph to serve and how to secure it. Only `agent` is required. The file is plain JSON, and the server also reads settings from environment variables, listed at the end of this page. Edit it by hand or with `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 [Add JWT authentication](/docs/how-to/api-cli/add-auth).

### 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 | `agentflow: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 |

## Which environment variables does the server read?

Set these in the process environment or in the file named by `env`.

| Variable | Default | Purpose |
|---|---|---|
| `MODE` | `development` | `production` disables the local telemetry store and the docs pages, and defaults authorization to ownership |
| `IS_DEBUG` | `true` | Set `false` in production |
| `LOG_LEVEL` | `INFO` | Logging level |
| `LOGGER_NAME` | `agentflow-cli` | Logger name |
| `APP_NAME`, `APP_VERSION`, `SUMMARY` | `MyApp`, `0.1.0`, `Agentflow Backend` | Application metadata |
| `ORIGINS` | `*` | Comma-separated CORS origins. Wildcard with credentials is refused in production |
| `ALLOWED_HOST` | `*` | Allowed hosts. `*` warns in production |
| `CORS_ALLOW_CREDENTIALS` | `true` | Allow credentialed cross-origin requests |
| `ROOT_PATH`, `DOCS_PATH`, `REDOCS_PATH` | `/`, `/docs`, `/redocs` | URL paths. The docs paths are empty in production unless set |
| `MAX_REQUEST_SIZE` | `10485760` | Maximum request body in bytes |
| `REDIS_URL` | none | Redis for the ownership cache when `redis` is not set |
| `JWT_SECRET_KEY` | none | Signing secret. 32 bytes or more for HS* algorithms |
| `JWT_ALGORITHM` | `HS256` | Signing algorithm. Must be set for `auth: "jwt"` |
| `JWT_ISSUER`, `JWT_AUDIENCE` | none | When set, tokens must carry matching `iss` or `aud` |
| `SENTRY_DSN` | none | Enables Sentry in production, staging or development modes |
| `SENTRY_TRACES_SAMPLE_RATE`, `SENTRY_PROFILES_SAMPLE_RATE` | `0.1`, `0.0` | Sampling rates from 0 to 1 |
| `OTEL_ENABLED` | `false` | Enable OpenTelemetry |
| `OTEL_SERVICE_NAME` | `agentflow-api` | Service name |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | none | OTLP endpoint |
| `OTEL_LEVEL` | `standard` | `spans`, `standard` or `full` |
| `SECURITY_HEADERS_ENABLED` | `true` | Add security headers |
| `HSTS_ENABLED`, `HSTS_MAX_AGE`, `HSTS_INCLUDE_SUBDOMAINS`, `HSTS_PRELOAD` | `true`, `31536000`, `true`, `false` | Strict-Transport-Security |
| `FRAME_OPTIONS`, `CONTENT_TYPE_OPTIONS`, `XSS_PROTECTION`, `REFERRER_POLICY` | `DENY`, `nosniff`, `1; mode=block`, `strict-origin-when-cross-origin` | Other security headers |
| `PERMISSIONS_POLICY`, `CSP_POLICY` | none | Header values. Built-in defaults apply when unset |
| `GRAPH_PATH` | `10xgraph.json` | Config file path used by the server. `10xgraph api` sets it for you |
| `WEB_CONCURRENCY` | `2` in generated Dockerfile | Gunicorn worker count |
| `AGENTFLOW_NO_FULLSCREEN` | unset | Set to `1` to disable the full-screen CLI |

Media upload settings:

| Variable | Default | Purpose |
|---|---|---|
| `MEDIA_STORAGE_TYPE` | `local` | `memory`, `local` or `cloud` |
| `MEDIA_STORAGE_PATH` | `./uploads` | Local storage directory |
| `MEDIA_MAX_SIZE_MB` | `25.0` | Maximum upload size |
| `MEDIA_ALLOWED_CONTENT_TYPES` | empty (all) | Comma-separated allowlist, for example `image/*,application/pdf` |
| `MEDIA_REQUIRE_OWNER` | `false` | Deny files with no recorded owner |
| `DOCUMENT_HANDLING` | `extract_text` | `extract_text`, `pass_raw` or `skip` |
| `MEDIA_CLOUD_PROVIDER`, `MEDIA_CLOUD_BUCKET`, `MEDIA_CLOUD_REGION`, `MEDIA_CLOUD_PREFIX` | `aws`, empty, `us-east-1`, `10xgraph-media` | Used when storage is `cloud` |

Snowflake ID generation reads `SNOWFLAKE_EPOCH` (`1723323246031`), `SNOWFLAKE_TOTAL_BITS` (`64`), `SNOWFLAKE_TIME_BITS` (`39`), `SNOWFLAKE_NODE_BITS` (`7`), `SNOWFLAKE_NODE_ID` (`0`), `SNOWFLAKE_WORKER_ID` (`0`) and `SNOWFLAKE_WORKER_BITS` (`5`). Give each instance a unique node and worker id in distributed deployments.

## 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). Without a path, test and eval also try .10xgraph.json and agentflow.config.json.
