Meta API
Reference for the meta.svc HTTP surface. For concepts see the Meta block docs.
Meta serves a product’s files by path, as they are. It does not know what the files mean: each block reads its own folders through its meta client and interprets them itself. There are no typed, component or service-config reads.
The API is mounted at the server root. /health and /ready are plain endpoints; everything else goes through the middleware chain: auth → error status → conditional requests → compression (gzip/zstd).
Customer. Every product route names the product in the path. The customer comes from the
caller’s tenant token, or from X-Customer under an API key; meta serve defaults it when it
serves one customer. Per-repo access rules (access.read / access.write) and license
entitlements apply after authentication.
Conditional requests. Every file read returns a SHA-256 ETag; send If-None-Match to get a 304.
Versions. Read routes accept ?version=<name>&versionType=branch|tag to read a branch or tag
instead of what the repository serves (for a remote repository, as its mirror last fetched it). A
folder has one version. A write that names a version is refused with 400: a write goes to the
working tree.
.git is never served, listed, written or removed, at any depth.
Product routes: /product/{id}/…
Section titled “Product routes: /product/{id}/…”| Method | Path | Purpose |
|---|---|---|
| GET | file/{path…} | Raw bytes + ETag; 404 when the product has no such file |
| GET | files | A listing (?path=&depth=) |
| POST | batch | {"paths":[…]} → {"files":[{path,content,hash,size,error}]}; a missing file is an entry with error (not found, invalid path, unreadable), not a failed batch |
| GET | manifest | ?path= → {root_hash, entries:[{path,hash,size,mod_time,is_dir}]}: a cheap “did anything change?” check; a folder the product does not have is an empty manifest |
| GET | fs/stat/{path…} | {name,size,mode,mod_time,is_dir}; 404 when absent |
| GET | fs/readdir/{path…} | The entries directly in a folder, same shape |
mod_time is Unix milliseconds. file/{path…} reads a file and fs/stat answers whether a path
exists: the fs/readfile and fs/exists routes that duplicated them are gone.
Change stream
Section titled “Change stream”GET /product/stream?id={id} is an event stream: server-sent events when asked for (Accept: text/event-stream; a browser EventSource asks), one JSON object per line
(application/x-ndjson) otherwise:
| Event | Payload | When |
|---|---|---|
snapshot | {repo, ref, hash, ts} | On connect: a client that reconnects compares hash |
repo-updated | {repo, ref, hash, prevHash, ts} | The product’s content moved: a fetch of a remote repository, an edit on disk of a checkout or folder, a write through this API |
ping | Every 30 s |
hash is the commit a git repository serves, or a stamp of a working tree’s or folder’s files.
ref is empty for a folder.
Writes (the repository must not be readonly)
Section titled “Writes (the repository must not be readonly)”| Method | Path | Purpose |
|---|---|---|
| PUT | files?path= | Create a folder; 409 if it exists |
| DELETE | files?path= | Remove a folder; 404 if absent; the product root cannot be removed (400) |
| POST | file/{path…} | Upload (multipart field files) |
| POST | zip/{path…} | Upload a zip, expanded in place |
| Method | Path | Purpose |
|---|---|---|
| GET | git/refs | Branches and tags: a remote repository’s as last fetched; none for a folder |
| GET | git/version | The commit the repository serves |
| POST | git/status, git/diff | A local checkout’s working tree |
| POST | git/stage, git/reset, git/commit | git add ({"paths"}, "." for all), unstage, commit ({"message","paths"?}) |
| PATCH | git/commitfile | Write, stage and commit one file: {"path","file","message"?} |
| PATCH | git/patch | Apply a text patch to one file: {"path","patch"} |
| POST | git/fetch, git/pull, git/push | Against a remote of a local checkout |
An operation that needs a working tree (everything but git/refs and git/version) needs a
local checkout (400 otherwise), and a write one that is not read-only (403). git/version of a
folder is 400: it has no commit.
Marketplace routes: /market/{id}/…
Section titled “Marketplace routes: /market/{id}/…”GET file/{path…} · PATCH paths (several files as a zip) · GET git/zip · GET fileorfolderzip/{path…} · POST git/fetch.
Instance routes: /ops/…
Section titled “Instance routes: /ops/…”| Method | Path | Purpose |
|---|---|---|
| GET | repositories | The repositories meta holds: an operator sees every customer’s, anyone else their own |
| GET | tenant-repositories | The calling tenant’s on-demand repositories |
| GET | embedded-info | The products compiled into this binary; an encrypted payload reports only that it is encrypted |
| GET | license-status | The calling customer’s entitlements |
| POST | license-activate | {"token"} → entitlement map (not served by meta serve) |
| POST | license-deactivate | Drop a license (operators) |
Authentication
Section titled “Authentication”Meta accepts three credentials, tried in order, and the first that succeeds wins:
| Order | Credential |
|---|---|
| 1 | API key |
| 2 | Certificate JWT |
| 3 | Bearer token |
A credential that is simply absent falls through to the next. The last is strict: a request that reaches it without a valid token is rejected.
Meta is served over a protocol layer rather than plain HTTP handlers, so authentication runs as middleware over the request metadata rather than per route. The effect is the same cascade every other service reaches through its HTTP guard.
Tenancy
Section titled “Tenancy”CEPT is populated from request metadata before authentication runs, so tenant resolution and credential validation are separate steps. A request can therefore resolve a tenant and still be rejected for credentials, which is why a tenancy failure and an auth failure look different.
Errors
Section titled “Errors”A caller’s mistake answers its own status; only a server fault is a 500, so a 4xx is never worth retrying.
| Status | Meaning |
|---|---|
400 | Invalid request: a missing field, a path that escapes the product, a write naming a version, git porcelain against a repository that is not a local checkout |
401 | No credential validated |
403 | A read-only repository, a failed access rule or entitlement |
404 | No such product, file or folder |
409 | Already exists (creating a folder that is there) |
500 | Unexpected server error |