For the complete documentation index, see llms.txt. This page is also available as Markdown.

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.

Everything here is real: the paths match the Farmer Registry and the 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

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

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, code lists  (REQUIRED)
    ├── awe_meta_data/         ← approval-workflow seed SQL
    ├── templates/             ← DCI / message Jinja templates
    ├── score_compute/         ← optional: computed scores
    └── ingestion_pipeline/    ← optional: inbound enrichers

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

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.

The class names are a contract the platform resolves by register mnemonic — see Extensions contract for the exact names and methods, and Base models 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, screens, code lists.

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

lookup-data/

Code lists — g2p_attributes and g2p_attribute_values (plus their _defaults variants)

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.

Code lists are not the same as country data. The _defaults files are your registry's fallback; a deployment's country pack can override them from Master Data. See Country data & seeding.

awe_meta_data/ — approval workflow

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

Background: AWE integration.

templates/ — message rendering

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

The top-level keys your outbound template emits are the consent scopes a partner can be granted — get them right or clamping silently returns {}. See Partner APIs.

Two key contracts you cannot infer from the templates themselves:

  • Outbound — child registers arrive under the snake_case of the register mnemonic (AssistiveTechnologyassistive_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.

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.

  • ingestion_pipeline/enricher_services/ — enrichers that fill derived fields on inbound records. See Ingestion pipeline.

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/*.pyalways 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)

Domain translations

translation/domain.jsonadvisory copy only. Nothing reads it; the values reach the database via registry_languages.domain_translation in meta_data/registry-configurations/g2p_registry_languages.sql

Last updated

Was this helpful?