The published API and SDKs
Every publication (see [[Publish a folder or vault]]) is readable two ways:
the human-facing HTML render at /p/{slug}, and a versioned, machine-readable
JSON API at /api/v1/pub/{slug}/.... The JSON API is the source of truth an
OpenAPI contract describes — and the
generated SDKs are built directly from that contract.
Endpoints
| Endpoint | Purpose |
|---|---|
GET /api/v1/pub/{slug} |
The view's listing: {slug, site_title, scope, audience, updated_at, notes: [{note_id, path, title}]}. No bodies — stays small even for a whole-vault publish. |
GET /api/v1/pub/{slug}/notes/{path} |
One note: {note_id, path, title, frontmatter, markdown, html}. {path} accepts both the extension-stripped URL form and the literal .md path. markdown + frontmatter are the stable fields; html is presentation output and may change between releases. |
GET /api/v1/pub/{slug}/search?q=...&limit=... |
Full-text substring search across the view's published notes. New in this API surface. |
All three (plus their OPTIONS preflight) exist today; there is no
pagination yet on the listing route — it's a deliberate v1 tradeoff, called
out in the spec.
Search
search does case-insensitive substring matching over note bodies, ranked
by match count with a title-match boost, using the exact same result
vocabulary as every other Solomon search surface:
{
"query": "publish a folder",
"slug": "solomon-docs",
"results": [
{ "path": "guides/publishing.md", "note_id": "01...", "title": "Publishing",
"snippet": "…how to publish a folder or vault…", "line": 12, "score": 7 }
],
"truncated": false
}
qshorter than 2 characters matches nothing (returns an empty result set, not an error).limitis clamped to[1, 50], default20;truncatedreports whether more notes matched thanlimitreturned.- The audience/grant gate runs before the query is even inspected — a restricted view with no valid credential 404s without leaking so much as "your query was too short".
- If the same slug is also an active KB-market listing (see [[Publish a folder or vault]] for that flag), snippets are clipped to the anonymous KB portal's cap, so this free endpoint can never out-leak content the portal is meant to charge for. This clipping only happens for slugs that are KB listings — an ordinary docs-style publish is unaffected regardless of whether the KB market flag is on anywhere else.
Cross-cutting posture
- CORS is wide open (
Access-Control-Allow-Origin: *,GET, OPTIONS, no credentials) — deliberately, because these routes never accept cookies and only ever authenticate viaAuthorization: Bearer. See [[Reader keys and audiences]] for the credential chain and the fail-closed 404 posture that makes*safe here. - Rate limits are per-IP, in-memory, per-process: 240 requests/min for
the listing and note routes, a tighter 60/min for search (its own bucket,
since each search query is O(published-note-set)). Over the limit returns
429 {"error":"rate_limited"}withRetry-After. - Fail-closed for anonymous callers: unauthenticated requests only ever
resolve
audience: publicviews. Everything else — unknown slug, restricted audience, missing/invalid/ungranted credential — is a404, never a401/403, so existence is never leaked. - Caching: public views send
Cache-Control: public, max-age=60plus a weakETag; send it back asIf-None-Matchfor a304. Restricted views sendprivate, max-age=60+Vary: authorizationinstead, so they never land in a shared cache.
The generated SDKs
Solomon's SDK factory is a small, proprietary,
config-driven generator: it reads the OpenAPI contract and emits a typed
client for four languages — TypeScript, Python, Rust, and Swift — each
with the same three ergonomic methods (mapped from the spec's
operationIds):
site(slug) // getPublishedView
note(slug, path) // getPublishedNote
search(slug, q, limit?) // searchPublishedView
It's a from-scratch generator rather than a vendor tool — the generator stays proprietary; only the emitted SDK code is meant to ship as open source.
Each generated SDK is exercised by a golden/drift test: a committed byte-for-byte snapshot of the generator's output that the generator's test suite diffs against on every run (54 passing assertions across all four languages as of the last recorded rehearsal), plus per-language CI (lint/build/test) and release workflows baked into the generated repo via per-language templates.
What's live vs. not yet
Be precise about this — it's easy to overstate:
- The generator, the IR, the OpenAPI contract, and the golden-tested generated SDK code are real and exercised in CI.
- A full staging rehearsal (2026-07-12) ran the real sync pipeline
against private staging repos for all four languages — generate → overlay templates → mirror →
commit → push → CI green — and a rehearsal release tag on the TypeScript
staging repo.
cargo publish --dry-run,cargo fmt/clippy, andswift build && swift testall passed locally against the generated Rust/Swift code. - Publishing to public package registries is not yet enabled. The public
repo slugs and package names (
@pantheon-ai/solomon-pubon npm,pantheon-solomonon PyPI,solomon-pubon crates.io,SolomonPubvia SwiftPM) are marked as placeholders — unconfirmed and, for the public (non-staging) repos, not yet created. The sync script defaults to a dry run (no--pushflag, nothing committed or pushed) unless explicitly told otherwise, and the generated TypeScript release workflow itself gates on aPUBLISH_ENABLEDvariable — unset, it runsnpm publish --dry-run --provenancerather than a real publish.
In short: you cannot npm install/pip install/cargo add a Solomon SDK
today. The correctly-generated code exists and is tested; going public is
a founder-gated step that hasn't happened. See [[TypeScript SDK
quickstart]] for the generated client's shape today.
See also
- [[Publish a folder or vault]] — what a slug actually exposes.
- [[Reader keys and audiences]] — the Bearer credentials these endpoints accept.
- [[TypeScript SDK quickstart]]