Bindings
How a typed Object instance is projected from a real source — managed table, knowledge-base page, process-flow task, or manual entry.
A binding is the rule that tells the ontology how to materialise a typed Object instance from a real source. Bindings are the bridge between L2 (schema) and the underlying data layer.
Projection on read
A binding does not copy rows into a new table:
regulated_entities (managed table)
┌─────────────────────────────────────────┐
│ EntityID │ LegalName │ RiskClass │
├─────────────────────────────────────────┤
│ e1...01 │ Acme Holdings│ medium │
│ e1...02 │ Neptune Cap. │ high │
└─────────────────────────────────────────┘
│
│ binding (silver_table)
│ columnMap:
│ id ← EntityID
│ legalName ← LegalName
│ riskClass… ← RiskClass
▼
RegulatedEntity (typed Object — projected on demand)
{ id: "e1...01", legalName: "Acme…", riskClassification: "medium" }When a caller reads a RegulatedEntity, the binding's projection runs against the source table and returns typed instances. Re-uploading the source CSV is reflected on the next read.
Binding kinds
| Kind | Source | Status |
|---|---|---|
silver_table | A managed table — your CSV / JSON uploads | shipped |
memex_page | A knowledge-base page | preview |
process_flow_task | A process-flow task instance | shipped |
manual | SDK / workbench writes — no upstream | shipped |
process_flow_action, extraction, stream | Entity-link actions, extraction pipelines, pub/sub topics | preview |
memex_page is preview because the kernel has no governed reader for it yet: the binding can be
declared and shipped in an extension, but every instance read answers VALIDATION_FAILED with
details.reasonCode: "BINDING_KIND_NOT_GOVERNED", and readiness reports binding kind has no
governed reader. That is a capability gap, not an outage — see Ready vs. not ready.
A pack can ship bindings of any kind. Bindings can also be authored directly from the workbench's Bindings tab without touching the SDK.
Column map
For a silver_table binding, the column map declares which source column populates each Object property:
Object property Source column
───────────────── ─────────────────
id ← EntityID
legalName ← LegalName
riskClass… ← RiskClassThe column map is editable from the Bindings tab. The picker accepts both the physical column name (fius_fiu_id) and the display name (fius.fiu_id) — see Column names for how the two are kept in sync.
Ready vs. not ready
A binding shows one of four status badges in the workbench:
| Badge | Meaning | What to do |
|---|---|---|
| Ready | Source resolved, every column-map value matches a column on the bound table. | Use it. |
| Not ready — table not registered | The pack ships a logical source name; no matching table exists in this workspace yet. | Click Pick table, choose the right managed table — the picker highlights an exact name match. |
| Not ready — missing columns | The bound table doesn't have every column the column map references. | Edit the column map — pick the right column for each property. |
| Not ready — no governed reader | The binding kind has no governed reader in this release (memex_page today). Reason reads binding kind has no governed reader. | Nothing to configure. The graph shows the binding as skipped with that reason, never as an error; rebind to a silver_table if you need rows now. |
The Ready check is a live probe, not a cache. It re-runs on every list, so a binding that flips from Ready to Not ready means something changed upstream (table archived, columns renamed).
Row-level marking visibility
For managed-table bindings, Scrydon filters marked source rows before it projects Objects. A row is
visible only when every marking attached to that row is in the caller's organization-authorized
marking set. Classification clearance and row markings are separate controls: a higher clearance
does not automatically grant a marking such as finance or operations.
An organization with no row-marking policy starts in the safe default state: unmarked rows are readable, while every marked row stays hidden. Marked rows become readable only after an authorized holder configures the corresponding organization policy. There is no dedicated row-marking UI in this release; use your organization's governed policy-administration process.
If Scrydon cannot resolve the caller's marking authority, the binding read fails. It never retries without markings and never treats an authority failure as an empty result.
DLP labels and masking
Every projected instance carries the DLP labels of its source:
- A property bound to a column classified
confidentialinherits that classification. - A masking strategy applied to the source column also applies to the projected property.
- A caller without clearance for the column sees the masked value in the projected Object, not the raw value.
This is what makes the ontology safely exposable to agents — there is no way for an agent to read a typed Object and bypass the column-level governance on its source.
Provenance
Every projected instance carries provenance:
| Field | Value |
|---|---|
bindingId | Which binding produced this instance |
bindingVersion | Which version of that binding |
sourceRef | A reference to the source row (e.g. EntityID="e1...01") |
materializedAt | The timestamp of the projection |
dlpLabels | The DLP labels that flowed through |
Workflow tools and the analyst surface this provenance alongside the data, so the user can always answer "where did this come from?".
Provenance is read-only at the binding layer — it's emitted by the projection, never authored by the user. Bindings authored manually in the workbench still carry provenance referring to the workbench itself as the source.
Durable materialization
Projection on read is what every query uses. Separately, a binding whose source is a process-flow environment can be enrolled so the kernel keeps asserting its rows in the background: a durable pass runs on a schedule, reads each instance through the same governed path /instances uses, and remembers where it got to (a watermark). Enrolling is a write on the binding's object type, either explicitly (bindings.enroll in the SDK) or as a side effect of a successful bindings.materialize run.
When a pass fails as a whole five times in a row, the kernel quarantines the binding with a stop code instead of retrying silently. A quarantined binding is visible rather than merely stale: bindings.enroll on it answers status: "quarantined" instead of clearing the stop, and nothing automatic will lift it — a retry cannot erase a stop nobody has looked at.
An operator lifts a quarantine with bindings.resume (POST /api/ontology/kernel/bindings/:name/resume). The call needs the same write permission as enroll and a signed-in person: a service account, a background job, or a caller acting on someone's behalf receives 403 FORBIDDEN with a message saying a human operator is required. The pass then continues from the watermark it stopped at; nothing is re-read from the beginning. The response names the stop code that was cleared, and the lift is recorded in the Audit Log as ontology.materialization-cursor.resumed against the operator.
| Result | Meaning |
|---|---|
200 { resumed: true, previousStopCode } | The binding is active again; previousStopCode is what you acknowledged. |
403 FORBIDDEN | The caller is not a person, or lacks write on the object type. |
404 NOT_FOUND | The binding does not exist, or was never enrolled. |
409 CONFLICT_DETECTED | The binding is not quarantined; details.status says what it is instead. |
Related
- Object types — what bindings produce instances of.
- Analytics → Column names — how source-column lookup works.
- Branches & proposals — bindings are versioned with the schema.
Correlated track streams (preview)
A stream binding can read an already provisioned, ontology-owned correlated-track
stream. Use this shape for a source-backed Track object:
{
"kind": "stream",
"source": {
"backend": "correlated_tracks",
"streamId": "north-sea",
"policyRevisionId": "mik-policy-1"
},
"identitySpec": { "identityColumns": ["trackId"] },
"columnMap": {
"latitude": "latitude",
"longitude": "longitude",
"lastObservedAt": "observedAt",
"freshness": "freshness"
}
}The stream must exist in the same ontology, workspace and environment, with the
exact policy revision. Declaring a binding does not create a feed or start ingestion.
An installed organization-scoped poll source can declare trackProjection for
this pinned stream. The provider adapter and credential remain separate from
the binding; the MIK reference has no operational AIS/radar feed.
Available source columns are trackId, vesselId, observationId, sourceId,
sourceEntityId, observedAt, receivedAt, latitude, longitude, heading,
speed, quality, freshness, and contributingObservationIds. A manifest that
maps a property to any other column, or filters on one, is refused when it is
validated, before install. Times use UTC ISO strings. Missing movement fields
are null. Nonempty transforms and link bindings are not supported by this reader.
Rows inherit the classification of contributing evidence and are filtered before projection. Notebook authors cannot map private correlation-policy fields into an app. A changing source may require a fresh read rather than continuing an old page. For installed poll sources, the reader combines API Table's configuration-bound completion evidence for every source named in the pinned policy. Missing, changed, paused, or expired evidence remains unknown or degraded. A fresh position alone never proves that all expected feeds are healthy or complete.