# rocketride_graph

A RocketRide-managed graph database node backed by PostgreSQL + Apache AGE in your own provisioned RocketRide cloud database — with **zero database setup**.

## What it does

Mirrors the `graph_neo4j` node: as a pipeline node it takes natural-language questions on the `questions` lane, asks a connected LLM to translate them to Cypher, executes, and emits results; as a tool node agents call `get_data`, `get_schema`, `get_query`, `execute`, and `dialect` (dialect: `age`).

Two differences from the generic graph nodes:

1. **No connection fields.** The per-tenant DSN is resolved from the account layer (`Account.resolve_db_dsn(client_id)`), keyed by the authenticated connection identity — the same seam as `rocketride_sql` and `rocketride_vector` (one database per tenant backs all three). Requires signing into RocketRide cloud; the open-source build without a cloud identity fails with `RocketRide cloud DB nodes require signing into RocketRide cloud`.
2. **Cypher → AGE translation.** Apache AGE cannot run bare Cypher, so every query path routes through the translation layer at `ai.common.graph.age` (openCypher ANTLR parse → firewall → dialect capability gate → `cypher()` envelope with synthesized column list → prepared-statement parameter binding → agtype decode). Even the raw EXECUTE path translates — only the *semantic* firewall is skipped there, never the resource caps.

## Safety model

- **Safe path** (LLM/tool reads): runs in a server-side **READ ONLY transaction** (writes are refused by Postgres itself), plus the layer's semantic firewall (no write clauses, no CALL) and the base's `is_cypher_safe` regex as defence-in-depth.
- **Resource caps** (both paths): query length limit, variable-length traversal depth cap (unbounded `*` patterns are rejected), and a per-transaction `statement_timeout`.
- **EXECUTE** is gated by `allow_execute` (default off); isolation for raw writes is the database-per-tenant boundary.
- All per-query settings are `SET LOCAL` — the cloud endpoint is a transaction-mode pooler, so session-level `SET` would bleed across tenants. AGE is preloaded server-side (no `LOAD`).

## Graph provisioning (open)

Ownership of per-tenant `create_graph` is **pending** (cloud provisioner vs node). Until decided, the node fails fast at pipeline start when the configured graph does not exist rather than creating one silently.

## Configuration

### Fields

| Field | Type | Description |
|---|---|---|
| `graph` | string | Default "rocketride". Name of the AGE graph to query |
| `db_description` | string | Default empty. What the graph contains; improves LLM query quality |
| `max_attempts` | integer | Default 5. LLM re-ask ceiling when validation rejects generated Cypher |
| `max_rows` | integer | Default 1000. Row ceiling for the read path |
| `query_timeout_ms` | integer | Default 30000. Per-transaction statement timeout |
| `allow_execute` | boolean | Default false. Enables the raw EXECUTE path |

There are intentionally no `host` / `user` / `password` / `database` fields.

### Dialect notes (AGE 1.5.0)

The capability table (see `ai.common.graph.age.capabilities`) rejects constructs the cloud's AGE 1.5.0 cannot run with actionable messages: `datetime()` (store ISO-8601 strings or epoch numbers), `RETURN *` (list columns explicitly), `ORDER BY` on a projection alias (order by the expression), `MERGE ... ON CREATE/MATCH SET` (plain `MERGE` then a separate `SET`), label predicates in `WHERE` (put the label in the `MATCH` pattern), multi-labels like `(n:A:B)` (model the second label as a property or category-node edge), and `shortestPath()` (use a bounded variable-length match). All cells are empirically verified against the exact cloud pin — none pass through unverified.

<!-- ROCKETRIDE:GENERATED:PARAMS START -->
<!-- Generated by nodes:docs-generate. Do not edit by hand. -->

## Schema

| Field | Type | Description | Default |
|---|---|---|---|
| `rocketridegraph.allow_execute` | `boolean` | **Allow direct query execution**<br/>Permit QuestionType.EXECUTE callers to run raw Cypher without LLM translation or read-only safety checks. Resource limits still apply. Leave OFF unless a trusted application explicitly needs to issue Cypher directly. | `false` |
| `rocketridegraph.db_description` | `string` | **Graph description**<br/>What is this graph used for? Describe its content and purpose, this helps the LLM generate more accurate queries. | `""` |
| `rocketridegraph.graph` | `string` | **Graph name**<br/>Name of the AGE graph to query in your RocketRide cloud database | `"rocketride"` |
| `rocketridegraph.max_attempts` | `integer` | **Max validation attempts**<br/>Maximum number of times to re-ask the LLM if validation rejects the generated Cypher | `5` |
| `rocketridegraph.max_rows` | `integer` | **Max read rows**<br/>Row ceiling for read queries on the questions lane and get_data tool | `1000` |
| `rocketridegraph.profile` | `string` |  | `"default"` |
| `rocketridegraph.query_timeout_ms` | `integer` | **Query timeout (ms)**<br/>Per-query statement timeout applied inside each query's transaction | `30000` |

## Dependencies

- `psycopg2-binary` `==2.9.12`

## Source

[<svg viewBox="0 0 16 16" width="15" height="15" fill="currentColor" aria-hidden="true" style="vertical-align:-0.15em;margin-right:0.35em"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> View source](https://github.com/rocketride-org/rocketride-server/tree/develop/nodes/src/nodes/rocketride_graph)
<!-- ROCKETRIDE:GENERATED:PARAMS END -->
