Registry stack documentation: machine-readable Markdown.
Index of all pages: https://docs.registrystack.org/llms.txt
Full corpus: https://docs.registrystack.org/llms-full.txt

# Configure Registry Mint

> Configure Registry Mint to issue short-lived, audience-bound access tokens for Evidence Gateway or Registry Relay when a deployment has no identity provider.

Configure Registry Mint when Evidence Gateway or Registry Relay needs short-lived access tokens
for a closed set of registered machine clients and the deployment has no identity provider to
issue them.

## When to use Registry Mint

Registry Mint is a supporting service, not a product pattern of its own. Use it when a resource
server such as Evidence Gateway or Registry Relay needs signed, expiring, audience-bound tokens
and standing up a general-purpose identity provider is not an option for the deployment.

Version 1 runs one active Mint process. Its client-assertion replay cache is memory-only and
clears on restart. Do not claim high availability or durable replay protection for this deployment
shape.

Registry Mint answers a narrower question than a shared JWKS can. A pooled key set can only say
that a token was signed by a trusted key; it cannot say which caller signed it or what that
caller is permitted to assert. Registry Mint splits the two questions across two places: the
client registry binds a client id to that client's own public keys and to the authority Registry
Mint will assert for it, and the token endpoint verifies an incoming request against the keys of
the client it claims to be, then writes the authority from the registry, never from the request.

For the whole round trip in one picture, from a client holding only its own private key to a
signed assertion, see
[how a client, Registry Mint, and Evidence Gateway interact](../../reference/mint/#how-a-client-registry-mint-and-evidence-gateway-interact).

## When not to use Registry Mint

Skip Registry Mint when an identity provider already issues client-credentials tokens for the
deployment. Registry Mint exists only for the case where none does; pointing Evidence Gateway at an
existing IdP's token endpoint and JWKS does not require Registry Mint at all.

Registry Mint is also not a place to route caller identity for people. It authenticates
registered machine clients by private key, and any token bound to one named person rides inside
a client's own signed request rather than a separate login. If a deployment needs a person to
authenticate directly, that is an identity provider's job, not Registry Mint's.

## Before you start

You need:

- The registered clients this deployment will serve. Each client needs one id, one principal,
  and either Evidence authority fields or a standard authorization block with fixed scopes and
  direct claims.
- A governed public P-256 JWK for Registry Mint itself. Its service `kid` is the derived RFC 7638
  thumbprint. Strict deployments use a workload-local Vault or OpenBao Transit proxy; supervised
  local development may use an owner-only local P-256 private JWK.
- An independently generated audit HMAC key containing at least 32 bytes, stored in another
  owner-only, non-symlink file.
- A private JWK per client. Registry Mint only ever stores and reads each client's public half;
  keep the private half with the client.
- A directory to hold one registration file per client.
- An owner-only directory on durable storage for the keyed Mint audit chain. Plan its capacity,
  backup, and retention because Registry Mint rotates segments but never deletes or compacts
  them.
- For an Evidence deployment, the claim names Evidence Gateway reads for its principal,
  requester tags, evidence audience, and grant pair. Registry Mint's `accessTokens.claims` must
  match them exactly. A scoped-only Relay deployment omits this block.
- TLS in front of Registry Mint for strict deployments. Registry Mint serves plain HTTP and expects
  TLS termination it does not manage. Supervised local development alone admits the exact
  `http://127.0.0.1:<nonzero-port>` issuer and matching token and JWKS paths.

## Configure the deployment

Registry Mint reads one YAML document. Every relative path in it resolves against the document's
own directory, and every field in it is startup-only: changing issuer identity, signing keys,
the listener, or token policy means restarting the process.

```yaml
version: 1
validationMode: strict
issuer: https://mint.example.org
listener:
  address: 127.0.0.1
  port: 8081
signing:
  algorithm: ES256
  activePublicJwkFile: public-keys/<thumbprint>.jwk.json
  publishedPublicJwkFiles: []
  revokedKeyIds: []
signer:
  kind: transit
  unixSocketPath: /run/registry-mint/transit-proxy.sock
  mount: transit
  keyName: mint-signing
  keyVersion: 7
  timeoutMilliseconds: 2000
secretProviders:
  file:
    root: /run/registry-mint/secrets
audit:
  path: audit/mint.jsonl
  maximumFileBytes: 1073741824
  hashKeyRef: secret:file/audit-hmac-key
  hashKeyVersion: 1
accessTokens:
  audiences: [evidence]
  lifetimeSeconds: 300
  claims:
    principal: sub
    requesterTags: evidence_tags
    evidenceAudience: evidence_audience
    grantId: evidence_grant_id
    grantAuthority: evidence_authority
clientAssertion:
  audience: https://mint.example.org/token
  algorithms: [EdDSA, ES256, RS256]
clients:
  directory: clients
```

`issuer` must be an `https` URL with a host and no credentials, query, or fragment; resource
servers compare it exactly. `accessTokens.lifetimeSeconds` is bounded to `60..=3600` for
Evidence-only deployments. A standard authorization registration reduces the maximum to 900
seconds so the token cannot exceed Registry Relay's fixed lifetime ceiling.
`accessTokens.claims` must match Evidence Gateway's own claim names field for field. A deployment
may omit `accessTokens.claims` only when every registered client uses standard authorization.
`clientAssertion.audience` is the value every client's signed request must carry as its own
`aud`, which stops a request built for one endpoint from being replayed at another.

`validationMode` defaults to `strict`, which requires the Transit signer. Set it to
`supervised-local-development` only for the disposable local developer environment; that mode may
use `signer.kind: local-jwk` with `privateKeyRef: secret:file/<name>`.
Use
[Configure Transit signing for Evidence Gateway and Registry Mint](../../tutorials/move-evidence-to-production-signing/)
to provision a strict signer and its governed public JWK.

The secret named by `audit.hashKeyRef` must contain at least 32 bytes and remain separate from the signing key. Mint
verifies the keyed JSONL chain and takes a single-writer lock at startup. Before returning an
access token it synchronizes a token-release record to the chain and its parent directory. If the
write fails, Mint returns `server_error`, does not release the token, and fails readiness. The
chain keeps only keyed pseudonyms where correlation is needed, never raw assertions, access
tokens, client ids, authority values, actors, or subject values.
`audit.maximumFileBytes` is the per-segment rotation threshold. Mint seals a full segment as
`<audit.path>.<eight-digit-sequence>` and continues online at `audit.path`; it never deletes or
compacts sealed segments.

Delegated tokens, bound to one named subject a client acts on behalf of, are a further optional
step layered on top of this base configuration. `crates/registry-mint/README.md` covers the
`accessTokens.claims.actor` field and the per-client `delegation` block that step needs; this
walkthrough covers the base, undelegated flow.

For the complete field list, including every default, see the
[Registry Mint reference](../../reference/mint/).

## Register an Evidence client

Add one file per client under the directory named in `clients.directory`:

```yaml
clientId: health-desk
principal: service:health-desk
evidenceAudience: https://evidence.example.org
requesterTags: [health-desk, region-north]
keys:
  - kty: OKP
    crv: Ed25519
    kid: health-desk-2026-01
    x: "<public-key-x-coordinate>"
```

`keys` accepts public JWKs only; a document carrying a private key member is rejected outright.
Loading the client registry is all-or-nothing, so one malformed registration fails the whole
load rather than serving a partial registry.

## Register a Relay client

Use the standard authorization profile when Registry Mint issues tokens for Registry Relay:

```yaml
clientId: registry-consumer
principal: urn:example:consumer
authorization:
  scopes:
    - registry:business:read
  claims:
    purpose: statutory-consultation
    authority: district-17
keys:
  - kty: OKP
    crv: Ed25519
    kid: registry-consumer-2026-01
    x: "<public-key-x-coordinate>"
```

Set `accessTokens.audiences` to the one exact audience in the Relay runtime's issuer
configuration. Set `authorization.scopes` to the exact operation or access-profile scopes this
client may use. Add a direct string under `authorization.claims` only when the compiled Relay
access rule reads that claim for purpose or authority-to-row binding.

The registration is the only authority source. Registry Mint joins the registered scopes into
the access token's space-delimited `scope` claim and copies the direct claims as strings. The
client assertion and token form cannot provide or override these values. A Relay registration
cannot also contain `evidenceAudience`, `requesterTags`, `grant`, or `delegation`.

The standard profile admits 1 to 64 unique RFC 6749 scope-tokens and at most 32 direct claims.
Each direct value is a non-empty string of at most 512 bytes. Claim names cannot shadow `iss`,
`aud`, `exp`, `iat`, `nbf`, `jti`, `client_id`, `sub`, or `scope`.
When `accessTokens.claims` is configured for Evidence clients in the same deployment, a scoped
client's direct claims cannot reuse any of those configured Evidence names.
Mint projects the complete signed response and refuses a standard registration whose token
response could exceed the shared client's 16 KiB read ceiling.

Registry Relay validates the resulting token through its standard OAuth JWT verifier. It has no
Registry Mint-specific authentication branch. The Relay client library's private-key-JWT token
provider can acquire, retain, and refresh this token; see the
[Registry Relay client API](../../reference/relay-client-api/).

Validate the deployment before opening a socket:

```sh
mint check --config /etc/mint/mint.yaml
```

`check` loads the configuration, governed public keys, signer, audit key, and client registry, then
performs the signer self-test before it exits. It
deliberately does not open the audit chain, which admits one writer at a time, so you can check
an edited configuration against the deployment it is about to replace. `check`, `serve`, and
`verify-audit` accept `MINT_CONFIG` in place of `--config`.

## Start Registry Mint

```sh
mint serve --config /etc/mint/mint.yaml
```

Onboarding, offboarding, and caller key rotation only need the client registry reloaded, not the
process restarted: send the running process `SIGHUP` and it re-reads `clients.directory`,
keeping the previous registry in place if the new one fails to load.

## Obtain a token

A client authenticates with the `client_credentials` grant and `private_key_jwt` client
authentication (RFC 7523): it signs a short-lived JWT assertion with its own private key and
posts it to the token endpoint.

```sh
curl -sS https://mint.example.org/token \
  -d grant_type=client_credentials \
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  --data-urlencode "client_assertion=<compact-jws-client-assertion>"
```

The assertion must carry `iss` and `sub` equal to the client id, `aud` equal to the configured
`clientAssertion.audience`, a unique `jti`, and `iat`/`exp` inside
`clientAssertion.maximumLifetimeSeconds`. Every `jti` is accepted once; presenting the same
assertion twice is refused.

The `mint token` subcommand builds and sends that request for local testing. It is a client
tool: it signs with the caller's own key and never touches Registry Mint's signing key.

```sh
mint token --url https://mint.example.org/token \
  --client-id health-desk --key ./dev/health-desk.jwk
```

It prints the access token alone on stdout, so `TOKEN=$(mint token ...)` is the whole usage.

To build the same request from an application rather than a terminal, see
[Request an access token from your own code](../request-an-access-token/), which has worked
Python and TypeScript examples.

## Verify the deployment

Request a token and confirm the response shape:

```sh
curl -sS https://mint.example.org/token \
  -d grant_type=client_credentials \
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  --data-urlencode "client_assertion=<compact-jws-client-assertion>"
```

```json
{
  "access_token": "SYNTHETIC_FIXTURE_TOKEN",
  "token_type": "Bearer",
  "expires_in": 300
}
```

This is the Evidence-profile response. A standard authorization response also carries `scope`
with the same space-delimited value Mint placed in the access token, for example
`"scope": "registry:business:read"`.

Confirm the published key set resolves at the configured `signing.jwksPath` (default
`/.well-known/jwks.json`), and that `GET /ready` returns success once at least one client is
registered, the audit writer is healthy, and the signing provider passes its self-test.

Verify the retained keyed chain with the same configuration and audit key:

```sh
mint verify-audit --config /etc/mint/mint.yaml
```

The command reports the record count and keyed chain tail, and exits unsuccessfully if retained
records were corrupted or reordered.

## Troubleshooting

| Symptom | Cause | Fix |
| --- | --- | --- |
| The token request fails with `401 invalid_client` | Registry Mint collapses every client authentication failure, an unknown client id, a bad signature, a replayed `jti`, an expired assertion, into this one code, so the endpoint cannot be used to probe which client ids are registered. | Check the client id, the signing key, the assertion's `iat`/`exp`, and that the `jti` has not already been used. |
| The token request fails with `400 unsupported_grant_type` | `grant_type` is missing or is not exactly `client_credentials`. | Send `grant_type=client_credentials` in the form body. |
| `mint check` or `mint serve` refuses to start over signing | The active public key is not an ES256 P-256 JWK with its RFC 7638 `kid`, a revoked key is published, or the signer cannot prove it matches the governed active key. | Correct the governed key set and signer configuration. In strict mode, restore the local Transit proxy and its pinned key version. |
| `mint serve` refuses to start over audit | The audit key, directory, chain, or lock file is unsafe, another writer holds the chain, or retained records do not verify. | Check owner-only permissions, run one writer per `audit.path`, then run `mint verify-audit` before deciding whether recovery is needed. Do not discard the chain to make startup pass. |
| `mint check` refuses the configuration over audit | The audit hash key file is missing, is not owner-only, or is too short. `check` does not open the chain, so it never reports a running writer as a fault. | Restore the key file with owner-only permissions. Use `mint verify-audit` for the chain itself. |
| The token request fails with `500 server_error` and readiness changes to `503` | Mint could not durably append the audit decision and permanently poisoned the writer for this process. | Stop traffic, restore writable durable storage, preserve and verify the retained chain, then restart Mint. The failed request did not receive an access token. |
| Evidence Gateway rejects a token that Registry Mint minted | `accessTokens.claims` on Registry Mint and the resource server's own claim-name configuration name different claims for the same authority field. | Align every claim name (`principal`, `requesterTags`, `evidenceAudience`, `grantId`, `grantAuthority`, and `actor` where used) between the two configurations. |
| Registry Relay conceals a registered operation as `404 resource.not_found` | The Mint registration does not contain the selected operation or access-profile scope, or the token audience does not equal the Relay runtime audience. | Set `accessTokens.audiences` to the one Relay audience and register the exact compiled scope for this client. Do not copy scope from the request. |
| `GET /ready` returns `503` | No client is currently registered, the client registry failed to load, the audit writer is poisoned, or the signing provider is unavailable. | Check startup or reload diagnostics and audit storage. Add a valid client, restore and verify audit storage, or restore the Transit proxy and pinned version. Provider readiness recovers after a successful self-test. |