> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mnemom.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Governance Signals Schema

> Table layout, RPC surface, and webhook event taxonomy for governance_signals.

This page documents the platform schema for [governance signals](/concepts/governance-signals), the operator-actionable observation surface.

## Tables

### `governance_signals`

The single row per open observation. The platform writes via the `governance_signal_emit` RPC; consumers (REST handlers, dispatcher, UI, CLI) read directly.

| Column | Type | Notes | | | | | |
| - | - | - | - | - | - | - | - |
| `id` | `text` PK | `gs-{12-hex}` (mirrors `pa-` convention from `pending_advisories`). | | | | | |
| `scope` | `text` CHECK | \`platform | org | team | agent\`. | | |
| `scope_id` | `text` | platform\_id / org\_id / team\_id (uuid::text) / agent\_id. | | | | | |
| `source` | `text` CHECK | \`sideband.drift | sideband.coherence | sideband.fault\_line | sideband.fleet`. Future `protection.*`/`posture.*\` land additively. | | |
| `pattern_type` | `text` | Free-form per source (e.g., `cluster_partition`, `pairwise_governance_floor`). | | | | | |
| `severity` | `text` CHECK | \`info | warn | high | critical\`. | | |
| `detected_at` | `timestamptz` | Default `now()`; refreshed on coalescing upsert. | | | | | |
| `detected_by` | `text` | Detector name (e.g., `observer.sweepFleet`). | | | | | |
| `org_id` | `text` FK → `orgs.org_id` | Denormalized for RLS perf. | | | | | |
| `team_id` | `uuid` | Nullable for platform/org/agent scope. | | | | | |
| `agent_ids` | `text[]` | Affected agents — informational fan-out. | | | | | |
| `detail` | `jsonb` | Pattern-specific payload. | | | | | |
| `source_ref` | `jsonb` | Detector run / sweep\_id / cadence info. | | | | | |
| `status` | `text` CHECK | \`open | acknowledged | resolved | dismissed | expired\`. | |
| `acknowledged_by` | `uuid` FK → `auth.users.id` | | | | | | |
| `acknowledged_at` | `timestamptz` | | | | | | |
| `acknowledged_actor_role` | `text` CHECK | \`platform\_admin | org\_owner | org\_admin | team\_admin | member | system\` (mirrors the audit actor\_role). |
| `resolution_status` | `text` CHECK | \`action\_taken | wont\_fix | duplicate | false\_positive | self\_resolved\`. | |
| `action_taken` | `text` | Operator-authored note. | | | | | |
| `resolved_by` | `uuid`, `resolved_at` `timestamptz` | | | | | | |
| `expires_at` | `timestamptz` | TTL from posture (default 30d). | | | | | |
| `webhook_delivery_id` | `uuid` | Last dispatch attempt FK (informational). | | | | | |
| `notification_state` | `jsonb` | `{<channel>: {state, attempts, destination_id, delivered_at, last_error}}`. | | | | | |
| `created_at`, `updated_at` | `timestamptz` | `updated_at` maintained by trigger. | | | | | |

#### Indexes

```sql theme={null}
-- Write-time idempotency: repeated cron emissions of the same open
-- signal coalesce. The architectural fix to the duplicate-paragraph
-- symptom that motivated the redesign.
CREATE UNIQUE INDEX governance_signals_open_dedup
  ON governance_signals (scope, scope_id, source, pattern_type)
  WHERE status = 'open';

CREATE INDEX governance_signals_org_status_idx
  ON governance_signals (org_id, status, detected_at DESC);
CREATE INDEX governance_signals_team_status_idx
  ON governance_signals (team_id, status, detected_at DESC)
  WHERE team_id IS NOT NULL;
CREATE INDEX governance_signals_severity_open_idx
  ON governance_signals (severity, detected_at DESC)
  WHERE status = 'open';
CREATE INDEX governance_signals_agent_ids_gin
  ON governance_signals USING GIN (agent_ids);
```

### `governance_notification_destinations`

| Column | Type | Notes | | | |
| - | - | - | - | - | - |
| `id` | `uuid` PK | | | | |
| `org_id` | `text` FK | | | | |
| `channel` | `text` CHECK | \`webhook | slack | email | pagerduty\`. |
| `config` | `jsonb` | Channel-specific. | | | |
| `filter` | `jsonb` | `{sources?, severities?, scopes?, pattern_types?}` AND-folded narrowing. | | | |
| `enabled` | `boolean` | Disable without delete to preserve audit. | | | |
| `display_name` | `text` | UI label. | | | |
| `last_tested_at` | `timestamptz` | Set by `… /test` endpoint. | | | |
| `last_test_status`, `last_test_error` | `text` | | | | |

### `governance_escalation_rules`

| Column | Type | Notes |
| - | - | - |
| `id` | `uuid` PK | |
| `org_id` | `text` FK | |
| `name` | `text` | Operator-authored. |
| `predicate` | `jsonb` | `{source?, pattern_type?, severity_min?, severity_max?, scope?, team_id?, threshold_count?, window_minutes?}` AND-folded. |
| `destination_ids` | `uuid[]` | Non-empty CHECK. |
| `enabled` | `boolean` | |
| `last_fired_at`, `fire_count` | | Telemetry for tuning. |

## RLS

Service-role bypass model: the API boundary applies authorization in the application layer, and direct database access uses a service role that bypasses row-level security. Row-level security is enabled on all three tables with **no user-facing policies** — this is fail-closed against accidental exposure (a future direct-database access path can't leak governance signals across orgs).

Future tightening to user-driven row-level UPDATE policies is a follow-up once a shared cross-type user-identity policy helper is in place.

## RPCs

### `governance_signal_emit`

```sql theme={null}
governance_signal_emit(
  p_scope TEXT,
  p_scope_id TEXT,
  p_source TEXT,
  p_pattern_type TEXT,
  p_severity TEXT,
  p_org_id TEXT,
  p_team_id UUID,
  p_agent_ids TEXT[],
  p_detail JSONB,
  p_source_ref JSONB,
  p_detected_by TEXT,
  p_expires_at TIMESTAMPTZ DEFAULT NULL
) RETURNS governance_signals
```

`SECURITY DEFINER` + service-role only. `INSERT ... ON CONFLICT DO UPDATE` on the open-dedup index — repeated cron emissions of the same condition refresh `detected_at`, `severity`, `agent_ids`, `detail`, `source_ref` on the existing open row instead of creating a new one.

### `governance_signal_acknowledge` / `_resolve` / `_dismiss`

Operator state transitions. Each captures `acknowledged_actor_role` and is `SECURITY DEFINER` so the API can invoke after applying RBAC in TypeScript.

```sql theme={null}
governance_signal_acknowledge(p_id, p_actor, p_actor_role, p_action_taken DEFAULT NULL)
governance_signal_resolve(p_id, p_actor, p_actor_role, p_resolution_status, p_action_taken DEFAULT NULL)
governance_signal_dismiss(p_id, p_actor, p_actor_role, p_reason DEFAULT NULL)
```

## REST endpoints

See [api-reference/governance](/api-reference/governance) for full schemas. Quick map:

| Method | Path | RBAC |
| - | - | - |
| GET | `/v1/orgs/:org_id/governance/signals` | any org member |
| GET | `/v1/teams/:team_id/governance/signals` | any org member |
| GET | `/v1/agents/:agent_id/governance/signals` | any org member |
| GET | `/v1/governance/signals/:id` | any org member of row's org |
| POST | `/v1/governance/signals/:id/{acknowledge,resolve,dismiss}` | org\_admin / org\_owner |
| GET | `/v1/orgs/:org_id/governance/coverage` | any org member |
| `*` | `/v1/orgs/:org_id/governance/notification-destinations[/:id][/test]` | org\_admin / org\_owner |
| `*` | `/v1/orgs/:org_id/governance/escalation-rules[/:id]` | org\_admin / org\_owner |

## Notification dispatch

Governance signals are **not** delivered through the account-wide webhook subscription surface (`/v1/orgs/:org_id/webhooks`, [Webhooks](/guides/webhooks)). They dispatch only to the channels an org explicitly configures under `governance_notification_destinations` (`webhook`, `slack`, `email`, or `pagerduty` — see [REST endpoints](#rest-endpoints) above).

`governance.signal.fired` is the only event this system dispatches today, sent whenever a new signal is inserted or an existing open signal is coalesced (`detected_at`/`severity`/`agent_ids`/`detail` refreshed on the open row). Acknowledging, resolving, or dismissing a signal changes its row but does not currently dispatch any notification. An escalation rule only decides **which configured destinations** a given signal's `governance.signal.fired` reaches — matching a rule does not produce a separate event type.

The `webhook` channel signs its POST body HMAC-SHA256 (`X-Mnemom-Signature: sha256=<hex>`, `X-Mnemom-Event: governance.signal.fired`, `X-Mnemom-Delivery-Id: <uuid>`) — subscribers should verify the signature before trusting the payload. This is a distinct signing scheme from the account-wide webhook surface's `X-Webhook-Signature: v1={hex}`; do not conflate the two.

## Naming convention discipline

`source` is closed, hierarchical, append-only — mirrors the `pending_advisories.source` taxonomy. Adding a new value requires:

1. A schema amendment.
2. Migration extending the CHECK constraint with ASSERT-after-DDL guard.
3. Producer code (typically observer).
4. Consumer-tolerance discipline (gateway / UI / CLI / SDKs).
5. Posture-gating extension if the source is detector-driven.

Removing a source is forbidden. Deprecation is the only valid path.

## Related

* [Governance signals concept](/concepts/governance-signals).
* [Pending Advisories Schema](/specifications/pending-advisories-schema) — narrowed to `runtime.*` + `manual.*`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.