READING EDITION / RESEARCH PREVIEW

This is a static guide, not a live service status. Public remote writes and execution remain disabled. No JavaScript is needed to read this page.

Cursor Logic: one relational substrate for SQLite, Lua, Nelua, JavaScript, and Scheme

Status: architectural plan. The SQLite + Lua Cursor Machine and the parity-gated Nelua/Wasm core exist experimentally; the relational engine described here does not yet.

Central rule: logic may explain and propose work. It does not silently create identity, authority, delivery, execution, or an external effect.

Cursor Logic is a bounded relational language for asking questions about Cursors and deriving new inspectable facts from existing evidence.

It is not a second database beside SQLite, a Prolog process hidden behind the cockpit, or a collection of language-specific object models. SQLite owns durable operational state and evaluator scheduling. Lua, S-expressions, JavaScript, Nelua, later Imba, Rust, Scheme, and other bodies all read and write one versioned term representation.

The intended shape is:

canonical signed Cursors records
               |
               v
      SQLite operational body
  facts / rules / deltas / horizons
               |
       +-------+--------+
       |                |
       v                v
 bounded Datalog    Prolog-like queries
 fixpoint engine    variables + explanations
       |                |
       +-------+--------+
               v
      provenance / causal cone
               |
               v
     cockpit views and proposals
               |
       explicit authority gate
               |
               v
   continuation / envelope / effect

Can SQLite control Datalog the way it controls Lua?

Yes, with a stronger fit than Lua in several places.

SQLite is already good at:

The first Cursor Logic engine should therefore be SQLite-owned:

  1. A transaction admits a bounded set of new or retracted facts.
  2. SQLite freezes the input generation and causal horizon.
  3. The scheduler selects rules affected by the delta.
  4. An evaluator receives an immutable task packet containing relation IDs, rule IR, budgets, and input rows.
  5. The evaluator returns relation deltas and derivation edges.
  6. SQLite validates and commits those deltas atomically.
  7. Subscriptions, guide embeds, and cockpit views observe the committed generation.

This is deliberately similar to the current coarse Lua continuation protocol. SQLite selects and leases work; a language body performs bounded pure computation; SQLite commits explicit results. Do not invoke arbitrary Lua or logic callbacks once per SQL row. That would combine poor crossing performance, reentrancy hazards, and a security boundary designed by committee.

What SQLite should not do

SQLite should not become canonical signed protocol truth. CURSO records, grants, manifests, and receipts remain portable immutable material. SQLite stores verified bytes, indexes, projections, scheduler state, caches, leases, and provenance.

SQLite should also not expose unrestricted SQL handles to untrusted programs. Application-defined functions and virtual tables must be narrowly registered, deterministic where appropriate, and unavailable from untrusted schema objects. Production bodies should disable trusted schema execution and use the SQLite authorizer plus explicit statement limits.

Datalog core, Prolog-like surface

Datalog is the correct semantic center because it is set-oriented, bottom-up, finite under a bounded term universe, incrementally maintainable, and naturally aligned with SQLite relations.

A Prolog-like surface is still useful for humans:

ancestor(?cursor, ?ancestor).
resumable_on(?cursor, ?body).
missing_receipt(?cursor).
visible_via(?subject, ?curve, ?carrier).
why_resumed(?cursor, ?body).

The surface may offer variables, conjunction, bounded disjunction, pattern matching, and requests for one or more derivations. It compiles to the same relational intermediate representation as the Datalog and Lua forms.

Cursor Logic v0 does not include:

Later Scheme support may host a bounded miniKanren-like relational layer for synthesis and bidirectional interpreters. That remains a separate search engine over the same Cursor Terms and SQLite snapshots, not an excuse to weaken the Datalog core.

Cursor Terms: one value model, several views

The integrated substrate needs one language-neutral term model.

Cursor Term v0 should include:

null
boolean
integer
fixed decimal
symbol / atom
UTF-8 text
bytes
content reference / hash
logic variable
ordered tuple
list
record with named fields
constructor / form
explicit error

A term has several representations with distinct jobs:

Representation Job
CURSO canonical bytes signatures, hashes, portable immutable identity
SQLite normalized rows indexes, joins, deltas, provenance, horizons
Canonical S-expression readable plans, rules, guides, fixtures
Plain Lua tables restricted Lua and Nelua-facing programs
JavaScript/Imba values browser reference evaluator and UI
Scheme values later language-level implementation

JSON remains diagnostic or live-edge material. It is not the signature authority.

A dataspace tuple, rule AST, continuation instruction, cryptofabric plan, capability caveat, and cockpit specimen should all be expressible as Cursor Terms. They remain different semantic types even when they share a representation.

The same rule in three surfaces

Datalog-like text

ancestor(Child, Parent) :-
  parent(Child, Parent).

ancestor(Child, Ancestor) :-
  parent(Child, Parent),
  ancestor(Parent, Ancestor).

Canonical S-expression

(rule
  (ancestor ?child ?ancestor)
  (and
    (parent ?child ?parent)
    (ancestor ?parent ?ancestor)))

Restricted Lua DSL

local V = logic.variables()

logic.rule {
  head = { "ancestor", V.child, V.ancestor },
  body = {
    { "parent", V.child, V.parent },
    { "ancestor", V.parent, V.ancestor },
  },
}

All three compile to the same immutable rule IR. None is stored as executable host-language source after compilation.

Lua access to relations

Lua should see immutable plain tables and iterators, not raw database handles:

local parents = cursors.relation("parent")
  :at(horizon)
  :where { child = cursor_id }
  :limit(256)
  :rows()

for _, edge in ipairs(parents) do
  inspect(edge.parent)
end

Semantic writes are explicit intents:

cursors.assert {
  tuple = { "available", body_id },
  lease = lease_id,
}

cursors.interest {
  pattern = { "receipt", intent_id, logic.ANY },
}

cursors.derive {
  relation = "candidate_resume",
  tuple = { cursor_id, body_id },
  evidence = evidence_ids,
}

An effect request remains outside logic:

cursors.request_effect {
  intent_id = stable_intent_id,
  capability = grant_id,
  operation = "publish",
  resource = release_id,
}

SQLite then performs the existing capability, lease, idempotency, and receipt checks. A derived candidate_resume fact is not authority to resume.

Proposed SQLite schema

This is a logical schema, not a frozen migration:

CREATE TABLE logic_programs (
  program_id TEXT PRIMARY KEY,
  version INTEGER NOT NULL,
  term_root BLOB NOT NULL,
  source_kind TEXT NOT NULL,
  status TEXT NOT NULL
);

CREATE TABLE logic_rules (
  program_id TEXT NOT NULL,
  rule_id TEXT NOT NULL,
  stratum INTEGER NOT NULL,
  rule_root BLOB NOT NULL,
  PRIMARY KEY (program_id, rule_id)
);

CREATE TABLE logic_horizons (
  horizon_id TEXT PRIMARY KEY,
  curve_id TEXT NOT NULL,
  generation INTEGER NOT NULL,
  observed_at INTEGER,
  evidence_class TEXT NOT NULL
);

CREATE TABLE logic_horizon_heads (
  horizon_id TEXT NOT NULL,
  cursor_id TEXT NOT NULL,
  PRIMARY KEY (horizon_id, cursor_id)
);

CREATE TABLE logic_facts (
  fact_id TEXT PRIMARY KEY,
  relation_id TEXT NOT NULL,
  tuple_root BLOB NOT NULL,
  horizon_id TEXT NOT NULL,
  source_cursor TEXT,
  source_body TEXT,
  lease_id TEXT,
  evidence_class TEXT NOT NULL,
  polarity INTEGER NOT NULL DEFAULT 1
);

CREATE INDEX logic_facts_relation_horizon
  ON logic_facts(relation_id, horizon_id);

CREATE TABLE logic_deltas (
  run_id TEXT NOT NULL,
  relation_id TEXT NOT NULL,
  tuple_root BLOB NOT NULL,
  weight INTEGER NOT NULL,
  PRIMARY KEY (run_id, relation_id, tuple_root)
);

CREATE TABLE logic_derivations (
  derivation_id TEXT PRIMARY KEY,
  output_fact_id TEXT NOT NULL,
  rule_id TEXT NOT NULL,
  horizon_id TEXT NOT NULL
);

CREATE TABLE logic_derivation_inputs (
  derivation_id TEXT NOT NULL,
  input_fact_id TEXT NOT NULL,
  ordinal INTEGER NOT NULL,
  PRIMARY KEY (derivation_id, ordinal)
);

CREATE TABLE logic_relation_policy (
  relation_id TEXT PRIMARY KEY,
  world_assumption TEXT NOT NULL,
  max_arity INTEGER NOT NULL,
  max_rows INTEGER NOT NULL,
  lease_required INTEGER NOT NULL,
  authority_profile TEXT
);

CREATE TABLE logic_runs (
  run_id TEXT PRIMARY KEY,
  program_id TEXT NOT NULL,
  input_horizon TEXT NOT NULL,
  output_horizon TEXT,
  evaluator_profile TEXT NOT NULL,
  step_budget INTEGER NOT NULL,
  row_budget INTEGER NOT NULL,
  status TEXT NOT NULL,
  receipt_id TEXT
);

Term nodes may use normalized tables or compact canonical blobs plus generated/index columns. Benchmark both. Do not normalize every scalar into a universal entity-attribute-value swamp unless the query workload proves it useful.

Evaluator ladder

All engines consume identical Cursor Term, rule IR, task packet, result-delta, provenance, and error vectors.

1. JavaScript reference evaluator

The existing JavaScript evaluator remains the universal browser reference and no-Wasm fallback. It should be small, readable, deterministic, and heavily tested. The user-facing implementation may later be rewritten or optimized in Imba without changing the IR or expected results.

2. Nelua to C to Wasm first accelerator

Nelua is the first native/Wasm competitor because it fits the existing Lua/C/SQLite direction:

Nelua source
   -> C
   -> native object or Emscripten Wasm
   -> parity gate against JavaScript vectors

The accelerator should begin with:

It receives no ambient authority and is admitted only after parity vectors pass.

3. Imba browser implementation

Imba may later replace or generate portions of the JavaScript evaluator and UI for ergonomics and performance. It remains the same semantic implementation family and must continue to run without Wasm.

4. Rust and other competitors

Rust arrives later as an independent implementation, not as the presumed winner. It competes on correctness, artifact size, startup, memory, SQLite crossing, optimizer quality, fuzzability, and maintainability.

5. Scheme language-level Cursors

A later Scheme profile should be capable of describing Cursor Terms, rules, continuations, and capability-scoped host calls in its own language facilities. Suggested direction:

Scheme is valuable as a proof that the Cursors model is genuinely language-neutral and reflective. It should not become another mandatory runtime hidden under every deployment.

Incremental evaluation

The first engine can use conventional semi-naive evaluation:

new input delta
   -> rules touching changed relations
   -> joins using at least one delta input
   -> new relation delta
   -> repeat until fixed point or budget

SQLite can execute simple bounded recursion directly with recursive CTEs. More general stratified Datalog should compile to a relational plan and be orchestrated by the Cursor Machine.

Later work should compare:

The engine choice is replaceable. The committed facts, rule IR, horizons, and provenance are not.

Provenance and causal questions

Every derived fact should retain at least one bounded derivation:

candidate_resume(cursor-83, Mirror)
  because
    checkpoint(cursor-83, state-a91)
    body_available(Mirror)
    supports(Mirror, curso-continuation-v0)
    state_reachable(state-a91, local-store)

The cockpit can then answer:

A provenance semiring is a useful conceptual model:

The implementation should store concrete signed fact IDs and derivation edges rather than only an opaque algebraic expression.

Negation and absence

Distributed absence is treacherous. Cursor Logic must attach a world assumption to each relation:

Policy Meaning
open absence means only “not established here”
closed-at-horizon the relation is complete for this bounded snapshot
leased-live absence may mean no currently live assertion was observed
derived-closed completeness follows from a bounded input and terminating program
authority-closed accepted trust roots define the complete policy set for this decision

A why not query may conclude a negative fact only when the relevant relation is closed for the selected horizon. Otherwise the truthful answer is unknown at this horizon.

Safety and budgets

Freeze explicit limits before running untrusted programs:

Reject unknown syntax and unsupported versions. Cancellation and budget exhaustion are deterministic result states, not engine crashes.

Smallest executable milestone

Cursor Logic M0 should answer three queries over the existing SQLite + Lua Cursor Machine demo:

ancestor(?cursor, ?ancestor).
candidate_resume(?cursor, ?body).
missing_receipt(?intent).

It should provide:

  1. canonical S-expression and Lua DSL forms;
  2. one versioned rule IR;
  3. SQLite fact, delta, horizon, and provenance tables;
  4. JavaScript reference evaluation;
  5. a parity-gated Nelua/Wasm join kernel;
  6. exact why derivations;
  7. bounded why not explanations;
  8. links from every fact and derivation to a real cockpit subject;
  9. no ability to execute an effect.

Acceptance criteria

Research lineage