Docs

Turing-complete search

Scry search computes: recursive SQL and fixpoint programs run unbounded iteration with conditional branching over the corpus, under budgets and deadlines you set. Graph walks, negation, whole-population aggregates — the answer, not a page of links.

Search that computes

A search engine retrieves; Scry also computes. The query surface carries iteration with no structural ceiling (WITH RECURSIVE, fixpoint programs), conditional branching (if, CASE, in-walk filters), and state that feeds back and grows step to step — a recursive tape that doubles each iteration reaches 4 MiB in 21 steps and 129 ms on the live engine. That is general computation running next to the data instead of after a download: Turing-complete in the language-theoretic sense, and, like any physical machine, resource-bounded in execution — unlike most, the bounds are yours. You declare the deadline and the budget, the engine enforces both mid-flight, and the meter reports exactly what the computation read. The halting problem is yours to own, so the contract makes halting a first-class control, not a hope.

Recursive SQL, measured

POST /v1/scry/query serves WITH RECURSIVE (one anchor, UNION ALL, one step; the CTE is read in the step’s FROM/JOIN). The Collatz trajectory of 27 — 111 conditional iterations of branch-on-state arithmetic — returns from the live engine in ~50 ms. Each iteration rescans whatever the step joins, so recursive SQL is the right tool for computed sequences and small closures; for graph walks over big corpora, programs (below) read only the frontier.

curl https://api.scry.io/v1/scry/query \
  -H "Authorization: Bearer $SCRY_API_KEY" \
  -H "Content-Type: text/plain" \
  --data "WITH RECURSIVE collatz AS (SELECT toUInt64(27) AS n, 0 AS step UNION ALL SELECT if(n % 2 = 0, intDiv(n, 2), 3 * n + 1), step + 1 FROM collatz WHERE n != 1) SELECT max(step) AS steps FROM collatz LIMIT 1"

Fixpoint programs: graph walks that read only the frontier

A program is a closed JSON AST of named relations evaluated semi-naively to a fixpoint: each round expands only the newly discovered ids, one metered statement per step, under your key. Twelve registered edges span four graph corpora — OpenAlex citations (references, cited_by), Twitter reply and quote graphs (twitter.replies, twitter.quotes, index-native at ~120 ms a step), Hacker News and forum thread trees — each cataloged with its measured per-step cost, and naming an unknown edge returns that catalog. In-walk filters prune before expansion, which changes what the next round reads and bills; post-hoc filtering cannot do that. A live two-hop reply walk over a 374-node thread ran 4 statements in ~1 s.

{"program": {
  "relations": {
    "seed":   {"bodies": [[{"ids": ["1928716926609445169"]}]]},
    "thread": {"bodies": [[{"rel": "seed"}],
                          [{"rel": "thread"}, {"edge": "twitter.replies"}]],
               "emit": "counts"}
  },
  "out": ["thread"], "depth": 2}}

Negation, counts, semantic ranking

not_in is stratified negation applied while the walk runs: excluded nodes are never expanded, never billed. emit: "counts" answers with per-depth histograms and zero row egress — when the population’s shape is the answer, nothing else crosses the wire (every out relation carries its counts regardless). rank orders a derived set by exact cosine distance to an embedding handle you mint — the composition that works at corpus scale, where intersecting a walk with a global ANN top-k comes back near-empty. Rows return with {id, parent, depth} provenance, so every node explains how the walk reached it.

The whole population in one statement

Between walks, plain SQL carries the algebra: UNION ALL / DISTINCT, INTERSECT, and EXCEPT compose scans (each parenthesized branch bounded by its own LIMIT); six join kinds; bounded-state aggregates — topK(N), groupArray(N), exact quantiles, count(DISTINCT ...) — computed over every matching row; QUALIFY, ROLLUP, TOTALS, and WITH FILL shape the result at no extra charge. Answers are computed over the full population — there is no page 2.

SELECT src, n FROM (
  (SELECT 'hackernews' AS src, count() AS n FROM hackernews.items LIMIT 1)
  UNION ALL
  (SELECT 'forums' AS src, count() AS n FROM forums.posts LIMIT 1)
) LIMIT 10

Bounded by design

Every recursive shape runs inside bounds you declare: x-scry-max-seconds is a hard deadline the engine enforces mid-flight, x-scry-budget kills a statement the moment its metered machine burden crosses your ceiling, and programs check both at every statement boundary — depth caps and per-relation row caps besides. The envelope’s truncations list names every bound that fired; an empty list means the walk truly reached its fixpoint. That is the honest form of Turing-complete search: the computation is unrestricted, the resources are yours to grant, and the meter shows what happened either way.

Related docs