Mojo UpDocs
Self-hosted AIOE

Platform keys and rotation

The keys and secrets the AIOE API holds, how to make each one, and what happens when you change it.

Besides its database password, the API holds a handful of keys. Each one comes from the environment, belongs in your secret store, and has its own consequences when you change it. This page lists them, shows how to make them, and says what to expect when you rotate one.

The keys

VariableWhat it protectsIf unset
AIOE_SECRETS_KEYSeals (AES-256-GCM) the secrets administrators keep for agents' tools, people's linked source-control accounts, and workbench backups.The platform keeps none of them, and says so.
AIOE_SIGNING_KEYThe platform's Ed25519 key. It signs remote-control requests sent over the overlay and short-lived peer tokens; workbenches and nodes check them against /.well-known/aioe-jwks.json.Each replica generates its own key at start. Fine for one replica, wrong for more.
AIOE_GATEWAY_SECRETShared between the API and the overlay gateway.Remote control uses the relay only.
AIOE_ENTRA_CLIENT_SECRETThe API registration's client secret, used to act for people in Azure DevOps.Azure DevOps links with a personal token only.
DATABASE_URLHolds the database password.Required when STORE=postgres.

On Azure, the template's secure parameters secretsKey, signingKey, gatewaySecret and entraClientSecret set these for you.

Make a key

AIOE_SECRETS_KEY: 32 random bytes, base64
openssl rand -base64 32
AIOE_SIGNING_KEY: an Ed25519 private key as JSON (Node.js 20 or later)
node -e "crypto.subtle.generateKey({name:'Ed25519'},true,['sign']).then(k=>crypto.subtle.exportKey('jwk',k.privateKey)).then(j=>console.log(JSON.stringify(j)))"
AIOE_GATEWAY_SECRET: a long random string
openssl rand -base64 32

The API refuses to start if AIOE_SECRETS_KEY is set but is not exactly 32 bytes once decoded.

Rotate a key

Set the new value in your secret store, then restart every API replica so they all read it (kubectl rollout restart deployment/aioe-api with Helm; a new revision on Azure). What follows depends on the key.

AIOE_SECRETS_KEY

Rotating this key loses what it sealed

Nothing sealed under the old key can be opened with the new one, and the platform does not re-seal for you.

After a rotation:

  • Secrets kept for agents' tools must be entered again: in Secrets (and on each project's Policy tab), use Rotate on each one to give it a value under the new key.
  • Linked source-control accounts are invalid: people link again under Settings, Source control.
  • Workbench backups cannot be restored: each person's workbench backs up again.

Rotate this key only when you believe it was exposed, and plan for people to relink.

AIOE_SIGNING_KEY

Every replica must have the new value at the same time. The public key at /.well-known/aioe-jwks.json changes with it, and workbenches and nodes check signatures against what that address publishes. Peer tokens signed with the old key live at most ten minutes.

AIOE_GATEWAY_SECRET

The API and the gateway must agree. Change it on both, and restart both: the gateway's token file holds aioe_gw_<tenant>.<secret>, so rewrite that file too. Until both agree, remote control falls back to the relay.

AIOE_ENTRA_CLIENT_SECRET

Entra client secrets expire (a common choice is two years). Before the expiry date, create a new secret on the API registration under Certificates & secrets, set it on the API, restart, then delete the old one in Entra. People's Azure DevOps links carry on. On Azure:

Terminal
az containerapp secret set -g <resource group> -n aioe-prod-api --secrets entra-client-secret="<new secret>"
az containerapp update -g <resource group> -n aioe-prod-api \
  --set-env-vars AIOE_ENTRA_CLIENT_SECRET=secretref:entra-client-secret

The database password

Change the password in PostgreSQL, update DATABASE_URL (Helm: the aioe-api secret; Azure: run the deployment again with the new postgresAdminPassword), and restart the API.

Device and refresh tokens

You do not rotate the tokens AIOE issues to workbenches: they rotate themselves. An access token lives an hour and a refresh token thirty days, and every refresh issues a new pair and revokes the old refresh token. To cut one machine off, open it in the console and choose Revoke access. See Tenancy and tokens.

On this page