> For the complete documentation index, see [llms.txt](https://docs.openg2p.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openg2p.org/platform/platform-services/consent-management/design/registry-integration.md).

# Registry integration (the PEP side)

This page describes how an OpenG2P **Registry** (e.g. the Farmer Registry) integrates with the Consent Manager (CM) to govern outbound data sharing. The registry is the **Policy Enforcement Point (PEP)**; CM is the **Policy Decision Point (PDP)**. The registry never interprets consent — it forwards the partner-signed consent object to CM, and enforces the decision CM returns.

The concrete integration lives in the **registry partner-api** (the DCI search API, `POST /dci/registry/sync/search`).

## Two independent signatures, one key source

A partner call carries **two** signatures, both verified against the partner's public keys **served by Partner Management (PM)** — a single trust root:

| Signature                    | Covers                                       | Verified by             | Purpose                                                                |
| ---------------------------- | -------------------------------------------- | ----------------------- | ---------------------------------------------------------------------- |
| **DCI envelope signature**   | the whole `{header, message}` (detached JWS) | the **registry**        | transport auth — "this call is fresh and from partner X"               |
| **Consent object signature** | the CM consent object (a compact JWS)        | the **Consent Manager** | authorisation — "partner X holds valid consent for subject S, scope Z" |

Both are **JWS** verified against PM keys via the shared `CryptoHelper.verify_jwt` — one signature format, one verify path across the platform.

The registry verifies the envelope with openg2p-fastapi-common's `build_crypto_helper` using the **`partner-mgmt`** backend (partner keys fetched from PM). The legacy Mosip **Keymanager** remains a selectable backend (`crypto_backend: keymanager`) but is not the default — we are not encrypting payloads yet. Partner keys are looked up under the platform-standard reference **`PARTNER_<sender_id>`** (upper-cased, `-`→`_`), the same convention used by Partner Management, g2p-bridge, and openg2p-fastapi-partner-auth.

## Where the consent object is embedded

The DCI search criteria already reserve an `authorize` block (the DCI-standard slot for the authorisation artefact). The partner embeds the **CM consent object as a compact JWS string** at:

```
message.search_request[i].search_criteria.authorize.consent_jws
```

```jsonc
{
  "signature": "<detached JWS over {header, message}>",   // DCI envelope signature
  "header":  { "sender_id": "pilot-bank", ... },
  "message": {
    "transaction_id": "...",
    "search_request": [
      {
        "reference_id": "req-1",
        "search_criteria": {
          "reg_type": "Farmer",
          "reg_record_type": "spdci-extensions-dci:Farmer",
          "query_type": "predicate", "query": { ... },
          "authorize": {
            "@context": "...", "@type": "...",
            // the CM consent object, a self-contained compact JWS.
            // payload claims: jti, subject_id, aud, data_controller,
            //   purpose, data_scopes, validity, issued_at
            // protected header: alg + kid
            "consent_jws": "eyJhbGciOiJFZERTQS{...}.eyJqdGkiOiJ7...}.{signature}"
          }
        }
      }
    ]
  }
}
```

The registry forwards `consent_jws` **verbatim** to CM `/validate` (as `{ "consent_jws": "...", "partner_id": "<sender_id>" }`). The JWS is self-contained — its signed bytes travel in the payload segment — so no reshaping or byte-preservation care is needed. CM recovers the claims from the payload and keys the partner off the `aud` claim; `partner_id` is sent for traceability only.

## Field-level enforcement (the clamp)

CM `/validate` returns a decision with `effective_data_scopes` = consent scope ∩ partner policy. The registry **clamps every returned record to those scopes** — a strict allow-list over the rendered record's top-level fields. A narrower consent or policy can only ever *remove* fields, never add them.

Scope names are the registry's **outgoing-template output field names**. Deployers must keep a shared **scope ↔ field catalog** so a policy's `data_scopes` line up with what the registry can return (e.g. `first_name`, `birth_date`, and farmer-extension fields like `crop`, `livestock`). Fields the partner may *filter* on are separately bounded by `dci_expression_allowed_fields`.

## Data-scope catalog — design (NOT yet implemented) — handover note

> **Status:** design agreed, implementation deferred. This section is the handover note for whoever implements the scope catalog later. Nothing here is built yet; today `data_scopes` are opaque strings and the clamp is structural only.

**The decision: the catalog is owned by the data source (the registry), NOT the Consent Manager.** CM is a generic PDP — it only does set math on opaque scope strings (`consent.data_scopes ⊆ policy.allowed_data_scopes`; `effective = consent ∩ policy`). It never needs to know what a scope maps to. So a registry adding/renaming a field must **never** require a CM code change or release. Baking a per-registry field taxonomy into CM would be wrong — it's data, and it belongs to the source that owns the schema and changes it.

**Ownership split:**

| Concern                                  | Owner                         | Form                                                                                     |
| ---------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------- |
| Field taxonomy / scope vocabulary        | **Registry (PEP)**            | data/config, changes with the registry schema — no CM release                            |
| Scope → field mapping (for the clamp)    | **Registry (PEP)**            | data/config (a config table, or derived from the DCI outgoing template already in MinIO) |
| Publishing the scope catalog (discovery) | **Registry (PEP)**            | a discovery endpoint / documented list                                                   |
| Set-math authorization (`⊆`, `∩`)        | **Consent Manager**           | opaque strings — unchanged, no catalog                                                   |
| Knowing which scopes to request/grant    | **Partner + CM policy admin** | read the registry's published catalog                                                    |

**The only shared contract is the scope-name vocabulary** — CM policies and the registry's mapping must use the same strings. That is a naming convention, not a code dependency. The registry **publishes** its catalog so partners and policy admins know the vocabulary; CM's admin UI *may* fetch it dynamically to populate a scope picker, but must never hardcode it.

**Recommended model — scopes ARE the registry's published field/bundle names.** Then the "mapping" is identity and the clamp already written (`record.keys() ⊆ scopes`) needs no mapping table. Adding a farmer field = a registry data/config change, zero CM impact.

**To implement (registry side, later):**

* Define the scope → field mapping as **config/data** in the registry (registry-platform / farmer-extension) — or make scope = field name (identity).
* Add a small **discovery endpoint** publishing the scope catalog (alongside the DCI capabilities).
* Extend `dci_expression_allowed_fields` with the farmer-extension fields.
* The existing `_clamp_record_fields` stays; it reads the mapping instead of guessing.
* **CM: no structural change** (optionally, the admin UI fetches the registry catalog).
* Document the scope naming convention in GitBook as the shared contract.

**Open decision (registry-side, pick when implementing):** scope **granularity** — field-level (`first_name`, `crops`; scope = field name, no mapping table) vs coarse **bundles** (`farmer_profile.basic`; needs a bundle→fields mapping, still registry-owned). Lean: **field-level to start**, add bundles later if raw field lists prove tedious for partners.

## Two kill-switches (testing)

Two **independent** flags gate the two checks. Both default **on** in code (safe PII posture); the Farmer Registry chart ships them **off** so a fresh install works before CM/PM are wired. Turn both **on** for production.

| Config (env)                   | Off behaviour                                               |
| ------------------------------ | ----------------------------------------------------------- |
| `signature_validation_enabled` | skip DCI envelope verification — accept any/unsigned caller |
| `consent_enforcement_enabled`  | skip CM `/validate` — return **all** fields (no clamp)      |

When a switch is off the bypass is logged (`WARNING`) and **stamped into the response header meta** (`signature_validation` / `consent_enforcement` = `enabled`/`disabled`), and the response `signature` carries a `signature_validation_disabled` marker — so a bypassed response is never mistaken for an authorised one. Enforcement is otherwise **fail-closed**: a missing consent object, a non-permit decision, or an unreachable CM rejects the request.

## Configuration (registry partner-api)

| Env var                                             | Meaning                                           |
| --------------------------------------------------- | ------------------------------------------------- |
| `REGISTRY_PARTNER_API_CRYPTO_BACKEND`               | `partner-mgmt` (default) / `keymanager` / `local` |
| `REGISTRY_PARTNER_API_PARTNER_MGMT_API_URL`         | PM partner-api (source of partner keys)           |
| `REGISTRY_PARTNER_API_CONSENT_MANAGER_URL`          | CM partner-api base URL (the `/validate` PDP)     |
| `REGISTRY_PARTNER_API_SIGNATURE_VALIDATION_ENABLED` | gate the envelope signature check                 |
| `REGISTRY_PARTNER_API_CONSENT_ENFORCEMENT_ENABLED`  | gate consent enforcement + field clamp            |

In the Farmer Registry Helm chart these map to `global.registryCryptoBackend`, `global.partnerManagementApiUrl`, `global.consentManagerUrl`, `global.partnerSignatureValidationEnabled`, and `global.consentEnforcementEnabled`.

## Request flow

1. Partner signs the consent object as a compact JWS (its key, PM-registered) and embeds it at `search_criteria.authorize.consent_jws`.
2. Partner signs the whole DCI envelope (same key) and calls the registry partner-api.
3. Registry verifies the envelope signature via PM keys (if `signature_validation_enabled`).
4. Registry POSTs each item's `consent_object` to CM `/validate` (if `consent_enforcement_enabled`); CM verifies, evaluates policy, returns `effective_data_scopes`.
5. Registry fetches records and **clamps** each to the effective scopes.
6. Registry returns the DCI response, signed and stamped with the enforcement posture.

> **Note — farmer consent is never a government approval.** The AWE approval workflow gates only partner onboarding and policy widening (see [Approval Workflow integration](/platform/platform-services/consent-management/design/approval-workflow-integration.md)); it is never in the path of a beneficiary's data-share consent or of `/validate`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.openg2p.org/platform/platform-services/consent-management/design/registry-integration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
