Scrydon
SDKsOntology Kernel SDK

Ontology Kernel SDK

Typed, request-scoped access to Scrydon's workspace-environment ontology kernel.

Ontology Kernel SDK

Use createKernelClient from @scrydon/ontology-sdk/client in server-side callers. The client is request scoped: provide the admitted caller transport plus the exact organization, workspace, and workspace-environment headers for the current request. Never cache user headers, expose service credentials to browser code, or substitute an organization-only scope.

import { createKernelClient } from "@scrydon/ontology-sdk/client";

const ontology = createKernelClient({
  baseUrl: `${ontologyServiceUrl}/api/ontology`,
  headers: () => ({
    ...admittedCallerHeaders(),
    "x-organization-id": organizationId,
    "x-workspace-id": workspaceId,
    "x-workspace-environment-id": workspaceEnvironmentId,
  }),
});

const result = await ontology.objects.query({
  ontology: "crm",
  objectTypes: ["Customer"],
  limit: 50,
});

Published reads resolve the current revision unless you pass an immutable revisionId; branch reads use a branch apiName. Writes use actions.submit with a final action type such as schema.declare, schema.publish, branch.submit, or proposal.approve. Errors are OntologyApiError values with a generated stable code, httpStatus, and bounded details. A non-2xx answer that carries no kernel error envelope — a proxy's HTML 502, an empty 401 from the mesh — is also an OntologyApiError: its code is MALFORMED_ERROR_RESPONSE, httpStatus is the status that was returned, and details holds the response content type plus a bounded body preview.

Client namespaces

NamespaceMethods
actionssubmit
actionTypesget, list
aliaseslist, resolve
bindingscount, enroll, get, instances, list, materialize, materializeByObjectType, readiness, readinessOf, resolveByObjectType, resume, table
branchesarchive, create, declare, get, list, manifest, publish, rebase, submit
decisionsget
extensionsapply, autoInstall, installed
geolayers
graphexpand, instanceGraph, layout, layoutRun, layoutStatus, neighbors, schemaSlice, search, trail, viewport
linkslist
linkTypesget, list
mappingssuggest
objectsget, list, neighbors, query
ontologieslist
proposalsget, list
rdfexport, import
revisionsresolve
searchquery
typesget, list
watchesdispatches.claim, dispatches.settle, evaluate, list

Error contract

Typed errorHTTP
APPROVAL_REQUIRED409
BRANCH_BASE_STALE409
CONFLICT_DETECTED409
EXPORT_DENIED403
FORBIDDEN403
HISTORICAL_SOURCE_UNAVAILABLE422
HUMAN_REVIEW_REQUIRED422
IDEMPOTENCY_CONFLICT409
IDEMPOTENT_REPLAY200
MIGRATION_REQUIRED422
NOT_FOUND404
PROJECTION_LAGGING503
RECOMMENDATION_STALE409
REVISION_MISMATCH409
RULES_REJECTED422
SHAPE_VIOLATION422
SOURCE_READ_UNAVAILABLE503
STALE_READ_SET409
UNAUTHORIZED401
VALIDATION_FAILED400

Capability gaps, outages, and the kernel's "not yet"

Three families of rejection look alike on the wire and must be handled differently. The SDK exports one helper per family so callers never re-derive the rule from raw details.

A binding that cannot produce rows

bindingReadCapabilityReason(error) answers why a binding read cannot return a page, or null when the error is a genuine fault to surface as one. The verdict is keyed on details.reasonCode — never on whether a human-readable details.reason happens to be present:

Wire shapeMeaningHelper returns
VALIDATION_FAILED (400) with details.reasonCode: "BINDING_KIND_NOT_GOVERNED"The binding kind has no governed reader (memex_page today). Permanent for this release; retrying never helps.the server message — binding kind has no governed reader
SOURCE_READ_UNAVAILABLE (503) with details.reasonCode: "BINDING_SOURCE_NOT_PROVISIONED" (plus details.reason when the kernel has a sentence)The source is not provisioned yet — e.g. Table "aircraft_position" not registered — upload a CSV with that name in /tables. Only an operator can clear it.details.reason, or the server message when the kernel sent no sentence
Any other shape, including a SOURCE_READ_UNAVAILABLE (503) carrying only free-text details.reasonA real outage. Retry it.null

BINDING_READ_REASON_CODES is the exported constant object (KIND_NOT_GOVERNED, SOURCE_NOT_PROVISIONED); branch on it rather than on the literal, and use isBindingReadReasonCode(value) when narrowing a code that arrived as unknown. A graph or list surface should degrade around the first two rows (show the binding as skipped with the reason) and render only the third as an error.

A 503 with a details.reason but no details.reasonCode is an outage, not a capability gap. Earlier releases classified on the presence of the sentence, which meant any 503 that happened to carry diagnostic text stopped being retried. The code is the contract; the sentence is a courtesy for whoever reads the screen.

Waiting for a projection

The kernel is event-sourced: a write is acknowledged when its ledger row commits, and reads see it once the projector has caught up. A read issued immediately after a write can therefore answer NOT_FOUND or PROJECTION_LAGGING for a resource that exists. isTransientProjectionError(error) is true for exactly those two codes — the set is TRANSIENT_PROJECTION_ERROR_CODES — and is the only predicate a bounded waiter should retry on. REVISION_MISMATCH is deliberately not in the set: it means the caller's premise changed, and re-reading with the same premise is a spin, not a recovery.

import { isTransientProjectionError } from "@scrydon/ontology-sdk/client";

const deadline = Date.now() + 5_000;
for (;;) {
  try {
    return await ontology.branches.get({ ontology, branch });
  } catch (error) {
    if (!isTransientProjectionError(error) || Date.now() >= deadline) throw error;
    await new Promise((resolve) => setTimeout(resolve, 100));
  }
}

In graph.schema, a link with a declared join has no stored edge rows: its edges are joined from the visible instances at read time. Such a link reports edgeCount: null with reason: GRAPH_SCHEMA_LINK_REASONS.COMPUTED_FROM_DECLARED_JOIN, never edgeCount: 0 — a zero would read as "bound, but no column values match", which is a different remedy. The full reason vocabulary is GRAPH_SCHEMA_LINK_REASONS; a failed governed read keeps SOURCE_COUNT_UNAVAILABLE.

Branch on the published constants rather than the raw strings — GRAPH_LINK_KIND for a link's kind (REFERENCE / PROXIMITY) and GRAPH_DIAGNOSTIC_STATUS for a binding diagnostic's status (READY / NOT_READY / SKIPPED). Both are exported from @scrydon/ontology-sdk/client alongside their derived GraphLinkKind / GraphDiagnosticStatus types, and the graph-slice schema's own z.enum(...) reads from them, so a retyped literal fails the type-check instead of silently un-matching one branch of your UI.

GRAPH_LINK_KIND describes a link on the wire. Use LINK_TYPE_KINDS from @scrydon/sdk-authoring/ontologies when you are discriminating a link type you authored in a manifest — the two vocabularies happen to spell the same words today and are not interchangeable.

On this page

On this page