HTTP daemon¶
Optional. Everyday open / put / get is the in-process Python API in Getting started (GraphStore.open, make_node, get_node). This daemon is localhost JSON over HTTP so another process can run openCypher queries without importing graph_ted_db. One process owns the graph folder and the process lock. It is plain JSON over HTTP, not a hosted multi-tenant server.
Default bind: 127.0.0.1:8099 (not 7474/7687, so it can run next to another graph database on the same machine).
First-time clone: run setup.bat (Windows) or ./setup.sh (macOS/Linux), then serve.bat / ./serve.sh. Or:
graph-ted-db init ./my-graph --name demo
graph-ted-db put-node ./my-graph --label Entity --prop name=Alice --prop group_id=default
graph-ted-db serve ./my-graph
A client stores http://127.0.0.1:8099 as its connection URL.
Endpoints¶
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness only: {"ok": true, "version": "0.1.0"}. It needs no token, so it reveals nothing about the graph. Use it (or RETURN 1 AS ok) as a connection test. |
GET |
/info or / |
graph.json fields (graph id, name, format), the folder path (root), live node/edge counts, and problems counts (see doctor). Needs the token when one is set |
POST |
/cypher |
One query (query) or an atomic batch (statements). Writes allowed. |
GET |
/cypher?query=… |
Read convenience for curl. Mutating queries return 405. params is a JSON object string |
POST /cypher¶
Request:
{
"query": "MATCH (n:Entity {group_id: $group_id}) RETURN n.name AS name",
"parameters": {"group_id": "default"},
"updated_by": "alice"
}
params is accepted as an alias of parameters. Optional updated_by is stored on records written by this request (default "cypher").
GET /cypher is read-only. Queries whose text contains (case-insensitive, word-boundary) CREATE, MERGE, DELETE, DETACH, SET, REMOVE, DROP, or FOREACH are rejected with 405. Use POST /cypher for writes.
Response:
{
"records": [{"name": "Alice"}],
"truncated": false
}
Results are truncated after 500 rows (--max-records). Send "max_records": 0 to raise the cap (hard max 100000). Query errors are 400 with {"error": "…", "type": "cypher"}.
Atomic batch (statements)¶
One lock, one transaction. All statements commit or none do. Use it when several writes must land together rather than as a sequence of auto-commit POSTs.
{
"statements": [
{"query": "MERGE (n:Entity {uuid: $uuid}) SET n.name = $name", "parameters": {"uuid": "…", "name": "Alice"}},
{"query": "RETURN 1 AS ok"}
]
}
Response:
{
"results": [
{"records": [], "truncated": false},
{"records": [{"ok": 1}], "truncated": false}
],
"truncated": false
}
Connection check¶
curl -s http://127.0.0.1:8099/health
curl -s http://127.0.0.1:8099/cypher \
-H 'Content-Type: application/json' \
-d '{"query":"RETURN 1 AS ok"}'
What you can test from a shell¶
No client application is needed. Against a folder:
- CLI CRUD —
put-node/ls-nodes/put-edge/delete-node/doctor - CLI queries —
graph-ted-db cypher ./my-graph 'MATCH (n) RETURN n.name AS name' - This daemon —
serve+ the curls above
Writes through openCypher queries include CREATE / MERGE / SET / DELETE / DETACH DELETE (POST only). The optional Graphiti driver (GraphTedDbDriver) accepts an http:// URL for this daemon. Opening GraphStore in-process remains available for library and CLI use.
put-node / put-edge from another terminal (a second process) are visible on the next /cypher request. The daemon reloads its catalog when shard files change; you do not need to restart serve.
Auth¶
Loopback binds (127.0.0.1, localhost, ::1) may run with no token.
Binding any other host requires a token: --token or env GRAPH_TED_DB_TOKEN. Serve refuses to start (exit 1) if the host is off-loopback and the token is empty. Default bind remains 127.0.0.1. Even with a token, serve prints a warning when it listens off loopback: traffic is plain HTTP, so anyone on the network path can read it. Prefer loopback, or put the daemon behind TLS.
If the port is taken, serve exits with port N in use; pass --port.
When a token is configured, every route except GET /health requires one of:
Authorization: Bearer <token>X-Graph-Ted-Token: <token>
Missing or wrong token is 401. The token protects the port, not the files on disk — see Security. This is not at-rest encryption and not a hosted multi-tenant server.
If GET /info shows a different name/id than the serve banner, another graph-ted-db serve is still bound to 8099 (common on Windows). Stop it, then start once:
netstat -ano | findstr :8099
taskkill /PID <pid> /F
Only serve.bat / serve.sh / graph-ted-db serve start the daemon. setup.* and shell.* do not.
WSL curl → Windows serve¶
WSL2 127.0.0.1 is the Linux VM, not Windows. A Windows serve bound to 127.0.0.1 is invisible from WSL. For dev:
- Do not run
servein WSL (nothing else on 8099 there). - On Windows, bind all interfaces and pass a token (
--host 0.0.0.0without--tokenorGRAPH_TED_DB_TOKENis refused):
graph-ted-db serve --host 0.0.0.0 --token YOUR_TOKEN_HERE D:\graphs\test-graph
- In WSL, curl the Windows host, not localhost.
GET /healthstays open; other routes need the token:
WIN=$(grep -m1 nameserver /etc/resolv.conf | awk '{print $2}')
curl -s "http://$WIN:8099/health"
curl -s "http://$WIN:8099/cypher" -H "Authorization: Bearer $GRAPH_TED_DB_TOKEN" \
-H "Content-Type: application/json" -d '{"query":"RETURN 1 AS ok"}'
If that times out, allow TCP 8099 in Windows Firewall (WSL uses a virtual adapter, so loopback-only rules are not enough):
netsh advfirewall firewall add rule name="graph-ted-db 8099" dir=in action=allow protocol=TCP localport=8099
Optional: Windows 11 WSL mirrored networking (%UserProfile%\.wslconfig) makes localhost the same on both sides — then keep a single serve, still on Windows:
[wsl2]
networkingMode=mirrored
Restart WSL (wsl --shutdown) after changing that. If a WSL serve is also running, curl will hit WSL again.