Skip to content
Ocean-Atlasv0.1.0Canonical Knowledge

Ocean-Atlas Knowledge API

The Knowledge API lets Ocean components and external tools — Ocean Studio, Ocean CLI, Ocean Assistant, generators, compiler diagnostics — read Ocean-Atlas knowledge programmatically, without depending on the website.

It is implemented by services/knowledge-api, a small node:http service with no HTTP framework dependency. It depends only on @ocean-atlas/builder-core’s buildAtlasModel, never on canonical Markdown directly and never on generated presentation output. The architectural decision behind it, including alternatives considered, is recorded in adr.knowledge-api-service.

The Knowledge API is not public. It is reachable only from other Ocean services over Fly’s private network (6PN), at:

http://ocean-atlas.internal:8080/v1

This matches the convention originally used by sibling Ocean services (for example ocean-audit.internal:8080/v1, ocean-bundle.internal:8080/v1). It is never declared as a public Fly service, and the public website’s nginx process does not proxy to it — the two are independent processes in the same container, sharing nothing but the Machine they run on.

Sibling backend services have since moved from .internal to Fly .flycast addressing (proxy-side load balancing and concurrency limits). The Knowledge API deliberately stays on .internal: .flycast routing requires the target port to be a declared [[services]]/[http_service] block, and on this app — which also serves the public website from public IPs — declaring port 8080 would expose it on those public IPs too. Keeping port 8080 undeclared is exactly what adr.knowledge-api-service relies on for privacy, so .internal remains the correct addressing here until a new ADR revisits that boundary.

There is no application-level authentication for v1. The trust boundary is “any Machine on the Ocean Fly organization’s private network.” Every route is GET-only and read-only.

buildAtlasModel({ repositoryRoot }) runs once at process startup. There is no live filesystem watching in production — a knowledge change requires a redeploy, the same deterministic, snapshot-per-build model Ocean-Atlas already uses for its static site.

// single resource
{ "data": { /* ... */ }, "meta": { "schemaVersion": 1, "apiVersion": "v1" } }
// collection
{
"data": [ /* ... */ ],
"meta": { "schemaVersion": 1, "apiVersion": "v1", "total": 74, "limit": 50, "offset": 0 }
}
// error (alongside a real HTTP status code)
{ "error": { "status": 404, "code": "not_found", "message": "..." } }

GET /v1/openapi.json and GET /v1/docs are the two exceptions: the former returns the OpenAPI document itself, unwrapped, since external tooling expects a plain OpenAPI file at that path; the latter returns an HTML page (Swagger UI), not JSON at all.

All routes are under /v1.

Route Description
GET /navigation The knowledge navigation hierarchy.
GET /documents List documents. Filters: type, area, status, authority. Paginated: limit (default 50, max 200), offset. Returns summaries only — no content.
GET /documents/:id One document by stable ID, including content.
GET /documents/:id/metadata Just the document’s metadata — no content.
GET /documents/:id/relationships { outgoing, incoming } — resolved, typed edges in both directions.
GET /documents/:id/related Deduped, resolved union of outgoing and incoming relationships.
GET /examples List examples. Paginated.
GET /examples/:id One example by stable ID (usage, explanation, implementation facets).
GET /knowledge/:id Resolves a stable ID to either a document or an example: { kind, data }.
GET /search Ranked keyword search across documents and examples. Query: q, plus the same filters as /documents. Filters work standalone without q.
GET /semantic-search Ranked semantic (meaning-based) search over knowledge chunks — see §7. q is required; same structured filters. Returns 503 if not configured or not yet usable (§7).
GET /taxonomy Facet counts by type, area, status, and authority.
GET /version Schema and API version, document/example counts, build timestamp.
GET /health Process liveness — distinct from the site’s static /healthz.
GET /openapi.json This API’s OpenAPI specification.
GET /docs Interactive Swagger UI for exploring and trying routes. Serves an HTML page whose Swagger UI assets load from a CDN — not bundled, to keep this service dependency-free — so opening it requires internet access.

Documents and examples are kept as separate resources rather than folded together: NormalizedExample’s shape (usage, explanation, and implementation facets) is materially different from a flat document. GET /knowledge/:id exists specifically for callers that don’t already know which kind an ID resolves to — relationship targets can point at either.

A small, hand-rolled inverted index — no external search dependency. Indexed fields, weighted: title (5), summary (3), metadata’s free-form additional values and, for examples, usage.tags (2), and full document/example content (1). A query matching the exact stable ID of a record ranks first. authority: Authoritative records get a small ranking boost; deprecated/superseded records get a small penalty. Deliberately the simplest keyword search that could work for a corpus this size.

GET /v1/semantic-search ranks by meaning, not keyword overlap, over chunks rather than whole documents/examples — see adr.knowledge-chunking-and-indexing for the full design. Summary:

  • Chunking: canonical content is split along ## /### heading boundaries (falling back to paragraphs only when a section is still too large), with a stable, ID-derived chunk identity (<document-stable-id>#<heading-slug>) — never a route or file path.
  • Storage: chunk text and a 1024-dimension Voyage AI (voyage-4) embedding live in Postgres/pgvector, in a dedicated ocean_atlas database on the shared Fly Postgres cluster — see adr.vector-storage-and-embedding-provider. Every row is a rebuildable cache, reconstructable from canonical Markdown at any time.
  • Indexing lifecycle: one reconciliation pass per process start, not a live watcher and not re-embedding the whole corpus every time — only new or changed chunks are embedded, removed chunks are deleted, and the result is published atomically (all writes commit together, or a failure leaves the previous completed index fully untouched).
  • Availability: 503 only when no index generation has ever completed. A routine restart, or a background refresh already in progress, never blocks semantic search from serving the last completed (“usable”) index — even if that generation isn’t yet “current” (doesn’t yet reflect today’s content or embedding model). GET /v1/health reports both semanticSearchReady (usable) and semanticSearchCurrent separately.
  • Results carry the same source attribution as everything else in this API — documentId, heading, route, type/area/status/authority — plus the matched chunk’s own text, so a caller (or an LLM) can cite precisely what justified the result.

Deliberately a separate endpoint from keyword /v1/search, not merged with it — hybrid ranking is an explicitly deferred future decision, not built here.

atlas.knowledge-architecture §20–21 describe “version-aware retrieval” as retrieving knowledge applicable to a specific Ocean version (an appliesTo-style field). DocumentMetadata does not carry that field yet — only a generic additional catch-all — and the reference describing it (atlas.metadata-schema) is itself still Status: Draft. The Knowledge API’s /version route reports API/schema/build version only. Full per-document Ocean-version-aware retrieval would require a prior builders/core metadata change; it is not something this service can fabricate on its own.

These semantic relationships are declared in the document metadata.