Runbooks/Secrets And Dpapi

Secrets and DPAPI

This document covers how IdentityMesh stores connector secrets, the constraints DPAPI imposes on the deployment, and what to do when those constraints bite.

TL;DR

Where secrets live

Secrets are stored in a SQL table on the IdentityMesh database. Each row carries a name (the secret reference) and an encrypted-value blob. A secretscli command-line tool ships with the engine for provisioning and rotation.

A connector configuration does not carry the cleartext secret. It carries a ref string like secret://ad/svc/password. The engine resolves the ref at sync time, decrypts the blob locally, and uses the value to authenticate to the source system.

Why LocalMachine scope

LocalMachine lets the secret blob be read by any account on the host that has access to the database, without coupling the secret to a specific service account. If it were CurrentUser, the secret would only be readable by the exact user that wrote it — restarting the service under a different identity (for example, switching from an interactive user to a managed service account) would break every secret on the box.

The trade-off is the host-binding constraint described above.

Operational consequences

Host migration

You cannot lift-and-shift the IdentityMesh SQL database to a new Windows host and have the existing secret blobs work. The blobs are unreadable on the new machine.

When migrating:

  1. Inventory the connectors that have a PasswordSecretRef / ClientSecretRef.
  2. Stand up the new host with the IdentityMesh service installed and pointed at the database.
  3. On the new host, run secretscli set <ref> <value> for each affected secret. The set operation re-encrypts under the new host’s DPAPI key.
  4. Verify by running a sync and checking that authentication succeeds.

Disaster recovery

A SQL backup-and-restore to a different host has the same problem as migration: the row contents are restored intact, but the DPAPI blobs are unreadable on the new host. The fix is the same: re-provision via secretscli set.

If you need true DR portability, two options:

Service account changes

If the IdentityMesh service runs under, say, LOCALSYSTEM initially and is later changed to a domain service account, secrets written under the first identity are still readable because of the LocalMachine scope. This is the main reason LocalMachine was chosen over CurrentUser.

Decryption failures

When the engine cannot decrypt a stored secret, the run fails with an error naming the secret reference. The error appears in the run history and in the IdentityMesh Windows event log. If you see this error, the host’s DPAPI key has changed relative to when the secret was written — re-provision.

Escape hatch: Secrets:ProviderOverride

The active provider is stored in the database and is chosen on Settings → Secrets. Secrets:Provider in appsettings.json only seeds that value on the first start of an upgraded host. To force one host back to DPAPI regardless of the database, set "Secrets": { "ProviderOverride": "DPAPI" } in its appsettings.json; it takes effect within the poll interval (30 s) and is flagged in red on the Secrets page. Remove it once the database setting is right again.

The poll notices the override being set, changed to another provider, or removed. It does not notice an edit to the connection settings the override uses from the same file (for example a new Secrets:KeyVault:VaultUri while ProviderOverride stays AzureKeyVault): the host keys its store on the provider name and a fixed override marker, which that edit does not change. Restart the host after editing those settings under an override.

How to add or rotate a secret

secretscli set <ref> <plaintext>

Examples:

secretscli set secret://ad/svc-imadmin/password "S3cr3tP@ss"
secretscli set secret://sql/hr-source/password "OtherP@ss"

The set operation upserts the row. To read back (for verification):

secretscli get <ref>
secretscli exists <ref>

get prints the plaintext to the console — only run interactively, never with output captured to a file or log.

This runbook is about connector credentials — the secrets IdentityMesh uses to authenticate to source and target systems (bind passwords, SQL passwords, OAuth client secrets, API keys). Those are what the DPAPI-encrypted secret store protects.

That is a separate concern from synced password attributes — the password values that flow through the mesh as identity data (for example a user’s unicodePwd on its way to Active Directory). Those are redacted to ***REDACTED*** at storage, in the API, and in the UI, while a SHA-256 ContentHash keeps them change-detectable. See secret-redaction.md (/runbooks/secret-redaction).