Tables
Availability says whether a table is served today. A planned table is not referenceable yet: a query that names it fails at validation withPV_REF_002.
Secrets, API tokens, sessions, and credential payloads are never exposed in
this pool.
List active Providers
Every connected Provider instance is a row, disabled ones included, so “active” is a predicate rather than a hidden rule:connector_slug is the {connector} of providers.{connector}.{table}, and
connection_status carries its enum values in the served schema. id is the
tenant_connector_id of the per-connector config tables, so an instance’s
settings and allowlist sit one join away:
List a Provider’s streams
Every stream configured on a Provider connection is a row, disabled ones included, so “enabled” is a column rather than a hidden rule. Join toworkspace.providers on provider_id to name the connector and to drop the
streams of disabled or deleted connections:
kind carries its enum values in the served schema. An enabled override
stream is served as providers.{connector_slug}.{platform_tap_name} and an
enabled custom stream as providers.{connector_slug}.{name}, both
lowercased; a disabled stream has no Provider table. An enabled artifact
stream is served as artifacts.{name} unless the upload was published under
another slug, and artifact_id names the upload behind it. Streams of a
disabled or deleted connection stay rows of provider_connections and drop
out through the join above.
Roles are platform-level and scoped through membership
Roles are permission bundles defined once for the platform, not per Workspace.workspace.roles lists the roles that at least one member of the
Workspace holds, with every membership status counted. Because the name is a
display value an administrator can change, join on id, not name:
parent_name is NULL both when a role has no parent and when no member of
the Workspace holds the parent; child.parent_role_id still carries the id in
the second case.
Permissions cover their descendants
workspace.role_permissions lists the permission strings granted directly to
each role in workspace.roles. A permission is a dotted path, and a grant
covers every descendant of its path: a role granted tenant can do
everything under tenant., including tenant.users.manage. The table does
not expand that hierarchy, and it does not fold in the parent chain a role
inherits through parent_role_id; both are one predicate away. To list the
tenant-scoped permissions each held role grants:
role_id, never on the role name, and join through workspace.roles
to reach the parent chain when a role inherits from another. Which members
hold a role is workspace.user_roles.
Who holds each role
workspace.user_roles is the assignment edge: one row per (member, role)
pair. user_id joins workspace.users.id and role_id joins
workspace.roles.id, so the members page is a three-way join:
SUSPENDED and
REVOKED included, so filter on u.status = 'ACTIVE' to see who can act
today. An assignment of a role that has since been deleted is still a row in
workspace.user_roles; the join to workspace.roles drops it, because that
table lists only live roles. assigned_by is the user ID of the person who
made the assignment and may name someone who has since left the Workspace,
so a LEFT JOIN to workspace.users is the right shape for it. To count
members per role:
Read your Preferences from a Plot
workspace.preferences returns all records admitted by the current Workspace,
Parable, actor, and version boundaries. It does not rank scopes or select one
record per slot. A Person, Workspace, and authored record can all be visible
for the same slot:
id, or filter explicitly by scope, actor, slot, and
ref/commit context. The id identifies the current stored row; rebuilding
an authored projection may replace it. If a request names both a draft and
a commit, their admitted records remain distinct rather than one winning.
Existing SQL that assumed a scalar result per handle must now make its
selection explicit.
Other actors’ Person-scope rows remain hidden by the current access rule.
Custom Protection sharing has not replaced that boundary yet. The legacy
Preference SDK/API effective[] resolver also retains its scope fallback;
this SQL projection no longer performs that selection.
Reads are live: a Plot re-run after a Preference changes sees the new value,
and a run records this table as unpinned. Because the rows depend on the
person running the Plot, a scheduled Plot cannot read workspace.preferences
and is refused at publish and schedule time.
Declared projections
Tables below are generated from psgen@projection declarations in
platform-schemas/services/web-db: the same declaration produces the
Postgres view, the Arrow schema the query layer serves, and this block.
Generated from psgen @projection declarations; edit the schema, not this block.
workspace.preferences
All visible Preference records; id identifies each record and refId/commitId distinguish simultaneously visible authored versions. Applications select values.
Rows are scoped by the session settings
platform.workspace_id, platform.parable_root_ref, platform.actor_id, platform.actor_kind, platform.preference_filter_protocol, platform.parable_ref, platform.parable_commit; the query layer sets them for every scan.
workspace.provider_connections
Streams of the current Workspace’s Provider connections: one row per tenant tap (an override of a platform tap, a custom tap, or an artifact-derived tap), joined to the platform tap it is bound to. Disabled streams are served with enabled = false so “enabled” is explicit in the consumer’s SQL; soft-deleted streams are not. Streams of a disabled or deleted connection are served too and drop out through the join to workspace.providers. The platform tap is reference data and is never filtered on its own soft-delete. Served by the query layer as workspace.provider_connections (EDR-0081).
Rows are scoped by the session settings
platform.workspace_id; the query layer sets them for every scan.
workspace.providers
Connected Providers of the current Workspace: one row per connector instance, joined to the Provider definition it was created from. Disabled instances are served with disabled_at set so “active” is explicit in the consumer’s SQL; soft-deleted instances are not. The definition is global reference data and is never filtered on its own soft-delete: a deleted definition with live instances is an inconsistency worth seeing. Served by the query layer as workspace.providers (EDR-0081).
Rows are scoped by the session settings
platform.workspace_id; the query layer sets them for every scan.
workspace.role_permissions
Permission strings directly granted to each role held in the current Workspace: one row per grant on a platform role that at least one member holds, whatever the membership status. Direct grants only: a role also inherits its parent chain (workspace.roles.parent_role_id), and a permission covers every dotted descendant of itself (‘tenant’ covers ‘tenant.users.manage’), so a coverage question is permission = ‘x’ OR starts_with(permission, ‘x.’). Grants of soft-deleted roles and of roles held only through soft-deleted memberships are not served. Served by the query layer as workspace.role_permissions (EDR-0081).
Rows are scoped by the session settings
platform.workspace_id; the query layer sets them for every scan.
workspace.roles
Roles held in the current Workspace: one row per platform role that at least one member holds, whatever the membership status. Roles are platform-level permission bundles with no Workspace of their own, so a role no member holds is not served, and a parent_role_id may name a role that is absent from this table. Soft-deleted roles and memberships are not served. Served by the query layer as workspace.roles (EDR-0081).
Rows are scoped by the session settings
platform.workspace_id; the query layer sets them for every scan.
workspace.user_roles
Role assignments for members of the current Workspace: one row per (member, role) pair, keyed by the person’s user id so it joins workspace.users directly and by role id so it joins workspace.roles. Assignments of every membership status are served, SUSPENDED and REVOKED included; join workspace.users.status to filter. Assignments of soft-deleted memberships are not served. Served by the query layer as workspace.user_roles (EDR-0081).
Rows are scoped by the session settings
platform.workspace_id; the query layer sets them for every scan.
workspace.users
Members of the current Workspace: one row per membership, joined to the person’s profile. Every membership status is served, including REVOKED, so a query can reconcile who left; soft-deleted memberships and profiles are not. Served by the query layer as workspace.users (EDR-0081).
Rows are scoped by the session settings
platform.workspace_id; the query layer sets them for every scan.
Join live metadata to Provider data
workspace.users.email and Provider columns with the Contact.Email semantic
scalar represent compatible values:
workspace.* table records that input as unpinned in its run history: the run
says what it read and when, and a replay reads the current state rather than
the original rows.
Use catalog discovery
for the current table schemas and
types and metadata before
joining semantic values.