Skip to content

The query surface (OSL-SQL)

POST /osl/query is the single consumption endpoint. Structured analytics, metrics, governed vector retrieval and graph traversal are all expressed here — you don’t learn five surfaces to read a domain.

POST /osl/query
Authorization: Bearer <jwt> # attested delegation chain, see Authentication
Content-Type: application/json

The body is one of three mutually-exclusive shapes (mixing them is 400 E0513); all three go through the same governed seam and share one request_id:

Body What it is
{ "osql": "…" } OSL-SQL — the semantic SQL dialect (the canonical surface)
{ "sql": "…" } A raw read-only SELECT over the tenant allowlist (the SQL slice)
{ "metrics": […], "group_by": […], … } MetricFlow-style metrics query

osql is a semantic SQL dialect. It names only semantic objects — osl.entities.<model> and declared columns, or the table functions RETRIEVE(...) / TRAVERSE(...). It never names a physical table or a raw column.

The engine parses osql (via sqlglot, trino dialect) and lowers it to a closed operator IR — the deterministic ladder the governance model enforces over. Lowering is total and default-deny: any construct that does not lower to a known IR node is refused.

lower() classifies each query into exactly one form and validates its grammar:

SELECT segment, COUNT(*) AS n
FROM osl.entities.Customer
WHERE country IN ('ES','FR','DE')
GROUP BY segment
ORDER BY n DESC
LIMIT 100

Lowers to to_trino() → the governed SQL seam. Joins between structured entities are allowed only as a sealed-key equijoin (see below).

The only cross-relational node is an equijoin whose ON is a scalar equality on a sealed entity key (the model’s entity_key_columns, or a facet’s scalar_indexed field) on both legs. Any other JOIN … ON400 E0514.

There is no syntax that hands a second relational argument to a similarity or LLM operator — a relation×relation semantic join at query time (the non-goal R3) is a structural property of the grammar, not a blocklist to maintain.

X JOIN RETRIEVE(...) ON X.k = R.k is not a relational join of two physical tables. It runs as:

  1. RETRIEVE (governed) → passages, each carrying its denormalized key.
  2. Cleartext gate → the key values, or refuse (403 E0515).
  3. Structured prefilter: … WHERE k IN (<keys>)to_trino → governed SQL (the IN-list is built with sqlglot literal conversion — injection-safe).
  4. In-cell hash join on the sealed key, projecting each leg’s columns.

In the RETRIEVE form, <op>(col) in the SELECT is a projection operator (RFC 0004). They are unary maps — never a join.

Class Operators Runs If missing
Encoder (deterministic) SIMILARITY, CLASSIFY, TAG, RANK, DEDUP, OUTLIER, CLUSTER On embeddings already in the chunk
Decoder (inference) needs LLM EXTRACT, SUMMARIZE, SCORE, GENERATE, COMPLETE An LLM backend 503 E0521

{ "sql": "<SELECT>" } is a read-only SELECT validated against the tenant allowlist with the policy enforcement point applied (row filters + projection from the composed snapshot). { "metrics": …, "group_by": … } follows MetricFlow semantics. Both share the same governed seam as osql.

Code Meaning
200 Success
400 Malformed / out-of-grammar (E0511), non-declared object (E0512), body mixes shapes (E0513), non-sealed-key JOIN (E0514), empty sql (E0246)
403 PDP deny (E2101/E2102), cross-modal key not cleartext (E0515), object outside allowlist (E0512)
422 Valid but unresolvable — e.g. MetricFlow not yet implemented (E1001)
503 PDP snapshot missing (E2001, fail-closed), decoder op with no LLM backend (E0521)
502 Trino execution failure (E0510)

See Errors & status codes for the full envelope.