Langonrock
Guides

HTTP API

The read endpoints, their ETags, and the one response that is not JSON.

HTTP/1.1 and JSON, with one deliberate exception. The manifest goes out as raw bytes, because JSON-escaping a TSV adds bytes and parse cost to the payload transferred most often.

The same routes serve a unix socket and a TCP listener. The socket still speaks HTTP underneath.

Read endpoints

GET  /v1/{tenant}/manifest[?bundle=sales]  the manifest, ETag is the snapshot digest
GET  /v1/{tenant}/snapshot                 {"snapshot": "…", "concepts": n}
POST /v1/{tenant}/get                      {"ids": [...], "section"?: "schema"}
POST /v1/{tenant}/search                   {"q": "...", "k"?: n, "expand"?: false, "bundle"?: "..."}

The tenant segment is optional when a token identifies one, so /v1/manifest and /v1/acme/manifest both work. It is required when the server has no tokens at all, which only happens on a unix socket.

curl -H 'Authorization: Bearer read-only' http://127.0.0.1:7777/v1/manifest

curl -X POST -H 'Authorization: Bearer read-only' -H 'content-type: application/json' \
  -d '{"ids":["orders"],"section":"schema"}' http://127.0.0.1:7777/v1/get

Conditional requests

The manifest carries an ETag set to the snapshot digest, or to digest:bundle when narrowed. Send If-None-Match with the ETag you hold and an unchanged manifest answers 304 with no body.

curl -H 'Authorization: Bearer read-only' -H 'If-None-Match: "9ac37f10832c…"' \
  http://127.0.0.1:7777/v1/manifest

Between turns of a conversation that is the whole transfer skipped. snapshot is the cheaper check when you only want to know whether anything moved.

Responses

EndpointContent typeShape
manifesttext/tab-separated-valuesRaw TSV, comment lines then a header row
snapshotapplication/json{"snapshot": "…", "concepts": n}
getapplication/jsonAn object keyed by id, missing ids absent
searchtext/tab-separated-valuesManifest rows, with # query and # hits comments

get returns an object rather than an array, so a missing id is simply not a key. Nothing errors when one of a batch does not exist.

search returns manifest rows and never bodies. Narrow with search, then fetch what you chose with get.

Search parameters

FieldDefaultEffect
qrequiredThe query. An empty string is a 400.
k8Ranked matches before link expansion
expandtruefalse disables the one-hop expansion over the link graph
bundlenoneRank only within this bundle, and expand only into it

The expansion is capped at k and ranked by how many hits point at a target, so a hub concept with sixteen outgoing links cannot drag most of the manifest into the result.

Status codes

CodeWhen
200Fine
204A write or delete on the source API succeeded
304If-None-Match matched the current ETag
400A malformed body, or a missing concept path
401Missing or unknown bearer token
403The token does not grant that tenant, or may read but not write
404No such route, no such concept, no such bundle
405Wrong method for the route
409The tenant has no source directory, or the server cannot recompile
412A precondition failed on a write
413A concept larger than 1,000,000 bytes
428A write with no precondition header

Errors carry a plain-text message, not a JSON envelope.

Write endpoints

Creating, changing and deleting concepts goes through the source API, which writes Markdown rather than snapshots. Those routes are covered in Editing over the network.

GET    /v1/{tenant}/source
GET    /v1/{tenant}/source/{bundle}/{path}
PUT    /v1/{tenant}/source/{bundle}/{path}
DELETE /v1/{tenant}/source/{bundle}/{path}
DELETE /v1/{tenant}/bundles/{bundle}
POST   /v1/{tenant}/sync

No HTTP request ever writes a snapshot

The watcher recompiles from source, so a snapshot written directly would be overwritten within seconds. Writing source is the path the design endorses, and it leaves the read API above untouched.

On this page