Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RFD 0014 — Runtime Serving Surface

State: discussion

Question

Should Argon expose a first-party serving surface for executing a loaded .oxbin against tenant/fork scoped runtime state?

Context

The §19 runtime contract defines the in-process Engine / Module / Store semantics, but Ode and Tide workflows need an executable boundary: load the workspace .oxbin, keep Store state warm, dispatch generated SDK descriptors, and expose forks, snapshots, derive output, and debug traces.

The old kernel and devbox-kernel path mixed this runtime work with live source loading, generated SDK transport concerns, tenant hardcoding, workflow state, and Tide-specific globals. New Argon has a compiled .oxbin, so the runtime serving layer can be Argon-native and narrower.

Decision

Add ox runtime serve backed by an oxc-serve crate. The command serves a versioned /v1 API around a loaded .oxbin:

  • GET /v1/health
  • GET /v1/module
  • GET /v1/schema
  • POST /v1/dispatch/query
  • POST /v1/dispatch/mutation
  • POST /v1/dispatch/compute
  • fork-scoped dispatch aliases under /v1/forks/{fork}/dispatch/*
  • GET /v1/forks, POST /v1/forks, GET /v1/forks/{fork}
  • DELETE /v1/forks/{fork}, POST /v1/forks/{fork}/promote
  • GET /v1/forks/{fork}/diff/{other}
  • POST /v1/forks/{fork}/derive
  • POST /v1/forks/{fork}/derive/trace
  • GET /v1/snapshot
  • GET /v1/derived/individuals/{id}
  • GET /v1/derived/individuals/{id}/explain
  • GET /v1/derived/facts/{fact_id}/explain

The serving layer supports mem and pg storage. Both backends preserve the append-only ABox event-log model; derived state and snapshots are projections over .oxbin plus visible events. Postgres additionally owns durable fork records, generation counters, scoped/as-of scans, and projection-cache invalidation.

The generated SDK remains dependency-free and transport-agnostic. It emits types, validators, metadata, descriptors, and wire parse/serialize helpers. It does not import or generate a runtime client.

Rationale

This keeps the core runtime responsibilities inside Argon, where the .oxbin, storage, rule evaluation, and fork semantics live. It also keeps environment concerns outside Argon:

  • tenant/principal selection belongs to the caller or proxy,
  • Tide workflow state and run journals belong to Tide,
  • Ode UI state and visualization layout belong to Ode,
  • SDK transport adapters belong to the host application.

The /v1 API is descriptor-based so generated SDKs can call it through a small host transport without coupling generated code to HTTP, Tide, fetch, or a specific deployment.

Alternatives

One alternative was a separate ontology-tooling service that wrapped Argon. That would have duplicated runtime semantics and left forks, storage, and derive behavior outside the implementation that owns them.

Another alternative was generating a full SDK runtime client. That is more ergonomic for simple workflows, but it couples generated output to transport and deployment concerns. The chosen design leaves that as a hand-written host adapter.

Consequences

ox runtime serve is a long-running process and therefore introduces async HTTP dependencies in the compiler workspace. The serving layer must keep a strict boundary: no generic entity writes, no ad-hoc raw query/mutation execution, no Tide or Ode state, no tenant hardcoding, and no SDK transport generation.

Hot reload is allowed only after compatibility checks. Additive schema changes may load; declaration removal or field/relation type changes are rejected when live ABox data exists.

unsafe_logic and forget remain capability-gated. Where the underlying language/runtime substrate is not enabled, the serving API refuses the request instead of pretending the capability exists.

Open Questions

  • Whether the full PosBool DNF provenance witness tree should be exposed by the current explain endpoints or by a later provenance-specific API.
  • Whether projection caches should become durable response caches for selected read endpoints or stay as backend-maintained invalidation state until DBSP arrangements land.
  • The final production IAM mapping for fork, forget, and future unsafe_logic execution budgets.