Govern Specv0.1

Connector Author Guide

A Govern connector bridges an external system — a practice management system, document store, or billing platform — to the Govern policy engine. Agents call Govern’s MCP tools; Govern routes approved writes to your connector and records every decision to a tamper-evident ledger.

What a connector does

  1. Receives a governed write request (e.g. “create a billing entry for matter X”)
  2. Translates the canonical slot payload into the target system’s API shape
  3. Executes the write against the system of record
  4. Returns a structured result that Govern records to the tamper-evident ledger

Govern handles the policy gate, the capability envelope check, the ledger append, and the audit-pack generation. Your connector’s responsibility is the translation layer and the write itself.

The port contract

Your connector implements the Connector<TClient> port from @govern/core:

import type { Connector } from '@govern/core';

export const myConnector: Connector<MyClient> = {
  // ConnectorDescriptor fields (flat — no nested 'descriptor' object).
  id: 'my-system',
  displayName: 'My System',
  version: '1.0.0',
  capabilities: [
    'read.matters',
    'read.contacts',
    'write.expense',
    'write.note',
    'edit.matter',
    // ... declare every operation your system supports
  ],
  buildClient: async ({ env, firmId, log }) => {
    // Return your initialized client.
    // env is the Cloudflare Workers env bindings (cast to your env shape).
    // firmId is the Govern firm identifier for the current request.
    return new MyClient({ apiKey: (env as MyEnv).MY_API_KEY });
  },
};

The ConnectorDescriptor fields (all top-level on the connector object):

FieldTypeDescription
idstringStable, lowercase identifier. Used as a key in config tables, KV prefixes, and admin URLs. Never rename — migration cost.
displayNamestringHuman-readable system name (displayed in the admin UI).
versionstringConnector semver. Bump on breaking changes to the canonical projection contract.
capabilitiesConnectorCapability[]Declared operational capabilities (see below). The policy engine uses this to short-circuit tool dispatch.

Operational capabilities

The capabilities field is a closed set of operational surface declarations. The policy engine uses these to short-circuit dispatch — it rejects a write tool routed to a connector that hasn’t declared the matching operation.

CapabilityDescription
read.mattersRead matter records
read.contactsRead contact records
read.usersRead user / timekeeper records
read.documentsRead document metadata
read.time_entriesRead time-entry records
read.billing_codesRead billing-code catalog
read.billing_entryRead billing entries
read.attorney_rateRead attorney billing rates
write.time_entryCreate time entries
write.expenseCreate expense / billing entries
write.noteCreate notes
write.activityCreate activity records
write.documentCreate documents
write.billing_codeCreate billing codes
edit.matterMutate matter fields
edit.contactMutate contact fields
edit.billing_entryMutate billing entries
edit.pncMutate prospective-new-client records
diag.probe_item_fieldsWide-field probe for admin diagnostics (ADR-019 mechanism 3)

Slot policy tiers

Separate from connector operational capabilities, every canonical slot carries a policy tier annotation that describes the level of Govern policy gate applied when an agent reads or writes that slot. These tiers appear in the Slot vocabulary table.

TierDescription
observation-onlyRead access only. No writes permitted. Used for evidence-gathering, ledger projection, and matter-context resolution.
mediated-writeWrites gated by Govern’s policy engine. The standard tier for entity mutations (matter status, contact metadata, PNC intake).
mediated-write-financialMediated-write plus explicit per-firm grant required. Applies to financial-truth slots (billing-entry money fields, billing codes) that affect customer invoices and the firm’s books.

Canonical slots

Govern passes writes as canonical slot payloads — a standardized vocabulary that abstracts over target-system field IDs. Your connector maps slots to your system’s fields.

The three-tier slot namespace:

See the Slot vocabulary for the full core.* catalog (87 slots across matter, contact, PNC, and billing-entry entities).

Runtime slot discovery

The govern.describe_slots MCP method returns the active slot catalog at runtime, including which slots your connector has bound to your system’s field IDs.

Not yet implemented. govern.describe_slots is specified in the v1 Connector spec but the MCP endpoint is not yet live. It ships in Workstream A.2 when the second connector integration opens. Connectors can rely on the static slot catalog at this URL in the interim.

Mapping slots to your system

Your connector declares its slot bindings in a catalog-defaults map. For each slot you support, provide the field reference in your system’s schema:

// packages/connectors/my-system/src/canonical-slots.ts
export const CATALOG_DEFAULTS: Partial<Record<CanonicalSlot, string>> = {
  'matter.name':              'title',        // your system's field name
  'matter.responsible_attorney': 'user_id',
  'matter.status':            'status',
  // Declare null for slots your system does not support.
  'matter.trust_balance':     null,
};

Slots declared as null produce a structured slot_unavailable error from the policy engine when a tool tries to read or write them — not a runtime adapter crash.

Per-firm overrides: firm admins can remap any slot to a different field ID through the /admin/fields/slots UI. Your catalog default is the floor; the per-firm override wins when set.

Intent declaration (proactive tier)

Agents that opt into the proactive tier call govern.declare_intent before executing. This gives Govern a chance to issue a capability envelope — pre-authorizing a batch of writes within stated bounds — before the agent commits to action.

The envelope carries:

Your connector receives approved writes pre-authorized by a capability envelope. The envelope ID arrives on every write tool call; Govern enforces the bounds before forwarding to your connector. Your connector does not need to evaluate bounds — that’s Govern’s job.

// Example: agent calls govern.declare_intent
const result = await mcpClient.call('govern.declare_intent', {
  scope: {
    tool_id: 'anthropic.claude-opus-4',       // the AI tool making the call
    mcp_method: 'govern.create_govern_expense', // MCP method it plans to call
    matter_id: 'matter_abc',                   // optional: specific matter
    description: 'Create AI-spend expense entry',
  },
  forecast_cost_cents: 450,  // integer cents (not decimal USD)
  agent_id: 'agent-007',     // optional: sub-agent identifier
  task_id: 'task-xyz',       // optional: orchestrator task ID for correlation
});

// Govern returns:
// {
//   intent_id: string,
//   status: 'approved' | 'denied' | 'conditional',
//   concerns: string[],       // non-empty when status === 'conditional'
//   denial_reason: string | null,
//   envelope: { envelope_id, bounds, expires_at, issued_by_rule_id } | null,
// }

// Agent then passes envelope_id on the write call:
if (result.status !== 'denied' && result.envelope) {
  await mcpClient.call('govern.create_govern_expense', {
    matter_id: 'matter_abc',
    envelope_id: result.envelope.envelope_id,
    // ...
  });
}

Conditional intents (status conditional) have concerns that go to the reviewer queue at /review for human ratification before the write can proceed. Approved intents were cleared by a matching standing rule and the agent can proceed immediately. Denied intents were blocked by policy — the agent must not proceed with the write.

Identity contract

Your connector receives an IdentityClaims object on every authenticated request. It carries the IDP-native subject identifier (attorney_oid), the Govern firm scope (firm_id), and optional role claims.

See the Identity contract for the full JWT shape, required vs. optional claims, and how to plug in a non-Entra IDP.

Audit trail

Every write Govern routes produces a ledger row linked to the capability envelope and the provenance chain. Your connector does not need to manage audit logging — Govern handles it.

The ledger row captures:

Audit packs are exportable as signed zip files. See the Audit-pack signing spec for the protocol.

Conformance

Run the conformance suite against your implementation before declaring it spec-compliant:

npx @govern/conformance verify <your-audit-pack.zip>

All 8 golden test cases must pass. See the Conformance suite page for the full case catalog and expected outputs.

Checklist for a new connector

  1. Implement Connector<TClient> from @govern/core with a stable descriptor.id in the connector.<id> namespace.
  2. Declare your capability tier(s) in descriptor.capabilities.
  3. Publish your CATALOG_DEFAULTS — map every slot you support to a field reference in your system; declare null for unsupported slots.
  4. Implement buildClient — initialize and return your API client from Cloudflare Workers env bindings. Avoid constructor side-effects; the function is called once per firm per Worker lifetime.
  5. Wire the identity contract — verify incoming tokens and map IDP claims to IdentityClaims. The Entra adapter in packages/adapters/src/entra-auth.ts is the reference.
  6. Export a signed audit pack and run npx @govern/conformance verify against it. All 8 cases must pass.
  7. Open a PR against the spec repo adding your connector to the connector registry in docs/connectors/. The PR is where the spec WG reviews your slot bindings for correctness.