-
Notifications
You must be signed in to change notification settings - Fork 6
Graphs and Multi Tenancy
RoboSystems is multi-tenant at the graph layer: every customer dataset lives in its own isolated graph database keyed by a graph_id. This page explains the graph_id model, the available graph tiers, subgraphs, and the day-to-day tasks of creating, listing, and querying graphs.
A graph in RoboSystems is a single, isolated LadybugDB database. Each graph has a unique identifier, graph_id, that scopes every request to exactly one tenant's data. There is no shared table space across tenants and no cross-tenant data access at the storage layer.
Two stores work together:
- Platform metadata (users, organizations, billing, the graph registry) lives in PostgreSQL.
- Business data (entities, transactions, facts, custom nodes and relationships) lives in the LadybugDB graph itself.
The multi-tenancy model has a few load-bearing properties:
- One graph = one isolated database. Each graph is its own embedded LadybugDB database. No shared tables, no cross-tenant leakage at the data layer.
-
The
graph_idis the tenant boundary. Authentication and per-graph access are validated by FastAPI dependencies before any handler runs. Thegraph_idis always a URL path parameter, never a query argument. -
Two kinds of graphs. Customer/entity graphs hold your own data; shared repositories (such as
sec) are platform-managed and read-only for everyone. - Tiers are dedicated instances. A graph runs on a dedicated EC2 instance sized by tier. Subgraphs share that same instance and its resource budget.
- Only AI operations consume credits. Database operations (queries, ingestion, backups) are free. Subgraphs draw from their parent graph's credit pool.
Before working with graphs locally, ensure you have:
- Docker running locally
- RoboSystems development environment set up
- Services started with
just start - Demo credentials created with
just demo-user(writes your API key and agraph_idto.local/config.json)
All authenticated examples below read the API key from .local/config.json and target http://localhost:8000. Use the X-API-Key header for backend testing; Authorization: Bearer is a frontend concern only.
The graph_id is the primary multi-tenant identifier. It appears as a URL path parameter on every graph-scoped route, and the platform resolves it to a specific LadybugDB database before the handler executes.
| Kind | Format | Example |
|---|---|---|
| Parent graph |
kg + 16 or more hex characters |
kg1234567890abcdef |
| Subgraph | {parent_id}_{subgraph_name} |
kg1234567890abcdef_dev |
| Shared repository | Fixed reserved name | sec |
The parent graph identifier follows the regex kg[a-f0-9]{16,}. A subgraph appends an underscore and an alphanumeric name ([a-zA-Z0-9]{1,20}) to its parent's ID, so the parent is always recoverable from the subgraph ID.
Because the graph_id lives in the URL path, the tenant scope is unambiguous and is checked by middleware before your request reaches business logic. This applies uniformly across REST, GraphQL, and MCP:
-
REST:
POST /v1/graphs/{graph_id}/query -
GraphQL:
POST /extensions/{graph_id}/graphql— thegraph_idcomes from the URL, so queries do not take agraphIdargument. Write{ entity { … } }, not{ entity(graphId: "kg_x") { … } }. -
MCP: tools read the
graph_idfrom connection context rather than as a tool argument.
A tier determines the instance size, storage budget, subgraph capacity, backup limits, and API rate multiplier for a graph. RoboSystems uses instance-based naming so the tier name reflects exactly what infrastructure you get.
| Tier (technical) | Display | Instance | Max subgraphs | Storage | API rate multiplier | Backup retention |
|---|---|---|---|---|---|---|
ladybug-standard |
Standard | m7g.large (8 GB, 2 vCPU) | 3 | 20 GB | 1.0x | 7 days |
ladybug-large |
Large | r7g.large (16 GB, 2 vCPU) | 10 | 50 GB | 1.5x | 30 days |
ladybug-xlarge |
XLarge | r7g.xlarge (32 GB, 4 vCPU) | 25 | 100 GB | 2.5x | 90 days |
ladybug-shared |
Shared Repository | r7g.2xlarge (64 GB) | Platform-managed | — | — | 90 days |
Notes:
-
A graph and its subgraphs share one instance. On
ladybug-standard, that is 1 parent plus up to 3 subgraphs (4 databases total) on a single m7g.large. The subgraph cap, storage budget, and RAM are shared across the whole instance. -
ladybug-sharedis platform-managed and not user-creatable. It backs read-only public repositories such as theseccorpus. You consume it through queries; you do not provision it. -
Only Standard, Large, and XLarge are creatable tiers. The
instance_tierfield on graph creation acceptsladybug-standard,ladybug-large, orladybug-xlarge.
Do not use marketing names such as Professional, Enterprise, or Premium — the canonical names are the technical tier identifiers above.
Graphs are created with POST /v1/graphs. Creation is asynchronous: the call returns 202 Accepted with an OperationEnvelope carrying an operation_id, and the graph is provisioned in the background.
There are two flavors of graph:
-
Entity graphs — pre-wired for a financial entity (the default for RoboLedger). Supply an
initial_entityand anyschema_extensions(such asroboledger). -
Custom graphs — your own node and relationship model. Supply a
custom_schema. See Custom Graph Schema for the full how-to.
The fastest path to a working custom graph is the demo command, which creates a user, provisions a graph, ingests sample data, and runs verification queries:
# Ensure RoboSystems is running
just start
# Create user, graph, sample data, and run queries
just demo-custom-graphThis creates a RoboLedger-enabled entity graph on the Standard tier. The Idempotency-Key header makes retries safe.
API_KEY=$(jq -r .api_key .local/config.json)
curl -X POST "http://localhost:8000/v1/graphs" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: $(date +%s)" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"graph_name": "Acme Consulting LLC",
"description": "Professional consulting services",
"schema_extensions": ["roboledger"]
},
"instance_tier": "ladybug-standard",
"initial_entity": {
"name": "Acme Consulting LLC",
"uri": "https://acmeconsulting.com",
"ein": "12-3456789",
"state_of_incorporation": "Delaware",
"entity_type": "llc"
},
"create_entity": true,
"tags": ["consulting"]
}'Note: The response is an OperationEnvelope with an operation_id, not a finished graph. Track progress over Server-Sent Events at GET /v1/operations/{operation_id}/stream. The entity_type (such as llc, corporation, or partnership) drives the default reporting style for entity graphs.
For the full request and response schema — every field, type, and validation rule — see the live OpenAPI docs rather than re-deriving it here: api.robosystems.ai/docs (or http://localhost:8000/docs locally).
GET /v1/graphs returns the graphs you can access, along with available shared repositories.
curl "http://localhost:8000/v1/graphs" \
-H "X-API-Key: $(jq -r .api_key .local/config.json)"GET /v1/graphs/{graph_id}/info returns database details for one graph. Locally you can use the just shortcuts:
# Database info (node/edge counts, size, status)
just graph-info kg1234567890abcdef
# Graph API health check
just graph-healthA subgraph is an isolated child database that lives on the same instance as its parent. Subgraphs are useful for separating environments (dev / staging / prod) or for forking a copy of a parent's data.
| Property | Behavior |
|---|---|
| Data | Fully isolated from the parent and from sibling subgraphs |
| Credit pool | Shared with the parent graph |
| Permissions | Inherited from the parent graph |
| Instance / RAM / storage | Shared with the parent (same EC2 instance) |
| Schema | Inherits the parent's schema_extensions by default |
-
Name: alphanumeric only, 1–20 characters, no hyphens or underscores. The name is normalized to lowercase.
dev,staging, andprod1are valid;dev-1andmy_envare rejected. -
ID format:
{parent_id}_{subgraph_name}. A subgraph nameddevunderkg1234567890abcdefbecomeskg1234567890abcdef_dev. - Single-level only: you cannot create a subgraph of a subgraph, and shared repositories cannot have subgraphs.
-
Capacity: enforced per tier (3 / 10 / 25 for Standard / Large / XLarge). Exceeding the cap returns
403.
Subgraph creation is a graph operation and returns an OperationEnvelope. By default it creates an empty subgraph; set fork_parent: true to clone the parent's data.
curl -X POST "http://localhost:8000/v1/graphs/kg1234567890abcdef/operations/create-subgraph" \
-H "X-API-Key: $(jq -r .api_key .local/config.json)" \
-H "Content-Type: application/json" \
-d '{
"name": "dev",
"display_name": "Development Environment",
"description": "Sandbox for testing",
"fork_parent": false
}'The resulting subgraph ID is kg1234567890abcdef_dev.
GET /v1/graphs/{graph_id}/subgraphs returns the parent's subgraphs along with the tier's max_subgraphs, the current subgraph_count, and total size.
curl "http://localhost:8000/v1/graphs/kg1234567890abcdef/subgraphs" \
-H "X-API-Key: $(jq -r .api_key .local/config.json)"Subgraph lifecycle writes (create, delete) follow the same operation pattern as other graph operations. See Graph Operations for the complete operations surface.
Graphs are queried with Cypher over POST /v1/graphs/{graph_id}/query.
curl -X POST "http://localhost:8000/v1/graphs/kg1234567890abcdef/query" \
-H "X-API-Key: $(jq -r .api_key .local/config.json)" \
-H "Content-Type: application/json" \
-d '{"query": "MATCH (n) RETURN labels(n) AS label, count(*) AS count"}'Locally, the just shortcuts wrap the same path:
# Query through the Graph API
just graph-query kg1234567890abcdef "MATCH (n) RETURN count(n)"
# Query LadybugDB directly (bypasses the API — local debugging only)
just lbug-query kg1234567890abcdef "MATCH (n) RETURN count(n)"Important: The main graph is read-only over Cypher — its data arrives through the staging and materialization pipeline, not through write queries. Subgraphs support full writes.
Any MCP-compatible AI tool (Claude Desktop, Claude Code, Cursor, Cline, and others) can query a graph through the RoboSystems MCP server. The graph_id comes from the connection's context, so it is never passed as a tool argument. The graph-facing tools are:
-
get-graph-schema— view available node and relationship types (run this first) -
read-graph-cypher— run read-only Cypher queries -
get-example-queries— get sample queries for the graph
read-graph-cypher is read-only: CREATE, SET, DELETE, MERGE, and DROP, along with CALL db. and CALL apoc., are blocked. For the GraphQL plane (typed extensions reads), use get-graphql-schema and query-graphql. See Custom Graph Schema for a worked MCP example.
You have hit the tier's subgraph cap (3 / 10 / 25 for Standard / Large / XLarge).
Solution: Delete an unused subgraph, or change the parent graph to a higher tier with a change-tier operation. See Graph Operations.
The graph's tier reports no subgraph capacity, or you are attempting to add a subgraph to a shared repository or to another subgraph.
Solution: Subgraphs are single-level and live only under creatable tiers. Confirm you are operating on a parent graph (kg…, no underscore) on a tier that allows subgraphs.
Graph creation is asynchronous.
Solution: Read the operation_id from the returned OperationEnvelope and stream progress at GET /v1/operations/{operation_id}/stream. The graph is usable once the operation completes.
Names must be alphanumeric, 1–20 characters, with no hyphens or underscores.
Solution: Replace separators. Use dev1 instead of dev-1, or myenv instead of my_env.
The main graph is read-only over Cypher.
Solution: Write to a subgraph (which supports full writes), or load data into the main graph through the staging and materialization pipeline.
Wiki Guides:
- Graph Operations - Lifecycle operations (create-subgraph, delete-subgraph, change-tier, backups, materialize) and the CQRS operation envelope
-
Authentication and API Keys - API key creation, the
X-API-Keyheader, and per-graph access control - Custom Graph Schema - Designing node and relationship schemas and querying a custom graph
Codebase Documentation:
- Graph Routing - Graph ID parsing, subgraph resolution, and multi-tenant routing
- Authentication - Authentication system internals
- Graph API - LadybugDB backend and query execution
API Reference:
- API Documentation - Full endpoint and schema reference with machine-readable OpenAPI spec
© 2026 RFS LLC
- Authentication & API Keys
- Graphs & Multi-Tenancy
- Shared Repositories
- Graph Operations
- Querying the Analytical Graph
- Credits & Billing
- AI Operators & MCP
- Pipeline Guide
- Extensions Surface Overview
- GraphQL Reads
- RoboLedger Operations
- RoboInvestor Operations
- Connecting QuickBooks Locally