A change that affects a tenant boundary should be reconstructable afterwards. CYPEX records part of that automatically and leaves the rest to you. This page documents the three layers where evidence lives, in order of increasing persistence:
- The permission audit log — queryable in PostgreSQL.
- The write path — what CYPEX captures automatically, and what it does not.
- Documentation approvals — recorded in product docs and release notes.
If you make a non-trivial decision about an Organization (split, merge, role remap, schema revoke), keep the audit query and its output with the change record, and add an ADR-style note under CYPEX internals.
Permission decisions are queryable directly in PostgreSQL, in
cypex_log.t_permission_audit_log. Use it to answer “why was this user allowed
or denied on this row?”
| |
The table is protected by row-level security. A system admin sees every row. An
organization admin sees rows for the organizations they are mapped to, and also
every row whose organization_id is NULL.
WarningThe permission audit log is deployment-wide evidence, not per-tenant evidence. Only connector-execution rows carry anorganization_id; every other kind of row leaves it NULL, and NULL rows are visible to every organization admin. Do not rely on this table to keep one tenant’s decisions hidden from another tenant’s administrator, and do not assume that filtering onorganization_idreturns a complete picture for that organization.
Access-control decisions are recorded in cypex_log.t_permission_audit_log.
The audit row carries:
| Column | Type | Contents |
|---|---|---|
id | bigint | Sequence-generated primary key. |
created_at | timestamptz | When the decision was recorded. |
action | text | ACCESS_GRANTED, ACCESS_DENIED, CREATE, UPDATE, DELETE, REVOKE, and the connector-execution outcomes EXECUTE_STARTED, EXECUTE_SUCCESS, EXECUTE_FAILED, EXECUTE_BLOCKED, EXECUTE_RETRIED, EXECUTE_TIMEOUT. |
entity_type | text | schema, organization, user, role, connector, connector_secret, connector_execution, connector_enablement, connector_allowlist. |
entity_id | text | Identifier of the affected row. |
entity_name | text | Human-readable name of the affected row. |
user_id | bigint | The acting user. NULL for actions taken by the platform itself. |
user_name | text | The acting user’s name at the time of the decision. |
user_role | text | The acting user’s role at the time of the decision. |
before_state | jsonb | Snapshot before the change. |
after_state | jsonb | Snapshot after the change. |
organization_id | bigint | Set only for connector executions. Indexed, but NULL for every other kind of row. |
ip_address | inet | Request IP. |
user_agent | text | Request user agent. |
context | jsonb | Correlation id, plus a per-domain payload — for connector executions the operation key, HTTP method, sanitised URL, status code, duration, error category and policy decision. |
Both action and entity_type are plain text columns with no database
constraint, so a value you have not seen before is possible after an upgrade.
When an audit row is written while handling a request, that request’s
correlation id is stamped into context ->> 'correlationId' automatically. The
id comes from the inbound x-request-id header when the caller supplies a
well-formed one, and is generated otherwise. Rows written outside a request —
platform-initiated actions such as connector runtime credential access — have no
correlation id, and context may be NULL. Join on context ->> 'correlationId'
to reconstruct everything a single request touched.
Creating, updating or deleting an Organization, and mapping roles to it, is
recorded in cypex_log.t_log_api. It does not appear in
t_permission_audit_log at all, so “who created this organization?” and “when
was this role granted?” must be answered here:
| |
| Operation | Stored event value |
|---|---|
| Create organization | organization.create |
| Update organization | organization.update |
| Delete organization | organization.delete |
| Add role to organization | organization.role.add |
| Remove role from organization | organization.role.remove |
The payload column carries the organization id, name and domain. t_log_api
also has an organization_id column, but nothing populates it — filter on
event and payload, not on that column.
| Trigger | Where it shows up |
|---|---|
| Schema access (internal-vs-client), org-scope validation, schema-access errors | Permission audit / Schema Access |
| Access refused on an organization, user or role operation | Permission audit — action = 'ACCESS_DENIED', with the reason in context |
| Connector execution outcomes | Connector Audit |
| Connector secret create/rotate/revoke/read | Credentials |
| Connector CRUD | Connectors admin — archiving a connector is recorded as REVOKE, not DELETE |
| Enablement toggle | Connector Execution |
| Outbound host allowlist changes | Connector Allowlist |
Successful decisions are recorded as well as refusals, so an empty result for a user is meaningful: it means no decision was ever evaluated, not that access was granted silently.
Connector executions are the one domain with a dedicated admin screen. Open Connector Audit under Monitor & Audit in the sidebar — it is reserved for system admins — instead of querying the table by hand.
Some evidence lives outside the database, in product documentation and release artifacts:
| Evidence | Location |
|---|---|
| Organizations terminology reference | CYPEX Glossary |
| Default Organization seeded | The Organizations-bootstrap migration (part of the v2.0.0 upgrade) |
| RLS helper functions | The RLS-helpers migration (part of the v2.0.0 upgrade) |
| Per-table RLS policies | The RLS-enablement migration (part of the v2.0.0 upgrade) |
| v2.0.0 release notes (introduces multi-tenancy) | Release Notes v2.0.0 |
| Mandatory operator upgrade entry point | Upgrade to CYPEX v2.0.0 |
| Post-upgrade assignment procedure | Detailed organization setup |
| Future ADR-style evidence (where to put new decisions) | CYPEX internals |
The three migrations run automatically as part of the upgrade. To confirm they took effect on a given deployment:
| |
TipWhen you make a non-trivial decision about an Organization, leave evidence in at least two of the three layers:
- Run the relevant query on
cypex_log.t_permission_audit_logand, for organization management, oncypex_log.t_log_api, and keep the output with the change record.- Add an ADR-style note under CYPEX internals describing the decision, the alternatives considered, and the rollback plan.
- If the decision changes the migration sequence or the tenancy contract, update the relevant page in this section and the v2.0.0 release notes.
- SSO — Pending and rejected users — this same evidence model applied to SSO approve/reject/un-reject decisions.
- What is an Organization? — the tenancy contract.
- Connector Audit — the admin screen for connector execution evidence.
- Upgrade to CYPEX v2.0.0 — the mandatory operator upgrade entry point, including post-upgrade verification queries.
- CYPEX internals — where new ADR-style evidence should live.
- Release Notes v2.0.0 — the release entry that introduces multi-tenancy.