SQL definition
Every field of a SQL connector's definition.json, what Owlie adds to each install's configuration, and
what it derives from the definition. The machine-readable form is
sql.schema.json, a JSON Schema
(draft-07).
In the tables, Editable marks a field that accepts an install-editable value: instead of a literal,
the bare expression { "$oxl": "config.<name>" } (an object whose only key is $oxl), where
<name> is a field declared under config.properties with the same JSON type. Each install then reads its own copy of that field, seeded
from the field's default. Only pageSize, sync.table and sync.where are editable; any expression
elsewhere is refused.
Top level
| Field | Type | Required | Default | Editable | What it does |
|---|---|---|---|---|---|
engine | integer | Yes | No | Always 1. | |
dialect | string | Yes | No | postgres, mysql, mssql or oracle. | |
description | string | No | No | The catalog description. | |
icon | string | No | No | The catalog icon: @ plus a domain, or an https:// image URL. | |
validationQuery | string | No | SELECT 1; on Oracle SELECT 1 FROM DUAL | No | The connection test's query. Starts with SELECT or WITH. |
config | object | No | No | Holds properties. | |
config.properties | object | No | No | The install fields the definition reads, keyed by name. Each is a JSON Schema object: type, title, description, default (the seed), and for objects additionalProperties. A field without a default, or with any seed other than "" or null, is required at install. The base field names below, user, and password are refused. | |
pageSize | integer | No | 500 | Yes | Rows per page, 1 to 5000. |
entities | object | Yes | No | At least one entity, keyed by entity key: lowercase letters, digits and _, starting with a letter. | |
grant_kinds | object | No | No | Grant kinds, keyed by lowercase letters, digits and _, starting with a letter. | |
custom_ops | object | No | No | PowerShell custom operations, keyed by operation key. | |
handlers | object | No | No | PowerShell scripts that replace engine handlers, keyed by handler key. |
No other top-level field is accepted.
Entity fields
Each entry of entities:
| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
name | string | No | The entity key | What Owlie calls the records. |
is_account | boolean | No | false | The account entity. At most one entity has it. |
identifier | string | Yes | The column whose value is the record's external id. One of columns; always kept as text. | |
columns | object | Yes | At least one column, keyed by column name. | |
sync | object | Yes | Which rows Sync reads. | |
statements | object | No | Write statements, keyed by operation. |
A connector with any statement or any Grant kind add needs an account entity.
columns
Each column name matches ^[A-Za-z_][A-Za-z0-9_$]*$; owlie_src, owlie_rel and owlie_source are
refused. Each entry:
| Field | Type | Required | Default | Allowed values | What it does |
|---|---|---|---|---|---|
type | string | No | string | string, number, bigint, int8, boolean, datetime | How the value is read. bigint and int8 are the same: always text. number stays text past exact integer precision. boolean is true for 1, t, true, y, yes. datetime is ISO 8601; a zoneless timestamp is read as UTC. |
title | string | No | The label Owlie shows. | ||
sensitive | boolean | No | false | Mark the value as secret or personal data. |
sync
| Field | Type | Required | Editable | What it does |
|---|---|---|---|---|
table | string | One of table and selectStatement | Yes | A table or view, table or schema.table. An unqualified table resolves in the install's defaultSchema, when set. |
selectStatement | string | One of table and selectStatement | No | A SELECT or WITH whose rows are the records. |
where | string | No | Yes | With table only: a condition without the word WHERE. |
orderKey | string | Yes | No | A unique, orderable column. Pages are keyset pages on it. |
updatedAt | string | No | No | A timestamp column. Enables <entity>.fetchAllSince; each run reads rows at or after the last marker. |
selectStatement and where contain no ;, no comments and no :name placeholders. An install's
where is checked the same way. orderKey and updatedAt are plain identifiers, like column names.
statements
Each statement is one INSERT, UPDATE, DELETE or MERGE (createdId is a SELECT), with no ;
and no comments.
| Field | Type | May bind | Must bind | What it does |
|---|---|---|---|---|
create | string | Any column | Nothing | Creates a record. Returns the new identifier with RETURNING or OUTPUT (the column named like the identifier), or relies on createdId or the input. |
createdId | string | Any column | Nothing | With create only: a SELECT, run in the create's transaction, that returns the new row's identifier, from the column named like it or else the first column. |
update | string | Any column | The identifier | Updates a record. Every column is bound from the current row, with the input on top. |
delete | string | The identifier | The identifier | Deletes a record. No row changed is an error. |
enable | string | The identifier | The identifier | Enables an account. No row changed is an error. |
disable | string | The identifier | The identifier | Disables an account. No row changed is an error. |
A column that create or update binds is writable; every other column is read-only. A placeholder
must name a declared column. Statements run as written, in the install's defaultSchema when it is set,
so a table named without a schema resolves there.
grant_kinds
Each entry:
| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
source | string | Yes | The entity that holds the Grant. | |
target | string | Yes | The entity the Grant is on. | |
select | string | Yes | A SELECT or WITH returning one row per Grant; no ;, comments or :name placeholders. | |
sourceColumn | string | Yes | The column of select holding the source's identifier. | |
targetColumn | string | Yes | The column of select holding the target's identifier. | |
membership | boolean | No | false | The source holds what the target holds. |
add | string | No, but with remove | Adds one Grant. Binds exactly :source and :target. No row changed reads as already present. | |
remove | string | No, but with add | Removes one Grant. Binds exactly :source and :target. No row changed reads as already absent. |
The engine does not look up either record before add or remove; the statement's row count is its
only evidence (Grant write contract).
custom_ops and handlers
A custom operation binds a script, scripts/<name>.ps1:
| Field | Type | Required | What it does |
|---|---|---|---|
script | string | Yes | The script's name: letters, digits, _ and -, starting with a letter or digit, up to 64 characters. |
entity | string | No | The account entity, the default and the only value: custom operations are actions on a person's account. |
title, description | string | No | What admins and workflows see. |
params_schema | object | No | JSON Schema the parameters are checked against before the script runs. |
config | list of strings | No | Install field names passed to the script as $Params.config. |
secrets | object | No | Parameter name to gateway secret name. identifiers and config cannot be bound, and params_schema cannot declare a property with either name: Owlie fills both itself. |
timeoutMs | integer | No | 1 to 900000. The default is 60000. The gateway operator's limits cap it: the connector's powershellTimeoutMs and customConnectorLimits.timeoutMs, both 60000 by default. |
The operation's handler is <account entity>.op.<key>. Each handlers entry, keyed by a handler key the
definition enables, replaces the engine's handler with a script and takes script, config, secrets
and timeoutMs. A key the definition does not enable, or a read-only key
(connection.validateStatements), is refused: there is no PowerShell broker in a read-only context,
so a script bound there would fail every time it ran.
What the script reads as $Params depends on the key. fetchOne, create, fetchAll,
fetchAllSince, delete, enable, disable, a Grant kind's add and remove, and
connection.testConnection/connection.validateConfig take one value: the script reads it as
$Params.payload, beside $Params.identifiers for the entry the operation runs on: null when the
operation has none, such as create, fetchAll, fetchAllSince, a Grant write, or either connection
operation, which has no entry at all. update takes the entry and the change, so the script reads
$Params.identifiers and $Params.input. fetchRelationships takes the external id of the entry
whose relationships are read (the group, for a membership grant) and the page request, so the script
reads $Params.externalId and $Params.options, with no $Params.identifiers.
A script replacing fetchOne, fetchAll, fetchAllSince or fetchRelationships stands in for a
read, so its output is checked before Owlie uses it: a page must be { records, nextCursor, hasMore },
with nextCursor set whenever hasMore is true, and fetchOne must be { record } (the entry, or
null when there is none). A script that returns something else fails the operation with a message
that says so.
A handlers entry's secrets cannot name a $Params field the key itself fills: config, always,
plus payload and identifiers for a one-argument key, identifiers and input for update, or
externalId and options for fetchRelationships.
What the engine adds to each install
| Field | Type | Required | What it is |
|---|---|---|---|
dialect | string | Yes | Fixed to the definition's dialect. |
host | string | Yes | The database server: a plain hostname or IP address. |
port | integer | No | 1 to 65535. The default is 5432 (Postgres), 3306 (MySQL), 1433 (SQL Server) or 1521 (Oracle). |
database | string | Yes | The database name; for Oracle, the service name. The gateway accepts 1 to 128 letters, digits, _, ., $, # and -. |
defaultSchema | string | No | Postgres and Oracle only. A plain identifier of up to 63 characters (Postgres) or 128 (Oracle): the schema every statement runs in, Sync reads, writes and Grants alike. The gateway sets it on each connection (on Postgres as the whole search_path, on Oracle as the current schema), so a table named without a schema resolves there. Empty: the connecting user's default schema, or its search_path. |
ssl | boolean or object | No | Absent or true: verified TLS. false: no TLS. An object: caCert (PEM) and rejectUnauthorized (default true). false, a caCert or rejectUnauthorized: false needs the gateway operator's allowTlsOverrides. |
The one authentication option, password ("Username and password"), has user and password, both
required. password is a gateway secret: an install gives the secret's name, which the gateway resolves
on your network.
What Owlie derives
| Part | Derived value |
|---|---|
kind | sql |
provides | ["sync"], plus provision when the account entity has create or a Grant kind has add and remove |
protocols | ["sql"], plus powershell when a script is bound |
execution_targets | ["gateway"] |
| Entities | One per entity. The schema has exactly the declared columns; identifier_paths is [identifier]; the identifier is a read-only string; a column bound by create or update is writable; bigint columns are strings of digits. |
| Account | sync_support.supports_incremental follows updatedAt. No sync_profile, so each install's account Sync job starts in Identity mode (Sync creates identities from rows) until an admin switches it to Resource. No Sync active rule, so Sync reads every listed row as active, and a single lookup reports a readable row as active and a missing one as deleted. When the connector provisions, a provisioning profile with the operations its statements offer and no baseline attributes; without create, create_semantics is external. |
| Grant kinds | One per entry, discovered through its target per entity, with membership as declared, writable with add and remove. |
| Custom operations | One per custom_ops entry. |
The handlers the engine runs:
| Handlers | When |
|---|---|
connection.validateConfig, connection.testConnection, connection.validateStatements | Always |
<entity>.fetchOne, <entity>.fetchAll | For each entity |
<entity>.fetchAllSince | With sync.updatedAt |
<entity>.create, <entity>.update, <entity>.delete, <entity>.enable, <entity>.disable | For each statement present |
<target>.fetchRelationships | For each entity that is a Grant kind's target |
grant.<kind>.add, grant.<kind>.remove | For each Grant kind with add and remove |
<entity>.op.<key> | For each custom operation |
connection.validateStatements takes { "keys": [...] } and returns
{ "statements": { "<key>": { "ok": true } } }, or { "ok": false, "message": "..." } for a statement
the database refuses. It executes nothing, and it checks only the statements the definition declares,
never the paged, incremental, single-record and relationship reads the engine builds around them. The
handler context is in the
Handler API.