Skip to main content

Overview

Change authorization proves which key approved each change to your Formal configuration:
  1. You register the public key of a key pair as a signing key.
  2. You sign API requests with the private key, using the FORMAL-SIG authorization scheme instead of an API key.
  3. Formal verifies the signature and records it with the change.
  4. Optionally, Connectors verify those recorded signatures before they load configuration.
Use it to require that configuration changes come from a reviewed pipeline, such as CI with a key in a hardware security module. With Connector verification, a change made without your key never reaches your data plane. Signing is opt-in. Requests authenticated with an API key keep working, unless Connectors run in enforce mode.

Supported Algorithms

All values use standard base64 with padding.

1. Generate a Key Pair

Keep the private key in your signing system. Only the public key goes to Formal.
The last command prints the base64 public key to register.

2. Register the Signing Key

1

Open Signing Keys

Go to Signing Keys, the Signing Keys tab of API Keys & Signing Keys. You can also search for Signing Keys in the command menu. You need the Developer permission.
2

Create the key

Click Create Signing Key. Enter a Name, choose the Algorithm, and paste the Public Key (Base64). Click Create.
3

Copy the ID

Copy the key’s ID from the table. You use it as KeyId when you sign.
The key belongs to you. Requests signed with it run as you, with your permissions. To sign as an automation identity, create the key while signed in as that identity, for example with its API key and CreateSigningKey. Verify: The key appears with status active.

3. Sign a Request

Send the signature in the Authorization header instead of X-API-KEY:

Build the Canonical Payload

Sign these four parts, joined by newlines (\n), with no trailing newline:
  1. The HTTP method, POST for every Formal API call
  2. The request path
  3. The Timestamp value, character for character
  4. The canonical body
To build the canonical body, sort the top-level JSON fields of the request body by name. For each field, write its name on one line and its value as compact JSON on the next. Join everything with newlines. Nested objects keep their key order, so sign the values exactly as you send them. An empty body or {} gives an empty canonical body. For the body {"status":"active","name":"prod-db"}, the canonical body is:

Example: Sign with OpenSSL

This script signs and sends a request with an Ed25519 key:
For ECDSA P-384, sign with openssl dgst -sha384 -sign signing-key.pem payload.txt instead. Verify: The API returns 200. A wrong signature, an unknown or revoked key, or a timestamp outside the window returns an authentication error.
The timestamp must be at most 5 minutes old and at most 1 minute in the future. Keep your signing system’s clock in sync.

4. Verify Signatures on Connectors (Optional)

Connectors can check the signature recorded with each configuration entity before they load it. This covers most configuration a Connector loads, such as Resources, policies, listeners, Native Users, users, and groups. Set these environment variables on the Connector:
  • audit: The Connector loads every entity, and logs Change auth verification failure (audit mode) for each unsigned or invalid one.
  • enforce: The Connector rejects any response that contains an unsigned or invalid entity. It needs at least one key in FORMAL_CHANGE_AUTH_PUBLIC_KEYS.
In enforce mode, any change made without a trusted signature stops the Connector from loading that configuration. This includes changes from the Control Plane UI, Terraform with an API key, and directory sync. Run in audit mode first, and switch only when the logs show no failures.

Rotate a Key

  1. Generate a new key pair and register it.
  2. Add the new public key to FORMAL_CHANGE_AUTH_PUBLIC_KEYS on your Connectors.
  3. Switch your signing system to the new key.
  4. Revoke the old key with UpdateSigningKey by setting its status to revoked, or delete it.
A revoked key can no longer authenticate requests. Remove it from FORMAL_CHANGE_AUTH_PUBLIC_KEYS once no configuration still carries its signature.

API

Manage signing keys with core.v1.ChangeAuthorizationService: CreateSigningKey, ListSigningKeys, UpdateSigningKey, and DeleteSigningKey. See the Change Authorization section of the API reference.

Next Steps

API Introduction

Authenticate and call the Formal API

Permissions

Control who can manage signing keys