> 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/products/registry/registry/developer-zone/building-a-registry/anatomy-of-an-extension.md).

# Anatomy of an extension

Every folder in a registry extension repository and what belongs in it — the reference to keep open while building.

A registry is one repository. This page is the map: what each folder is for, and what you must put in it. It is a **reference**, not a sequence — the order to do things in is [Phase 1](/products/registry/registry/developer-zone/building-a-registry/build-your-registry.md).

Everything here is real: the paths match the [Farmer Registry](https://github.com/OpenG2P/farmer-registry) and the [National Social Registry](https://github.com/OpenG2P/national-social-registry) as they are built today.

## The repository

```
<domain>-registry/
├── <domain>-extension/     ← the Python package: your domain (see below)
├── docker/                 ← thin Dockerfiles + the content your images carry
├── helm/openg2p-<domain>/  ← your Helm chart (wraps the platform chart)
├── test/                   ← your field-specific sanity tests + the pin guard
├── scripts/                ← bump-rp-version.sh, uninstall-registry.sh
└── .github/workflows/          ← ~40 lines; all logic lives in openg2p/packaging
```

| Folder                       | You own                                                                                                           | Notes                                                                                                                                                            |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<domain>-extension/`        | **All of it**                                                                                                     | The only place your domain logic lives                                                                                                                           |
| `docker/`                    | The `FROM` line and what you copy in                                                                              | Each Dockerfile is \~10 lines. See [Phase 1 § images](/products/registry/registry/developer-zone/building-a-registry/build-your-registry.md#4-build-thin-images) |
| `helm/openg2p-<domain>/`     | A values overlay, plus any templates for things the platform has no concept of (analytics jobs, dashboards, maps) | Everything else comes from the pinned platform subchart                                                                                                          |
| `test/`                      | Your **Set 2** field tests only                                                                                   | The harness and generic tests are inherited. See [Testing & the sanity suite](/products/registry/registry/deployment-and-extension/testing-and-sanity-suite.md)  |
| `scripts/bump-rp-version.sh` | Nothing — copy it                                                                                                 | Moves the platform pin in the Dockerfiles and chart together                                                                                                     |

## The extension package

```
<domain>-extension/
├── pyproject.toml
├── README.md                  ← REQUIRED — pyproject declares readme = "README.md"
├── LICENSE
└── src/openg2p_registry_<domain>_extension/
    ├── __init__.py            ← version string + __variant__
    ├── app.py                 ← wires the extension into the platform app
    ├── config.py              ← extension settings
    ├── register_domain/       ← the domain model  (REQUIRED)
    ├── meta_data/             ← seed SQL: registers, UI  (REQUIRED)
    ├── awe_meta_data/         ← approval-workflow seed SQL
    ├── templates/             ← DCI / message Jinja templates
    ├── score_compute/         ← optional: computed scores
    └── ingestion_pipeline/    ← optional: inbound enrichers
```

{% hint style="danger" %}
**`README.md` is not optional.** The `pyproject.toml` you copy declares `readme = "README.md"`, and Hatchling validates it while generating metadata — so a missing file fails the **Docker build**, not at runtime, with:

```
OSError: Readme file does not exist: README.md
error: metadata-generation-failed
```

{% endhint %}

{% hint style="warning" %}
The package installs under **its own import name** and is selected at runtime by `REGISTRY_EXTENSION_MODULE`. Do **not** alias it onto `openg2p_registry_extensions` in `pyproject.toml` — that was the previous mechanism and it prevents your extension from coexisting with the platform's reference extension in one image.

The mirror of that rule: the **factories must still import the alias** (`openg2p_registry_extensions.register_domain.services`), because the entrypoint installs it into `sys.modules` before any platform import runs. See [Extensions Contract](/products/registry/registry/developer-zone/building-a-registry/concepts/registry-extensions/extensions-contract.md#the-module-alias-two-halves-that-look-contradictory).
{% endhint %}

### `register_domain/` — the domain model

The heart of the extension. One set of files per register.

| Subfolder       | Contains                                                                                                                                                                              | Farmer Registry has |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| `models/`       | SQLAlchemy ORM models — one file per register, plus `enums.py`. Each declares three tables: the register, its `history`, and its `intake_form`                                        | 9 files             |
| `schemas/`      | Pydantic mirrors of the models, used by the APIs                                                                                                                                      | 9 files             |
| `services/`     | `G2PRegisterDomainService{Mnemonic}` — the per-register behaviour the platform calls into. `services/utils/` holds anything shared between them (validation helpers, recompute logic) | 10 files            |
| `factory/`      | `g2p_register_domain_factory.py` and `g2p_id_generator_factory.py` — resolve mnemonic → your classes. **Copy unchanged**; the only thing you adjust is which services exist           | 2 files             |
| `id_generator/` | Functional-ID generation, if your registers need it                                                                                                                                   | 1 file              |

{% hint style="info" %}
**Declare each register's columns once.** Put them on a plain mixin class and compose the register, history and intake-form tables from it. A column added to only the live table makes approval fail when it copies the record into history.
{% endhint %}

The class names are a contract the platform resolves by **register mnemonic** — see [Extensions contract](/products/registry/registry/developer-zone/building-a-registry/concepts/registry-extensions/extensions-contract.md) for the exact names and methods, and [Base models](/products/registry/registry/developer-zone/building-a-registry/concepts/base-models.md) for what you inherit.

### `meta_data/` — the seed SQL

Applied to the registry database at install, in sorted path order. This is what turns your ORM tables into a working registry: registers and screens.

| Subfolder                               | What it defines                                                                                                                                                |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `register-metadata/`                    | The core: `g2p_register_definitions`, `_schemas`, `_sections`, UI tabs and tab-sections, intake-form equivalents, score definitions, documents. **Start here** |
| `registry-configurations/`              | Registry-wide settings: languages, themes, input mechanisms, VC configuration                                                                                  |
| `data-models/`                          | `data_models.sql` — the model definitions used by ingestion/outgestion                                                                                         |
| `registry-inbound-message-rules/`       | Incoming templates, key paths, semantic patterns                                                                                                               |
| `registry-outbound-messages-templates/` | Outgoing templates                                                                                                                                             |
| `awe-integration/`                      | Binds registers to approval policies                                                                                                                           |

Each metadata table is documented under [Concepts → Register Metadata](/products/registry/registry/developer-zone/building-a-registry/concepts/registry-and-register-metadata.md).

{% hint style="info" %}
**There is no code-list folder.** Code lists are country data: they live in the country pack, Master Data loads them, and your dropdowns read them from Master Data live. Your extension only names which list each coded field uses — its `attribute_id` in the section metadata. See [Country data & seeding → Code lists](/products/registry/registry/deployment-and-extension/country-data-and-seeding.md#code-lists).
{% endhint %}

### `awe_meta_data/` — approval workflow

Numbered so they apply in dependency order, and written to the **AWE** database rather than the registry's:

```
10_approval_policy.sql      the policy
20_approval_stage.sql       its stages
30_approver_rule.sql        who approves each stage
40_callback_secret.sql.tpl  per-release HMAC secret (templated at seed time)
```

Background: [AWE integration](/products/registry/registry/design/awe-integration.md).

### `templates/` — message rendering

Flat Jinja files, uploaded to MinIO by db-seed. Typically one per direction:

```
<domain>_to_dci.json.j2      outbound: your record → DCI
dci_to_<domain>.json.j2      inbound: DCI → your record
dci_commons_response.json.j2 shared response envelope
```

The records reach the outbound template already filtered to the partner's consented [data scopes](/products/registry/registry/design/data-scopes.md) (fields outside them are null; child records outside them are an empty list), so write the template to tolerate missing values. Scopes are defined by the catalogue in `meta_data/data-scopes/`, not by the template's keys. See [Partner APIs](/products/registry/registry/design/partner-apis.md).

Two key contracts you cannot infer from the templates themselves:

* **Outbound** — child registers arrive under the *snake\_case of the register mnemonic* (`AssistiveTechnology` → `assistive_technology`).
* **Inbound** — the keys you emit are registry field names for the **master register only**. Everything else is keyed by the **`section_mnemonic`** of the section that owns those fields. A key matching no section mnemonic is dropped silently.

Both are spelled out in [Contracts that fail silently](/products/registry/registry/developer-zone/building-a-registry/contracts-that-fail-silently.md).

### `score_compute/` and `ingestion_pipeline/` — optional

* `score_compute/services/` — implementations of `G2PScoreComputeInterface`, bound to a register by seed metadata rather than code. See [Score computation framework](/products/registry/registry/design/score-computation-framework.md).
* `ingestion_pipeline/enricher_services/` — enrichers that fill derived fields on inbound records. See [Ingestion pipeline](/products/registry/registry/design/ingestion-pipeline.md).

Omit either folder entirely if you do not need it.

## What is *not* in the extension

A frequent source of confusion — these live in the repo but outside the package:

| Thing                                                     | Where it actually lives                                                                                                                                                                                                               |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Sample data (demo records)                                | `docker/db-seed/seed-data/*.json`                                                                                                                                                                                                     |
| Sample-data loader, image uploader                        | `docker/db-seed/*.py` — **always yours.** The platform's are written against the reference registry's tables and will crash-loop against yours; the entrypoint hard-fails if `LOAD_SAMPLE_DATA=true` and no variant loader is present |
| Reporting views, `reporting.yaml`                         | `docker/db-seed/`                                                                                                                                                                                                                     |
| Dashboards (the **builder**), maps                        | `docker/dashboards/`, `helm/openg2p-<domain>/files/`                                                                                                                                                                                  |
| Analytics jobs, maps ConfigMap, reporting-refresh CronJob | `helm/openg2p-<domain>/templates/` — the umbrella chart owns no *service* templates, but it does own the analytics layer                                                                                                              |
| Sanity field tests                                        | `test/sanity/`                                                                                                                                                                                                                        |
| Repository guards (metadata consistency, pin lockstep)    | `test/` — see [Contracts that fail silently](/products/registry/registry/developer-zone/building-a-registry/contracts-that-fail-silently.md)                                                                                          |
| Domain translations                                       | `translation/domain.json` — **advisory copy only.** Nothing reads it; the values reach the database via `registry_languages.domain_translation` in `meta_data/registry-configurations/g2p_registry_languages.sql`                     |


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openg2p.org/products/registry/registry/developer-zone/building-a-registry/anatomy-of-an-extension.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
