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/queryAuthorization: Bearer <jwt> # attested delegation chain, see AuthenticationContent-Type: application/jsonThe 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 |
OSL-SQL
Section titled “OSL-SQL”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 lowering is the security boundary
Section titled “The lowering is the security boundary”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.
The five forms
Section titled “The five forms”lower() classifies each query into exactly one form and validates its grammar:
SELECT segment, COUNT(*) AS nFROM osl.entities.CustomerWHERE country IN ('ES','FR','DE')GROUP BY segmentORDER BY n DESCLIMIT 100Lowers to to_trino() → the governed SQL seam. Joins between structured entities
are allowed only as a sealed-key equijoin (see below).
SELECT SUMMARIZE(text) AS summary, scoreFROM RETRIEVE(facet => 'meeting_transcripts', query => 'european equity exposure', top_k => 5)Lowers to the governed Lance store + retrieval. A bare column passes through; an
<op>(col) in the SELECT is a projection operator.
SELECT concepto, amount_eurFROM TRAVERSE(anchor => 'customer', target => 'orders', keys => ('c-anna', 'c-bruno'))Lowers to the governed L2 traversal from the anchor keys (hop-by-hop, bounded cardinality + dedup).
SELECT name, segment, recent_meeting_topicsFROM osl.entities.CustomerWHERE customer_id IN ('9c1e…f04a', 'e4f8…2a91')The entity-first “record” form: structured columns + Lance evidence + derived attributes for the given keys, in one resolution.
SELECT x.segment, r.text, r.scoreFROM osl.entities.Customer xJOIN RETRIEVE(facet => 'meeting_transcripts', query => 'churn risk') r ON x.customer_id = r.customer_id“The relation is a filter”: RETRIEVE runs first, its keys prefilter the structural leg, then an in-cell hash join on the sealed key. See below.
R3 is inexpressible, not blocked
Section titled “R3 is inexpressible, not blocked”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 … ON → 400 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.
The cross-modal join, executed
Section titled “The cross-modal join, executed”X JOIN RETRIEVE(...) ON X.k = R.k is not a relational join of two physical
tables. It runs as:
- RETRIEVE (governed) → passages, each carrying its denormalized key.
- Cleartext gate → the key values, or refuse (
403 E0515). - Structured prefilter:
… WHERE k IN (<keys>)→to_trino→ governed SQL (theIN-list is built with sqlglot literal conversion — injection-safe). - In-cell hash join on the sealed key, projecting each leg’s columns.
Projection operators
Section titled “Projection operators”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 |
Raw SQL & MetricFlow bodies
Section titled “Raw SQL & MetricFlow bodies”{ "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.
Status codes
Section titled “Status codes”| 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.