Docs/Publishing/The published API and SDKs

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
}
  • q shorter than 2 characters matches nothing (returns an empty result set, not an error).
  • limit is clamped to [1, 50], default 20; truncated reports whether more notes matched than limit returned.
  • 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 via Authorization: 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"} with Retry-After.
  • Fail-closed for anonymous callers: unauthenticated requests only ever resolve audience: public views. Everything else — unknown slug, restricted audience, missing/invalid/ungranted credential — is a 404, never a 401/403, so existence is never leaked.
  • Caching: public views send Cache-Control: public, max-age=60 plus a weak ETag; send it back as If-None-Match for a 304. Restricted views send private, max-age=60 + Vary: authorization instead, 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, and swift build && swift test all 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-pub on npm, pantheon-solomon on PyPI, solomon-pub on crates.io, SolomonPub via 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 --push flag, nothing committed or pushed) unless explicitly told otherwise, and the generated TypeScript release workflow itself gates on a PUBLISH_ENABLED variable — unset, it runs npm publish --dry-run --provenance rather 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]]