mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-09 01:35:18 +02:00
Some checks failed
CI / Classify Changes (push) Has been cancelled
CI / Check AGENTS.md Links (push) Has been cancelled
CI / Container Entrypoint (push) Has been cancelled
Release Edge / Prepare edge release (push) Has been cancelled
CI / Test Workspace (push) Has been cancelled
CI / Test omnigraph-server --features aws (push) Has been cancelled
CI / Test Windows release binaries (push) Has been cancelled
CI / RustFS S3 Integration (push) Has been cancelled
Release Edge / Build edge omnigraph-linux-x86_64 (push) Has been cancelled
Release Edge / Build edge omnigraph-macos-arm64 (push) Has been cancelled
Release Edge / Build edge omnigraph-windows-x86_64 (push) Has been cancelled
Release Edge / Smoke Windows installer (push) Has been cancelled
* test(optimize): pin Lance blob-column compaction failure as a surface guard
Lance compact_files mis-decodes blob-v2 columns under its forced BlobHandling::AllBinary read ("more fields in the schema than provided column indices"), failing even a pristine uniform-V2_2 multi-fragment blob table; reads use descriptor handling and are unaffected.
Guard 10 reproduces this and is self-retiring: it turns red on the Lance bump that fixes the bug, forcing LANCE_SUPPORTS_BLOB_COMPACTION to flip.
* fix(optimize): skip blob-bearing tables instead of crashing compaction
omnigraph optimize aborted the whole sweep when any node/edge table had a Blob property: Lance compact_files cannot decode blob-v2 columns under AllBinary (the column-index error pinned by the surface guard). Skip blob-bearing tables behind a LANCE_SUPPORTS_BLOB_COMPACTION gate and report them via TableOptimizeStats.skipped / SkipReason (surfaced in the CLI and a tracing::warn) instead of erroring, which also isolates the failure so the other tables still compact.
Reads/writes are unaffected; only fragment/space reclamation on blob tables is deferred until the upstream Lance fix. Adds a maintenance.rs regression test (validated red with the column-index symptom before the fix, green after), a concise v0.6.1 release note, and updates docs (maintenance, cli-reference, AGENTS capability matrix, invariants Known Gaps, lance.md audit, constants).
* refactor(optimize): make TableOptimizeStats and SkipReason non_exhaustive
Both are returned result types, never built by callers, so #[non_exhaustive] makes this the last field/variant addition that can break downstream literal construction and keeps future ones non-breaking (review feedback on the public-field addition). The v0.6.1 Compatibility Notes call out the source-level change.
Also drops the now-stale "RED today / GREEN after the fix lands" narration in the optimize_skips_blob_table_and_reports_skip test (historical regression context now that the fix is in this branch), and folds in the expanded v0.6.1 release note.
* chore(release): bump workspace to v0.6.1
Coherent version bump to accompany the v0.6.1 release note: all five crate manifests + path-dependency constraints, Cargo.lock, the AGENTS.md surveyed-version line, and openapi.json info.version move 0.6.0 -> 0.6.1. Matches the established release pattern (#118 landed the v0.6.0 note + bump together) and resolves the Codex/Devin review flag that a v0.6.1 note without a bump leaves CARGO_PKG_VERSION reporting 0.6.0 and mixed package versions.
2268 lines
65 KiB
JSON
2268 lines
65 KiB
JSON
{
|
|
"openapi": "3.1.0",
|
|
"info": {
|
|
"title": "Omnigraph API",
|
|
"description": "HTTP API for the Omnigraph graph database",
|
|
"license": {
|
|
"name": "MIT",
|
|
"identifier": "MIT"
|
|
},
|
|
"version": "0.6.1"
|
|
},
|
|
"paths": {
|
|
"/branches": {
|
|
"get": {
|
|
"tags": [
|
|
"branches"
|
|
],
|
|
"summary": "List all branches.",
|
|
"description": "Returns branch names sorted alphabetically. Read-only.",
|
|
"operationId": "listBranches",
|
|
"responses": {
|
|
"200": {
|
|
"description": "List of branches",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BranchListOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
},
|
|
"post": {
|
|
"tags": [
|
|
"branches"
|
|
],
|
|
"summary": "Create a new branch.",
|
|
"description": "Forks `name` off of `from` (defaults to `main`). The new branch shares\ntable data with its parent until it is mutated. Returns 409 if `name`\nalready exists.",
|
|
"operationId": "createBranch",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BranchCreateRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Branch created",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BranchCreateOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Branch already exists",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/branches/merge": {
|
|
"post": {
|
|
"tags": [
|
|
"branches"
|
|
],
|
|
"summary": "Merge one branch into another.",
|
|
"description": "Merges `source` into `target` (defaults to `main`). Outcome is one of\n`already_up_to_date`, `fast_forward`, or `merged`. Returns 409 with the\nlist of conflicts if the merge cannot be completed; the target is left\nunchanged in that case. **Destructive** to `target` on success.",
|
|
"operationId": "mergeBranches",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BranchMergeRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Branches merged",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BranchMergeOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Merge conflict",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/branches/{branch}": {
|
|
"delete": {
|
|
"tags": [
|
|
"branches"
|
|
],
|
|
"summary": "Delete a branch.",
|
|
"description": "**Irreversible.** Removes the branch pointer; commits remain reachable\nonly if referenced by another branch. Returns 404 if the branch does not\nexist.",
|
|
"operationId": "deleteBranch",
|
|
"parameters": [
|
|
{
|
|
"name": "branch",
|
|
"in": "path",
|
|
"description": "Branch name to delete",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Branch deleted",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BranchDeleteOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Branch not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/change": {
|
|
"post": {
|
|
"tags": [
|
|
"mutations"
|
|
],
|
|
"summary": "**Deprecated** — use [`POST /mutate`](#tag/mutations/operation/mutate) instead.",
|
|
"description": "Apply a GQ mutation to a branch. Behavior is unchanged; the route is\nkept indefinitely for back-compat. New integrations should target\n`POST /mutate`, which has identical semantics and a name that pairs\ncleanly with `POST /query`. Responses from this route include\n`Deprecation: true` and `Link: </mutate>; rel=\"successor-version\"`\nheaders per RFC 9745 / RFC 8288 so SDKs and proxies can surface the\nsignal.",
|
|
"operationId": "change",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ChangeRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Mutation results (response includes `Deprecation: true` + `Link: </mutate>; rel=\"successor-version\"`)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ChangeOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Merge conflict",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"deprecated": true,
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/commits": {
|
|
"get": {
|
|
"tags": [
|
|
"commits"
|
|
],
|
|
"summary": "List commits.",
|
|
"description": "Filter by `branch` to get the commits on a single branch (most recent\nfirst); omit to list across all branches. Read-only.",
|
|
"operationId": "listCommits",
|
|
"parameters": [
|
|
{
|
|
"name": "branch",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "List of commits",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/CommitListOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/commits/{commit_id}": {
|
|
"get": {
|
|
"tags": [
|
|
"commits"
|
|
],
|
|
"summary": "Get a single commit.",
|
|
"description": "Returns the commit's manifest version, parent commit(s), and creation\nmetadata. Read-only.",
|
|
"operationId": "getCommit",
|
|
"parameters": [
|
|
{
|
|
"name": "commit_id",
|
|
"in": "path",
|
|
"description": "Commit identifier",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Commit details",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/CommitOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Commit not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/export": {
|
|
"post": {
|
|
"tags": [
|
|
"queries"
|
|
],
|
|
"summary": "Stream the contents of a branch as NDJSON.",
|
|
"description": "Emits one JSON object per line (`application/x-ndjson`). Filter with\n`type_names` (node/edge type names) and/or `table_keys`; both empty\nstreams the entire branch. Suitable for large exports — the response is\nstreamed, not buffered. Read-only.",
|
|
"operationId": "export",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ExportRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Exported data as NDJSON",
|
|
"content": {
|
|
"application/x-ndjson": {}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/graphs": {
|
|
"get": {
|
|
"tags": [
|
|
"management"
|
|
],
|
|
"summary": "List every graph currently registered with this server (MR-668).",
|
|
"description": "Multi-graph mode only. In single mode, the route returns 405 — there's\nno registry to enumerate. Cedar-gated by the server-level policy via\nthe `graph_list` action against `Omnigraph::Server::\"root\"`.\n\nOrder: alphabetical by `graph_id` (server-sorted so clients see\ndeterministic output across requests).",
|
|
"operationId": "listGraphs",
|
|
"responses": {
|
|
"200": {
|
|
"description": "List of registered graphs",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/GraphListResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"405": {
|
|
"description": "Method not allowed (single-graph mode)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/healthz": {
|
|
"get": {
|
|
"tags": [
|
|
"health"
|
|
],
|
|
"summary": "Liveness probe.",
|
|
"description": "Returns server status and version. Unauthenticated; safe to call from any\ncaller. Use this to confirm the server is reachable before invoking other\nendpoints.",
|
|
"operationId": "health",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Server is healthy",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/HealthOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/ingest": {
|
|
"post": {
|
|
"tags": [
|
|
"mutations"
|
|
],
|
|
"summary": "Bulk-ingest NDJSON data into a branch.",
|
|
"description": "`data` is NDJSON with one record per line. `mode` controls behavior on\nexisting rows: `merge` upserts by id (default), `append` blindly inserts,\n`overwrite` replaces table contents. If `branch` does not exist it is\ncreated from `from` (defaults to `main`). **Destructive** when `mode` is\n`overwrite` or when ingest produces conflicting writes.",
|
|
"operationId": "ingest",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/IngestRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Ingest results",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/IngestOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/mutate": {
|
|
"post": {
|
|
"tags": [
|
|
"mutations"
|
|
],
|
|
"summary": "Apply a GQ mutation to a branch (canonical mutation endpoint).",
|
|
"description": "Writes to the named `branch` (defaults to `main`). Mutations are atomic\nper call and produce a new commit. Returns counts of nodes and edges\naffected. **Destructive**: on success the branch is updated; rejected\nmutations may still acquire locks briefly. Returns 409 on merge conflict.\n\nPairs with `POST /query` (read-only). The legacy `POST /change` route\nhas identical semantics and is kept as a deprecated alias.",
|
|
"operationId": "mutate",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ChangeRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Mutation results",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ChangeOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Merge conflict",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/queries": {
|
|
"get": {
|
|
"tags": [
|
|
"queries"
|
|
],
|
|
"summary": "List the graph's exposed stored queries as a typed tool catalog.",
|
|
"description": "Returns the `mcp.expose == true` subset of the `queries:` registry, each\nwith its MCP tool name, read/mutate flag, description/instruction, and\ntyped parameters — enough for a client to register them as tools without\nfetching `.gq` source. Read-gated; the catalog is graph-wide (branch\nindependent — `read` is authorized against `main`). **Not** Cedar-filtered\nper query yet, so it can list a query whose `invoke_query` the caller\nlacks (a known gap until per-query authorization lands).",
|
|
"operationId": "list_queries",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Stored-query catalog (the mcp.expose subset, with typed params)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/QueriesCatalogOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/queries/{name}": {
|
|
"post": {
|
|
"tags": [
|
|
"queries"
|
|
],
|
|
"summary": "Invoke a curated, server-side stored query by name.",
|
|
"description": "The query source comes from the graph's `queries:` registry, not the\nrequest body — callers send only runtime inputs (`params`, `branch`,\n`snapshot`). Gated by the `invoke_query` Cedar action at the boundary;\na stored *mutation* additionally passes the engine's `change` gate\n(double-gated). An actor **without** `invoke_query` cannot tell a denied\nquery from a missing one — both return the same 404, so the catalog\ncan't be probed without the grant. Once `invoke_query` is held, the\ninner `read`/`change` gate may surface a 403 for an existing query the\nactor can't run (the intended double-gate signal).",
|
|
"operationId": "invoke_query",
|
|
"parameters": [
|
|
{
|
|
"name": "name",
|
|
"in": "path",
|
|
"description": "Stored query name (the registry key)",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"oneOf": [
|
|
{
|
|
"type": "null"
|
|
},
|
|
{
|
|
"$ref": "#/components/schemas/InvokeStoredQueryRequest"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Read envelope (ReadOutput) or mutation envelope (ChangeOutput), serialized untagged",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InvokeStoredQueryResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request (param type error; snapshot on a stored mutation)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden (the inner `change` gate for a stored mutation)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Unknown stored query, or `invoke_query` denied — indistinguishable to a caller without the grant",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Merge conflict",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Policy evaluation error (a denial is reported as 404, not 500)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/query": {
|
|
"post": {
|
|
"tags": [
|
|
"queries"
|
|
],
|
|
"summary": "Execute an inline read query (friendlier-named alternative to `POST /read`).",
|
|
"description": "Designed for ad-hoc exploration and AI-agent tool-use: short field\nnames (`query`, `name`) match the CLI `-e` flag and the GQ `query`\nkeyword. Mutations (`insert`/`update`/`delete`) are rejected with 400\n-- use `POST /mutate` (or its deprecated alias `POST /change`) for\nwrite queries. Otherwise behaves identically to `POST /read`: same\ntarget semantics (branch xor snapshot), same Cedar action (Read),\nsame response shape.",
|
|
"operationId": "query",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/QueryRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Query results",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ReadOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request - also returned when the query body contains mutations; use POST /mutate (or its deprecated alias POST /change) for write queries",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/read": {
|
|
"post": {
|
|
"tags": [
|
|
"queries"
|
|
],
|
|
"summary": "**Deprecated** — use [`POST /query`](#tag/queries/operation/query) instead.",
|
|
"description": "Execute a GQ read query. Behavior is unchanged from prior releases; the\nroute is kept indefinitely for byte-stable back-compat. New integrations\nshould target `POST /query`, which has clean field names (`query` /\n`name`) and a 400-on-mutation guard. Responses from this route include\n`Deprecation: true` and `Link: </query>; rel=\"successor-version\"`\nheaders per RFC 9745 / RFC 8288 so SDKs and proxies can surface the\nsignal.",
|
|
"operationId": "read",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ReadRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Query results (response includes `Deprecation: true` + `Link: </query>; rel=\"successor-version\"`)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ReadOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"deprecated": true,
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/schema": {
|
|
"get": {
|
|
"tags": [
|
|
"schema"
|
|
],
|
|
"summary": "Read the current schema source.",
|
|
"description": "Returns the project's schema as a single string in `.pg` source form.\nUseful for clients that want to introspect available types and tables\nbefore constructing GQ queries. Read-only.",
|
|
"operationId": "getSchema",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Current schema source",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SchemaOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/schema/apply": {
|
|
"post": {
|
|
"tags": [
|
|
"mutations"
|
|
],
|
|
"summary": "Apply a schema migration.",
|
|
"description": "Diffs `schema_source` against the current schema and applies the resulting\nmigration steps (add/drop type, add/drop column, etc.). **Destructive**:\nsome steps drop data. Returns the list of steps applied; if `applied` is\nfalse the diff was unsupported and no changes were made.",
|
|
"operationId": "applySchema",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SchemaApplyRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Schema apply results",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SchemaApplyOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Per-actor admission cap exceeded; honor `Retry-After` header",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/snapshot": {
|
|
"get": {
|
|
"tags": [
|
|
"snapshots"
|
|
],
|
|
"summary": "Read the current snapshot of a branch.",
|
|
"description": "Returns the manifest version plus per-table metadata (path, version, row\ncount) for every table on the branch. Defaults to `main` when `branch` is\nomitted. Read-only.",
|
|
"operationId": "getSnapshot",
|
|
"parameters": [
|
|
{
|
|
"name": "branch",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Database snapshot",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SnapshotOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Unauthorized",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearer_token": []
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"components": {
|
|
"schemas": {
|
|
"BranchCreateOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"uri",
|
|
"from",
|
|
"name"
|
|
],
|
|
"properties": {
|
|
"actor_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"from": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"uri": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"BranchCreateRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"name"
|
|
],
|
|
"properties": {
|
|
"from": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Parent branch to fork from. Defaults to `main`."
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"description": "Name of the new branch. Must not already exist."
|
|
}
|
|
}
|
|
},
|
|
"BranchDeleteOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"uri",
|
|
"name"
|
|
],
|
|
"properties": {
|
|
"actor_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"uri": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"BranchListOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"branches"
|
|
],
|
|
"properties": {
|
|
"branches": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"BranchMergeOutcome": {
|
|
"type": "string",
|
|
"enum": [
|
|
"already_up_to_date",
|
|
"fast_forward",
|
|
"merged"
|
|
]
|
|
},
|
|
"BranchMergeOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"source",
|
|
"target",
|
|
"outcome"
|
|
],
|
|
"properties": {
|
|
"actor_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"outcome": {
|
|
"$ref": "#/components/schemas/BranchMergeOutcome"
|
|
},
|
|
"source": {
|
|
"type": "string"
|
|
},
|
|
"target": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"BranchMergeRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"source"
|
|
],
|
|
"properties": {
|
|
"source": {
|
|
"type": "string",
|
|
"description": "Source branch whose commits will be merged."
|
|
},
|
|
"target": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Target branch that will receive the merge. Defaults to `main`."
|
|
}
|
|
}
|
|
},
|
|
"ChangeOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"branch",
|
|
"query_name",
|
|
"affected_nodes",
|
|
"affected_edges"
|
|
],
|
|
"properties": {
|
|
"actor_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"affected_edges": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
},
|
|
"affected_nodes": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
},
|
|
"branch": {
|
|
"type": "string"
|
|
},
|
|
"query_name": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"ChangeRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"query"
|
|
],
|
|
"properties": {
|
|
"branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Target branch. Defaults to `main`."
|
|
},
|
|
"name": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Name of the mutation to run when `query` declares multiple.\n\nAccepts the legacy field name `query_name` as a deserialization alias."
|
|
},
|
|
"params": {
|
|
"description": "JSON object whose keys match the mutation's declared parameters."
|
|
},
|
|
"query": {
|
|
"type": "string",
|
|
"description": "GQ mutation source containing `insert`, `update`, or `delete` statements.\nMay declare multiple named mutations; pick one with `name`.\n\nAccepts the legacy field name `query_source` as a deserialization alias.",
|
|
"example": "query insert_person($name: String, $age: I32) {\n insert Person { name: $name, age: $age }\n}"
|
|
}
|
|
}
|
|
},
|
|
"CommitListOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"commits"
|
|
],
|
|
"properties": {
|
|
"commits": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/CommitOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"CommitOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"graph_commit_id",
|
|
"manifest_version",
|
|
"created_at"
|
|
],
|
|
"properties": {
|
|
"actor_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"created_at": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"description": "Commit creation time as Unix epoch microseconds.",
|
|
"example": 1714000000000000
|
|
},
|
|
"graph_commit_id": {
|
|
"type": "string"
|
|
},
|
|
"manifest_branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"manifest_version": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"minimum": 0
|
|
},
|
|
"merged_parent_commit_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"parent_commit_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"ErrorCode": {
|
|
"type": "string",
|
|
"enum": [
|
|
"unauthorized",
|
|
"forbidden",
|
|
"bad_request",
|
|
"not_found",
|
|
"method_not_allowed",
|
|
"conflict",
|
|
"too_many_requests",
|
|
"internal"
|
|
]
|
|
},
|
|
"ErrorOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"error"
|
|
],
|
|
"properties": {
|
|
"code": {
|
|
"oneOf": [
|
|
{
|
|
"type": "null"
|
|
},
|
|
{
|
|
"$ref": "#/components/schemas/ErrorCode"
|
|
}
|
|
]
|
|
},
|
|
"error": {
|
|
"type": "string"
|
|
},
|
|
"manifest_conflict": {
|
|
"oneOf": [
|
|
{
|
|
"type": "null"
|
|
},
|
|
{
|
|
"$ref": "#/components/schemas/ManifestConflictOutput",
|
|
"description": "Set when the conflict is a publisher CAS rejection\n(`ManifestConflictDetails::ExpectedVersionMismatch`). The caller's\npre-write view of `table_key` was at version `expected` but the\nmanifest is now at `actual`. Refresh and retry."
|
|
}
|
|
]
|
|
},
|
|
"merge_conflicts": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/MergeConflictOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ExportRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Branch to export. Defaults to `main`."
|
|
},
|
|
"table_keys": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"description": "Restrict the export to these table keys. Empty exports all tables."
|
|
},
|
|
"type_names": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"description": "Restrict the export to these node/edge type names. Empty exports all types."
|
|
}
|
|
}
|
|
},
|
|
"GraphInfo": {
|
|
"type": "object",
|
|
"description": "One entry in the response from `GET /graphs`. Cluster operators\nconsume this list to discover which graphs the server is currently\nserving. The shape is intentionally minimal — `graph_id` and `uri`\nare the only fields a routing client needs.",
|
|
"required": [
|
|
"graph_id",
|
|
"uri"
|
|
],
|
|
"properties": {
|
|
"graph_id": {
|
|
"type": "string"
|
|
},
|
|
"uri": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"GraphListResponse": {
|
|
"type": "object",
|
|
"description": "Response from `GET /graphs`. Lists every graph registered with the\nserver in alphabetical order by `graph_id` (sorted server-side so\nclients get deterministic output across requests).",
|
|
"required": [
|
|
"graphs"
|
|
],
|
|
"properties": {
|
|
"graphs": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/GraphInfo"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"HealthOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"status",
|
|
"version"
|
|
],
|
|
"properties": {
|
|
"source_version": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"status": {
|
|
"type": "string"
|
|
},
|
|
"version": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"IngestOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"uri",
|
|
"branch",
|
|
"base_branch",
|
|
"branch_created",
|
|
"mode",
|
|
"tables"
|
|
],
|
|
"properties": {
|
|
"actor_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"base_branch": {
|
|
"type": "string"
|
|
},
|
|
"branch": {
|
|
"type": "string"
|
|
},
|
|
"branch_created": {
|
|
"type": "boolean"
|
|
},
|
|
"mode": {
|
|
"$ref": "#/components/schemas/LoadMode"
|
|
},
|
|
"tables": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/IngestTableOutput"
|
|
}
|
|
},
|
|
"uri": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"IngestRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"data"
|
|
],
|
|
"properties": {
|
|
"branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Target branch. Created from `from` if it does not yet exist. Defaults to `main`."
|
|
},
|
|
"data": {
|
|
"type": "string",
|
|
"description": "NDJSON payload: one record per line, each shaped\n`{\"type\": \"<TypeName>\", \"data\": {...}}`.",
|
|
"example": "{\"type\": \"Person\", \"data\": {\"name\": \"Alice\", \"age\": 30}}\n{\"type\": \"Person\", \"data\": {\"name\": \"Bob\", \"age\": 25}}"
|
|
},
|
|
"from": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Parent branch used to create `branch` if it does not exist. Defaults to `main`."
|
|
},
|
|
"mode": {
|
|
"oneOf": [
|
|
{
|
|
"type": "null"
|
|
},
|
|
{
|
|
"$ref": "#/components/schemas/LoadMode",
|
|
"description": "How existing rows are handled. Defaults to `merge`."
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"IngestTableOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"table_key",
|
|
"rows_loaded"
|
|
],
|
|
"properties": {
|
|
"rows_loaded": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
},
|
|
"table_key": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"InvokeStoredQueryRequest": {
|
|
"type": "object",
|
|
"description": "Body for `POST /queries/{name}` — invokes the server-side stored query\nnamed in the path. The query source and name come from the registry,\nnever the body; only the runtime inputs are supplied here.",
|
|
"properties": {
|
|
"branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Branch to run against. Defaults to `main`; for a stored mutation the\nwrite targets this branch."
|
|
},
|
|
"params": {
|
|
"description": "JSON object whose keys match the stored query's declared parameters."
|
|
},
|
|
"snapshot": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Snapshot id to read from (read queries only — rejected for a stored\nmutation). Mutually exclusive with `branch`."
|
|
}
|
|
}
|
|
},
|
|
"InvokeStoredQueryResponse": {
|
|
"oneOf": [
|
|
{
|
|
"$ref": "#/components/schemas/ReadOutput"
|
|
},
|
|
{
|
|
"$ref": "#/components/schemas/ChangeOutput"
|
|
}
|
|
],
|
|
"description": "Response for `POST /queries/{name}`: the read envelope for a stored\nread, or the mutation envelope for a stored mutation. Serialized\n**untagged**, so the wire shape is exactly [`ReadOutput`] or\n[`ChangeOutput`] — classification follows the stored query, not a\nwrapper field."
|
|
},
|
|
"LoadMode": {
|
|
"type": "string",
|
|
"description": "Shadow enum for documenting [`LoadMode`] in the OpenAPI schema.",
|
|
"enum": [
|
|
"overwrite",
|
|
"append",
|
|
"merge"
|
|
]
|
|
},
|
|
"ManifestConflictOutput": {
|
|
"type": "object",
|
|
"description": "Structured details for a publisher-level OCC failure. Surfaces alongside\nHTTP 409 when a write was rejected because the caller's pre-write view of\none table's manifest version was stale relative to the current head. The\nexpected/actual fields tell the client which table to refresh.",
|
|
"required": [
|
|
"table_key",
|
|
"expected",
|
|
"actual"
|
|
],
|
|
"properties": {
|
|
"actual": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"minimum": 0
|
|
},
|
|
"expected": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"minimum": 0
|
|
},
|
|
"table_key": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"MergeConflictKindOutput": {
|
|
"type": "string",
|
|
"enum": [
|
|
"divergent_insert",
|
|
"divergent_update",
|
|
"delete_vs_update",
|
|
"orphan_edge",
|
|
"unique_violation",
|
|
"cardinality_violation",
|
|
"value_constraint_violation"
|
|
]
|
|
},
|
|
"MergeConflictOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"table_key",
|
|
"kind",
|
|
"message"
|
|
],
|
|
"properties": {
|
|
"kind": {
|
|
"$ref": "#/components/schemas/MergeConflictKindOutput"
|
|
},
|
|
"message": {
|
|
"type": "string"
|
|
},
|
|
"row_id": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"table_key": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"ParamDescriptor": {
|
|
"type": "object",
|
|
"description": "One declared parameter of a stored query, projected for the catalog.",
|
|
"required": [
|
|
"name",
|
|
"kind",
|
|
"nullable"
|
|
],
|
|
"properties": {
|
|
"item_kind": {
|
|
"oneOf": [
|
|
{
|
|
"type": "null"
|
|
},
|
|
{
|
|
"$ref": "#/components/schemas/ParamKind",
|
|
"description": "Element kind when `kind == list` (always a scalar — the grammar\nforbids lists of vectors or nested lists)."
|
|
}
|
|
]
|
|
},
|
|
"kind": {
|
|
"$ref": "#/components/schemas/ParamKind"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"nullable": {
|
|
"type": "boolean",
|
|
"description": "`false` → the caller must supply it; `true` → optional."
|
|
},
|
|
"vector_dim": {
|
|
"type": [
|
|
"integer",
|
|
"null"
|
|
],
|
|
"format": "int32",
|
|
"description": "Dimension when `kind == vector`.",
|
|
"minimum": 0
|
|
}
|
|
}
|
|
},
|
|
"ParamKind": {
|
|
"type": "string",
|
|
"description": "The kind of a stored-query parameter, decomposed so a client (e.g. an\nMCP server) can build a typed input schema with a closed `match` and\nnever re-parse omnigraph's type spelling. `bigint`/`date`/`datetime`/\n`blob` are carried as JSON strings on the wire: a 64-bit integer past\n2^53 loses precision as a JSON number, and Date/DateTime are ISO\nstrings, Blob a blob-URI string.",
|
|
"enum": [
|
|
"string",
|
|
"bool",
|
|
"int",
|
|
"bigint",
|
|
"float",
|
|
"date",
|
|
"datetime",
|
|
"blob",
|
|
"vector",
|
|
"list"
|
|
]
|
|
},
|
|
"QueriesCatalogOutput": {
|
|
"type": "object",
|
|
"description": "Response for `GET /queries`: the `mcp.expose` subset of a graph's\nstored-query registry, each with typed parameters.",
|
|
"required": [
|
|
"queries"
|
|
],
|
|
"properties": {
|
|
"queries": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/QueryCatalogEntry"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"QueryCatalogEntry": {
|
|
"type": "object",
|
|
"description": "One entry in the stored-query catalog (`GET /queries`).",
|
|
"required": [
|
|
"name",
|
|
"tool_name",
|
|
"mutation",
|
|
"params"
|
|
],
|
|
"properties": {
|
|
"description": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"instruction": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"mutation": {
|
|
"type": "boolean",
|
|
"description": "`true` for a stored mutation → an MCP read-only hint of `false`."
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"description": "Registry key / invoke path segment (`POST /queries/{name}`)."
|
|
},
|
|
"params": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/ParamDescriptor"
|
|
}
|
|
},
|
|
"tool_name": {
|
|
"type": "string",
|
|
"description": "MCP tool id (the `tool_name` override, else `name`)."
|
|
}
|
|
}
|
|
},
|
|
"QueryRequest": {
|
|
"type": "object",
|
|
"description": "Inline read-query request for `POST /query`.\n\nFriendlier-named alternative to [`ReadRequest`] for ad-hoc reads and\nAI-agent integration. Mutations are rejected with 400 — use `POST\n/mutate` (or its deprecated alias `POST /change`) for write queries.\nField names are deliberately short (`query`, `name`) to match the GQ\nkeyword and the CLI `-e` flag.",
|
|
"required": [
|
|
"query"
|
|
],
|
|
"properties": {
|
|
"branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Branch to read from. Mutually exclusive with `snapshot`. Defaults to `main`."
|
|
},
|
|
"name": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Name of the query to run when `query` declares multiple. Optional when\nonly one query is declared."
|
|
},
|
|
"params": {
|
|
"description": "JSON object whose keys match the query's declared parameters."
|
|
},
|
|
"query": {
|
|
"type": "string",
|
|
"description": "GQ read-query source. May declare one or more named queries; pick one\nwith `name` when more than one is declared. Mutations\n(`insert`/`update`/`delete`) get 400 — use `POST /mutate` (or its\ndeprecated alias `POST /change`) instead.",
|
|
"example": "query get_person($name: String) {\n match {\n $p: Person { name: $name }\n }\n return { $p.name, $p.age }\n}"
|
|
},
|
|
"snapshot": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Snapshot id to read from. Mutually exclusive with `branch`."
|
|
}
|
|
}
|
|
},
|
|
"ReadOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"query_name",
|
|
"target",
|
|
"row_count",
|
|
"rows"
|
|
],
|
|
"properties": {
|
|
"columns": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"query_name": {
|
|
"type": "string"
|
|
},
|
|
"row_count": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
},
|
|
"rows": {},
|
|
"target": {
|
|
"$ref": "#/components/schemas/ReadTargetOutput"
|
|
}
|
|
}
|
|
},
|
|
"ReadRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"query_source"
|
|
],
|
|
"properties": {
|
|
"branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Branch to read from. Mutually exclusive with `snapshot`. Defaults to `main`."
|
|
},
|
|
"params": {
|
|
"description": "JSON object whose keys match the query's declared parameters."
|
|
},
|
|
"query_name": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Name of the query to run when `query_source` declares multiple. Optional\nwhen only one query is declared."
|
|
},
|
|
"query_source": {
|
|
"type": "string",
|
|
"description": "GQ query source. May declare one or more named queries; pick one with\n`query_name` if there is more than one.",
|
|
"example": "query get_person($name: String) {\n match {\n $p: Person { name: $name }\n }\n return { $p.name, $p.age }\n}"
|
|
},
|
|
"snapshot": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
],
|
|
"description": "Snapshot id to read from. Mutually exclusive with `branch`."
|
|
}
|
|
}
|
|
},
|
|
"ReadTargetOutput": {
|
|
"type": "object",
|
|
"properties": {
|
|
"branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"snapshot": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"SchemaApplyOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"uri",
|
|
"supported",
|
|
"applied",
|
|
"step_count",
|
|
"manifest_version",
|
|
"steps"
|
|
],
|
|
"properties": {
|
|
"applied": {
|
|
"type": "boolean"
|
|
},
|
|
"manifest_version": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"minimum": 0
|
|
},
|
|
"step_count": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
},
|
|
"steps": {
|
|
"type": "array",
|
|
"items": {}
|
|
},
|
|
"supported": {
|
|
"type": "boolean"
|
|
},
|
|
"uri": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"SchemaApplyRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"schema_source"
|
|
],
|
|
"properties": {
|
|
"allow_data_loss": {
|
|
"type": "boolean",
|
|
"description": "When true, promote every `DropMode::Soft` step in the plan to\n`DropMode::Hard`, making the prior column data unreachable\nafter the apply. Matches the CLI's `--allow-data-loss` flag.\nDefaults to `false` (drops remain reversible via time travel)."
|
|
},
|
|
"schema_source": {
|
|
"type": "string",
|
|
"description": "Project schema in `.pg` source form. The diff against the current\nschema produces the migration steps that will be applied.",
|
|
"example": "node Person {\n name: String @key\n age: I32?\n}\n\nedge Knows: Person -> Person"
|
|
}
|
|
}
|
|
},
|
|
"SchemaOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"schema_source"
|
|
],
|
|
"properties": {
|
|
"schema_source": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"SnapshotOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"branch",
|
|
"manifest_version",
|
|
"tables"
|
|
],
|
|
"properties": {
|
|
"branch": {
|
|
"type": "string"
|
|
},
|
|
"manifest_version": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"minimum": 0
|
|
},
|
|
"tables": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/SnapshotTableOutput"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"SnapshotTableOutput": {
|
|
"type": "object",
|
|
"required": [
|
|
"table_key",
|
|
"table_path",
|
|
"table_version",
|
|
"row_count"
|
|
],
|
|
"properties": {
|
|
"row_count": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"minimum": 0
|
|
},
|
|
"table_branch": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"table_key": {
|
|
"type": "string"
|
|
},
|
|
"table_path": {
|
|
"type": "string"
|
|
},
|
|
"table_version": {
|
|
"type": "integer",
|
|
"format": "int64",
|
|
"minimum": 0
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"securitySchemes": {
|
|
"bearer_token": {
|
|
"type": "http",
|
|
"scheme": "bearer"
|
|
}
|
|
}
|
|
}
|
|
}
|