Skip to content

Harden registryctl governed live evidence validation#462

Closed
jeremi wants to merge 19 commits into
mainfrom
agent/adopter-exercise-packets
Closed

Harden registryctl governed live evidence validation#462
jeremi wants to merge 19 commits into
mainfrom
agent/adopter-exercise-packets

Conversation

@jeremi

@jeremi jeremi commented Jul 23, 2026

Copy link
Copy Markdown
Member

What changed

  • Close the public Notary claim-result and nested provenance schemas against unknown fields while documenting the intentional optional redacted_fields public field.
  • Make registryctl bind every returned result to its provenance identifiers, canonical result format, one evaluation ID, the authored project claim version, and the selected environment's generated Notary service.
  • Validate full and field-redaction semantics without echoing redacted values. Every runtime marker uses the bounded top-level stable-ID rule and field-level sets are capped at 64 unique entries whose fields are absent from the returned value. Default full-redaction evidence must contain the current claim ID exactly once. A deployed configured marker set is accepted only when it exactly matches the optional owner-approved redacted_fields set in that claim's expected-result entry.
  • Require target and optional requester handles to use the exact Notary rnref:v1:(hmac-sha256|sha256):<64 lowercase hex> pseudonym shape before evidence is accepted.
  • Reject incompatible mixed-disclosure live requests before source access and provide separate predicate, value, and redacted OpenSPP examples.
  • Bind live results to the current POST using a private request/receipt window with a 30-second remote-clock allowance. Reject stale or future issuance, expired non-null results, inconsistent target/requester refs across claims, and named policy provenance on the API-key evaluation path; explicit null expires_at remains valid.
  • Enforce RFC 3339 timestamps, required nullable expires_at, non-null optional fields when present, and production-classified environment refusal. Environment names are classified by exact prod or production tokens across every stable-ID separator: -, _, and ..

Why

The OpenSPP holdout must produce reviewable evidence through the generic Registry project path without accepting replayed, expired, cross-subject, or mixed evaluation results, responses from another generated Notary service, wrong evaluation policy provenance, raw entity handles, unbound redaction markers, schema drift, disclosure-incompatible requests, or structurally nonconforming Notary output.

Implementer impact

Implementers get early, value-free validation errors when a live request or result diverges from the generated project contract. Existing expected-result files remain valid for Notary's default claim-ID redaction marker. Deployments that configure top-level redaction fields add one bounded owner-approved redacted_fields set to the affected fully redacted claim expectation. The documented examples match Notary's one-disclosure-per-evaluation behavior.

Security review

The validator fails closed before evidence acceptance on unknown fields, stale/future/expired timestamps, inconsistent evaluation refs, unexpected named policy provenance, misbound service/result identifiers or versions, mixed evaluation IDs, invalid format, omitted required keys, forbidden nulls, raw or malformed public handles, stale provenance constants, inconsistent or unbound redaction semantics, and over-disclosure relative to the owner-approved expectation.

Validation

  • governed-live focused tests: 23 passed
  • live-request focused tests: 7 passed
  • registryctl: 277 tests
  • registry-notary-server: 556 passed, 6 external-database tests ignored
  • workspace check and test across all targets
  • workspace Clippy with warnings denied
  • cargo-deny policy checks; only the pre-existing transitive spin 0.9.8 yanked warning remains
  • Notary generated OpenAPI baseline/origin-main contract and Relay origin-main OpenAPI contract
  • formatting and git diff --check
  • independent review of redaction, evaluation, claim-version, selected-service, production classification, and public pseudonym bindings

Issue status

Progresses #357 but does not close it. Closure still requires the product-owner live OpenSPP journey against an exact version and operation through the final generated Relay-to-Notary path.

jeremi added 3 commits July 23, 2026 15:52
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi
jeremi marked this pull request as ready for review July 23, 2026 13:58

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50dc91a6ab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


- Sanitized run result: [link to the schema-valid public result]
- Frozen Registry Stack candidate: [link to the published release]
- Immutable Solmara release and release-pin evidence: [link]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the Solmara-only gate from this generic template

This template is linked from the external integration evidence README, whose scope is the OpenCRVS and DHIS2 integration profiles, but this required evidence item makes every pilot report depend on an immutable Solmara release. For an OpenCRVS or DHIS2 pilot, that release is unrelated, so following the template either blocks valid closing evidence or records a false prerequisite; make this field profile-neutral or move the Solmara-specific gate to the Solmara packet.

Useful? React with 👍 / 👎.

"programme-code",
"household-reference"
],
"format": "application/vnd.registry-notary.claim-result+json",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the live request acceptable to Notary

With this exact request body, registryctl test --live will post to Notary with Accept: application/json from execute_governed_live_test, but Notary's negotiate_request_format rejects a non-empty body format unless the Accept header permits that same media type. Following the runbook therefore fails with format_unsupported before any owner evidence is collected; omit this field from the live request example or have the live runner send the claim-result media type in Accept.

Useful? React with 👍 / 👎.

Unset all four variables after the run.

Repeat the live command with separate private request and expected-result files
for each owner-approved match or no-match case.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't label no-match live runs as matches

When an operator follows this instruction for an owner-approved no-match case, the appended live FixtureReport is still hard-coded to outcome: "match" in execute_governed_live_test, so any retained JSON report records the opposite outcome even though the expected no-match fields passed. That corrupts the match/no-match evidence this packet asks maintainers to review; derive the outcome from the expected claims or use a neutral outcome before recommending no-match live runs.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36459f2071

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +116 to +117
Each claim value must be a non-empty object containing only the exact `value`,
`satisfied`, or `disclosure` fields that the owner intends to verify.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't rely on partial expected fields for redaction evidence

When the owner uses this expected-result shape to prove predicate or redacted disclosure, validate_live_response only iterates the fields present in the expected object and never rejects extra fields returned by Notary, so a response that includes an unexpected value can still pass as long as satisfied/disclosure match. That can turn a live run with over-disclosure into closing evidence; tighten the live validator to reject unapproved result fields or require the expected file to enumerate forbidden fields before using this runbook for redaction evidence.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Comment on lines +41 to +44
want the full multi-service country demo. Solmara Lab does not yet have an immutable release. The
final 1.0 release-candidate reader run remains gated in
[GH#198](https://github.com/registrystack/registry-stack/issues/198) on a tagged Registry Stack
candidate and an atomically pinned Solmara Lab release.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Anchor the Solmara release-gate claim

This paragraph updates a current tutorial with live source-repo/release-gate facts but only backs them with a GitHub issue link; in the docs subtree, source-repo claims need code, tests, fixtures, OpenAPI, or an upstream standard, or a TODO[evidence] marker. Without that anchor, the published tutorial can present an unverified Solmara release status as current guidance, so cite a stable source artifact or mark/demote the claim.

AGENTS.md reference: docs/site/AGENTS.md:L25-L28

Useful? React with 👍 / 👎.


## Closing evidence

- Sanitized run result: [link to the schema-valid public result]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require runner-validated public results

This closing-evidence slot asks only for a schema-valid public result, but the schema does not prove the candidate signatures/provenance, canary scan, source-access classifications, or generated-file comparisons that integration-e2-runner.py validate enforces before a result can close the release evidence gate. In pilot reports that follow the template literally, a merely schema-valid JSON artifact could be cited as closing evidence without the release-provenance checks; make this say the result was validated by the public runner.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

jeremi added 4 commits July 23, 2026 22:47
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e1a9f9bb8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +118 to +121
The live runner rejects keys outside its accepted result, reference, and
provenance structures; does not accept provenance `pack_id` or `pack_version`
keys; requires exact `value`, `satisfied`, and `disclosure` matches; requires
an empty `derived_from` array; and requires a non-zero Relay consultation

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate provenance constants before accepting live evidence

When an owner run uses this as closing evidence, validate_live_response only deserializes the provenance shape and checks derived_from/Relay count, so a response with provenance.schema_version or generated_by.type set to any arbitrary string still passes as long as the disclosed fields match. That can record invalid or stale provenance as a successful live evaluation even though the public claim-result schema constrains those constants; make the runner compare them to CLAIM_PROVENANCE_SCHEMA_VERSION and PROVENANCE_GENERATED_BY_CLAIM_EVALUATION before treating the evidence as valid.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Split cross-area changes before merging

This commit changes crate runtime/client validation, a docs-site tutorial, and release conformance materials together, which crosses three owning areas in one review unit. That makes it harder to apply the different verification gates and security-review notes for adopter evidence versus Notary model behavior; split the crate, docs, and release changes into scoped changes before merging.

AGENTS.md reference: AGENTS.md:L77-L78

Useful? React with 👍 / 👎.

Comment on lines +724 to +729
let result_view: registry_notary_core::ClaimResultView =
serde_json::from_value(result.clone()).map_err(|_| {
anyhow!(
"governed Notary result does not match the closed public claim-result schema"
)
})?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require expires_at in live claim results

When a candidate response omits expires_at instead of returning null, this deserialization still succeeds because expires_at is an Option, and the subsequent fixture comparison only checks value, satisfied, and disclosure. The runbook says the live runner validates the closed claim-result envelope, but the public schema requires expires_at, so a non-conforming response can still become successful live evidence; explicitly require that key before returning success.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
jeremi added 2 commits July 24, 2026 00:13
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi jeremi changed the title Add 1.0 adopter exercise packets Harden registryctl governed live evidence validation Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 121be1b100

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +735 to +738
if result_view.provenance.schema_version
!= registry_notary_core::CLAIM_PROVENANCE_SCHEMA_VERSION
|| result_view.provenance.generated_by.entry_type
!= registry_notary_core::PROVENANCE_GENERATED_BY_CLAIM_EVALUATION

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind live provenance to the returned result

When a live Notary or proxy returns claim fields that match the expected file but provenance.generated_by.evaluation_id, claim_id, or claim_version names a different result, this validator still passes because it only checks the provenance constants and relay count. That can turn source-backed provenance for another evaluation or claim into accepted closing evidence for the requested claim; compare the generated_by identifiers to result_view before accepting the live run.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

registryctl test --project-dir . --environment <owner-environment> --live
```

The command refuses a production environment.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Narrow the production-refusal guarantee

For an owner environment named production-us or another live name whose YAML still has deployment.profile: local, this guarantee is broader than the implementation: execute_governed_live_test blocks only exact prod/production, prod-*, *-prod, or Production/EvidenceGrade profiles before sending the live request. Operators following this may believe the CLI will catch every production target; either narrow the wording to the exact guard or enforce the non-production classification/origin before the request.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Comment on lines +729 to +733
let result_view: registry_notary_core::ClaimResultView =
serde_json::from_value(result.clone()).map_err(|_| {
anyhow!(
"governed Notary result does not match the closed public claim-result schema"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate live result timestamps

When a candidate response uses arbitrary strings for issued_at or non-null expires_at, this serde_json::from_value check still succeeds because ClaimResultView stores both as plain strings, and the later checks never parse them. The public claim-result schema marks those fields as date-time, so a response with invalid freshness metadata can be accepted as live evidence; parse issued_at and any non-null expires_at as RFC3339 before returning success.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a4ff20273

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
for field in expected_result.keys() {
if result.get(field) != expected_result.get(field) {
for field in ["value", "satisfied", "disclosure"] {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject schema-excluded redaction fields in live results

When a live redaction run returns a redacted_fields member, this comparison still passes because the new closed-shape check deserializes into ClaimResultView, where redacted_fields is a known Rust field, and then compares only value, satisfied, and disclosure. Fresh evidence after the earlier over-disclosure fix is that current Notary can populate redacted_fields for redacted disclosures while the public ClaimResultView OpenAPI schema has additionalProperties: false and no redacted_fields, so registryctl can accept a schema-nonconforming/redaction-detail-bearing result as closing evidence; reject that member here or add it to the public schema/expected file before accepting the run.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Comment on lines +729 to +734
let result_view: registry_notary_core::ClaimResultView =
serde_json::from_value(result.clone()).map_err(|_| {
anyhow!(
"governed Notary result does not match the closed public claim-result schema"
)
})?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject nulls for optional public result fields

When a live response includes explicit null for optional fields such as requester_ref or provenance.generated_by.policy_id, this closed-shape check still succeeds because those members are modeled as Option, and the later validation only checks the provenance constants and Relay count. The public claim-result schema makes those properties optional but non-null when present, so a schema-nonconforming provenance/reference result can still be accepted as governed live evidence; either validate against the public schema or explicitly reject null for these optional members before returning success.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Comment on lines +735 to +739
if result_view.provenance.schema_version
!= registry_notary_core::CLAIM_PROVENANCE_SCHEMA_VERSION
|| result_view.provenance.generated_by.entry_type
!= registry_notary_core::PROVENANCE_GENERATED_BY_CLAIM_EVALUATION
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check the result format before accepting live evidence

When a candidate Notary or proxy returns an otherwise matching result with format set to some other media type, this validation still passes because the new constant check only covers provenance. The governed runner sends Accept: application/vnd.registry-notary.claim-result+json and Notary only renders this { results: [...] } envelope on the claim-result path, so accepting a result that self-identifies as a different format can record an inconsistent/non-claim-result response as live evidence; compare result_view.format to FORMAT_CLAIM_RESULT_JSON before accepting the run.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50d4197f82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// The transport model carries these fields, but the closed public
// ClaimResultView schema does not expose them.
const LIVE_RESULT_SCHEMA_EXCLUDED_PATHS: &[&str] = &[
"/redacted_fields",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don’t reject Notary’s redaction marker

When the live case uses a redacted or field-redacted disclosure, current Notary populates redacted_fields in view_claim for those results (crates/registry-notary-server/src/runtime/disclosure.rs lines 110-117), and the new OpenSPP runbook includes a redacted expected claim. Fresh evidence after the earlier thread is that this commit now hard-rejects /redacted_fields before comparing the claim, so any real redaction evidence run fails against the generated Notary instead of producing closing evidence; either stop emitting the field on the public response/add it to the public schema, or don’t reject the current Notary shape here.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Comment on lines +99 to +103
"claims": [
"social-registry-record-exists",
"social-registry-active",
"programme-code",
"household-reference"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Split the sample live request by disclosure

With this exact claim list and no disclosure, Notary picks one disclosure profile for the whole evaluation from the first requested claim; here that is predicate, while programme-code and household-reference are authored as value and redacted with downgrade: deny in the generated Notary config, so the request fails with DisclosureNotAllowed before any governed live evidence is returned. Split the example into per-disclosure live requests, or make the request use only claims that can all be rendered under the same disclosure mode.

Useful? React with 👍 / 👎.

Comment on lines +750 to +753
let generated_by = &result_view.provenance.generated_by;
if generated_by.evaluation_id != result_view.evaluation_id
|| generated_by.claim_id != result_view.claim_id
|| generated_by.claim_version != result_view.claim_version

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject mixed evaluation IDs

When multiple claims are requested, this loop only verifies that each provenance record matches its own result, so a candidate proxy or stale cache can assemble a response where claim A comes from one evaluation_id and claim B from another and still pass as one successful live-evaluation fixture if the claim values match. The Notary evaluation path creates one evaluation for the whole request, so record the first result_view.evaluation_id and reject any subsequent mismatch before accepting the evidence.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

if !result_view.provenance.derived_from.is_empty() {
bail!("governed Notary result provenance derived_from must remain empty");
}
let claim_id = result_view.claim_id.as_str();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind claim versions to the project

After deserializing the result, the validator only checks claim_id against the requested set and never compares result_view.claim_version to the version authored for that project claim. If a stale Notary deployment or replayed response still has the same claim IDs and values but an older claim version, this live run is accepted as current closing evidence even though it did not exercise the exact generated project contract; carry expected claim versions out of validate_live_request and reject mismatches here.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required DCO sign-off

This commit message has no Signed-off-by: trailer, but the repository requires every commit to carry a DCO sign-off. As-is, the change can be blocked by the DCO gate even if the code is correct, so recreate the commit with git commit -s or otherwise add the sign-off before merging.

AGENTS.md reference: AGENTS.md:L70-L70

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 623e1890c9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +752 to +756
let generated_by = &result_view.provenance.generated_by;
if generated_by.evaluation_id != result_view.evaluation_id
|| generated_by.claim_id != result_view.claim_id
|| generated_by.claim_version != result_view.claim_version
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind provenance to the generated Notary service

When the live origin points at a stale Notary or proxy for another generated deployment, this check accepts a result whose provenance.generated_by.service_id does not match the deployment.notary.service from the selected environment as long as the claim IDs, versions, and values match. The runtime populates that field from the Notary config's service id, so carrying the expected service id into validate_live_response and comparing it here would prevent evidence from being attributed to the wrong candidate service.

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ce574c244

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +627 to +630
|| environment.starts_with("prod-")
|| environment.starts_with("production-")
|| environment.ends_with("-prod")
|| environment.ends_with("-production")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Block prod names with every stable-id separator

When the owner environment is named prod_us, production.eu, eu_prod, etc. while deployment.profile is still local, this production check returns false even though those are valid environment stable IDs (the loader permits _ and .). Fresh evidence after the earlier production-name thread is that the current guard only covers hyphen-separated prod/production names, so registryctl test --live can still post governed live requests to a production-labeled environment; split/normalize on every allowed stable-id separator or require an explicit non-production profile before sending.

Useful? React with 👍 / 👎.

Comment on lines +737 to +742
let result_view: registry_notary_core::ClaimResultView =
serde_json::from_value(result.clone()).map_err(|_| {
anyhow!(
"governed Notary result does not match the closed public claim-result schema"
)
})?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject raw entity handles in live results

When a live Notary or proxy returns otherwise matching claim fields but puts a raw subject/requester identifier in target_ref.handle or requester_ref.handle, this closed-shape deserialize still accepts it because those handles are just strings and no later check constrains the pseudonymous rnref:v1: form that current Notary emits. That can turn a data-minimization failure into passing governed evidence; require public refs to use the Notary pseudonymous handle shape, or compare them with owner-approved expected refs, before accepting the run.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Comment on lines +869 to +871
if result.value.is_some()
|| result.satisfied.is_some()
|| result.redacted_fields.is_empty()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate full-redaction marker contents

When a redacted live result returns value: null and satisfied: null, this branch accepts any non-empty redacted_fields, so a proxy can include an empty string, duplicates, other-claim, or even a value-bearing marker such as ssn=123 and still pass as redaction evidence because the expected file only compares value, satisfied, and disclosure. Current Notary's marker is the claim id or top-level redacted field names, so apply the same bounded/top-level validation here before retaining the public evidence.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

jeremi added 2 commits July 24, 2026 05:42
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc304b58b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +940 to +944
|| *field == "value"
|| field.contains('.')
|| field.contains('[')
|| field.contains(']')
|| value.contains_key(*field)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Constrain field-redaction markers before accepting evidence

When a value-disclosure result includes redacted_fields, this check only rejects path-like names, duplicates, value, and fields that still appear in the returned object; it does not apply the bounded stable-id check used for owner-approved full-redaction markers. A live Notary proxy could therefore return redacted_fields: ["ssn=123"] with that field removed from value and still pass as governed evidence, leaking a redacted value through metadata. Reuse the same bounded top-level marker validation for runtime field-redaction markers before accepting the result.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 970b168f44

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +772 to +775
if OffsetDateTime::parse(&result_view.issued_at, &Rfc3339).is_err()
|| result_view.expires_at.as_deref().is_some_and(|expires_at| {
OffsetDateTime::parse(expires_at, &Rfc3339).is_err()
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject stale live claim-result timestamps

When a candidate Notary proxy or stale cache replays an older response whose claim values, service id, and claim versions still match, these lines only parse issued_at/expires_at; they never require issued_at to fall within the current live run or a non-null expires_at to still be in the future. Even an already-expired result is accepted, so registryctl test --live can record passing live evidence without exercising the current candidate deployment; compare the timestamps to the request/run time before accepting the result.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

if !result_view.provenance.derived_from.is_empty() {
bail!("governed Notary result provenance derived_from must remain empty");
}
validate_live_result_reference_handles(&result_view)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject mixed target refs in live evidence

When a multi-claim live response keeps the same evaluation_id but combines results for different target_ref or requester_ref handles, this validation only checks that each handle has the pseudonymous shape and then accepts the evidence if the claim values match. The Notary evaluation path renders all requested claims for one request target/requester, so a proxy or stale cache can splice facts from different subjects into one passing live-evaluation fixture; remember the first refs and reject subsequent mismatches before accepting the response.

Useful? React with 👍 / 👎.

Comment on lines +835 to +837
"/provenance/generated_by/policy_id",
"/provenance/generated_by/policy_version",
"/provenance/generated_by/policy_hash",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject policy provenance on API-key live runs

In registryctl test --live the evaluation is sent with the deployed x-api-key machine-client credential, and current Notary leaves policy_id, policy_version, and policy_hash unset for non-subject-access evaluations; this check only rejects explicit nulls, so a proxy can return otherwise matching results from a subject-access or other named-policy evaluation and still have them accepted as governed live evidence for the API-key run. Require these optional policy fields to be absent, or compare them with an owner-approved expected policy, before accepting the result.

AGENTS.md reference: AGENTS.md:L79-L81

Useful? React with 👍 / 👎.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

jeremi commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

Superseded by #469, the single ready integration PR for this reviewed 1.0 batch. This PR's exact head is preserved as an ancestor of #469, so its implementation and commit history are retained; only the merge path has been consolidated.

@jeremi jeremi closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant