Skip to content

feat(rstack): generate hosted ThunderID configuration from product identity facts - #2578

Merged
jeremi merged 1 commit into
mainfrom
feat/identity-generator
Nov 7, 2026
Merged

jeremi merged 1 commit into
mainfrom
feat/identity-generator

Conversation

@jeremi

@jeremi jeremi commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Summary

  • rstack identity generate thunderid writes hosted ThunderID configuration from each product's own identity facts plus one operator deployment file. rstack identity check checks either document offline.
  • bregctl, caseworkctl, evidencectl and messagingctl each gain generate identity-facts, which writes a registry.identity-facts/v1 document from that product's own configuration.
  • Two new platform formats, registry.identity-facts/v1 and registry.identity-deployment/v1, with generated JSON Schemas in products/platform/schemas, examples, and editor mappings.
  • Release manifests pin external images with an explicit kind: image-pin. Every release after 0.45.0 must carry the ThunderID pin.
  • The operator guide adds a page: Generate identity configuration.

Also in this branch, unrelated to identity: one commit removes the retired Notary config schema (schemas/registry-notary.config.schema.json). No gate or product reads it. Only the historical v0.12.0 release notes mention it, and a docs test writes its own copy.

BREAKING

  • Identity generation derives every principal sub from the issuer, in a separate namespace per kind. Every sub changes once when a deployment moves to the generator. Update stored sub values, token mappings and scope references from identity-receipt.json. There is no compatibility shim.
  • registry-thunderid-tooling refuses a machine-client JWKS that carries private or symmetric key members, in development mode too.

Security review notes

This change generates hosted ThunderID configuration from each product's own identity facts and one operator deployment file (rstack identity generate thunderid, rstack identity check), adds identity-facts writers to bregctl, caseworkctl, evidencectl and messagingctl, and pins external release images. The branch had an independent security review and three fix rounds, and every finding was rechecked against each round.

Trust model: the operator who writes the deployment file and the product configurations is trusted. The generator's job is to refuse inputs whose meaning differs from what the grants say, to keep secrets out of everything it writes, and to fail visibly with no partial output.

Authorization meaning

  • Facts from several products merge only when they agree. A different issuer, the same product twice, the same audience twice, one claim declared with two types, or one assertion issuer stated two ways is refused.
  • An empty allowedClients means unrestricted in every writer, matching the runtime verifier. An empty requester list is refused rather than read as "nobody". Evidence maps its admission gate by variant, so a future variant becomes a compile error instead of being read as unrestricted.
  • Access profiles can state the claim values that select them (selectedBy). Evidence states its actor kind and requester tags. Messaging states its scopes when it reads them from a renamed scope claim.
  • Among profiles that state selectors, the generator refuses claims that select a profile the principal was not granted. It also refuses a grant the principal's claims cannot select. Evidence requires every profile to name at least one requester tag, so each Evidence profile states a selector. A profile with no selector (every BReg and Casework profile, and Messaging's unless it reads a renamed scope claim) is not checked, because the facts do not determine who it selects. The refusal is conservative: any matching profile that is not granted is refused, including one the runtime would narrow further.
  • A profile that none of the principal's clients can present (the product's allowed clients and the profile's requester clients both decide) is never counted as selected for it. Messaging resolves a profile by client before it reads a scope, so two profiles may require the same scopes for different requester clients. A user reaches a product only through that product's browser applications that sign in the user's type: every application admits users with no type, and an extension type only where its userTypes list it. One per-user rule decides this for both the grant check and the selection check, so a grant no such application can present is refused.
  • A test runs Evidence's runtime profile selector and the generator's selection over the same claim sets. It checks that every profile the runtime selects, the generator also counts as selected, so refusing ungranted profiles covers what the runtime would serve. The test asserts that it is not vacuous.
  • A renamed scope claim may carry only scopes that the principal's grants give, counting only the non-exchange profiles of the products that read that claim.
  • registry_actor_kind follows the principal. Users are always human, and a machine client is never human:
    • a stated value that contradicts the principal kind is refused;
    • granting a machine client a profile that only human principals can be selected for is refused;
    • otherwise a machine client takes the kind its granted profiles agree on, or service.
  • BReg facts always declare the actor-kind claim and state each human or service profile's required kind. A profile that requires an agent is left out of the facts, since BReg admits it only through a delegated or task-grant token, which the generator does not issue. The facts command names each profile it leaves out, and a grant naming one is refused. The receipt lists users in their own users section.
  • registry_purpose follows the grants for users as for machine clients: a stated purpose must be one a granted profile lists, one granted purpose is set automatically, and several need an explicit choice. Only products that read the claim count, so Evidence (which takes its purpose from the request) no longer makes a token ambiguous.
  • Casework facts mark the profiles Casework admits only for a person (staff, supervisor, administrator, and a requester profile a trusted producer names as its initiator) as requiring a human actor, so granting one to a machine client is refused.
  • Browser application tokens carry registry_actor_kind whenever a product reads it, listed or not, as machine-client tokens already carry their derived claims. BReg refuses a user token without it. They also carry registry_purpose when their product requires a purpose. An application that already lists the maximum of 16 token attributes without one of these is refused, and rstack identity check reports the same refusal.
  • Each access profile in the facts lists the claims its product refuses a token without (requiredClaims; sub is never listed). BReg states a principal claim other than sub and every row-boundary claim of the profile; Casework and Messaging a principal claim other than sub; Evidence registry_actor_kind, its audience claim, its requester-tags claim and a principal claim other than sub. Generation and rstack identity check refuse a machine client or user granted the profile without each claim (identity.deployment.required-claim-missing), and a browser application whose token attributes leave one out (identity.deployment.required-claim-not-projected). A derived actor kind or purpose counts as carried. Merged facts may disagree on whether a claim is required, but not on its type.
  • An explicit registry_actor_kind on a machine client, a user or a facts selectedBy list must be human, agent or service, the kinds every product's runtime parses, whatever the granted profile states (identity.deployment.unknown-actor-kind).
  • Access profiles may state the actor kinds they admit (admittedActorKinds) and the claims that refuse them (refusedClaims). Evidence states [human, service] for a standing profile that names no kind, because an agent without a task grant reaches only profiles that name agents, and states its actor claim as refused on every profile that is not delegated. Generation refuses a principal of a kind a granted profile does not admit (identity.deployment.profile-refuses-actor-kind), stated or derived, and a principal carrying a refused claim (identity.deployment.profile-refuses-claim).
  • BReg facts state every compiled permission surface: record types, statistical datasets, Actions and Proposals. A profile that grants only an Action or a Proposal is stated with the row-boundary claims of its targets, and each claim a verified-claim lookup reads is required. bregctl refuses a row boundary on a boolean or integer field, because generated tokens carry every claim as a string and BReg would refuse it at authentication.
  • Every set list in both identity formats (scopes, purposes, requester clients, selectedBy values, required, admitted and refused lists; grant profiles, redirect URIs, token attributes, user types and user-type attributes) refuses a repeated item, and the schemas declare uniqueItems.
  • Protocol and registered claim names (JWT, token exchange, JWT access tokens, OpenID Connect) cannot be declared as product claims. The registry grant namespace stays protected.
  • BReg and Casework refuse a customized scopeClaim for hosted generation, because hosted tokens carry scopes in scope. Casework also refuses a customized human-identity claim, BReg a renamed purpose claim, and Evidence a renamed actor-kind, purpose, approver or grant claim, because generated tokens carry only the default ones. Evidence also refuses two task authorities whose issuers differ but derive the same assertion issuer id, and merging refuses two products that state different issuers under one id.
  • A deployment allows up to 256 browser applications and 256 users, matching what the deployment file accepts. Development sessions keep their limits of 8 and 32.

Subject identifiers (BREAKING, one-time re-key)

  • Every derived identifier now comes from {seed}:{kind}:{id}, with a separate namespace per kind: machine clients, browser applications, users, extension user types, connections, organization units, and roles per kind. The seed comes from the issuer. An identifier of one kind can no longer equal one of another kind, and the generator asserts that all derived identifiers are distinct.
  • Every sub changes once when a deployment moves to the generator. Update stored sub values, token mappings and scope references from identity-receipt.json. There is no compatibility shim.
  • Each sub is derived from the exact issuer string, so changing the issuer (even a trailing slash) re-keys every principal. The operator guide treats an issuer change as a migration.

Secrets and placeholders

  • Secrets are referenced only as secret:env/NAME, unique per deployment. Deployment output carries {{.NAME}} placeholders and never a value. No secret file is opened, and the schema directory carries no placeholder.
  • Template delimiters {{ and }} are refused in every string and member name of the facts and deployment documents, read after YAML parsing so an escaped delimiter is found too. After rendering, the generator checks that each {{.NAME}} placeholder for a variable the deployment names appears exactly once, and that the output holds no other template delimiter.
  • Rendering goes through a YAML serializer, so no input value can change the document structure.
  • Until a real ThunderID run confirms when its loader substitutes, the operator guide limits secret values to a safe character set.
  • rstack runs each product tool with a cleared environment, a fixed allowlist of variables and no stdin. Facts are written to a private scratch directory that is removed on success and on failure.

Public keys

  • Machine-client key files must be public RSA, EC or OKP keys with exactly their public members (plus kid, use, alg, key_ops). Private members (d, p, q, dp, dq, qi), symmetric keys (k), additional primes (oth), unknown types and unknown members are refused.
  • A key file must be a JSON object whose only member is keys; any other top-level member is refused and named.
  • Every key's member names and values are checked for template delimiters, after JSON parsing so an escaped delimiter is found too.

Process and filesystem

  • rstack holds the selected release generation's lease until generation finishes, so a concurrent cleanup cannot remove a tool between its version check and its run.
  • Operator paths are made absolute before they reach a product tool, so no path can be read as an option and tools that require absolute paths accept them.
  • The version check matches each tool's version to rstack's. It guards against a mismatch, not against a substituted binary. A binary named through RSTACK_<PRODUCT>CTL_BIN is trusted by the operator who names it, and the guide says so.
  • Output is generated fully in memory and written into a private staging directory (directories 0700, files 0600).
    • The output directory is then created exclusively (0700) and the finished entries are moved into it. An output path that exists at the start, or appears at any point before publication, is refused and left untouched.
    • The receipt, identity-receipt.json, moves in last, so an output without it is incomplete. The operator guide says not to use such an output. The order holds against an interrupted process; the generator does not sync the directory, so it is not guaranteed across a power loss.
    • A refused run writes nothing. A failed move removes only the directory this run created.
    • The output directory exists, empty and private, for a moment before the entries move in.
  • rstack identity check reports an input-caused refusal with its reason instead of as an internal error. rstack identity generate chooses its exit class by error type, not message text: an input refusal or an existing output exits 1, and a filesystem or internal rendering failure exits 3.

Dependency boundary

  • rstack links no signing, OIDC or token-session code. The tooling crate's session feature carries those dependencies, and rstack builds without it.
  • A CI gate reads rstack's normal dependency tree and fails on the platform crypto, OIDC and HTTP utility crates, FIPS or AWS-LC libraries, JWT and elliptic-curve signing crates, and async HTTP stacks. It runs on every Rust or manifest change. CI also builds rstack alone, as the release does.
  • The validators the tooling crate copies from the platform are byte-identical to the originals today. A sampled parity test compares them.

Release provenance

  • An external image pin is marked kind: image-pin explicitly. Declaring images without that kind, any other kind, or images on a documentation product is refused.
  • Each pin needs a repository, a 40-hex source commit, a version, and digest-pinned images.
  • The ThunderID pin must equal the tooling's pinned version file: image, commit, version and repository. Every release after 0.45.0 must carry it.

Stated limits

  • Product tools run with a cleared environment. A product configuration that falls back to a default when a variable is unset gets the default during facts generation. Set the values explicitly in the configuration used to generate facts.
  • Casework and Evidence locate an assertion authority's keys at {issuer}/.well-known/jwks.json. The task-authority issuer must be Casework's public base URL.
  • Static claims are emitted into tokens for every audience the client requests, not only the product that declared them.
  • A user satisfies a product's requester-client restriction through any one admitted browser application of that product. The product runtime still checks the actual client.
  • Two products may use the same scope string. The verifier's audience binding keeps them apart.
  • Facts accept a loopback http issuer for development. Deployment generation refuses any issuer that is not https.
  • The validator parity test is sampled, not exhaustive.
  • User claims are single strings, so a profile that a renamed (array) scope claim selects can be granted only to machine clients.
  • BReg and Casework do not support a customized scope claim for hosted generation, Casework no customized human-identity claim, BReg no renamed purpose claim, and Evidence no renamed member of authentication.oidc.claims. A deployment that renames those claims cannot use hosted generation yet.
  • The secret character-set guidance stands until a real ThunderID run confirms the loader's substitution order.
  • The output is created exclusively, but its entries are then moved in, and a failed run's output removed, by path rather than through an open directory handle. Generate into a parent directory that only the operator can write. A shared temporary directory with the sticky bit also works.
  • Required claims are checked for presence, not value. An empty Evidence tag list, an audience that is not a URL, or a multi-valued value for a boundary that needs one value passes generation and is refused at runtime.
  • A profile reached by token exchange is not checked against the presenting client's required claims or selectedBy, because the exchanged token takes its claims from the assertion. Its actorKind is checked against the client's own token.
  • BReg's required claims are combined across the record types a profile is granted on, so a claim bound on only some record types is required of every principal granted the profile. This is stricter than the runtime for requests to the other record types.
  • The actor kind is enforced through actorKind and the derived claim, not through requiredClaims. Casework's task-status profile requires a service and its assertion profile an agent.
  • A client that reaches Casework's assertion endpoint only through another product's exchange binding is kind-checked only if it is also granted Casework's assertion profile.
  • A machine client stated or derived as an agent and granted a BReg profile that only writes passes generation but is refused at the write: BReg admits agent writes only through a delegated or task-grant token, which hosted generation does not issue.
  • BReg facts do not state a profile that BReg reaches only through a task grant, so a deployment that grants one is refused as an unknown profile.
  • A refused key-file member is located by the deployment's key-file entry followed by the member name, although the member is in the key file. A member name that is not plain text of at most 32 characters is reported as other, which cannot be told apart from a member actually named other. Both are diagnostics only; no action is planned.

Not verified

  • Loading the generated configuration into a real ThunderID 1.0.1.
  • ThunderID's {{.NAME}} substitution: whether it runs before or after YAML parsing, and over which fields.
  • A real rstack identity generate thunderid run driving all four product tools from an installed release.
  • Release builds of rstack and the product tools.
  • The new CI steps on a hosted runner.

Open decision

  • The reserved claim names include the OpenID Connect profile claims (email, name, preferred_username and others), so a product cannot declare one as its own claim. A BReg deployment that reads its principal from one of those claims cannot generate facts today: the generator fails closed. Whether to allow that case is an open product decision.

Checks run

On the final tree, all passing:

  • cargo fmt --check;
  • cargo clippy --all-targets -D warnings for registry-thunderid-tooling (default features, and schema,session), registry-rstack, registry-bregctl, registry-caseworkctl, registry-messagingctl and registry-evidencectl;
  • registry-rstack built alone;
  • tests:
    • registry-thunderid-tooling: 201 with schema,session (including the committed-schema check);
    • registry-rstack: 115;
    • registry-bregctl: 942;
    • registry-caseworkctl: 263;
    • registry-messagingctl: 137;
    • registry-evidencectl: 990;
  • Vale on the changed docs and changelogs (no new findings);
  • the configuration-conventions lint (137 findings, all recorded; none unrecorded or stale);
  • runtime configuration conformance for the identity facts and identity deployment formats (119 pass, 0 fail, 66 not applicable), including the repeated-item cells;
  • the identifier catalogue check (regenerated and reproducible);
  • the rstack dependency-boundary gate.

Not re-run on the final tree: the strict full conformance corpus (left to CI), the BReg and Messaging contract checks (passed on the previous round's tree), and the registry-thunderid-tooling default-feature test run (its tests are a subset of the schema,session run).

On the first fix round's tree, all passing:

  • cargo clippy --all-targets -D warnings across the ten changed crates;

  • tests:

    Crate Passed Ignored
    registry-evidence 1021 8
    registry-evidencectl 980 13
    registry-caseworkctl 258 3
    registry-bregctl 931 13
    registry-messagingctl 136 0
    registry-cli-docs 28 0
  • the Evidence contract and source-neutrality checks, the Casework and Messaging schema checks, and the BReg source-neutrality check;

  • release tooling tests and the release source-model check;

  • CI change-selection tests;

  • the gate inventory;

  • the YAML reader boundary check;

  • cargo deny (advisories, bans, licenses, sources);

  • the docs release-metadata test.

The strict configuration conformance corpus passes on the second fix round's tree: 6301 cells, 4671 pass, 3 expected failures, 0 fail. Later rounds change no configuration check; their new refusals are in facts and configuration generation only.

Later rounds change only the tooling crate, rstack, the four product tools' facts writers, the identity-facts schema and examples, changelogs, the Casework README and the operator guide, with no manifest or lockfile change. Those crates were re-run on the final tree (above); the first round's results carry over for the other crates.

Squashed onto current main (conflicts only in changelogs; the generated identifiers catalogue was regenerated and its check passes). On that tree, cargo check --locked --all-targets passes for registry-rstack, registry-thunderid-tooling, registry-bregctl, registry-caseworkctl, registry-evidencectl, registry-messagingctl and registry-breg, and the registry-thunderid-tooling and registry-rstack tests pass.

Follow-up

  • The next release manifest must add the ThunderID image pin with kind: image-pin.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-07T12:49:58.485339Z edd3238 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: ef1d3e964e

ℹ️ 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 thread crates/registry-rstack/src/identity.rs Resolved

@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: 583f607771

ℹ️ 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".

@jeremi
jeremi force-pushed the feat/identity-generator branch from 583f607 to 52aa9db Compare November 7, 2026 09:52

@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: 52aa9dbcdf

ℹ️ 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".

@jeremi
jeremi force-pushed the feat/identity-generator branch from 52aa9db to 127ac4a Compare November 7, 2026 10:09

@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: 127ac4a81b

ℹ️ 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".

@jeremi
jeremi force-pushed the feat/identity-generator branch from 127ac4a to c9d493e Compare November 7, 2026 10:41

@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: c9d493ef56

ℹ️ 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".

@jeremi
jeremi force-pushed the feat/identity-generator branch from c9d493e to b531dec Compare November 7, 2026 11:16

@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: b531dec0cd

ℹ️ 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".

@jeremi
jeremi force-pushed the feat/identity-generator branch from b531dec to 98cc5b3 Compare November 7, 2026 11:52

@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: 98cc5b3f54

ℹ️ 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".

…entity facts

- `rstack identity generate thunderid` writes hosted ThunderID configuration from each product's own identity facts plus one operator deployment file. `rstack identity check` checks either document offline.
- `bregctl`, `caseworkctl`, `evidencectl` and `messagingctl` each gain `generate identity-facts`, which writes a `registry.identity-facts/v1` document from that product's own configuration.
- Two new platform formats, `registry.identity-facts/v1` and `registry.identity-deployment/v1`, with generated JSON Schemas in `products/platform/schemas`, examples, and editor mappings.
- `caseworkctl generate identity-facts` marks the profiles Casework admits only for a person as requiring a human actor, and refuses a customised human-identity claim; `bregctl` refuses a renamed purpose claim; `evidencectl` refuses a renamed actor-kind, purpose, approver or grant claim, and two task authorities whose issuers derive one assertion issuer id. Hosted generation does not support either.
- Each access profile in the identity facts states the actor kind its product pins (`actorKind`) and lists the claims its product refuses a token without (`requiredClaims`). Generation and `rstack identity check` refuse a machine client or user granted the profile without them, and a browser application that does not project them. On a profile reached by token exchange, `actorKind` applies to the client's own token.
- An explicit actor kind on a client, user or selector must be one the runtimes parse (`human`, `agent` or `service`), whatever the profile states.
- Access profiles may state the actor kinds they admit (`admittedActorKinds`) and the claims that refuse them (`refusedClaims`). Evidence states both, so generation refuses an agent on a standing profile and a principal carrying the actor claim on a profile that is not delegated.
- BReg facts state Action and Proposal profiles and the claims verified-claim lookups read, and `bregctl` refuses a row boundary on a boolean or integer field.
- Every set list in both identity formats refuses a repeated item.
- Release manifests pin external images with an explicit `kind: image-pin`. Every release after 0.45.0 must carry the ThunderID pin.
- The operator guide adds a page: Generate identity configuration.
- Remove the retired Notary config schema (`schemas/registry-notary.config.schema.json`); no gate or product reads it.

BREAKING CHANGE: identity generation derives every principal `sub` from the issuer, in a separate namespace per kind. Every `sub` changes once when a deployment moves to the generator. Update stored `sub` values, token mappings and scope references from `identity-receipt.json`. There is no compatibility shim.

BREAKING CHANGE: `registry-thunderid-tooling` refuses a machine-client JWKS that carries private or symmetric key members, in development mode too.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi
jeremi force-pushed the feat/identity-generator branch from 98cc5b3 to edd3238 Compare November 7, 2026 12:47
@jeremi
jeremi added this pull request to the merge queue Nov 7, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 7, 2026
@jeremi
jeremi added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit bbf90d4 Nov 7, 2026
74 checks passed
@jeremi
jeremi deleted the feat/identity-generator branch November 7, 2026 15:52
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