The point of the whole thing
The mapping is not the deliverable. This is: one binding that every
application snaps onto, so the next dashboard, assistant or control engine
is a consumer of a model that already exists rather than another modelling
project. Written once, here, and read by all of them.
The binding
generated from the accepted mappings105 of 936 fields bound
binding.yaml
binding: nordbank-ag
revision: 3
model: ict-assurance@2026-07-15
generated: 2026-09-08T14:20:00+02:00
sources:
warehouse:
kind: aws-rds-postgresql
endpoint: nb-grc-ro.eu-central-1.rds.amazonaws.com
auth: secretsmanager://dl-reader
access: select-only
documents:
kind: extracted
retained: page, sheet and cell of every value
entities:
ict_asset_inventory:
from: warehouse:itsm_prod.SYS_INVENTAR
grain: [SYS_ID]
fields:
asset_id: SYS_ID
asset_name: BEZEICHNUNG
owner:
transform: split_part(VERANTW, ' / ', 2)
runs: pushdown
coverage: 0.870 # 1,623 rows produce no owner
location:
transform: parse(STANDORT, 'DC-{site}-{rack}' | 'AWS {region}')
runs: pushdown
coverage: 0.970
asset_type: TYP
status: STATUS
owner: VERANTW # team and person in one column
location: STANDORT
network_segment: NETZ_ZONE
last_review_date: LETZTE_PRUEF
vendor_support_end_date: SUPPORT_ENDE
criticality_classification:
column: KRITIKALITAET
map: {KRITISCH: CRITICAL, HOCH: HIGH,
MITTEL: MEDIUM, NIEDRIG: LOW}
unbound: # no source at this tenant
- dependencies # required
- interfaces # required
- redundancy_configuration # required
- supporting_function_ids
critical_function_register:
from: documents:Kritische_Geschaeftsprozesse_2026.xlsx
at: "Prozesse 2026!A2:I215"
grain: [Prozess-ID]
fields:
function_id: Prozess-ID
function_name: Bezeichnung
function_owner: Verantwortlich
criticality_tier: Kritikalitätsstufe # 3 tiers, model has 4
recovery_time_objective:
transform: to_minutes(Wiederanlaufzeit)
runs: platform # the value came from a spreadsheet
coverage: 0.930 # 15 rows refused, incl. 4 "sofort"
supporting_asset_ids:
transform: resolve(split(Betroffene Systeme, ','),
against: SYS_INVENTAR.BEZEICHNUNG)
runs: platform
coverage: 0.610 # 190 of 487 names do not resolve
unbound:
- business_impact_rating # required
derived: # produced, not recorded. Not a binding.
ict_asset_inventory.dependencies:
from: co-occurrence in STOER_MELD within 15 minutes, 3+ times
coverage: 0.147 # 1,840 of 12,481 assets have evidence
marked: derived # every answer using it says so
relationships:
- from: ict_incident_register.affected_asset
to: ict_asset_inventory
via: STOER_MELD.BETROFF_SYS = SYS_INVENTAR.SYS_ID
resolves: 0.992
approved_by: m.bauer@nordbank.de
approved_at: 2026-09-08T14:11:00+02:00
- from: critical_function_register.supporting_asset_ids
to: ict_asset_inventory
via: "text match on SYS_INVENTAR.BEZEICHNUNG"
resolves: 0.610
coverage_warning: 83 of 214 rows do not resolve
approved_by: pending
writes: # the layer describes it, the application does it
ict_incident_register.status:
to: warehouse:itsm_prod.STOER_MELD.STATUS
map: {OPEN: OFFEN, INVESTIGATING: IN_BEARBEITUNG,
CLOSED: GESCHLOSSEN}
requires: [root_cause_description] # refuse to close without one
grant: dl_writer # a second role, never dl_reader
audit: [actor, reason, before, after]
ict_incident_register.closure_date:
to: warehouse:itsm_prod.STOER_MELD.ABGESCHL_AM
set: when status becomes CLOSED
grant: dl_writer
audit: [actor, reason, before, after]
ict_asset_inventory.*:
to: none
reason: >
Read-only source. The CMDB is authoritative and owned by IT
Infrastructure; an application that wants to change an asset
raises a change request instead.
remediation_action.*:
to: none
reason: >
Nothing at this tenant stores remediation actions. An
application that wants to create one has nowhere to put it,
and is told so rather than writing into a free text field.
What is in it, and what is deliberately not
Is in it
Which canonical field reads which physical column, the value maps, the grain, the join paths, and who approved each one and when.
Is in it
unbound. Every required field with no source is named, so a consumer knows what it cannot ask for before it asks.
Is in it
coverage_warning where a join does not fully resolve, so an answer built on it can say so.
Is not in it
Any customer data. The binding is a description of where things are, not a copy of them.
Is in it
transform and its coverage, so a consumer knows a field was reshaped and how many rows the reshaping refused. derived is kept separate from bound fields on purpose.
Is in it
writes. Which canonical fields can be written back, onto which column, through which value map, under which precondition, with which grant. And which ones cannot, with the reason.
Is not in it
Credentials. The source carries a reference to a secret, never the secret.
Is not in it
The writing itself. The layer never holds a write grant; it says what a write would have to look like.
Why the layer describes a write and does not perform one
Acting belongs to the application, which knows why it is acting. What
does not belong there is the mapping: if every application works out on
its own that CLOSED means
GESCHLOSSEN and that a root cause is required
first, they will eventually disagree, and that is the same drift the
read side exists to prevent. Defined here once, executed there.
Why this file is the product
Bind once and every application below reads the same thing. Without it
each one re-derives what KRITIKALITAET means,
and they disagree, which is how two dashboards end up giving a board
two different numbers for the same KPI.
How the data reaches them
Federated
Queries are resolved against the source when they are asked. Nothing
is copied and nothing goes stale.
Materialised
Bound entities are imported on a schedule into a store the platform
owns. Fast, and as old as the last run.
Both
The entities that are read constantly are materialised; the rest are
federated. One binding drives both.
Freshness
Load on the source
Data leaves the client
Suits
What snaps onto it
Same binding, six consumers
The Ask assistant
Answers questions in entities, not tables. Reads
unbound so it can decline rather than guess, and
coverage_warning so it can state its own limits.
POST /v1/semantic/query Authorization: tenant token
Live
Control assurance engine
Every deterministic control declares the entities
it needs. Binding one source makes every control referencing it executable,
and the unbound list is exactly the set of controls that cannot run yet.
binding.yaml GET /v1/entities/{name}
Live
Applications that write back
An assistant closing a finding, or a workflow
setting a status, reads writes and acts through its
own runtime with its own dl_writer grant. Two
applications closing the same finding therefore close it the same way,
which is the whole reason the read side is defined once.
writes: from binding.yaml grant assumed by the application, not the layer
Contract only
BI and dashboards
Generated views over the source, one per bound
entity, with the value maps already applied. Power BI and Tableau point at
these rather than at SYS_INVENTAR.
CREATE VIEW dl.ict_asset_inventory 18 views, refreshed with the binding
Live
dbt project
Sources and staging models emitted from the same
file, so the transformation layer inherits the definitions instead of
restating them in SQL.
models/staging/dl_*.sql sources.yml
On request
Any application, through the SDK
The agentic runtime already used for the other AI
use cases resolves entities, fields and relationships through the binding, so
a new application is a new prompt rather than a new data project.
Draft, not published
Publishing makes this binding the one all five consumers read. Revision 2
stays available, so a consumer that breaks can be pinned back to it.
Revisions of this binding
Revision
Changed
Fields bound
By
3
Critical functions bound from the spreadsheet; two document relationships approved