/ docs · functional specification

The full platform
functional reference.

Every functional area of AI API Mapper documented at implementation depth so architects, security reviewers, and platform engineers can evaluate the product with real technical context.

ESRA · Data sources

ESRA — Data Sources

A source is one governed searchable database. Modelling a source is the work of describing what it contains in business terms, which of its tables or views are searchable, and which of their columns may be searched, returned, filtered, or never seen at all.

Requires ESRA_DATA_MANAGE to create or edit; ESRA_DATA_READ to view.


Working in the Portal

ESRA & Data → Data sources. The grid carries identity, provider, selection mode, one status cell (lifecycle, health, enablement and entity count together) and a row of icon actions: view, edit, test, enable or disable, publish or unpublish, delete. Each action names itself in its tooltip, which is also its accessible name.

New source opens a seven-step wizard: identity and provider, connection, a connection test, relation discovery, column governance, what a model should be told, and a summary. It reads the database's own catalog rather than asking the operator to retype it — relations, columns, their types, primary keys and existing full-text indexes — and proposes a mapping from what it finds: which column is the record key, which is the title, which is a business timestamp, and which columns are worth searching.

What it deliberately does not propose is which columns are filterable or restricted. Both are governance decisions, and a security control set by heuristic is worse than one left unset. Those stay the operator's to make, on the column-governance step, with the consequence stated.

The last substantive step, before the summary, is selection metadata — the business description, usage and exclusion guidance, domain tags, content languages and example prompts that are the only source data a selection prompt ever sees. Ceilings and result governance are not part of the wizard at all: they are configured afterward, on the Ceilings and Result governance tabs of the same tabbed editor used to edit a source once it exists.

Everything the wizard collects is applied in one composite call at the end, so a wizard abandoned midway leaves nothing behind. There is no half-built source to find and delete.

A source that is serving cannot be edited. A published, enabled source is what the runtime is serving, so editing it in place would change what live audiences search with nothing recording that it happened. Publish is replaced by Unpublish on a published row, and Edit is disabled with the reason on it. Test connection stays available throughout — probing is operational, not configuration.

There are two ways to stop a source serving, and the tooltips name both.

Unpublish returns the source to draft, at which point it edits normally and can be published again. It is refused while a published exposure still reaches the source, and says which one — which is precisely the situation you are in when a source in real use turns out to need a correction.

Disable is the way through that. A disabled source is dropped from every scope resolution, so it reaches no caller and its configuration is editable again for the same reason a draft's is. The editor says the corrections are not live, because they are not: they take effect when you enable the source again, which is the deliberate act that puts it back in front of callers. The exposures that bind it are never touched, so nothing has to be republished.

View and edit are tabbed modals over the same six faces of a source: overview, selection metadata, connection, ceilings, result governance, and the searchable entities with their columns. Every field carries inline help stating what it means and what values are reasonable.

Saving is one action. Every change across every tab — including adding a column or removing an entity — is staged and committed together when you press Save, and Cancel discards all of it. Editors previously applied collection edits immediately and carried a disclaimer saying so; the disclaimer was accurate, which was the problem. Underneath, the save still applies only the sections that changed, each against its own endpoint, so an unrelated save never rewrites a connection whose secret bindings another administrator may have changed. If one section fails, the message names it.

Field weights are chosen as four named bands — highest, high, normal, lowest — rather than as a decimal. A free decimal invites tuning that nobody can evaluate. An operator who has already stored a non-standard weight keeps it, and sees it verbatim rather than having it silently rounded into a band.

Credentials are bound by choosing an existing secret, never by typing an identifier. The picker lists name and identity only; no secret value is returned by any Portal endpoint. Creating or rotating the secret itself remains SECURITY_MANAGE work on the secret store screen.

The internal document store

A source of provider type InternalPostgreSql is the tenant's own document repository. Its connection and its searchable structure are fixed by the platform and configured on the Document store screen, so both tabs are hidden here and the connection test is absent rather than disabled — there is nothing on this screen to test. Selection metadata, ceilings and result governance remain fully editable, because those are genuine per-source decisions whatever the storage is. A stale form cannot write the connection either: the save path skips it for internal sources.

Such a source is created with its canonical mapping already in place — one entity over the store's documents, keyed by a stable document identifier, with title and content searchable and document type returnable. The publication rule is unchanged for it: a source still cannot be published with nothing to search. The mapping simply is not the operator's to author, because the schema behind it is not theirs to change. A store source created before this existed acquires its mapping on the next publish rather than needing a migration.

Authority

Every control is gated individually. A holder with ESRA_DATA_READ can open any source and read all six tabs; every write action is disabled with the missing role named in its tooltip, rather than the screen quietly omitting the functionality.


Identity and Lifecycle

Field Meaning
Name tenant-unique
Provider type PostgreSql, SqlServer, MySql, Elasticsearch, MongoDb, Oracle, or InternalPostgreSql
Selection mode Mandatory, Optional, PolicyForced, Disabled
Lifecycle state Draft, Published, Retired
Enabled an operational kill switch, independent of lifecycle
Priority breaks ties in ranking and in truncation of the selected set
Environment, tags free-form operational grouping

Disabled at source level is absolute — no capability and no exposure can override it upward.


Connection

The connection record never contains credentials. Its shape depends on the provider type — there are three:

  • ADO/SQL — PostgreSQL, SQL Server, MySQL/MariaDB and Oracle. A connection-string template plus references to secrets resolved per use through the platform secret store, described below.
  • HTTP — Elasticsearch/OpenSearch, reached over REST rather than a database driver. A credential-free base URL (embedded userinfo such as https://user:pass@host is rejected) plus an authentication mode — API key or HTTP basic, each resolved from a secret — a connect timeout, a request timeout, TLS requirement and a per-host connection-pool ceiling.
  • MongoDB — reached over the native binary wire protocol rather than ADO or REST. A credential-free mongodb:// or mongodb+srv:// connection-string template, a username/password secret pair, an authentication database (admin by default, since MongoDB credentials are commonly stored separately from the target database), the target database name, an optional replica set name, a configurable read preference (default SecondaryPreferred, to keep search load off the primary), connect and server-selection timeouts, a per-query execution-time budget and TLS requirement.

The ADO/SQL shape:

Field Default Notes
Connection string template credential-free; placeholders for username and password
Username / password secret secret-store references; creating or rotating them requires SECURITY_MANAGE
Connect timeout 15 s
Command timeout 30 s also applied server-side as statement_timeout on PostgreSQL
Require read-only on
Require TLS on
Max pool size 10

Every host in the composed connection string, base URL or Mongo connection string is validated against the platform's outbound host allowlist before a connection is opened — the same control that governs audit delivery targets. A source pointing at a host that is not allowlisted for this tenant cannot connect, and the failure names the host, not the credential.

The database principal must be read-only. ESRA never issues DDL or DML against an external source. On SQL Server this is load-bearing rather than advisory — the engine has no session-level read-only transaction — so the connection test attempts a trivial write inside a rolled-back transaction and fails if it succeeds. This "Require read-only" field and the write-probe test apply to the ADO/SQL providers; MongoDB has no equivalent session-level concept either, so its read-only guarantee is instead a fixed, non-configurable ReadConcern.Majority applied to every read.

Test connection

Bounded, audited, and never echoes the connection string. It reports: reachability, whether the configured relation exists and is visible to the principal, whether a suitable full-text index is present, whether the text-search configuration exists, and whether the principal is genuinely read-only.


Transaction isolation — SQL Server only

ESRA's reads run inside a transaction, and which isolation level that transaction uses is configured per source rather than detected.

Mode Behaviour Requires
Read committed (default) brief shared locks; can block a writer on a heavily contended table nothing
Snapshot no shared locks — an ESRA read is invisible to the rest of the workload ALTER DATABASE … SET ALLOW_SNAPSHOT_ISOLATION ON, and row versions in tempdb

ALLOW_SNAPSHOT_ISOLATION is off by default on SQL Server, which is why read committed is the default here: it is the mode that works against an untouched database without its owner enabling anything.

Choosing snapshot against a database that forbids it fails, with the classification SNAPSHOT_ISOLATION_UNAVAILABLE and a connection test naming both remedies. It does not quietly fall back. A silent downgrade would begin taking shared locks on someone's production database with nobody having agreed to it — and the whole point of the setting is that this is the database owner's decision, not the platform's. The connection test does detect the database's setting, but only to advise: a source on read committed against a database that permits snapshot is told so, once, where the operator is already looking.

ReadUncommitted is deliberately absent and should stay absent. It would let ESRA hand an AI client a row that is subsequently rolled back, and there is no configuration under which returning data that never existed is acceptable.

The setting is hidden for every other provider type — PostgreSQL (including the internal document store), MySQL/MariaDB, Elasticsearch, MongoDB and Oracle. PostgreSQL's MVCC already guarantees readers never block writers, so there is nothing to choose there; the non-relational and non-ADO providers have no comparable session-level isolation concept to configure either.


Selection Metadata

This is the only source data that ever reaches a source-selection prompt. It is business language, by design: the model choosing sources never sees a schema, a relation name, a column, a host or a credential.

Field Purpose
Business description what this source contains, in the language of the business
Usage guidance when to prefer it
Exclusion guidance when not to use it — often more valuable than usage guidance
Domain tags contracts, hr, incidents, …
Languages content languages, BCP-47
Example prompts questions this source answers well
Freshness expectation "updated nightly", "real time"
Selection hints free-text steer for the selector

Search Mappings

A source exposes one or more search mappings — each one a governed searchable entity backed by a table or, preferably, a view.

Field Notes
Name, description business-facing; the description is what the planner sees
Relation name table or view; resolved by id from configuration, never from model output
Record key field stable identity of a record
Title field display label
Business timestamp field powers freshness weighting and the result timestamp
Record URL template optional deep link, returned only when the source permits record references
Text-search configuration e.g. english, italian — the analyzer used at query time
Ranking profile selects the normalization flags applied to the native rank
Max result count server-side row cap
Snippet rules max fragments, max fragment length, whether to use native snippets

Prefer a view over a table. Complex joins, computed columns and pre-applied business filters belong in a governed database view maintained by the DBA — not in dynamically generated SQL. ESRA never generates a join.

Adding an entity from the database

The Searchable entities tab offers Read from the database beside Add entity. It asks the source's own catalog what it contains, lists the relations, and proposes a mapping for each one you pick — record key, title, business timestamp and the columns worth searching — exactly as the onboarding wizard does, because it is the same implementation. Everything is editable before it is staged, and nothing is written until you save the source.

Two things it will not do. It never proposes which columns are filterable or restricted: both are governance decisions, and a security control set by heuristic is worse than one left unset. And it can only show what the connection's database principal may select from — so if a table you expect is missing, grant that principal read privilege on it, or on the catalog views, and read the structure again. An empty list is far more often a missing GRANT than an empty database.

The control is withheld, with the reason on it, when the source has no connection or its last probe found it unreachable.

All six external providers — PostgreSQL, SQL Server, MySQL/MariaDB, Elasticsearch/OpenSearch, MongoDB and Oracle — can be read this way, each against its own catalog (relations and columns for the SQL-shaped providers, indices and mappings for Elasticsearch, collections for MongoDB). Where a relation carries no full-text index, the list says so — and says what it costs on that engine, because it is not the same thing on every one of them. On PostgreSQL the relation is searchable and slow. On SQL Server, MySQL, MongoDB (in $text mode) and Oracle (absent Oracle Text licensing or an index) it cannot be searched at all: the entity models cleanly and every query against it fails until the engine-appropriate index exists. Neither blocks the choice; both are worth knowing before you make it.


Fields

Field-level governance is where most of the security of a source actually lives.

Flag Meaning
Searchable contributes to the document vector
Returnable may appear in a result's fields
Filterable may be used in a structured filter, with the listed operators
Restricted never returned, never sent to a model, never filterable — excludes Returnable
Weight 0–1; maps to the provider's weight classes (A–D on PostgreSQL)

A field that is neither searchable nor returnable nor filterable is invisible to ESRA entirely.


Limits

Per-source ceilings that no capability or exposure can exceed: max queries per source, max terms per query, max filters per query, max rows per query, max candidates, max concurrency, max retries, command timeout.


Result Governance

Setting Effect
Allowed response fields the returnable allowlist, applied after every stage
Masking rules Full, PartialTail (keep last n), or Hash per field
Record filter template a parameterized predicate with claim placeholders, applied in the query
Claim-to-filter mappings which caller claim binds to which placeholder
Retention classification governs whether prompts and fragments may be captured in audit
Allow answer generation gates grounded answers for this source
Allow record references gates returning the record URL

Record-level filters

A template such as:

tenant_scope = @claim:tenant_id AND department = ANY(@claim:departments)

is validated at publication — no statement separators, no comment sequences, balanced parentheses, every placeholder resolvable — and bound at request time from the caller's claims.

A missing claim is fail-closed. The query is not executed, the source is dropped from the invocation, and the response carries a source-unavailable warning. ESRA never substitutes a default and never omits the predicate.


Health

Sources are probed on a schedule, not on the readiness path. A failing optional source degrades a retrieval; it does not fail the Runtime.

The scheduled probe matters more than it looks. Scope resolution excludes an unhealthy source, so without it a database that went away overnight keeps being selected — and keeps failing every invocation that selects it — until somebody happens to press Test connection. The sweep turns that into a source that quietly drops out of scope and a row that says why. A probe that throws is an unhealthy source, not a failed sweep, so one broken source never prevents the others being checked. The sweep can be switched off for a deployment behind a connection-rate-limiting gateway; the Portal then shows when the probe last actually ran, rather than implying the health value is current.

State Cause
Healthy reachable, relation visible, index present, text-search configuration present
Degraded searchable, but something is misconfigured — a missing index, a missing text-search configuration, or an index whose state could not be read
Unhealthy unreachable, relation missing, or permission denied
Disabled switched off by an administrator

An unhealthy source whose effective mode is Optional is dropped from the effective scope. An unhealthy Mandatory source is retained and handled by the mandatory-failure policy at execution time. Degraded gates nothing — not scope, not publication, not execution. It is what the operator is told, never what a caller can reach.

A full-text index still crawling is not degraded. On a corpus under continuous ingestion an index is catching up more often than not, so treating it as a fault would leave a warning nobody can clear — and a warning that never clears is one nobody reads, which is how a real problem goes unnoticed. The crawl is still reported, as an informational note on the entity, because results from it may be incomplete until it finishes. An index whose state could not be read stays degraded: that one is not known to be fine, and granting the connected principal metadata visibility on the table resolves it.

Which entity is at fault. A source's state is a roll-up of a probe per entity, and the connection test now names the entities that produced it rather than reporting that at least one of them did. The Searchable entities tab carries the same answer where you can act on it: a warning triangle on the entity, a sentence saying what is wrong and whose job it is to fix — correcting a text-search configuration is a change on the entity, adding a full-text index is a change on a table ESRA does not own — and, where the engine can attribute it that precisely, a triangle on the individual columns. SQL Server can name the columns its index does not cover; a PostgreSQL index may be an expression over several columns, so there the finding names the relation and lists the entity's searchable columns as affected rather than blaming one of them.

Findings come from the last probe, so they appear without testing the connection — the scheduled sweep establishes them — and each probe replaces them entirely. If a finding disappears after a sweep, it was fixed.