Skip to main content

Graph synchronization keys and certificates

Graph synchronization uses two different kinds of cryptographic material. Complete Microsoft Graph setup before configuring provider access.

MaterialPurposeSecret materialConsumer
Delta data keyAES-256-GCM encryption of opaque @odata.nextLink / @odata.deltaLink URLsData keySync fetch/recovery runtime
Rich-notification certificatePublic-key encryption for Graph notification subscriptionsPrivate key onlySubscription manager and encrypted-resource processing

Neither replaces browser HTTPS certificates, login credentials, or personal email keys.

1. Keep a stable delta key​

For a new empty deployment, generate exactly 32 random bytes:

openssl rand -base64 32

Store the result as DELTA_LINK_ENCRYPTION_KEY in the unified root .env. Current Compose forwards that key and uses runtime reference local-dev-v1. It does not forward DELTA_LINK_ENCRYPTION_KEY_REFERENCE; adding that root assignment does not change the process reference.

A standalone backend can receive its own key and explicit DELTA_LINK_ENCRYPTION_KEY_REFERENCE. Preserve the original compatible key/reference with retained state. Moving state created under another reference needs a checked compatible runtime configuration or explicit migration.

The decoded key must contain exactly 32 bytes. The runtime encrypts complete opaque Graph URLs in folder/message delta tokens and staging checkpoints. Raw URLs and secret keys must not reach browser responses, logs, or dead-letter payloads.

Reuse the original key after every restart that retains encrypted delta state. Production requires explicit key material. Nonproduction code can derive a deterministic development key from generated local certificate material when no explicit key exists. That fallback ties the key to those files. For retained data, configure and preserve an explicit key rather than depending on regenerated certificates.

Legacy GRAPH_SYNC_DATA_KEY_BASE64 / GRAPH_SYNC_DATA_KEY_REFERENCE aliases remain supported. Prefer canonical DELTA_LINK_ENCRYPTION_KEY* names.

Lost or rotated keys​

Restore the original secret and reference first. A replacement cannot decrypt old delta URLs. If restoration is impossible, plan an explicit delta-state reset and full reconciliation. A clean disposable development database can instead be onboarded again after an explicitly authorized reset.

Never silently place a new key behind an old reference. A normal reset does not need to rotate a retained key. Keep key backups with the database recovery material.

2. Prepare rich-notification certificate files​

Default gitignored files under the backend:

FileContentPermissions
mailGen/msmicroservices-architexture/certs/cert/certificate.pemSelf-signed X.509 public certificate600
mailGen/msmicroservices-architexture/certs/private_key/private_key.pemRSA private key600
mailGen/msmicroservices-architexture/certs/cert_id/certificate_idSHA-256 fingerprint without colons600

Parent directories use 700. Keep private material outside Git.

Normal initialization invokes the existing certificate preparation. For a standalone manual check, start from the motherrepo root:

cd mailGen/msmicroservices-architexture
bash certgen.sh
pnpm graph-sync:preflight

certgen.sh checks all three files, certificate format/expiry, private-key format, matching public keys, and fingerprint identity. A complete missing set produces a 2048-bit RSA/SHA-256 self-signed certificate. The generated certificate uses a 3650-day validity period. A partial, expired, corrupt, or mismatched set stops the script instead of being overwritten automatically.

The TypeScript path loader validates the PEM pair and converts the public certificate to Base64 DER for Graph subscription requests.

Resolution order​

The following direct/custom-path settings apply to standalone execution or another deployment that explicitly forwards them. Current unified root Compose does not expose these arbitrary direct inputs. It supplies fixed /runtime/graph/... mounts and an optional certificate ID.

For a standalone process, a complete explicit configuration takes precedence:

GRAPH_WEBHOOK_ENCRYPTION_CERTIFICATE=<Base64 DER public certificate>
GRAPH_WEBHOOK_ENCRYPTION_CERTIFICATE_ID=<certificate ID>
GRAPH_WEBHOOK_ENCRYPTION_CERTIFICATE_REFERENCE=vault://graph-webhook/v1

Direct certificate input requires an ID. Otherwise, path mode uses:

GRAPH_WEBHOOK_ENCRYPTION_CERTIFICATE_PATH=certs/cert
GRAPH_WEBHOOK_ENCRYPTION_PRIVATE_KEY_PATH=certs/private_key
GRAPH_WEBHOOK_ENCRYPTION_CERTIFICATE_ID=certs/cert_id

The certificate/private-key paths form a pair. Each can name a file or directory containing a valid PEM file. The ID can be a literal, file, or directory. In complete path mode, an omitted ID defaults to the certificate fingerprint.

Production rejects partial direct/path configuration. Nonproduction resolution warns and attempts the generated-file fallback. The certificate script still refuses a corrupt or partial file set.

GRAPH_SYNC_CERTS_DIR can select another root containing cert/, private_key/, and cert_id/. The script also reads the canonical path names from its supported environment inputs.

Unified Compose fixes GRAPH_SYNC_CERTS_DIR=/runtime/graph, certificate path /runtime/graph/cert/certificate.pem, and private-key path /runtime/graph/private_key/private_key.pem. Normal initialization supplies those files from instance-local state. These are container paths. Do not replace them with host absolute filenames. Standalone execution uses its own process environment/local paths.

3. Configure notification callbacks​

The certificate alone does not enable subscriptions. Supply both public HTTPS callbacks and a stable client-state secret:

GRAPH_WEBHOOK_URL=https://your-public-host.example/graphWebhook
GRAPH_LIFECYCLE_WEBHOOK_URL=https://your-public-host.example/graphWebhook
GRAPH_WEBHOOK_CLIENT_STATE=<private random value>

These illustrate a supported direct WebhookCatcher path. Use the exact ingress paths configured for your deployment. Both route to WebhookCatcher, normally internal port 3080. The same handler can accept notification and lifecycle batches.

Keep client-state at most 128 characters and stable for the subscriptions' lifetime. Microsoft cannot reach http://localhost. Use this developer's public HTTPS ingress/tunnel, not another developer's shared domain.

Incomplete lifecycle configuration can produce:

Rich subscription lifecycle disabled because its runtime configuration is incomplete.

This disables rich subscription create/renew/recreate. Periodic folder/message delta and category polling are separate mechanisms. A browser certificate check does not prove public webhook reachability.

4. Rotate certificates deliberately​

Back up the existing material before rotation. From the backend directory:

bash certgen.sh --force

Then restart affected services and reconcile/recreate subscriptions that still name the old certificate. Retain the old private key until old encrypted notifications can no longer arrive. A disposable local environment can use an explicitly authorized reset/onboarding route, but retained data needs controlled recovery.

Certificate rotation and delta-key rotation are separate. --force does not rewrite an explicit delta key. A derived development key can change when its certificate material changes.

5. Diagnose configuration failures​

ErrorMeaningNext action
DELTA_LINK_ENCRYPTION_KEY ... requiredMissing explicit production key or unavailable development fallbackRestore/configure the correct stable key
must be valid base64 encoding exactly 32 bytesInvalid encoding or decoded sizeRestore the correct secret, or generate only for empty data
certificate set is partial, expired, corrupt, or mismatchedInvalid file setBack up and inspect. Rotate only intentionally.
must be configured togetherIncomplete production configuration groupSupply the complete group
must use public HTTPSInvalid or local-only callbackCorrect the public ingress/tunnel
encryption key reference unavailableStored URL needs another key/referenceRestore it or start explicit recovery

Never log secret/certificate contents. References, IDs, and file paths can support diagnosis. Keep data keys, private keys, and raw delta URLs out of the BFF/browser. Use a secret manager for production material.