Documentation preview. These pages target Registryctl v0.15.0, which is not published yet. Public download commands will not work until that release exists. For runnable released instructions, use v0.13.0.
The following canonical paths are derived from the complete deserialization schema.
Named properties use dot notation, [] denotes array items, and .* denotes map values.
This inventory is checked in both directions so neither the schema nor this reference can gain a key
without the other.
auditaudit.chainaudit.formataudit.hash_secret_envaudit.include_healthaudit.pathaudit.rotateaudit.rotate.max_filesaudit.rotate.max_size_mbaudit.sinkaudit.write_policyauthauth.api_keysauth.api_keys[]auth.api_keys[].fingerprintauth.api_keys[].fingerprint.nameauth.api_keys[].fingerprint.pathauth.api_keys[].fingerprint.providerauth.api_keys[].idauth.api_keys[].scopesauth.api_keys[].scopes[]auth.failure_throttleauth.failure_throttle.enabledauth.failure_throttle.max_failuresauth.failure_throttle.window_secondsauth.modeauth.oidcauth.oidc.allow_dev_insecure_fetch_urlsauth.oidc.allowed_algorithmsauth.oidc.allowed_algorithms[]auth.oidc.allowed_clientsauth.oidc.allowed_clients[]auth.oidc.allowed_token_typesauth.oidc.allowed_token_types[]auth.oidc.audiencesauth.oidc.audiences[]auth.oidc.discovery_urlauth.oidc.issuerauth.oidc.jwks_cache_ttlauth.oidc.jwks_urlauth.oidc.leewayauth.oidc.scope_claimauth.oidc.scope_mapauth.oidc.scope_map.*auth.oidc.scope_object_required_keysauth.oidc.scope_object_required_keys[]catalogcatalog.authority_typecatalog.base_urlcatalog.default_spatial_coveragecatalog.participant_idcatalog.publishercatalog.publisher_iricatalog.titleconfig_trustconfig_trust.antirollback_state_pathconfig_trust.break_glass_override_pathconfig_trust.bundle_pathconfig_trust.trust_anchor_pathconsultationconsultation.artifactsconsultation.artifacts.evidenceconsultation.artifacts.evidence[]consultation.artifacts.evidence[].classconsultation.artifacts.evidence[].pathconsultation.artifacts.evidence[].sha256consultation.artifacts.integration_packsconsultation.artifacts.integration_packs[]consultation.artifacts.integration_packs[].hashconsultation.artifacts.integration_packs[].pathconsultation.artifacts.integration_packs[].sha256consultation.artifacts.private_bindingsconsultation.artifacts.private_bindings[]consultation.artifacts.private_bindings[].hashconsultation.artifacts.private_bindings[].pathconsultation.artifacts.private_bindings[].sha256consultation.artifacts.public_contractsconsultation.artifacts.public_contracts[]consultation.artifacts.public_contracts[].hashconsultation.artifacts.public_contracts[].pathconsultation.artifacts.public_contracts[].sha256consultation.artifacts.rhai_scriptsconsultation.artifacts.rhai_scripts[]consultation.artifacts.rhai_scripts[].pathconsultation.artifacts.rhai_scripts[].sha256consultation.audit_pseudonym_materialsconsultation.audit_pseudonym_materials[]consultation.audit_pseudonym_materials[].key_idconsultation.audit_pseudonym_materials[].sourceconsultation.audit_pseudonym_materials[].source.nameconsultation.audit_pseudonym_materials[].source.providerconsultation.authorized_workloadconsultation.authorized_workload.audienceconsultation.authorized_workload.client_claim_selectorconsultation.authorized_workload.client_valueconsultation.authorized_workload.principal_idconsultation.source_credentialsconsultation.source_credentials[]consultation.source_credentials[].client_id_envconsultation.source_credentials[].client_secret_envconsultation.source_credentials[].generationconsultation.source_credentials[].password_envconsultation.source_credentials[].refconsultation.source_credentials[].token_envconsultation.source_credentials[].typeconsultation.source_credentials[].username_envconsultation.source_credentials[].value_envconsultation.state_planeconsultation.state_plane.audit_pseudonym_keyring_lock_keyconsultation.state_plane.chain_key_epoch_idconsultation.state_plane.database_url_envconsultation.state_plane.root_certificate_pathconsultation.state_plane.serving_fence_lock_keydatasetsdatasets[]datasets[].access_rightsdatasets[].aggregatesdatasets[].aggregates[]datasets[].aggregates[].accessdatasets[].aggregates[].access.aggregate_only_executiondatasets[].aggregates[].access.aggregate_scopedatasets[].aggregates[].access.metadata_scopedatasets[].aggregates[].allowed_filtersdatasets[].aggregates[].allowed_filters[]datasets[].aggregates[].allowed_filters[].fielddatasets[].aggregates[].allowed_filters[].opsdatasets[].aggregates[].allowed_filters[].ops[]datasets[].aggregates[].default_group_bydatasets[].aggregates[].default_group_by[]datasets[].aggregates[].descriptiondatasets[].aggregates[].dimensionsdatasets[].aggregates[].dimensions[]datasets[].aggregates[].dimensions[].codelistdatasets[].aggregates[].dimensions[].fielddatasets[].aggregates[].dimensions[].iddatasets[].aggregates[].dimensions[].labeldatasets[].aggregates[].disclosure_controldatasets[].aggregates[].disclosure_control.methoddatasets[].aggregates[].disclosure_control.method[]datasets[].aggregates[].disclosure_control.min_cell_sizedatasets[].aggregates[].disclosure_control.min_group_sizedatasets[].aggregates[].disclosure_control.report_suppressed_rowsdatasets[].aggregates[].disclosure_control.suppressiondatasets[].aggregates[].group_bydatasets[].aggregates[].group_by[]datasets[].aggregates[].iddatasets[].aggregates[].indicatorsdatasets[].aggregates[].indicators[]datasets[].aggregates[].indicators[].columndatasets[].aggregates[].indicators[].decimalsdatasets[].aggregates[].indicators[].definition_uridatasets[].aggregates[].indicators[].frequencydatasets[].aggregates[].indicators[].functiondatasets[].aggregates[].indicators[].iddatasets[].aggregates[].indicators[].labeldatasets[].aggregates[].indicators[].unit_measuredatasets[].aggregates[].indicators[].unit_multdatasets[].aggregates[].joinsdatasets[].aggregates[].joins[]datasets[].aggregates[].joins[].relationshipdatasets[].aggregates[].measuresdatasets[].aggregates[].measures[]datasets[].aggregates[].measures[].columndatasets[].aggregates[].measures[].functiondatasets[].aggregates[].measures[].namedatasets[].aggregates[].required_filter_bindingsdatasets[].aggregates[].required_filter_bindings[]datasets[].aggregates[].required_filter_bindings[].fielddatasets[].aggregates[].required_filter_bindings[].sourcedatasets[].aggregates[].required_filtersdatasets[].aggregates[].required_filters[]datasets[].aggregates[].source_entitydatasets[].aggregates[].spatialdatasets[].aggregates[].spatial.bbox_fieldsdatasets[].aggregates[].spatial.bbox_fields.max_xdatasets[].aggregates[].spatial.bbox_fields.max_ydatasets[].aggregates[].spatial.bbox_fields.min_xdatasets[].aggregates[].spatial.bbox_fields.min_ydatasets[].aggregates[].spatial.collection_iddatasets[].aggregates[].spatial.dimensiondatasets[].aggregates[].spatial.geometry_entitydatasets[].aggregates[].spatial.geometry_fielddatasets[].aggregates[].spatial.geometry_id_fielddatasets[].aggregates[].spatial.max_geometry_verticesdatasets[].aggregates[].spatial.modedatasets[].aggregates[].temporal_fielddatasets[].aggregates[].titledatasets[].applicable_legislationdatasets[].applicable_legislation[]datasets[].conforms_todatasets[].conforms_to[]datasets[].defaultsdatasets[].defaults.materializationdatasets[].defaults.refreshdatasets[].defaults.refresh.intervaldatasets[].defaults.refresh.modedatasets[].descriptiondatasets[].entitiesdatasets[].entities[]datasets[].entities[].accessdatasets[].entities[].access.aggregate_scopedatasets[].entities[].access.evidence_verification_scopedatasets[].entities[].access.metadata_scopedatasets[].entities[].access.read_scopedatasets[].entities[].aggregatesdatasets[].entities[].aggregates[]datasets[].entities[].aggregates[].accessdatasets[].entities[].aggregates[].access.aggregate_only_executiondatasets[].entities[].aggregates[].access.aggregate_scopedatasets[].entities[].aggregates[].access.metadata_scopedatasets[].entities[].aggregates[].allowed_filtersdatasets[].entities[].aggregates[].allowed_filters[]datasets[].entities[].aggregates[].allowed_filters[].fielddatasets[].entities[].aggregates[].allowed_filters[].opsdatasets[].entities[].aggregates[].allowed_filters[].ops[]datasets[].entities[].aggregates[].default_group_bydatasets[].entities[].aggregates[].default_group_by[]datasets[].entities[].aggregates[].descriptiondatasets[].entities[].aggregates[].dimensionsdatasets[].entities[].aggregates[].dimensions[]datasets[].entities[].aggregates[].dimensions[].codelistdatasets[].entities[].aggregates[].dimensions[].fielddatasets[].entities[].aggregates[].dimensions[].iddatasets[].entities[].aggregates[].dimensions[].labeldatasets[].entities[].aggregates[].disclosure_controldatasets[].entities[].aggregates[].disclosure_control.methoddatasets[].entities[].aggregates[].disclosure_control.method[]datasets[].entities[].aggregates[].disclosure_control.min_cell_sizedatasets[].entities[].aggregates[].disclosure_control.min_group_sizedatasets[].entities[].aggregates[].disclosure_control.report_suppressed_rowsdatasets[].entities[].aggregates[].disclosure_control.suppressiondatasets[].entities[].aggregates[].group_bydatasets[].entities[].aggregates[].group_by[]datasets[].entities[].aggregates[].iddatasets[].entities[].aggregates[].indicatorsdatasets[].entities[].aggregates[].indicators[]datasets[].entities[].aggregates[].indicators[].columndatasets[].entities[].aggregates[].indicators[].decimalsdatasets[].entities[].aggregates[].indicators[].definition_uridatasets[].entities[].aggregates[].indicators[].frequencydatasets[].entities[].aggregates[].indicators[].functiondatasets[].entities[].aggregates[].indicators[].iddatasets[].entities[].aggregates[].indicators[].labeldatasets[].entities[].aggregates[].indicators[].unit_measuredatasets[].entities[].aggregates[].indicators[].unit_multdatasets[].entities[].aggregates[].joinsdatasets[].entities[].aggregates[].joins[]datasets[].entities[].aggregates[].joins[].relationshipdatasets[].entities[].aggregates[].measuresdatasets[].entities[].aggregates[].measures[]datasets[].entities[].aggregates[].measures[].columndatasets[].entities[].aggregates[].measures[].functiondatasets[].entities[].aggregates[].measures[].namedatasets[].entities[].aggregates[].required_filter_bindingsdatasets[].entities[].aggregates[].required_filter_bindings[]datasets[].entities[].aggregates[].required_filter_bindings[].fielddatasets[].entities[].aggregates[].required_filter_bindings[].sourcedatasets[].entities[].aggregates[].required_filtersdatasets[].entities[].aggregates[].required_filters[]datasets[].entities[].aggregates[].source_entitydatasets[].entities[].aggregates[].spatialdatasets[].entities[].aggregates[].spatial.bbox_fieldsdatasets[].entities[].aggregates[].spatial.bbox_fields.max_xdatasets[].entities[].aggregates[].spatial.bbox_fields.max_ydatasets[].entities[].aggregates[].spatial.bbox_fields.min_xdatasets[].entities[].aggregates[].spatial.bbox_fields.min_ydatasets[].entities[].aggregates[].spatial.collection_iddatasets[].entities[].aggregates[].spatial.dimensiondatasets[].entities[].aggregates[].spatial.geometry_entitydatasets[].entities[].aggregates[].spatial.geometry_fielddatasets[].entities[].aggregates[].spatial.geometry_id_fielddatasets[].entities[].aggregates[].spatial.max_geometry_verticesdatasets[].entities[].aggregates[].spatial.modedatasets[].entities[].aggregates[].temporal_fielddatasets[].entities[].aggregates[].titledatasets[].entities[].apidatasets[].entities[].api.allowed_expansionsdatasets[].entities[].api.allowed_expansions[]datasets[].entities[].api.allowed_filtersdatasets[].entities[].api.allowed_filters[]datasets[].entities[].api.allowed_filters[].fielddatasets[].entities[].api.allowed_filters[].opsdatasets[].entities[].api.allowed_filters[].ops[]datasets[].entities[].api.default_limitdatasets[].entities[].api.governed_policydatasets[].entities[].api.governed_policy.allowed_assurancedatasets[].entities[].api.governed_policy.allowed_assurance[]datasets[].entities[].api.governed_policy.max_source_age_secondsdatasets[].entities[].api.governed_policy.minimum_assurancedatasets[].entities[].api.governed_policy.permitted_jurisdictionsdatasets[].entities[].api.governed_policy.permitted_jurisdictions[]datasets[].entities[].api.governed_policy.permitted_purposesdatasets[].entities[].api.governed_policy.permitted_purposes[]datasets[].entities[].api.governed_policy.redaction_fieldsdatasets[].entities[].api.governed_policy.redaction_fields[]datasets[].entities[].api.governed_policy.require_consentdatasets[].entities[].api.governed_policy.require_legal_basisdatasets[].entities[].api.governed_policy.trusted_contextdatasets[].entities[].api.governed_policy.trusted_context.asserted_assurancedatasets[].entities[].api.governed_policy.trusted_context.consent_refdatasets[].entities[].api.governed_policy.trusted_context.jurisdictiondatasets[].entities[].api.governed_policy.trusted_context.legal_basis_refdatasets[].entities[].api.governed_policy.trusted_context.source_observed_age_secondsdatasets[].entities[].api.max_limitdatasets[].entities[].api.require_purpose_headerdatasets[].entities[].api.required_filter_bindingsdatasets[].entities[].api.required_filter_bindings[]datasets[].entities[].api.required_filter_bindings[].fielddatasets[].entities[].api.required_filter_bindings[].sourcedatasets[].entities[].api.required_filtersdatasets[].entities[].api.required_filters[]datasets[].entities[].attribute_release_profilesdatasets[].entities[].attribute_release_profiles[]datasets[].entities[].attribute_release_profiles[].claimsdatasets[].entities[].attribute_release_profiles[].claims[]datasets[].entities[].attribute_release_profiles[].claims[].expressiondatasets[].entities[].attribute_release_profiles[].claims[].expression.celdatasets[].entities[].attribute_release_profiles[].claims[].formatdatasets[].entities[].attribute_release_profiles[].claims[].localedatasets[].entities[].attribute_release_profiles[].claims[].namedatasets[].entities[].attribute_release_profiles[].claims[].requireddatasets[].entities[].attribute_release_profiles[].claims[].sensitivitydatasets[].entities[].attribute_release_profiles[].claims[].source_fielddatasets[].entities[].attribute_release_profiles[].descriptiondatasets[].entities[].attribute_release_profiles[].iddatasets[].entities[].attribute_release_profiles[].purposedatasets[].entities[].attribute_release_profiles[].release_conditionsdatasets[].entities[].attribute_release_profiles[].release_conditions.expressiondatasets[].entities[].attribute_release_profiles[].release_conditions.expression.celdatasets[].entities[].attribute_release_profiles[].release_scopedatasets[].entities[].attribute_release_profiles[].responsedatasets[].entities[].attribute_release_profiles[].response.include_source_metadatadatasets[].entities[].attribute_release_profiles[].subjectdatasets[].entities[].attribute_release_profiles[].subject.id_typedatasets[].entities[].attribute_release_profiles[].subject.source_fielddatasets[].entities[].attribute_release_profiles[].titledatasets[].entities[].attribute_release_profiles[].versiondatasets[].entities[].concept_uridatasets[].entities[].descriptiondatasets[].entities[].fieldsdatasets[].entities[].fields[]datasets[].entities[].fields[].codelistdatasets[].entities[].fields[].concept_uridatasets[].entities[].fields[].fromdatasets[].entities[].fields[].languagedatasets[].entities[].fields[].namedatasets[].entities[].fields[].sensitivedatasets[].entities[].fields[].unitdatasets[].entities[].namedatasets[].entities[].relationshipsdatasets[].entities[].relationships[]datasets[].entities[].relationships[].concept_uridatasets[].entities[].relationships[].foreign_keydatasets[].entities[].relationships[].kinddatasets[].entities[].relationships[].namedatasets[].entities[].relationships[].targetdatasets[].entities[].spatialdatasets[].entities[].spatial.bbox_fieldsdatasets[].entities[].spatial.bbox_fields.max_xdatasets[].entities[].spatial.bbox_fields.max_ydatasets[].entities[].spatial.bbox_fields.min_xdatasets[].entities[].spatial.bbox_fields.min_ydatasets[].entities[].spatial.collection_iddatasets[].entities[].spatial.datetime_fielddatasets[].entities[].spatial.descriptiondatasets[].entities[].spatial.geometrydatasets[].entities[].spatial.geometry.crsdatasets[].entities[].spatial.geometry.fielddatasets[].entities[].spatial.geometry.kinddatasets[].entities[].spatial.geometry.latitude_fielddatasets[].entities[].spatial.geometry.longitude_fielddatasets[].entities[].spatial.max_bbox_degreesdatasets[].entities[].spatial.max_geometry_verticesdatasets[].entities[].spatial.titledatasets[].entities[].tabledatasets[].entities[].titledatasets[].iddatasets[].ownerdatasets[].public_servicesdatasets[].public_services[]datasets[].public_services[].descriptiondatasets[].public_services[].iddatasets[].public_services[].titledatasets[].sensitivitydatasets[].spatial_coveragedatasets[].statusdatasets[].tablesdatasets[].tables[]datasets[].tables[].accessdatasets[].tables[].access.aggregate_scopedatasets[].tables[].access.metadata_scopedatasets[].tables[].aggregatesdatasets[].tables[].aggregates[]datasets[].tables[].aggregates[].accessdatasets[].tables[].aggregates[].access.aggregate_only_executiondatasets[].tables[].aggregates[].access.aggregate_scopedatasets[].tables[].aggregates[].access.metadata_scopedatasets[].tables[].aggregates[].allowed_filtersdatasets[].tables[].aggregates[].allowed_filters[]datasets[].tables[].aggregates[].allowed_filters[].fielddatasets[].tables[].aggregates[].allowed_filters[].opsdatasets[].tables[].aggregates[].allowed_filters[].ops[]datasets[].tables[].aggregates[].default_group_bydatasets[].tables[].aggregates[].default_group_by[]datasets[].tables[].aggregates[].descriptiondatasets[].tables[].aggregates[].dimensionsdatasets[].tables[].aggregates[].dimensions[]datasets[].tables[].aggregates[].dimensions[].codelistdatasets[].tables[].aggregates[].dimensions[].fielddatasets[].tables[].aggregates[].dimensions[].iddatasets[].tables[].aggregates[].dimensions[].labeldatasets[].tables[].aggregates[].disclosure_controldatasets[].tables[].aggregates[].disclosure_control.methoddatasets[].tables[].aggregates[].disclosure_control.method[]datasets[].tables[].aggregates[].disclosure_control.min_cell_sizedatasets[].tables[].aggregates[].disclosure_control.min_group_sizedatasets[].tables[].aggregates[].disclosure_control.report_suppressed_rowsdatasets[].tables[].aggregates[].disclosure_control.suppressiondatasets[].tables[].aggregates[].group_bydatasets[].tables[].aggregates[].group_by[]datasets[].tables[].aggregates[].iddatasets[].tables[].aggregates[].indicatorsdatasets[].tables[].aggregates[].indicators[]datasets[].tables[].aggregates[].indicators[].columndatasets[].tables[].aggregates[].indicators[].decimalsdatasets[].tables[].aggregates[].indicators[].definition_uridatasets[].tables[].aggregates[].indicators[].frequencydatasets[].tables[].aggregates[].indicators[].functiondatasets[].tables[].aggregates[].indicators[].iddatasets[].tables[].aggregates[].indicators[].labeldatasets[].tables[].aggregates[].indicators[].unit_measuredatasets[].tables[].aggregates[].indicators[].unit_multdatasets[].tables[].aggregates[].joinsdatasets[].tables[].aggregates[].joins[]datasets[].tables[].aggregates[].joins[].relationshipdatasets[].tables[].aggregates[].measuresdatasets[].tables[].aggregates[].measures[]datasets[].tables[].aggregates[].measures[].columndatasets[].tables[].aggregates[].measures[].functiondatasets[].tables[].aggregates[].measures[].namedatasets[].tables[].aggregates[].required_filter_bindingsdatasets[].tables[].aggregates[].required_filter_bindings[]datasets[].tables[].aggregates[].required_filter_bindings[].fielddatasets[].tables[].aggregates[].required_filter_bindings[].sourcedatasets[].tables[].aggregates[].required_filtersdatasets[].tables[].aggregates[].required_filters[]datasets[].tables[].aggregates[].source_entitydatasets[].tables[].aggregates[].spatialdatasets[].tables[].aggregates[].spatial.bbox_fieldsdatasets[].tables[].aggregates[].spatial.bbox_fields.max_xdatasets[].tables[].aggregates[].spatial.bbox_fields.max_ydatasets[].tables[].aggregates[].spatial.bbox_fields.min_xdatasets[].tables[].aggregates[].spatial.bbox_fields.min_ydatasets[].tables[].aggregates[].spatial.collection_iddatasets[].tables[].aggregates[].spatial.dimensiondatasets[].tables[].aggregates[].spatial.geometry_entitydatasets[].tables[].aggregates[].spatial.geometry_fielddatasets[].tables[].aggregates[].spatial.geometry_id_fielddatasets[].tables[].aggregates[].spatial.max_geometry_verticesdatasets[].tables[].aggregates[].spatial.modedatasets[].tables[].aggregates[].temporal_fielddatasets[].tables[].aggregates[].titledatasets[].tables[].apidatasets[].tables[].api.allowed_filtersdatasets[].tables[].api.allowed_filters[]datasets[].tables[].api.allowed_filters[].fielddatasets[].tables[].api.allowed_filters[].opsdatasets[].tables[].api.allowed_filters[].ops[]datasets[].tables[].api.default_limitdatasets[].tables[].api.max_limitdatasets[].tables[].api.require_purpose_headerdatasets[].tables[].iddatasets[].tables[].materializationdatasets[].tables[].primary_keydatasets[].tables[].refreshdatasets[].tables[].refresh.intervaldatasets[].tables[].refresh.modedatasets[].tables[].schemadatasets[].tables[].schema.fieldsdatasets[].tables[].schema.fields[]datasets[].tables[].schema.fields[].codelistdatasets[].tables[].schema.fields[].concept_uridatasets[].tables[].schema.fields[].languagedatasets[].tables[].schema.fields[].namedatasets[].tables[].schema.fields[].nullabledatasets[].tables[].schema.fields[].sensitivedatasets[].tables[].schema.fields[].typedatasets[].tables[].schema.fields[].unitdatasets[].tables[].schema.strictdatasets[].tables[].sourcedatasets[].tables[].source.change_token_sqldatasets[].tables[].source.connect_timeoutdatasets[].tables[].source.connection_envdatasets[].tables[].source.formatdatasets[].tables[].source.format.csvdatasets[].tables[].source.format.csv.delimiterdatasets[].tables[].source.format.csv.header_rowdatasets[].tables[].source.format.csv.quotedatasets[].tables[].source.format.parquetdatasets[].tables[].source.format.xlsxdatasets[].tables[].source.format.xlsx.data_rangedatasets[].tables[].source.format.xlsx.header_rowdatasets[].tables[].source.format.xlsx.sheetdatasets[].tables[].source.pathdatasets[].tables[].source.querydatasets[].tables[].source.query_timeoutdatasets[].tables[].source.tabledatasets[].tables[].source.table.namedatasets[].tables[].source.table.schemadatasets[].tables[].source.typedatasets[].titledatasets[].update_frequencydeploymentdeployment.evidencedeployment.evidence.api_key_rotationdeployment.evidence.audit_ack_cursor_pathdeployment.evidence.audit_ack_max_age_secsdeployment.evidence.audit_offhost_shippingdeployment.evidence.ingress_rate_limitdeployment.profiledeployment.waiversdeployment.waivers[]deployment.waivers[].expiresdeployment.waivers[].findingdeployment.waivers[].referencedeployment.waivers[].summaryinstanceinstance.environmentinstance.idinstance.jurisdictioninstance.ownermetadatametadata.ecosystem_bindingmetadata.ecosystem_binding.idmetadata.ecosystem_binding.versionmetadata.sourcemetadata.source.digestmetadata.source.pathserverserver.admin_bindserver.bindserver.cache_dirserver.corsserver.cors.allowed_originsserver.cors.allowed_origins[]server.http1_header_read_timeoutserver.max_connectionsserver.max_source_file_bytesserver.openapi_requires_authserver.request_body_timeoutserver.request_timeoutserver.trust_proxyserver.trust_proxy.enabledserver.trust_proxy.trusted_proxiesserver.trust_proxy.trusted_proxies[]server.xlsx_max_file_bytesstandardsstandards.spdcistandards.spdci.disability_registrystandards.spdci.disability_registry.datasetstandards.spdci.disability_registry.disabled_positive_valuesstandards.spdci.disability_registry.disabled_positive_values[]standards.spdci.disability_registry.disabled_status_fieldstandards.spdci.disability_registry.entitystandards.spdci.disability_registry.query_fieldstandards.spdci.disability_registry.query_keystandards.spdci.registriesstandards.spdci.registries.*standards.spdci.registries.*.datasetstandards.spdci.registries.*.default_limitstandards.spdci.registries.*.entitystandards.spdci.registries.*.expression_fieldsstandards.spdci.registries.*.expression_fields.*standards.spdci.registries.*.identifiersstandards.spdci.registries.*.identifiers.*standards.spdci.registries.*.record_typestandards.spdci.registries.*.registry_typestandards.spdci.registries.*.response_fieldsstandards.spdci.registries.*.response_fields.*standards.spdci.registries.*.response_mapping_pathstandards.spdci.registries.*.response_schema_pathvocabulariesvocabularies.*registry-relay is configured by one YAML document. The binary chooses the first available source:
--config <path>REGISTRY_RELAY_CONFIG./config/example.yaml
The canonical sample is config/example.yaml. Keep examples aligned with this guide and the API and operations documentation.
Root shape
Section titled “Root shape”instance: {}server: {}metadata: {} # optional split portable metadata manifestcatalog: {}vocabularies: {}auth: {}audit: {}consultation: {} # optional restart-only purpose-aware consultation runtimedeployment: profile: local # required; use local only for developmentconfig_trust: {} # optional signed bundle boot trustdatasets: []standards: {} # optional, feature-gated adaptersUnknown fields are rejected for most blocks. Config validation runs after YAML parsing and checks ids, scopes, table/entity references, filter references, aggregate references, env var presence, and vocabulary prefixes.
The complete deserialization-oriented Draft 2020-12 schema is committed at
schemas/registry-relay.config.schema.json.
Reproduce it from this directory with just config-schema-generate, verify
drift with just config-schema-check, or print the exact same bytes with
registry-relay schema --format json. The schema checks document structure,
closed objects, tagged variants, scalar shapes, and constrained reference
syntax. registry-relay doctor remains authoritative for environment and
secret availability, filesystem and source access, activation rules, and
cross-field runtime validation.
Environment expansion
Section titled “Environment expansion”Relay expands ${VAR} expressions before YAML parsing. ${VAR} requires
VAR to be set to a non-empty value. ${VAR:-fallback} uses fallback when
VAR is unset or empty, including ${VAR:-} for an explicit empty result.
${VAR:?message} fails with message when VAR is unset or empty.
Whitespace-only values are non-empty. Diagnostics name the variable or use the
supplied message; they never include the variable value.
Environment-reference fields follow the invariant of their runtime consumer:
auth.api_keys[].fingerprint.nameaccepts any non-empty operating-system environment name except names containing=or NUL. It does not impose an identifier grammar or a 128-byte limit, and its consumer permits whitespace-only names.audit.hash_secret_envuses the same operating-system name rules but must contain at least one non-whitespace character, matching the audit runtime’s fail-closed empty-name check. Names containing dots or hyphens remain valid.- Postgres
connection_envuses[A-Za-z_][A-Za-z0-9_]*, matching source validation, without an artificial length limit. - Consultation database, credential, and pseudonym secret references use the
portable
[A-Za-z_][A-Za-z0-9_]{0,127}grammar enforced during deserialization and consultation operations.
A minimal entity-serving deployment needs server (a listener), catalog (public metadata base), auth (one auth mode), audit (a sink and hash secret), and at least one entry in datasets. A consultation-only deployment can use datasets: [] when the complete consultation block activates at least one profile.
Every other root block is optional.
This example shows the required shape.
For a runnable starting point, use config/example.yaml.
Env-backed API key configs name the secret-store entry that contains the canonical fingerprint.
server: bind: 127.0.0.1:8080
catalog: title: Example Registry Relay base_url: http://127.0.0.1:8080 publisher: Example Ministry
auth: mode: api_key api_keys: - id: demo_client fingerprint: provider: env name: API_KEY_HASH scopes: - people:metadata - people:rows
audit: sink: stdout hash_secret_env: REGISTRY_RELAY_AUDIT_HASH_SECRET
datasets: - id: people title: People registry description: Demo people records owner: Example Ministry sensitivity: personal access_rights: restricted update_frequency: monthly tables: - id: people_table source: type: file path: ./data/people.csv format: csv: header_row: 1 primary_key: person_id schema: strict: true fields: - name: person_id type: string nullable: false - name: name type: string nullable: false entities: - name: person table: people_table fields: - name: person_id - name: name access: metadata_scope: people:metadata aggregate_scope: people:metadata read_scope: people:rows api: default_limit: 50 max_limit: 100The API_KEY_HASH environment variable must contain a canonical fingerprint in the form sha256:<64 lowercase hex chars>.
The raw API key stays outside the config and is given only to the authorized client.
The REGISTRY_RELAY_AUDIT_HASH_SECRET environment variable must contain at least 32 bytes of random secret material; startup fails closed when it is absent or weak.
See config/example.yaml for a larger working starting point; the sections that follow document each block in full.
Purpose-aware consultations
Section titled “Purpose-aware consultations”consultation activates Relay’s restart-only native API for one exact authorized
workload and the exact profiles in one hash-pinned artifact closure. It
is all-or-nothing: Relay refuses startup when OIDC, workload identity, artifact
closure, state-plane identity, pseudonym material references, source credential
references, or compiled plan support are incomplete or inconsistent.
Use the generic Registry Stack project-authoring workflow to produce the complete Relay input for a deployment. The maintained DHIS2 journey is one interoperability example, not a source-product or version-specific runtime shape. Its generated config keeps exact typed artifact hashes and raw file digests next to that example.
authorized_workload fixes one OIDC client identity. The issuer comes from
auth.oidc; the audience and selected azp or client_id must agree with the
OIDC client allowlist. Each compiled public contract adds its own exact scope,
purpose, tenant, registry, and source-plan binding.
state_plane.database_url_env names the environment variable containing the
PostgreSQL runtime connection URL. The optional root_certificate_path pins a
private PostgreSQL trust root exclusively: when configured, Relay does not
also trust system roots. Omit it to use the host’s normal system trust store.
Relay rejects a custom root on Android because that platform’s native-tls
backend cannot exclude the Android system root directory.
The epoch id and advisory-lock keys are stable, deployment-owned identifiers
and must not collide with another state-plane user. Do not place a database URL
in YAML.
artifacts is a complete catalog. Every public contract listed there is an
enabled consultation. Local development pins every file with both its typed
artifact hash where applicable and its raw SHA-256 digest. Non-local profiles
must receive the files through the verified signed Config Bundle path. Relay
does not discover, download, or hot-reload consultation profiles.
Each private destination binding defaults to
dns_family: dual_stack_strict: Relay requires definitive A and AAAA lookup
outcomes before it connects. For a domain destination intentionally operated
over IPv4, set dns_family: ipv4_only on that destination. This is an A-only
security policy, not a fallback preference. It never queries or accepts IPv6,
still re-resolves and validates the complete A answer set for every call, and
rejects literal origins or IPv6 private CIDRs. The selected mode is covered by
the private-binding hash, so changing it requires reviewing and repinning that
binding.
audit_pseudonym_materials and source_credentials contain environment
references only. An audit key id is immutable: replace material under a new id
instead of changing bytes behind an existing id. A source credential generation
is also explicit and positive. V1 supports environment-backed HTTP Basic source
credentials for the concrete maintained DHIS2 journey. Secret values must not
appear in YAML, diagnostics, logs, or evidence.
Run registry-relay doctor --config <path> --profile <profile> --format json
before bootstrap or startup. Consultation activation is restart-only, so deploy
the reviewed config and artifact closure as one unit.
Orchestration that publishes generated files through a container bind mount can
also pass --expected-config-digest sha256:<digest>. Doctor then fails before
configuration loading unless the mounted configuration bytes match that exact
digest. Use this together with the normal consultation artifact checks: the
digest binds the intended generated revision, while the artifact checks prove
that the revision’s complete hash-covered closure is readable and valid.
Doctor does not include the expected digest in its output or error messages.
Instance
Section titled “Instance”instance: id: registry-relay-local environment: development owner: Ministry of Digital Government jurisdiction: example-countryinstance gives posture and operations tooling a stable public identity for the
running service. id defaults to registry-relay-local; environment, owner,
and jurisdiction are optional public labels.
Server
Section titled “Server”server: bind: 0.0.0.0:8080 admin_bind: 127.0.0.1:8081 openapi_requires_auth: true cache_dir: ./cache max_source_file_bytes: 268435456 xlsx_max_file_bytes: 268435456 request_timeout: 30s request_body_timeout: 10s http1_header_read_timeout: 10s max_connections: 1024 cors: allowed_origins: - https://portal.example.gov trust_proxy: enabled: false trusted_proxies: []bind is the public data-plane listener.
admin_bind is optional and must be private in production.
Listener addresses use canonical dotted-decimal IPv4 or bracketed hexadecimal
IPv6 followed by a canonical decimal port from 0 through 65535.
IPv4-embedded IPv6 and IPv6 zone identifiers are outside this portable config
grammar.
cache_dir must be writable by the process.
Source data must be mounted read-only.
openapi_requires_auth defaults to true. Set it to false only for local testing or controlled tooling environments that need unauthenticated access to /openapi.json; the unauthenticated document includes the full configured OpenAPI surface.
request_timeout bounds total request service time after HTTP headers are parsed. request_body_timeout bounds body reads for handlers that consume a request body. http1_header_read_timeout closes incomplete HTTP/1 headers before request work is admitted, and max_connections caps concurrent accepted sockets per listener. All timeouts must be non-zero and max_connections must be greater than zero.
Every duration field uses the same stable humantime subset.
A value contains one or more non-negative integer components of at most 10
digits, separated by one ASCII space, with units ns, us, ms, s, m,
h, d, or w.
The complete value is at most 255 bytes.
Examples include 30s, 10m, 1h, and 2h 37m.
Bare numbers, negative or fractional components, long unit aliases, adjacent
components such as 1h30m, and repeated spaces are rejected by both runtime
deserialization and the JSON Schema.
HTTP/2 connections use the same finite connection cap and keepalive timeout. If production terminates HTTP/2 at a reverse proxy, configure bounded proxy header/body read timeouts and per-client connection limits before forwarding to Registry Relay.
The default CORS policy is deny by omission. Add explicit trusted origins only.
Config Bundle Trust
Section titled “Config Bundle Trust”Most deployments can skip this section. config_trust is optional; it makes
startup config come from a signed, local config bundle. Simple local deployments
omit it and keep using the local YAML loaded at startup.
This example is syntactically valid but illustrative. Generate the trust anchor
and signed bundle with registryctl anchor and registryctl bundle before using
it in an environment.
config_trust: trust_anchor_path: /etc/registry-relay/config/trust-anchor.json bundle_path: /etc/registry-relay/config/bundle antirollback_state_path: /var/lib/registry-relay/config-state/antirollback.json break_glass_override_path: /run/registry-relay/config-override.jsonConfig bundle trust is boot-time only. Relay reads no remote metadata, exposes no admin config apply endpoint, and does not hot-apply runtime config. At boot it verifies the anchor permissions, the bundle manifest and signature, product and environment binding, bundle file closure, anti-rollback sequence, and full Relay config validation. The accepted bundle is audited before the anti-rollback state is advanced.
antirollback_state_path must point to durable local state such as a mounted
volume. break_glass_override_path is optional and points to a root-owned
one-shot override file. Rollback overrides may accept the exact signed bundle
hash named by the file. accept_unsigned overrides may pin an absolute local
config path and hash for emergency startup; signature, binding, and sequence
checks are skipped, but file permissions, hash pinning, and Relay config
validation still run.
Catalog and vocabularies
Section titled “Catalog and vocabularies”catalog: title: Internal Government Registry Relay base_url: https://data.example.gov publisher: Ministry of Digital Government participant_id: did:web:data.example.gov
vocabularies: psc: https://publicschema.org/ m8g: http://data.europa.eu/m8g/base_url is used in generated catalog links and OpenAPI servers. participant_id is optional and defaults from the catalog base URL when omitted.
Vocabulary prefixes let entity fields and dataset metadata use compact semantic references such as psc:concepts/Person.
Split metadata manifest
Section titled “Split metadata manifest”metadata: source: path: ./metadata.yamlmetadata.source.path points at a portable metadata manifest. Relative paths
are resolved from the runtime config file. At startup, Registry Relay compiles
the manifest and validates that runtime datasets, entities, fields, filters, and
relationships are present in the metadata model. Add
metadata.source.digest: sha256:<digest> when the deployment must pin the
exact reviewed manifest.
| Mode | Required config | Digest rule | Delivery |
|---|---|---|---|
| Simple local | metadata.source.path | Optional | Local file read at startup |
| Pinned local | metadata.source.path, metadata.source.digest | Must match the local manifest | Local file read at startup |
| Governed | config_trust, metadata.source.path, metadata.source.digest | Required before startup | Signed config target plus signed metadata target; optional signed package index when package_digest is claimed |
Keep operational details in this runtime config: sources, tables, physical columns, scopes, filters, aggregates, standards adapters, ingest, and refresh. Keep standard-facing meaning in the manifest: catalog, datasets, entities, fields, constraints, vocabularies, codelists, profiles, conformance claims, and descriptive ODRL policy metadata.
See metadata.md for the manifest schema, static publication, and
the metadata.manifest.* / runtime.binding.* startup error codes.
ODRL policy belongs in the portable metadata manifest, not in runtime dataset
bindings. A dataset policy block is published as an odrl:Offer for discovery
and review evidence. When a runtime config selects a governed ecosystem binding,
Relay also uses supported metadata purpose constraints as governed PDP purpose
constraints on entity-derived evidence routes. The metadata policy still does not
grant API-key scopes, OIDC roles, row filters, evidence verification privileges,
or SP DCI access by itself.
metadata: source: path: ./disability_registry.metadata.yaml
# In disability_registry.metadata.yaml:datasets: - id: disability_registry policy: uid: https://demo.example.gov/datasets/disability_registry#illustrative-offer assigner: did:web:social-affairs.demo.example.gov permissions: - action: odrl:use constraints: - left_operand: odrl:purpose operator: odrl:isA right_operand: iri: https://demo.example.gov/purpose/disability-benefit-eligibility duties: - action: odrl:attribute prohibitions: - action: odrl:sellThe demo policy IRIs under demo.example.gov are hypothetical examples for
catalog consumers. They are not official policy, legal advice, or a declaration
that a client has been approved to use the data.
SP DCI sync adapter
Section titled “SP DCI sync adapter”SP DCI (the Social Protection Digital Convergence Initiative) sync adapters are optional and feature-gated. Build with --features spdci-api-standards to enable them. Without that feature, any standards.spdci config is rejected with spdci.config.feature_disabled.
The adapter does not add new storage semantics. Configure a normal Registry Relay entity, often backed by an XLSX worksheet, then bind the SP DCI sync routes to it:
standards: spdci: disability_registry: dataset: disability_registry entity: disabled_person query_key: member.member_identifier query_field: id disabled_status_field: disability_status disabled_positive_values: [approved, yes] registries: dr: dataset: disability_registry entity: disabled_person registry_type: ns:org:RegistryType:DR record_type: spdci-extensions-dci:DisabledPerson identifiers: DISABILITY_ID: id MEMBER_ID: id expression_fields: disability_status: disability_status disability_details.impairment_type: impairment_typeWhen enabled and configured, Registry Relay serves these SP DCI sync endpoints on the protected data-plane listener:
POST /dci/{registry}/registry/sync/searchPOST /dci/{registry}/registry/sync/disabledPOST /dci/{registry}/registry/sync/get-disability-detailsPOST /dci/{registry}/registry/sync/get-disability-supportFor sync/search, the {registry} segment selects any named standards.spdci.registries entry such as dr, sr, crvs, or fr, which lets one listener host multiple DCI registry APIs without path ambiguity. The disabled, get-disability-details, and get-disability-support routes are Disability Registry-specific and resolve only when the named registry entry points at the same dataset/entity as standards.spdci.disability_registry. The async /registry/search, subscribe, callback, and transaction-status APIs are intentionally not implemented by this sync adapter.
For generic sync search, identifiers maps DCI idtype-value query types to entity fields. expression_fields maps DCI expression or predicate attribute names to entity fields. Mapped fields must be exposed entity fields and allowed filters. The adapter currently supports idtype-value, expression $and with eq, in, ge, and le, and predicate conditions joined with and.
query_key is read from message.disabled_criteria.query in the SP DCI request envelope. It may be represented as a literal dotted JSON key ("member.member_identifier") or as nested objects ({"member": {"member_identifier": ...}}). query_field must be an allowed entity filter because the adapter delegates reads to the normal entity query engine.
For /dci/{registry}/registry/sync/disabled, the caller needs the entity evidence_verification_scope. Generic search, details, and support need the entity read_scope. API-key authentication is still Registry Relay’s normal auth layer. If a registry entry uses response_mapping_path, the binary must also be built with --features standards-cel-mapping; otherwise config validation fails with spdci.config.mapping_feature_disabled.
API keys
Section titled “API keys”auth: mode: api_key api_keys: - id: program_system fingerprint: provider: env name: PROGRAM_SYSTEM_API_KEY_HASH scopes: - social_registry:metadata - social_registry:rowsThe YAML stores fingerprint references, never raw API keys. Each env var value must be:
sha256:<64 lowercase hex chars>Generate a raw key and its fingerprint:
registry-relay generate-api-key --id program_systemThe command emits three shell-friendly lines:
api_key_id=program_systemapi_key=<send-this-raw-key-to-the-client>fingerprint=sha256:<store-this-in-the-secret-store>Store the emitted fingerprint in the platform secret store under the configured fingerprint.name.
Give the raw key only to the authorized client.
Restart Registry Relay, or apply a governed config change that points at a new immutable or versioned fingerprint reference, before expecting the new credential to authenticate.
Worked standalone example, using demo_client and API_KEY_HASH:
api_key_id=demo_clientapi_key=registry-relay-standalone-example-key-0001fingerprint=sha256:db3f2a02c6ead9bf0387e8a97ec090a549daa46610ca87bd4b651631b2411defexport API_KEY_HASH='sha256:db3f2a02c6ead9bf0387e8a97ec090a549daa46610ca87bd4b651631b2411def'auth: mode: api_key api_keys: - id: demo_client fingerprint: provider: env name: API_KEY_HASH scopes: - people:metadata - people:rowsDo not reuse the example raw key in a real deployment.
OIDC (OAuth2)
Section titled “OIDC (OAuth2)”Set auth.mode: oidc to verify bearer JWTs against an external OpenID Connect / OAuth2 IdP. Registry Relay is a resource server: it validates inbound tokens against the IdP’s JWKS but never mints, refreshes, or stores tokens. A given deployment runs in exactly one auth mode at a time; mixed-mode operation is not supported.
OIDC field names follow the shared Registry service runtime configuration conventions. Field names from earlier releases are rejected with an error naming the replacement field.
auth: mode: oidc oidc: issuer: https://idp.example.gov audiences: - registry-relay discovery_url: https://idp.example.gov/.well-known/openid-configuration allowed_algorithms: - RS256 jwks_cache_ttl: 10m leeway: 60s scope_claim: scope scope_map: "role:social-registry-reader": "social_registry:rows" scope_object_required_keys: [] allowed_clients: - registry-relay-client allowed_token_types: - JWT - at+jwtA full drop-in alternative to config/example.yaml lives at config/example.oidc.yaml. It targets a local Zitadel instance.
| Field | Purpose |
|---|---|
issuer | Compared verbatim against the JWT iss claim. Must match the IdP’s published issuer URL. |
audiences | One or more accepted aud values. Tokens whose aud does not intersect this list are rejected. |
jwks_url | Explicit JWKS endpoint. Exactly one of jwks_url and discovery_url must be set; the validator rejects configs that supply both or neither. |
discovery_url | OIDC discovery document (.well-known/openid-configuration). The JWKS URL is resolved from jwks_uri at startup. |
allow_dev_insecure_fetch_urls | Development-only opt-in for loopback HTTP issuer, discovery, and JWKS URLs. Defaults to false; non-loopback private and metadata IPs remain denied by the platform fetch policy. |
allowed_algorithms | Signature algorithms accepted by the verifier. RS256, ES256, EdDSA. HS* and none are intentionally absent. |
jwks_cache_ttl | Steady-state JWKS cache TTL. The cache also refreshes on unknown kid (rate-limited), so this is the rotation pickup latency, not the upper bound. |
leeway | Clock skew tolerance on exp and nbf. Bounded at 5 minutes by validation. |
scope_claim | Name of the JWT claim to read scopes from (the config field itself is always a single string; defaults to scope). The claim’s value in the token may be a space-separated string (RFC 8693 / RFC 9068), a JSON array of strings, or a JSON object whose keys are the scope names. The aud claim is rejected as a scope source because it is used only for token audience validation. Object-valued role keys grant scopes only when scope_object_required_keys names a key present in the role value and that nested value is active: true, a non-empty string, or a non-empty object/array containing an active value. |
scope_map | Optional rename map applied before scope-based access checks. Adapt IdP role names to Registry Relay’s <dataset_id>:<level> shape. |
scope_object_required_keys | Allowlist of keys that must appear inside object-valued role claim values before the role key is accepted. For Zitadel organization-scoped role objects, set this to the expected organization id key or keys. Defaults to empty, which means object-valued claims grant no scopes. String and array scope claims do not require this setting. |
allowed_clients | Optional allowlist matched against the token’s azp (preferred) or client_id. Empty list means any client is accepted and is intended only for tightly controlled development. |
allowed_token_types | Accepted JOSE typ header values. Defaults to JWT and at+jwt (RFC 9068). ID tokens (id+jwt) are intentionally rejected by default, and tokens without typ are rejected by the shared verifier. |
Discovery vs explicit JWKS
Section titled “Discovery vs explicit JWKS”discovery_url triggers a single discovery fetch at startup to resolve jwks_uri; a failure here aborts the binary so an operator sees the IdP wiring problem instead of a process that runs but silently rejects every token. The JWKS document itself is fetched lazily on first verify, so a transient JWKS outage at boot does not block startup. Production defaults require HTTPS; local loopback HTTP requires allow_dev_insecure_fetch_urls: true.
Resource-server semantics
Section titled “Resource-server semantics”Registry Relay never mints or refreshes tokens. Operators are responsible for provisioning OIDC applications, machine users, and grant types on the IdP. The Principal’s principal_id is taken from the token’s sub (preferred), then client_id, then azp; auth_mode=oidc is recorded on every audit record.
Granular failure codes
Section titled “Granular failure codes”Token verification failures map to specific auth.* codes so audit pipelines can distinguish IdP outages from bad tokens from policy denials:
| Code | HTTP | Meaning |
|---|---|---|
auth.missing_credential | 401 | No Authorization header |
auth.malformed_credential | 401 | Wrong scheme, empty bearer, or unparseable JWT structure |
auth.token_expired | 401 | exp claim is in the past (after leeway) |
auth.token_not_yet_valid | 401 | nbf claim is in the future (after leeway) |
auth.token_signature_invalid | 401 | JWKS key found but signature did not verify |
auth.issuer_mismatch | 401 | iss claim does not match oidc.issuer |
auth.audience_mismatch | 401 | aud claim does not intersect oidc.audiences |
auth.kid_unknown | 401 | Header kid is absent from the JWKS even after one refresh |
auth.algorithm_not_allowed | 401 | Header alg is not in the configured allowlist |
auth.client_not_allowed | 403 | azp / client_id is not in the configured allowed_clients |
auth.invalid_credential | 401 | JWT decode failure not covered by a more specific variant |
auth.jwks_unavailable | 503 | JWKS fetch failed; Registry Relay cannot verify any token |
auth.rate_limited | 429 | Local auth-failure throttle tripped for this client address (see below) |
For a worked example of running Registry Relay against a local OIDC provider (using the project’s dev Zitadel stack), see development.md.
Auth-failure throttle
Section titled “Auth-failure throttle”auth: failure_throttle: enabled: false max_failures: 20 window_seconds: 60| Field | Purpose |
|---|---|
enabled | Off by default. When false, the throttle is never constructed and every request behaves exactly as it did before this feature existed. |
max_failures | Number of authentication failures allowed from one client address within window_seconds before further requests from that address are throttled. Must be greater than 0 when enabled: true. |
window_seconds | Fixed-window length in seconds. Must be greater than 0 when enabled: true. |
This is a local, in-process, coarse throttle applied in front of the auth provider (API-key or OIDC), keyed on the same trust-proxy-aware client address the audit record’s remote_addr field reports (server.trust_proxy). Once an address has reached max_failures failed authentication attempts within the window, every further request from that address (including ones presenting a valid credential) is short-circuited with a 429 and the stable code auth.rate_limited, plus a Retry-After header giving the remaining window in seconds, without invoking the auth provider. Successful authentication neither counts toward the limit nor resets it. The counter is process-local and bounded (a capped map with eviction of the oldest entry), so it recovers automatically on restart and cannot grow without bound under a flood of spoofed source addresses. The throttled short-circuit itself is audited like any other auth failure, with error_code: auth.rate_limited and status_code: 429.
Because the throttle key is the resolved client address, deploying behind a proxy or load balancer without effective trust-proxy support makes every request resolve to the proxy’s own socket address, so all clients share one bucket. Combined authentication failures from any client then reach max_failures and 429 everyone, including callers presenting valid credentials, until the window rolls. Trust-proxy support is only effective when server.trust_proxy.enabled is true and server.trust_proxy.trusted_proxies names at least one proxy: an empty trusted_proxies list matches no peer, so X-Forwarded-For is ignored and the shared-bucket collapse still applies even with enabled set. Startup validation emits a config.validation_warning finding for both cases (trust_proxy disabled, or enabled with an empty trusted_proxies list); enable server.trust_proxy and populate trusted_proxies with the proxy address when the relay sits behind one.
Denial-of-service posture
Section titled “Denial-of-service posture”Ingress rate limiting (a load balancer, API gateway, or reverse proxy in front of Registry Relay) is the primary control for absorbing high-volume or distributed abuse; deployment profiles that lack one surface the relay.ingress.rate_limit_missing finding (see deployment.evidence.ingress_rate_limit below). auth.failure_throttle is a local backstop scoped narrowly to repeated authentication failures from a single address, useful for deployments without a gateway in front of them, or as defense in depth behind one. It does not protect other expensive routes (aggregation, large collection scans) from abuse by authenticated callers; throttling those routes is deliberately deferred to a future iteration and is not addressed by this feature.
audit: sink: stdout format: jsonl hash_secret_env: REGISTRY_RELAY_AUDIT_HASH_SECRET chain: true include_health: falseinclude_health controls audit records for /healthz. Registry Relay always
excludes /ready: evidence-grade readiness requires the shipper cursor to equal
the live audit-chain tail, so appending a record after that comparison would
make a successful probe invalidate the next probe.
Supported sinks:
audit: sink: file format: jsonl hash_secret_env: REGISTRY_RELAY_AUDIT_HASH_SECRET path: /var/log/registry-relay/audit.jsonl rotate: max_size_mb: 100 max_files: 14audit: sink: syslog format: jsonl hash_secret_env: REGISTRY_RELAY_AUDIT_HASH_SECREThash_secret_env is required at runtime and must be a non-whitespace environment variable name containing no = or NUL. The named variable must contain at least 32 bytes of deployment-specific random secret material. Startup fails closed when the name is missing or whitespace-only, or when the variable is unset, empty, or weak.
Registry Relay uses this secret to pseudonymize sensitive audit handles. Values for configured sensitive fields, record primary keys, table identifiers, and attribute-release subject identifiers are written as stable audit hashes instead of raw strings. This gives an auditor a way to see that the same subject or source was accessed more than once without storing the underlying person identifier, address, date of birth, or table id in the audit sink.
The handles are stable only for the same hash secret and audit hash domain. If you rotate the secret, retain the old secret under your audit retention controls for any period when older records must remain comparable, or accept that new records will not match old handles.
Audit output uses registry-platform-audit envelopes with prev_hash and record_hash on every record. These fields detect edits, reordering, and gaps inside the retained log set, starting from the first retained record. They do not prove that earlier records were never deleted, or protect against an actor who can rewrite the entire local sink. Use off-host audit shipping when completeness matters. chain is retained in config for compatibility with older deployments, but platform audit envelopes are always chained.
A normally booted relay always reports keyed integrity hmac in its posture because startup requires the audit hash secret (hash_secret_env); the none value appears only in dev or test configurations that build the posture without that secret.
Audit records are separate from operational logs, which go to stderr as readable text by default. Set REGISTRY_RELAY_LOG_FORMAT=json or REGISTRY_RELAY_LOG_FORMAT=jsonl when operational logs are emitted as JSON Lines for collection or redirected files.
Write policy
Section titled “Write policy”write_policy selects what happens when an audit record cannot be written (for example the sink is unreachable or the disk is full):
audit: sink: file hash_secret_env: REGISTRY_RELAY_AUDIT_HASH_SECRET path: /var/log/registry-relay/audit.jsonl write_policy: fail_closed # fail_closed | availability_firstfail_closed(default): a request whose audit record cannot be written fails with HTTP503and the stable error codeaudit.write_failed(application/problem+json). No request outcome is returned without a durable audit record.availability_first: an audit write failure is logged and the request returns its original outcome unchanged. The deployment stays available even when audit is degraded. Use this only when an explicit availability exception accepts best-effort audit durability.
The policy applies to every audited route. Per-route-family selection is not configurable. The selected policy is reported truthfully as the write_policy fact in the operations posture audit block, so a deployment cannot claim a stronger guarantee than it runs.
Deployment profile
Section titled “Deployment profile”The deployment block lets an operator declare the assurance level a deployment claims. The profile is never inferred from hostname, environment, or network position: it is an explicit statement. Each profile binds a set of gates that check the running configuration and contribute findings at a defined severity.
deployment: profile: production # local | hosted_lab | production | evidence_grade evidence: ingress_rate_limit: true # operator asserts a gateway enforces rate limiting api_key_rotation: true # operator asserts an API-key rotation process exists audit_offhost_shipping: true # operator asserts audit records are shipped off-host audit_ack_cursor_path: /var/lib/registry-relay/audit-ack-cursor.json # local state file the shipper updates audit_ack_max_age_secs: 900 # how old acked_at may get before the cursor reads as stale waivers: - finding: relay.openapi.public reference: OPS-2026-0042 summary: Public API catalog is intentional for this deployment expires: 2026-12-31deployment.profile is required at startup. Use local as the explicit development opt-out, or declare hosted_lab, production, or evidence_grade for deployed environments. When the profile is omitted, startup fails with deployment.profile_undeclared. An unknown profile value is rejected at startup.
Profiles and severities
Section titled “Profiles and severities”Each gate maps to one of four severities per profile:
startup_fail: the process refuses to start. Never waivable.readiness_fail: the readiness endpoint reports not-ready; the process keeps running. Never waivable.finding_error/finding_warn: a posture finding only.
The four profiles escalate from local (binds no hard gates) through hosted_lab and production to evidence_grade (the strictest). For example, evidence_grade requires a signed, governed config bundle: running it from a plain local YAML file trips a startup_fail gate (relay.config.unsigned) and the process refuses to start.
Evidence declarations
Section titled “Evidence declarations”Some controls live outside the relay and cannot be observed by the process (for example ingress rate limiting enforced by a gateway, an API-key rotation process, or audit records shipped off-host to a log collector or SIEM). The evidence flags let the operator assert those controls are in place. Each flag defaults to false, which leaves the corresponding gate active until the operator declares the control.
audit_ack_cursor_path and audit_ack_max_age_secs are not booleans: they point at the regular, non-symlink state file a trusted off-host shipper atomically replaces after each successful hand-off (the registry.audit.ack_cursor.v1 contract: acked_at, last_acked_hash, optional writer; maximum 16 KiB) and set how old acked_at may get before it reads as stale (defaults to 900 seconds). Mount the cursor read-only for Relay and keep it on local storage. Runtime health is ok only when the timestamp is fresh and the watermark equals the live keyed chain tail. Public readiness and posture reads use one blocking worker with a 500 ms deadline; a stalled read fails closed without queuing more readers. Config load rejects audit_ack_max_age_secs without a cursor path, and rejects a cursor on a local file sink without audit_offhost_shipping. stdout and syslog do not need that declaration, but evidence-grade policy still requires their cursor so shipping progress is observed.
Waivers
Section titled “Waivers”A triggered, waivable finding can be suppressed by a waiver that names the finding id, carries a required operator reference, and sets a mandatory expiry date (YYYY-MM-DD). An optional summary can add short operational context:
deployment: profile: hosted_lab waivers: - finding: relay.ingress.rate_limit_missing reference: OPS-2026-0042 summary: Rate limiting is handled by the lab gateway expires: 2026-09-30A waived finding reports status waived instead of its severity effect.
Once the expiry date passes, the waiver stops suppressing the finding and the posture additionally
raises deployment.waiver_expired.
The reference is 1 to 128 bytes, has no surrounding whitespace, uses only letters, digits, .,
_, :, and -, and cannot contain ...
References cannot start, case-insensitively, with Bearer:<value> or Basic:<value>, directly or
after Authorization:.
Use a ticket-style reference such as OPS-2026-0042.
The optional summary is 1 to 256 Unicode characters when present, is already trimmed, contains
no control characters, and cannot be an authorization value or contain a private-key begin
marker.
Omit summary when it is not needed; explicit null is invalid.
Keep credentials and private keys out of both fields.
These rules implement
RS-OP-POSTURE (REQ-OP-POSTURE-011).
A waiver naming a hard gate (startup_fail or readiness_fail severity under the active profile)
fails config load instead of being silently accepted and ignored: there is no config-level
override for a non-waivable gate.
Waiver references and summaries are visible only in the restricted posture tier; the default tier reports finding id, severity, and status without the per-finding waiver object or deployment waivers array.
Findings catalog
Section titled “Findings catalog”| Finding id | hosted_lab | production | evidence_grade |
|---|---|---|---|
relay.admin.public_exposure | error | readiness_fail | startup_fail |
relay.openapi.public | warn | error | error |
relay.ingress.rate_limit_missing | warn | error | error |
relay.oidc.client_allowlist_empty | warn | error | readiness_fail |
relay.auth.api_key_no_rotation_evidence | warn | error | error |
relay.config.unsigned | warn | error | startup_fail |
relay.audit.best_effort | (not bound) | warn | readiness_fail |
relay.audit.sink_missing | error | readiness_fail | startup_fail |
relay.audit.retention_local_only | (not bound) | warn | startup_fail |
relay.audit.shipping_unverified | (not bound) | warn | startup_fail |
relay.audit.shipping_stale | (not bound) | error | readiness_fail |
relay.audit.retention_local_only fires when the audit sink is a local rotating file sink and evidence.audit_offhost_shipping is not declared: a local rotating file caps retention, and an attacker with host access can destroy the audit trail. stdout sinks are exempt (retention is the orchestrator’s log pipeline’s concern) and syslog sinks are exempt (forwarding is the syslog daemon’s own surface).
relay.audit.shipping_unverified and relay.audit.shipping_stale read the ack cursor’s observed health. shipping_unverified fires when any shipping target (stdout, syslog, or an attested local file sink) lacks evidence.audit_ack_cursor_path. It warns under production and refuses startup under evidence_grade, because a missing observation capability cannot heal at runtime. shipping_stale fires when a cursor is configured but is missing, unreadable, malformed, too old, too slow to read, or names a last_acked_hash other than the live keyed audit-chain tail. It fails readiness under evidence_grade and recovers when the trusted shipper advances a fresh cursor to the current tail. Neither hard gate is waivable. Runtime tail equality establishes that the claimed watermark belongs to this chain and the local backlog is zero; the unsigned local cursor is not cryptographic proof of remote receipt. Offline doctor cannot bind to a live chain and therefore reports a fresh cursor as unverified, never ok; an evidence-grade offline check consequently reports the hard shipping gate. The signed-bundle acceptance audit advances the tail before Relay serves requests, so the shipper must run independently of application readiness and acknowledge that boot record before /ready can return 200. Remediation: configure the cursor maintained by the off-host shipper, restore shipping, adjust evidence.audit_ack_max_age_secs if the cadence is legitimately slower, or repair a path or watermark mismatch. Removing the cursor does not satisfy evidence_grade.
The current deployment profile, its findings, and active waivers are reported under deployment in the operations posture (GET /admin/v1/posture).
Boot-time visibility
Section titled “Boot-time visibility”Reduced posture is loud at boot, not only visible on the posture surface. Every config load warns once per waiver-suppressed finding (deployment.gate_waived, with the finding id, reference, optional summary, and expiry), once per expired waiver (deployment.waiver_expired), and once when the profile is undeclared (deployment.profile_undeclared). The serve path additionally writes one operational audit record per waived gate at boot, once the audit pipeline exists: event deployment.gate_waived at audit path /__events/deployment.gate_waived, with error_code set to the gate id. That minimized audit record does not copy waiver metadata.
This boot-time audit write inherits audit.write_policy (see below). Under fail_closed (the default), a failed write aborts startup. Under availability_first, the failure is logged (audit.operational_event_write_failed) and startup continues, so the durable record is best-effort; the per-gate boot log warnings above remain the guaranteed floor.
Datasets
Section titled “Datasets”Each dataset combines private storage tables with public entities:
datasets: - id: social_registry title: Social Registry description: Registry of households participating in Program X owner: Ministry of Social Affairs sensitivity: personal access_rights: restricted update_frequency: monthly conforms_to: - psc:concepts/Person defaults: materialization: snapshot tables: [] entities: []sensitivity, access_rights, and update_frequency are catalog metadata. Set them precisely in production configs; governance reviews depend on them. Allowed values:
sensitivity:public,internal,personal,confidential, orsecret.access_rights:public,restricted, ornon_public.update_frequency:continuous,daily,weekly,termly,monthly,quarterly,annual,irregular,as_needed, orunknown.
defaults is optional. It may provide materialization and refresh defaults for tables in the same dataset. Source configuration stays table-level.
Sources
Section titled “Sources”Sources are configured on each private table. File sources read CSV, XLSX, or Parquet data:
source: type: file path: ./data/social_registry.xlsx format: xlsx: sheet: Individuals header_row: 1 data_range: A1:E100000For CSV files, set format.csv.header_row: 1 when the first row contains column names. For XLSX files, header_row and data_range can be used when a worksheet has notes or title rows around the rectangular table. Source configuration is table-local: put file/database settings and format hints under each tables[].source.
Postgres snapshot sources are supported. Credentials are never stored in YAML:
source: type: postgres connection_env: SOCIAL_REGISTRY_DATABASE_URL table: schema: public name: individuals change_token_sql: "select max(updated_at)::text from public.individuals"connection_env is the environment variable name containing the connection string. Validation and logs may mention the env var name but must not read or print its value. The connection string must set sslmode=require; missing sslmode, sslmode=prefer, and sslmode=disable are rejected when the connector reads the environment variable. The native TLS connector validates the server certificate and hostname against the system trust store. Use read-only database credentials. Registry Relay opens read-only Postgres sessions during controlled ingest and refresh, and credentials must enforce the same boundary at the database. table and query are mutually exclusive; prefer structured table configs for production.
Snapshot ingest reads Postgres through COPY (SELECT ...) TO STDOUT WITH CSV HEADER, then applies the same declared-schema coercion and validation as CSV files. The exported snapshot is bounded by server.max_source_file_bytes. For table sources, Registry Relay projects the declared schema fields from the table and casts them to CSV-friendly values. Extra database columns are ignored. For query sources, write a single SELECT or WITH statement without semicolons; public request input is never interpolated into SQL.
The connection string must include sslmode=require and point to a read-only database role that can SELECT only the configured table or view. Declared schema fields are the exported contract. Public queries run against the ingested snapshot and never cause request-time access to the configured Postgres source.
Minimal source-only form:
source: type: postgres connection_env: SOCIAL_REGISTRY_DATABASE_URL table: schema: public name: individuals connect_timeout: 5s query_timeout: 30sSupported Postgres field mappings are:
string -> textinteger -> bigintnumber -> double precisionboolean -> booleandate -> datetimestamp -> timestamptz rendered as RFC 3339 UTC textRefresh
Section titled “Refresh”refresh: mode: mtime interval: 60srefresh: mode: interval interval: 1hrefresh: mode: manualmtime reloads when the source change token changes. It is supported for file sources and for Postgres snapshot sources only when change_token_sql is configured. interval reloads on every interval. manual reloads only through the admin listener’s table reload route.
Tables
Section titled “Tables”Tables are private storage resources. Their ids do not appear in public URLs.
tables: - id: individuals_table materialization: snapshot source: type: file path: ./data/social_registry.xlsx format: xlsx: sheet: Individuals refresh: mode: mtime interval: 1h primary_key: individual_id schema: strict: true fields: - name: individual_id type: string nullable: false - name: payment_amount type: number nullable: true unit: EURSupported formats are csv, xlsx, and parquet. If format is omitted, the loader infers from the source file extension where possible.
materialization may be snapshot. File and Postgres sources are ingested into snapshots.
Datasource capability matrix
Section titled “Datasource capability matrix”Registry Relay derives datasource capabilities from source.type and materialization. Operators do not configure these flags directly.
| Source | Materialization | Filters | Projection | Limit | Validators and cursors | Provenance |
|---|---|---|---|---|---|---|
file | snapshot | gateway-side | gateway-side | gateway-side | strong snapshot tokens | snapshot-backed |
postgres table or query | snapshot | gateway-side | gateway-side | gateway-side | strong snapshot tokens | snapshot-backed |
materialization: live is rejected at config parse time. Postgres table and query sources are snapshot-only, so operator SQL is executed only during controlled ingest or refresh and never per public request. Future request-time source access requires a request-aware backend with explicit policy enforcement and bounded execution.
At startup, Registry Relay logs one ingest.datasource_capabilities event per configured table.
Field types:
string, number, integer, boolean, date, timestampUse sensitive: true on source or entity fields whose query values need audit
correlation without raw value storage. With audit.hash_secret_env configured,
Registry Relay writes a deterministic hmac-sha256:<digest> audit handle for
those lookup values. As of v0.8, this flag is audit-only: it does not hide a field
from API responses and does not grant or deny read access. Choose it for
identifiers, names, dates of birth, addresses, consent references, and other
values you may need to investigate later without retaining the raw value in
audit logs.
Entities
Section titled “Entities”Entities are the public REST resources:
entities: - name: individual title: Individual description: A person enrolled in Program X table: individuals_table concept_uri: psc:concepts/Person fields: - name: id from: individual_id sensitive: true - name: payment_amount from: payment_amount relationships: - name: household kind: belongs_to target: household foreign_key: household_id access: metadata_scope: social_registry:metadata aggregate_scope: social_registry:aggregate read_scope: social_registry:rows evidence_verification_scope: social_registry:evidence_verification api: default_limit: 100 max_limit: 1000 require_purpose_header: true required_filters: - id allowed_filters: - field: id ops: [eq, in] allowed_expansions: - householdWhen fields is present, only listed fields are exposed. When it is omitted, every table column is exposed. For sensitive datasets, prefer an explicit field list. Use entity read_scope, required filters, purpose-header requirements, and explicit field projection for exposure control; sensitive: true controls audit redaction only.
required_filters is an OR gate, not an AND gate: a principal-bound equality filter on any listed field satisfies the requirement. Use required_filter_bindings for the principal-derived fields that may satisfy the gate, and list multiple required_filters only when each field is an acceptable row boundary.
Row-level authorization scopes are not supported. The row_scope resource setting is rejected by config parsing; model row exposure with dataset/entity read scopes, required filters, purpose headers, and projected fields instead.
Relationships are dataset-local in V1. Cross-dataset workflows must compose client-side with separate scoped calls and separate audit records.
OGC API features
Section titled “OGC API features”Build with --features ogcapi-features to expose spatial entities through the protected /ogc/v1 surface. The feature does not add a top-level standards config block. Instead, opt in per entity with spatial:
spatial: collection_id: facilities title: Public facilities description: Public facility locations from the civic registry. geometry: kind: point longitude_field: lon latitude_field: lat crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 datetime_field: updated_at max_bbox_degrees: 5.0 max_geometry_vertices: 10000V1 supports kind: point and kind: geojson. Point longitude, point latitude, datetime, and bbox helper fields must be exposed entity fields with compatible types. kind: geojson may use optional precomputed bbox fields:
spatial: collection_id: parcels geometry: kind: geojson field: geometry crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 bbox_fields: min_x: bbox_min_x min_y: bbox_min_y max_x: bbox_max_x max_y: bbox_max_yOnly CRS84 is accepted. wkt and wkb parse as reserved geometry kinds but are rejected by V1 validation. Collection ids default to the entity name and must be unique within a dataset. OGC discovery uses metadata scope; feature item reads use read_scope and preserve entity required filters, purpose-header requirements, projection, and audit behavior.
Evidence verification
Section titled “Evidence verification”Evidence offerings expose Registry Notary discovery metadata:
GET /metadata/evidence-offeringsGET /metadata/evidence-offerings/{offering_id}Relay’s evidence-offering routes do not verify claims or evidence.
registry-notary is the verifier for those offerings. The portable metadata
manifest declares public offerings with access.kind: registry-notary,
endpoint_url, discovery_url, and ruleset so clients can discover the
Notary service that owns verification. This handoff is independent of Relay’s
native, profile-bound source consultation API.
access: evidence_verification_scope: social_registry:evidence_verificationevidence_verification_scope remains a scope label for standards adapters and integrations that need to distinguish evidence-oriented access from row reads. It does not enable a Relay-local verification endpoint.
Aggregates
Section titled “Aggregates”Aggregates are declared on datasets and name their source entity:
aggregates: - id: by_municipality title: Individuals by municipality description: Number of individuals by municipality source_entity: individual default_group_by: - municipality_code dimensions: - id: municipality_code label: Municipality field: municipality_code indicators: - id: individual_count label: Individuals function: count column: id unit_measure: people allowed_filters: - field: municipality_code ops: [eq, in] - field: enrolled_on ops: [gte, lte, between] temporal_field: enrolled_on disclosure_control: min_group_size: 5 suppression: omitSupported aggregate functions include the configured V1 set used by tests and examples, such as count, sum, and avg. The runtime config key remains indicators for compatibility; public aggregate APIs expose these configured series as measures. temporal_field is optional; when present, native aggregate temporal.from and temporal.to are translated into the declared range-capable allowed filter for that source-entity field. Dataset measure and dimension discovery is derived from these aggregate declarations, so keep ids stable and labels consumer-friendly. Keep disclosure thresholds explicit and reviewable.
Spatial EDR aggregates
Section titled “Spatial EDR aggregates”Spatial EDR exposure is opt-in. Requires --features ogcapi-edr.
aggregates: - id: by_admin_area description: Individuals by administrative area source_entity: individual # ...dimensions, indicators, disclosure_control as normal... spatial: mode: admin_area collection_id: by_admin_area # optional; defaults to aggregate id dimension: municipality_code # declared dimension id used to join geometry geometry_entity: municipality # entity name that holds geometry rows geometry_id_field: code # field in geometry_entity matching the dimension values geometry_field: geometry # geojson field in geometry_entity bbox_fields: # optional precomputed bbox fields in geometry_entity min_x: bbox_min_x min_y: bbox_min_y max_x: bbox_max_x max_y: bbox_max_y max_geometry_vertices: 10000 # optional; defaults to 10000| Field | Default | Notes |
|---|---|---|
mode | (required) | Must be admin_area |
collection_id | aggregate id | OGC collection identifier; must be unique within the dataset |
dimension | (required) | Declared aggregate dimension id whose values are joined to geometry |
geometry_entity | (required) | Entity that holds one geometry row per dimension value |
geometry_id_field | (required) | Field in geometry_entity that matches dimension values |
geometry_field | (required) | GeoJSON geometry field in geometry_entity |
bbox_fields | absent | Optional precomputed bbox columns; same subkeys as entity spatial.bbox_fields |
max_geometry_vertices | 10000 | Cap on GeoJSON vertices decoded from geometry_field |
geometry_entity must be an entity declared in the same dataset. geometry_id_field and geometry_field must be exposed entity fields with compatible types (string/integer for id, geojson-typed string for geometry). Only kind: geojson geometry is supported for spatial aggregates in V1.
Credential issuance
Section titled “Credential issuance”Relay no longer accepts provenance or entity publicschema config. Remove those blocks, Relay signer environment variables, and probes for /.well-known/did.json, /schemas/{claim_type}/{version}, and /contexts/{vocab}/{version} before upgrading.
Use Registry Notary for credential issuance and verification. Relay metadata can advertise Notary evidence offerings with access.kind: registry-notary; see provenance.md for the migration note.
Production checklist
Section titled “Production checklist”- Source files are read-only to the process.
cache_diris writable and on a filesystem with enough space.- Every env-backed
fingerprint.nameexists in the runtime environment. - No raw key, fingerprint, private JWK, or full environment dump is logged.
- Admin listener, if enabled, is private.
- CORS origins are explicit.
- Personal-data entities use explicit field projections.
- Row and evidence-verification routes that need purpose tracking set
require_purpose_header: true. - Sensitive identifier fields are marked
sensitive: truewhere audit redaction is required. - Audit sink and retention match the deployment’s governance requirements.
- Postgres credentials use a read-only role limited to configured tables or views.