DataShield Ontology · documentation

Connectors: connection paths, the connection umbrella, and the dialable kinds

*Measured against /home/datashield/library at v0.22.188 (tag 2b5f46a9c, 2026-09-21). Every claim below carries a file:line you can re-read; where a value was obtained by evaluating the config module under tsx rather than by reading a literal, the section says so. Normative keywords per RFC 2119.*

Audience: developers and AI agents connecting a data source to the Library/Ontology. Read §2 (paths), §3 (the connection umbrella) and §5 (your kind's row) before creating anything; read §9 before you trust a probe verdict; read §11 for the two registrations that were scripted on 2026-09-21 and what the ledger says actually ran.


On this page

1. Vocabulary

TermMeaningWhere it is defined
connectionAn encrypted credential row (registry.connections), AES-256-GCM at rest, owned by the caller's account, optionally sharedscripts/mcp-server/umbrellas/connection.ts:54-57
connection_typeThe auth shape of the stored config: oauth2 | api_key | basic | bearer | customconnection.ts:92
platformWhich provider the credential dials — a closed vocabulary equal to the connection-backed PROVIDER_KINDS ids (z.enum(CONNECTION_BACKED_KIND_IDS)); orthogonal to connection_typeconnection.ts:101
provider kindA PROVIDER_KINDS row in config/data-catalog.config.ts:483 with a path (A | B | ingest), a harvest transport and lane flagsconfig/data-catalog.config.ts:483, :734
probe kindThe wire protocol a liveness verdict rests on (postgres_dial, tds_dial, saas_get, …)config/connection.config.ts:771
laneOne of discover | profile | extract (LANE_CAPABILITIES) run by data_catalog.scan_run over a providerconfig/data-catalog.config.ts:1967

A connection is not a provider. The sequence is always: connection.create (credential) → data_catalog.provider_create (bind the credential to a kind) → data_catalog.provider_test / connection.test (liveness) → data_catalog.scan_run (lanes).


2. STANDARD-010 — exactly two connection paths, and who owns what

Source: docs/standards/STANDARD-010-connection-paths-and-ownership.md (operator ruling 2026-09-15, amended for the catalog-elt-1 wave).

PathForMechanism (STANDARD-010 §1)Kinds at v0.22.188 (measured, §5 below)
A — native dialdatabases and object stores that matter to customersvendor SDK loaded at dial time (NO_LOADTIME_RESOLUTION), DIALERS material table, one dialability predicate, dialect-keyed read-only SQL guard, metadata-only harvest, typed dial verdictspostgresql, snowflake, databricks, bigquery, sqlserver, mysql, s3 (7)
B — REST + vaultevery SaaS / API-shaped sourcesaas_api (or rest_api) transport, credentials in the Ontology vault, one OAuth2 acquirer, one SaaS metadata-catalog harvester contractsalesforce, dynamics365, rest_api, iceberg_rest, kafka (5 — the last two are declared only, §6)
ingest (not a path)pre-existing file/feed ingestion lanesnever dials a provider from the catalog; a kind that gains a dial moves to A or Bazure_blob, gcs, sftp, ftp, remote_storage, imap, socrata, tyler, ckan, arcgis, opendatasoft, dcat, filesystem, other (14)

Rules that bind a connector author (STANDARD-010 §1, §5):

new Path-A thin client or a Path-B kind. Native binaries only when no thin client exists AND the operator rules.

driverModule MUST be A; a saas_api/rest_api transport MUST be B.

(HARVEST_TRANSPORT_CAPABILITIES, config/data-catalog.config.ts:656: source_pool → engine_catalog, saas_api → metadata_api, both dials: true; local_projection → platform_registry, dials: false), not by the letter.

Ownership (STANDARD-010 §2). Ontology (this Library) is the loaded home of every data connection — all Path-A and Path-B kinds, their credential shapes, dial probes, lanes and vault entries. Auth is the exclusive home of security connectors (Entra ID, Okta, logging/security tooling, signing keys, MCP service registrations) and the passthrough gateway. Both MUST expose the full connector list read-only. Until the shared @datashield/connection-contracts package exists (§3), audit:auth-connection-type-parity keeps the two registries honest; a kind Auth does not carry declares library_only with a reason (e.g. BEARER_CREDENTIAL_SHAPE.authParity, config/data-catalog.config.ts:1730). Commander and third-party MCP clients are consumers through Auth entitlements, never a third runtime (§6).


3. The connection umbrella

File: scripts/mcp-server/umbrellas/connection.ts (623 lines). Description text at :54-86; Zod command union at :88-122; handler cases at :183 (create), :229 (list), :264 (get), :288 (delete), :297 (test).

CommandParams (from the Zod schema)Behaviour measured in the handler
createname, connection_type (oauth2|api_key|basic|bearer|custom), config (object), optional description, provider_name, base_url, scopes, tags, expires_at, platform; is_shared admin-onlyOwned by the caller. platform is validated against registry.provider_kinds WHERE connection_backed via assertKindUsable (:202-210, LIB-BUG-1350 K1)
listfilters provider_name, connection_type, tag; paginatedMetadata only — never returns config. Returns platform and the latest probe outcome (:246-259)
getidSame metadata shape as list (:273-282)
testidProbe chosen by the row's platform through PLATFORM_PROBES (:354); null platform → legacy classification (:348-350)
deleteid, confirm: true (literal)Soft-delete — deactivates the row, owner/admin only (:288-296, :117-121)

Config is write-only. create stores it encrypted; list/get return metadata only and the credential can never be read back (connection.ts:55). The latest probe outcome is deliberately readable (LIB-BUG-556 #2, :250, :278).

connection_type vs platform. connection_type says how the secret is shaped for auth; platform says what it dials and therefore which probe and which data_catalog kind can bind it (:101, :85). A row created without platform is "unplatformed"; data_catalog.provider_create can stamp it later. Note the 2026-09-21 fresh-client prompt (docs/audit/v0.22.188-fresh-client-test-prompt.md:125) passes connection_type:'mysql', which is not in the CONNECTION_TYPES enum — the prompt itself says "if connection_type does not accept mysql, that disagreement is the finding" (:129-130). Use connection_type:'basic' (or the shape your kind's material declares) and platform:'mysql'.

3.1 Probe kinds per platform — PLATFORM_PROBES

config/connection.config.ts:1011-1103 (kind vocabulary CONNECTION_PROBE_KINDS at :771; resolver-dial subset at :963).

platformprobe kindWhat the probe does
postgresqlpostgres_dialopens a real session; SELECT current_setting('server_version') (PLATFORM_DIAL_PROBES, config/data-catalog.config.ts:2110)
sqlservertds_dialreal session; SELECT @@VERSION
snowflakesnowflake_dialreal session; SELECT CURRENT_VERSION()
mysqlmysql_dialreal session; SELECT VERSION()
bigquerybigquery_dialsql_dry_run of SELECT 1
databricksdatabricks_dialSELECT current_version().dbsql_version
dynamics365, salesforcesaas_getONE kind for both: a credentialed HTTPS GET of a declared metadata path — /WhoAmI and /limits respectively (connection.config.ts:1065-1069, LIB-BUG-1433)
rest_apihttp_headSSRF-guarded HEAD of base_url, no credential (connection.ts:73)
s3, azure_blob, gcs, sftp, ftp, remote_storage, imapstorage_dialprovider's own testConnection(); for s3 the dial probe is object_store_list with maxKeys: 1
iceberg_rest, kafkanot_applicabletyped test_not_applicable naming the platform — never a fabricated unreachable (connection.ts:70, 366-370)

The probe kinds are also rows of registry.probe_kinds (migration 0239 FK), seeded from this tuple as an unconditional post-step of release:migrate — which is why release:migrate MUST run before any pdl-mcp/pdl-job-worker reload (connection.config.ts:1054-1061; confirmed at the v0.22.188 event, docs/audit/v0.22.188-release-event.md:23). Probe timeout: CONNECTION_TEST_TIMEOUT_MS = 10_000 (connection.config.ts:448).


4. Credential shapes — PLATFORM_CREDENTIAL_FIELDS and BEARER_CREDENTIAL_SHAPE

config/data-catalog.config.ts:1253 (evaluated under tsx; * = required, (s) = secret). These are the members of the config object you pass to connection.create.

kindfields
postgresqlhost*, port, database*, username*, password*(s), sslmode
mysqlhost*, port, database*, username*, password*(s), ssl
sqlserverhost*, port, database*, username*, password*(s), encrypt, trustServerCertificate
snowflakeaccount*, username*, password(s) or privateKey(s) + privateKeyPassphrase(s), warehouse, database, schema, role
bigqueryproject_id*, client_email*, private_key*(s), location
databricksserver_hostname*, http_path*, access_token*(s), catalog, schema
s3bucket*, region, endpoint, access_key_id(s), secret_access_key(s), force_path_style
salesforceinstance_url*, client_id*, client_secret*(s), token_url (derived from the my-domain host — omit), scope (never invent)
dynamics365org_url*, tenant_id*, client_id*, client_secret*(s), token_url, scope

BEARER_CREDENTIAL_SHAPE (data-catalog.config.ts:1730) is the material both Path-B kinds claim (PLATFORM_MATERIAL_KIND.salesforce = "bearer"): token_url*, client_id*, client_secret*(s), scope, audience. A module-load check (:1794) refuses a platform whose credential fields omit a field its material requires. Its authParity is library_only: Auth's registry declares no client-credentials grant shape, so no parity is claimed (:1744-1769).

S3-compatible endpoints (S3_COMPATIBLE_ENDPOINTS, connection.config.ts:104) and server-side-encryption decisions (decideServerSideEncryption, :368) are declared as data; resolveS3Endpoint (:259) picks the endpoint from the provider name.


5. Per-kind capability table (measured)

runnableLanes(kind) and KIND_HARVEST_TRANSPORT (config/data-catalog.config.ts:2028, :734) evaluated under tsx at v0.22.188. kindCanRun (:1915) derives these from each kind's implemented flags — no hand-maintained list exists.

kindpathtransportdriver (driverModule)discoverprofileextractcredential read-only probe
postgresqlAsource_poolpgyesyesyesyes — SQL over information_schema.role_table_grants (CREDENTIAL_READONLY_PROBES, :3914)
snowflakeAsource_poolsnowflake-sdkyesyesyesnull (declared)
sqlserverAsource_poolmssqlyesyesyesnull
mysqlAsource_poolmysql2 (:349; pinned exactly at 3.24.4 in package.json:383)yesyesyesnull
bigqueryAsource_pool@google-cloud/bigqueryyesyesyesnull
databricksAsource_pool@databricks/sqlyesyesyesnull
s3Asource_pool@aws-sdk/client-s3yesnononull
salesforceBsaas_apiyesnononull (declared, v0.22.187-task-elt1-b2-salesforce.md §1 row 8)
dynamics365Bsaas_apiyesnononull
rest_apiBlocal_projectionyesnoyes (scoped assets)

What the lanes mean:

catalog.bindings. For source_pool kinds this reads the engine's own information_schema-class views through a pooled session; for saas_api kinds it walks the metadata API only (§7).

the acknowledgement rungs in lib/data_catalog/policy.ts:114, :373.


6. Declared-only kinds — stated honestly

iceberg_rest and kafka are PROVIDER_KINDS rows with path: "B", transport source_pool, **no driverModule, no credential shape, no dial probe and no runnable lane** (runnableLanes[]). connection.test returns test_not_applicable for them by name (connection.config.ts:1101-1102; connection.ts:70). LIB-BUG-1043 was narrowed to exactly mysql, iceberg_rest, kafka at the v0.22.188 event (v0.22.188-release-event.md:170); mysql then shipped in the same release, so the residue is these two. azure_blob, gcs, sftp, ftp, remote_storage, imap are ingest kinds: they have a storage_dial liveness probe through the legacy provider registry but no catalog lanes (runnableLanes[]); the open-data portal kinds and filesystem/other have neither transport nor lanes. A data_catalog.provider_create over any of these is a typed refusal, not a silent empty scan.


7. Path-B specifics: metadata-only allowlists, budgets, ceilings

7.1 Salesforce — config/salesforce.config.ts

sobject_describe (/sobjects/{Name}/describe) and limits (/limits — the liveness path of the bullet below). The reason is load-bearing: /sobjects/Account/001xx… is a customer record and /sobjects/Account/listviews names filter values; a prefix rule admits both. assertMetadataOnlyPath() (:561) refuses anything else and names the segment at which the path left the allowlist (:199-202).

(:72-89); unnormalised path markers are refused (:124).

report a credential healthy without presenting one (SALESFORCE_LIVENESS, :228-235). reportsServerVersion: false.

2026-09-16: 1700 sObjects, 1433 queryable, 2 custom (:270-271).

object against a 15 000-call daily allocation shared with the customer's other integrations; 1000 ≈ 6.7 % of a day (:310-330). scope.max_entities_per_scan on the crawler outranks it; SAAS_API.paging.maxEntities bounds both.

evidenceStrength: "provider_declared", scoreOnly: true. It names enrolment in Shield Platform Encryption, not an effective read restriction — a reader MUST NOT render it as "masked".

7.2 Dynamics 365 (Dataverse) — config/dynamics365.config.ts

/GlobalOptionSetDefinitions, /WhoAmI. A prefix suffices here because Dataverse metadata collections are disjoint from record collections by URL (:20-23); traversal probes (/EntityDefinitions/../accounts, %2e%2e) are refused at module load (:545). Refusal code path_not_in_metadata_allowlist (:445) — the sentence is value-free by construction.

odata.maxpagesize, per-request timeout PT60S (ISO 8601, :364), credential statuses [401, 403] (:380).

provider_declared, scoreOnly — enrolment in field-level security, not an effective restriction.

7.3 The shared SaaS contract and the entity-ceiling ladder

config/saas-api.config.ts SAAS_API (evaluated): paging = { defaultPageSize: 200, maxPages: 250, maxEntities: 5000 }; `rateLimit = { maxAttempts: 4, baseBackoff: PT1S, maxBackoff: PT30S, jitterRatio: 0.2, maxRetryAfter: PT2M, retryStatuses: [429, 503], credentialStatuses: [400, 401, 403], transientStatuses: [500, 502, 504, 408, 425] }`. RFC 9110 §10.2.3 Retry-After is honoured in both syntaxes; a credential-fact status is never retried (HARVEST_TRANSPORT_CAPABILITIES.saas_api.rateLimitPosture, data-catalog.config.ts:687-691).

The ceiling ladder (SAAS_ENTITY_CEILING, lib/data_catalog/harvest/saas.ts:211-246, resolved at :249-266): rung 1 the crawler's own scope.max_entities_per_scan; rung 2 the kind's declared per-scan ceiling — SAAS_KIND_ENTITY_CEILING_DEFAULTS (data-catalog.config.ts:726): salesforce: 1000 (= SALESFORCE_BUDGET.maxDescribesPerScan), dynamics365: null; rung 3 the contract's maxEntities: 5000. The driver enforces the winner and records which rung supplied it, so a partial scan is diagnosable (salesforce_objects_truncated, dynamics365_relationships_truncated).


8. Guards every dial passes through

GuardWhereWhat it does
Read-only SQL guardconfig/sql-readonly-guard.config.tsSQL_GUARD_DIALECT_IDS = ["snowflake","sqlserver","bigquery","databricks","mysql"] (:56), SQL_GUARD_DIALECTS (:275), terminalVerb (:1024), hasStatementSeparator (:951)Dialect-keyed lexer that strips preambles/comments and refuses any statement whose terminal verb is not a read, or that carries a second statement. Postgres is covered by the credential read-only probe in §5 instead
Host policyHOST_POLICY, config/data-catalog.config.ts:2731 (evaluated): blockMetadataRanges: true, blockSelfDatabase: true, allowPrivateRanges: false (per-connection key allow_private_ranges), allowLoopback: false (env PDL_CATALOG_ALLOW_LOOPBACK), allowUnixSockets: false, dsnQueryAllowlist: [sslmode, application_name, connect_timeout]Refuses cloud-metadata ranges, the Library's own database, private ranges, loopback and unix sockets before any socket opens
Pinned dialconfig/host-pinning.config.ts HOST_PINNING (:61) declares a rule for every dialable kind, in two strategies: socket_address for mysql (:66) and sqlserver (:75), and recheck for postgresql (:81), snowflake (:90), s3 (:97), bigquery (:104) and databricks (:128); saas_api/dynamics365/salesforce share SAAS_API_HOST_PINNING (:138-159), socket_address via a DNS-pinned undici Agent (:54-55)On a socket_address row the address that passed policy is the address dialled — no DNS rebinding between check and connect. A recheck row does not close that window and says so in its own residual field ("the window is narrowed to the recheck-to-connect interval, not closed"): those drivers take a host NAME and resolve it themselves
Cleartext refusalSAAS_CLEARTEXT_REFUSAL_CODE = "cleartext_refused" (config/saas-api.config.ts:261); raised in lib/data_catalog/resolver/index.ts:430 (LIB-BUG-1435)An http:// token or instance URL is refused before a bearer secret is sent
Sample policylib/data_catalog/policy.ts:114, :373, :530Values leave the customer's engine only under an acknowledged rung

8.1 The extract lane on the shared loop

EXTRACT_LANE_UNIT (data-catalog.config.ts:2939, evaluated): source_pool → "bindings", `local_projection → "scoped_assets", saas_api → "scoped_assets". lib/data_catalog/extract/handler.ts:133` reads the unit from the transport (close-fix L3: no silent literal, :125); for bindings it iterates catalog.bindings joined to assets (:171), stops at HARVEST_BUDGETS.maxExtractedObjectsPerScan (:190), skips a binding with no dataset (:194) and runs FULL_EXTRACT_PROFILE.mode (:216). Row egress goes through lib/data_catalog/extract/sharedLoop.ts; MySQL became the fifth engine on it with "one spelling row, one KIND_EXTRACT row, one EXTRACTORS slot" and zero loop changes (docs/audit/v0.22.188-task-elt2-a-mysql.md:21).


9. Dial verdicts — the classifier and the typed refusals

classifyDialError (config/connection.config.ts:707) maps err.code / err.name / err.cause.code (undici wraps the socket error) to a registered wire code, first match wins, fallback unreachable (:450-467). Messages are fixed author templates; raw upstream text is logged server-side only.

coderetryableretry_after_ms (DIAL_RETRY_AFTER_MS, :681)class
dns_failureyes30 000unreachable
connect_timeoutyes60 000timeout
unreachableyes30 000unreachable (the honest generic)
credential_rejectednoauth
config_rejectednoconfig
response_malformedno
dial_failed_unclassifiednounclassified

A module-load self-check refuses a retryable class without a positive budget, or a budget on a non-retryable class (:1105-1122).

Refusals raised before or around a dial are not dial verdicts and are re-thrown, not flattened (G1 close-vet F-V1, :850-870): host_form_refused (resolver/index.ts:523), the host-policy family (loopback / private range / metadata / self-dial / unix socket), credential_shape_invalid, credential_expired, driver_unavailable, cleartext_refused, and the SaaS token family token_acquire_failed, token_endpoint_unreachable, token_response_too_large (resolver/saasToken.ts:103-140). The probe result then carries a probe_not_run reason selected by phase (CONNECTION_PROBE_NOT_RUN_REASONS, :829: host_unresolvable, resolve_refused, and the third row for refusals raised after preparation succeeded) so the caller is never told "the host did not resolve" about a host that was never queried.

Reading a verdict: ok:false + a code from the table = the provider was reached or attempted; test_not_applicable = the platform has no probe (§6); probe_kind: 'no_declared_probe' = a configuration gap in PLATFORM_PROBES, not a statement about the credential (connection.ts:355-362).


10. Governance evidence harvested per kind

The classification kernel (config/classification-evidence.config.ts) admits provider governance facts as the provider_declared signal: role score (:420), weight 0.10 (:438), strictly below fingerprint (:1259-1262), evidence: "external_claim", mayCorroborate: false (:490) — a provider tag is a customer-authored claim about the column, never an observation of it, so it can raise a score but never gate, veto or corroborate.

kindfact harvestedkeysource
salesforceShield Platform Encryption enrolment per fieldfield_encryption_declaredconfig/salesforce.config.ts:427-435
dynamics365field-level-security enrolment (IsSecured)field_level_security_declaredconfig/dynamics365.config.ts:352
mysqlgovernance absence declared explicitlysee config/mysql-harvest.config.ts ("governance-ABSENCE declarations", v0.22.188-task-elt2-a-mysql.md:31)
bigquery, postgresql, sqlserver, snowflake, databricksengine-catalog facts (keys, nullability, native type family via NATIVE_TYPE_FAMILY_RULES)per-kind harvest/*.ts

STANDARD-009 applies to every artefact about the data: value-free, unclassified ≠ public, masking OFF by default.


11. Worked example — registering Salesforce and MySQL (2026-09-21)

What the ledgers record, verbatim in disposition: the v0.22.188 event deployed both kinds (docs/audit/v0.22.188-release-event.md:55-74: mysql.path = A, mysql lanes {discover, profile, extract} all true, salesforce {discover:true, profile:false, extract:false}, four new probe kinds in registry.probe_kinds and in the served bundle, mysql2@3.24.4 installed additively in the primary inside the lock, node_modules 839 → 847). **Deviation 12 (:239-240): "No post-deploy live acceptance was run"** — the MySQL fixture pass and the Salesforce rescan were left to the orchestrator. The registrations below are therefore the scripted acceptance (docs/audit/v0.22.187-task-elt1-b2-salesforce.md §6, :314-349; docs/audit/v0.22.188-fresh-client-test-prompt.md:121-133), not a recorded production run. Do not cite this section as proof a customer org was catalogued.

11.1 Salesforce (Path B, saas_get, discover only)

Prerequisite in the org: an External Client App with the client-credentials flow enabled and a run-as user; without the run-as user the grant fails and provider_test answers provider_dial_auth_failed, which is the correct and diagnosable answer (§6 step 1).

// 1. credential — token_url and scope OMITTED (derived / never invented)
connection {command:'create', name:'acme-salesforce', connection_type:'oauth2', platform:'salesforce',
  config:{instance_url:'https://acme.my.salesforce.com', client_id:'<consumer key>', client_secret:'<consumer secret>'}}
// 2. provider over that connection
data_catalog {command:'provider_create', kind:'salesforce', connection_id:'<id from step 1>', name:'Acme CRM'}
// 3. liveness
data_catalog {command:'provider_test', provider_id:'<pid>'}
//    expect ok:true, probe_kind:'saas_get', server_version:null (an org reports none)
// 4. discover
data_catalog {command:'scan_run', provider_id:'<pid>', purpose:'discover'}

Expected discover outcome (§6 step 4): saas_object assets > 0 with salesforce_objects_seen ≈ 1700 and salesforce_objects_filtered as denominators; salesforce_api_usage_used / _allocation (≈ 15 000) present; fk on reference columns and field_encryption_declared on every column; salesforce_objects_truncated: 1 means the describe ceiling (§7.1, now 1000) bound a partial, resumable pass. The first check to run: grep the whole result for any 15/18-character Salesforce id outside a key_prefix attr — there must be none. Then read the job-worker log for the tick: no durable-tick DEGRADED, no assertHostPolicy refusal on the my-domain host (§6 step 6).

11.2 MySQL (Path A, mysql_dial, discover + profile + extract)

connection {command:'create', name:'acme-mysql', connection_type:'basic', platform:'mysql',
  config:{host:'db.acme.example', port:3306, database:'sales', username:'catalog_ro', password:'<secret>', ssl:true}}
connection {command:'test', id:'<id>'}
//   expect probe_kind:'mysql_dial'; a rejected credential → credential_rejected with driver_code + stage
data_catalog {command:'provider_create', kind:'mysql', connection_id:'<id>', name:'Acme Sales DB'}
data_catalog {command:'provider_test', provider_id:'<pid>'}      // SELECT VERSION() over a pinned, policy-checked session
data_catalog {command:'scan_run', provider_id:'<pid>', purpose:'discover'}
data_catalog {command:'scan_run', provider_id:'<pid>', purpose:'profile'}
data_catalog {command:'scan_run', provider_id:'<pid>', purpose:'extract'}   // bindings unit on the shared loop, §8.1

The fresh-client prompt's local fixture (127.0.0.1:13306) will be refused by HOST_POLICY.allowLoopback: false unless PDL_CATALOG_ALLOW_LOOPBACK is set — that refusal is resolve_refused, not a dial verdict (§9). Every session statement runs through SQL_GUARD_DIALECTS.mysql (§8), and the four pinned session statements plus TLS policy live in config/mysql-session.config.ts.


12. Adding a kind — the checklist the config enforces

Module-load validators in config/data-catalog.config.ts (:4253-4416) and connectionProbeConfigProblems (connection.config.ts:975) refuse a process whose kind is half-declared. A new dialable kind needs, at minimum: a PROVIDER_KINDS row with path/pathWhy (T30); KIND_HARVEST_TRANSPORT; PLATFORM_CREDENTIAL_FIELDS; PLATFORM_MATERIAL_KIND; PLATFORM_DIAL_PROBES; PLATFORM_PROBES + a RESOLVER_DIAL_PROBE_KINDS member if it dials through the resolver; HOST_PINNING; CREDENTIAL_READONLY_PROBES (a declared null with a reason is valid); AUTH_CONNECTION_TYPE_PARITY; a DIALERS factory and HARVESTERS entry; for Path A a SQL_GUARD_DIALECTS row and an exact-pinned driver; for Path B an allowlist config in the shape of §7. No migration is required for the probe kind — the seeder fills registry.probe_kinds from config (connection.config.ts:1054-1056).

Measured against the release named in the text. Raw Markdown: /ontology/documentation/connectors.md. This page is also served as raw Markdown at the same URL with a .md suffix, for agents and search tools that prefer plain text. Every capability statement cites the source file and line it was measured from.

You've seen the proof

Ready for a number? Scope your deployment and we'll price it against your own economics.

Get your quote →