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

# Configure Transit signing for Evidence Gateway

> Configure non-exportable P-256 signing through a workload-local Vault or OpenBao Transit proxy without giving provider credentials to the service.

Use this procedure to move Evidence Gateway from disposable local signing to a strict deployment.
The service signs through its own workload-local Unix-socket proxy. The service
process receives no Vault or OpenBao token, and the provider retains the private key.

## Prerequisites

You need:

- A Vault or OpenBao Transit mount administered outside the application workload.
- A separate provider identity and Transit key for each environment.
- An auto-auth method appropriate for the deployment platform.
- A reviewed deployment target based on the ready-to-copy templates under
  `products/evidence/reference/deployment-targets/`.
- `evidencectl` from the same release as the Evidence Gateway runtime, and
  [`jq`](https://jqlang.org/), to convert each service's public key from PEM to JWK.
  [Publish the exact public projections](#publish-the-exact-public-projections) gives the exact
  command for Evidence Gateway.

The commands use a Transit mount named `transit`. Change the mount consistently when your provider
uses another name.

## Create one key per service

Run the provider administration commands through an authenticated operator session, not from the
Evidence Gateway container.

Settle three things before you create the key. Decide which provider instance and mount hold this
signing identity for as long as the key is in service. Confirm that the provider's own backup and
restore covers that mount, because there is no key-level export or plaintext backup to fall back on.
Record the mount, the key name, the service and environment the key serves, and the operator
identity authorized to rotate it, in the deployment record rather than in application configuration.

:::caution
A key created with `exportable=false` and `allow_plaintext_backup=false` never releases its private
material, and the pair is one-way in the permissive direction only: a provider administrator can set
either field to `true` on this key later, and Vault and OpenBao then refuse to set it back to
`false`. Evidence Gateway accepts a Transit key only while both fields read `false`, so exporting the key
retires that key name permanently, for every version under it.
There is no supported way to move this private key to another key manager or to sign with it outside
the provider. The recovery path is a new key and
[rotating the signing key](../rotate-evidence-signing-keys/), not export.
:::

Create non-derived, non-exportable P-256 keys with plaintext backup disabled:

```sh
vault write transit/keys/evidence-signing \
  type=ecdsa-p256 \
  derived=false \
  exportable=false \
  allow_plaintext_backup=false
```

Use the equivalent `bao` commands for OpenBao. The
[Vault Transit API](https://developer.hashicorp.com/vault/api-docs/secret/transit) and
[OpenBao Transit API](https://openbao.org/api-docs/secret/transit/) define the same fields used by
the runtime checks.

Read each key's metadata and record the nonzero version that the application will pin:

```sh
vault read -format=json transit/keys/evidence-signing > "<operator-review-path>/evidence-key.json"
```

The reviewed metadata must report `type: ecdsa-p256`, `derived: false`, `exportable: false`,
`allow_plaintext_backup: false`, and signing support. Remove the local review files after recording
the approved public projection and controls. Do not commit provider responses.

## Publish the exact public projections

Convert the selected version's public PEM to an exact public JSON Web Key (JWK). The `kid` is the
RFC 7638 thumbprint of `crv`, `kty`, `x`, and `y`, a 43-character value that also names the file:

```json
{
  "kty": "EC",
  "crv": "P-256",
  "x": "<base64url-x-coordinate>",
  "y": "<base64url-y-coordinate>",
  "alg": "ES256",
  "kid": "<rfc7638-thumbprint>"
}
```

The object must contain exactly those six members and no private member. `evidencectl jwk from-pem`
produces it: it reads one PEM public key, refuses a private key or an unsupported key type, derives
`x` and `y` from the P-256 point, computes the thumbprint, and with `--output-dir` writes the JWK as
`<thumbprint>.jwk.json`. Run it on the pinned version's PEM from each service's saved review
metadata:

```sh
jq -r '.data.keys["<nonzero-version>"].public_key' "<operator-review-path>/evidence-key.json" \
  | evidencectl jwk from-pem - --output-dir "environments/<environment>/evidence/public-keys"
```

The command prints the path it wrote and the thumbprint it used as the filename. The result lands
in the same environment target the command publishes to:

```text
environments/<environment>/
  evidence/public-keys/<evidence-thumbprint>.jwk.json
```

Keep Evidence Gateway signing, audit, subject-binding, and client keys distinct. Run the target's `check-public-key-separation.sh` before review when you
start from the maintained deployment-target templates.

## Configure the governed key and runtime signer

Reference the Evidence Gateway public JWK from `governance.yaml`:

```yaml
signing:
  format: flattened-jws-json
  algorithm: ES256
  activePublicJwkFile: public-keys/<evidence-thumbprint>.jwk.json
  publishedPublicJwkFiles: []
  revokedKeyIds: []
  jwksPath: /.well-known/evidence/jwks.json
  maximumAssertionValiditySeconds: 300
  verifierClockSkewSeconds: 30
```

Bind the matching provider key and version in `runtime.yaml`:

```yaml
signer:
  kind: transit
  unixSocketPath: /run/registry-evidence/transit-proxy.sock
  mount: transit
  keyName: evidence-signing
  keyVersion: <nonzero-version>
  timeoutMilliseconds: 2000
```

Do not put a provider token, auto-auth credential, or private JWK in this configuration.

## Isolate provider access in local proxies

Give each proxy identity only read access to its named key metadata and update access to its sign
endpoint:

```hcl
path "transit/keys/evidence-signing" {
  capabilities = ["read"]
}

path "transit/sign/evidence-signing/sha2-256" {
  capabilities = ["update"]
  required_parameters = ["input", "key_version", "marshaling_algorithm", "prehashed"]
  allowed_parameters = {
    "input"                 = []
    "key_version"           = [<nonzero-version>]
    "marshaling_algorithm" = ["jws"]
    "prehashed"             = [true]
  }
}
```

The parameter constraints make the service identity usable only for the pinned version and exact
JWS signing request shape. During a planned rotation,
temporarily allow the old and new numeric versions, for example `"key_version" = [7, 8]`, then
remove the retired version after the overlap window. The provider's `min_encryption_version`
provides a second retirement control.

The Evidence Gateway proxy's core boundary is:

```hcl
vault {
  address = "https://vault.example.com:8200"
  retry {
    num_retries = -1
  }
}

api_proxy {
  use_auto_auth_token = "force"
}

listener "unix" {
  address                = "/run/registry-evidence/transit-proxy.sock"
  tls_disable            = true
  socket_mode            = "0660"
  socket_user            = "<proxy-user>"
  socket_group           = "<evidence-group>"
  require_request_header = true
}
```

Keep `use_auto_auth_token` at `"force"`. Under `"force"` the proxy replaces any token on the
incoming request with its own auto-auth token, so the proxy identity is the only identity that
reaches the provider through this socket. Under the weaker `true`, the proxy attaches its auto-auth
token only when the request carries none, and a token supplied by whatever reached the socket is
used instead.

Add the deployment's reviewed `auto_auth` method and provider trust settings.

Configure the proxy to:

- Listen only on the service-specific Unix socket.
- Force its auto-auth token for proxied requests.
- Require the `X-Vault-Request` header.
- Set `retry.num_retries` to `-1` and leave `VAULT_MAX_RETRIES` unset.
- Set socket ownership so only the intended workload can connect.

These settings make one application signing attempt one provider signing request. The application
timeout remains the outer bound. The
[Vault API proxy documentation](https://developer.hashicorp.com/vault/docs/agent-and-proxy/proxy/apiproxy)
describes forced auto-auth, and the
[Vault Agent configuration](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent)
defines the retry and request-header controls. OpenBao deployments use the corresponding
[OpenBao Agent configuration](https://openbao.org/docs/agent-and-proxy/agent/).

## Verify before routing traffic

Start the proxy before checking Evidence Gateway. Run the checks from the final execution context so
the commands see the same socket, public keys, paths, ownership, and secret roots as the service:

```sh
evidence check --runtime-config "<deployment-target>/runtime.yaml"
```

Each check reads provider metadata, verifies the pinned version and custody controls, compares the
provider public key with the governed JWK, and performs a sign-and-verify self-test. A mismatch,
timeout, malformed response, or unavailable proxy fails the check.

Start the service only after the check passes. Route requests only after `/ready` reports ready. If provider access fails later, signing fails closed and readiness reports the signer
unavailable. Readiness recovers after a successful provider self-test.

## Troubleshooting

- If metadata validation fails, compare the key type, custody controls, signing support, and pinned
  version with the governed configuration.
- If public-key matching fails, regenerate the JWK from the pinned provider version. Do not edit
  `x`, `y`, or `kid` by hand.
- If the proxy refuses requests, check the named-key policy, forced auto-auth state, socket
  ownership, and required request header.
- If checks time out, inspect the workload-local proxy and provider path. Do not add application or
  proxy retries.

## Next

- [Build and deploy an Evidence Gateway project](../build-and-deploy-evidence-project/)
- [Rotate Evidence Gateway signing keys](../rotate-evidence-signing-keys/)