> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mortemlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Encrypt agent event payloads in Mortem with AES-256-GCM

> Enable optional AES-256-GCM payload encryption in Mortem by setting MORTEM_MASTER_KEY. Payloads are encrypted before leaving your process.

Mortem supports optional AES-256-GCM encryption for event payloads. When you set a master key, the SDK encrypts every event payload before it is buffered and sent to the ingest service. The encrypted ciphertext is what Mortem stores — the plaintext never leaves your process unencrypted.

Encryption is opt-in. If you do not set a master key, payloads are stored as plain JSON and the feature has no effect on the rest of the SDK.

## Generate a master key

Use OpenSSL to generate a cryptographically random 256-bit key encoded as base64:

```bash theme={null}
openssl rand -base64 32
```

Copy the output into your environment. Keep this value secret — treat it with the same care as your API key.

## Set the environment variable

```bash .env.local theme={null}
MORTEM_MASTER_KEY=your_base64_encoded_32_byte_key_here
```

The SDK reads `MORTEM_MASTER_KEY` automatically when it is set in `process.env`. You do not need to pass the key anywhere in code for the SDK's built-in instrumentation to encrypt payloads.

<Warning>
  Keep the master key stable across deployments. If you rotate or lose the key, any previously encrypted payloads cannot be decrypted. There is no recovery path for payloads encrypted with a lost key.
</Warning>

## How encryption works

When `MORTEM_MASTER_KEY` is present and valid, the SDK uses AES-256-GCM with a randomly generated 12-byte IV for every payload. The encrypted result is a structured object:

```ts theme={null}
interface EncryptedPayload {
  algorithm: "aes-256-gcm"
  ivBase64: string       // base64-encoded 12-byte initialization vector
  tagBase64: string      // base64-encoded 16-byte GCM authentication tag
  ciphertextBase64: string  // base64-encoded encrypted JSON
}
```

Each payload gets a unique IV, so encrypting the same payload twice produces different ciphertext. The GCM authentication tag prevents silent ciphertext tampering.

## Encrypt and decrypt manually

The SDK exports `encryptPayload` and `decryptPayload` for cases where you want to encrypt payloads yourself before passing them to `beginEvent` or `eventBuilder.complete`.

### `encryptPayload`

```ts theme={null}
import { encryptPayload } from "@mortemlabs/sdk"

const encrypted = encryptPayload(
  { step: "planning", result: "ready" },
  process.env.MORTEM_MASTER_KEY,  // optional — falls back to MORTEM_MASTER_KEY env var
)

if (encrypted !== undefined) {
  console.log(encrypted.algorithm)       // "aes-256-gcm"
  console.log(encrypted.ivBase64)        // base64 IV
  console.log(encrypted.ciphertextBase64) // base64 ciphertext
}
```

Returns `undefined` if the key is missing, invalid (not exactly 32 bytes after base64 decoding), or if encryption fails for any reason.

### `decryptPayload`

```ts theme={null}
import { decryptPayload } from "@mortemlabs/sdk"

const plaintext = decryptPayload(
  encrypted,
  process.env.MORTEM_MASTER_KEY,  // optional — falls back to MORTEM_MASTER_KEY env var
)

// plaintext is the original JsonValue, or undefined if decryption failed
```

Returns `undefined` if the key is wrong, the ciphertext has been tampered with (GCM tag mismatch), or if decryption fails for any other reason.

### Function signatures

<ParamField path="encryptPayload" type="(payload: JsonValue, masterKeyBase64?: string) => EncryptedPayload | undefined">
  Encrypts a JSON-serializable value. Pass the base64 master key explicitly, or omit it to use `process.env.MORTEM_MASTER_KEY`. Returns `undefined` on any failure.
</ParamField>

<ParamField path="decryptPayload" type="(encrypted: EncryptedPayload, masterKeyBase64?: string) => JsonValue | undefined">
  Decrypts an `EncryptedPayload` object. Pass the base64 master key explicitly, or omit it to use `process.env.MORTEM_MASTER_KEY`. Returns `undefined` on any failure.
</ParamField>

## Example: encrypt a custom event payload

```ts theme={null}
import { Mortem, encryptPayload } from "@mortemlabs/sdk"

const mortem = new Mortem({
  apiKey: process.env.MORTEM_API_KEY ?? "",
  agentId: process.env.MORTEM_AGENT_ID,
})

const session = await mortem.startSession({
  inputSummary: "Execute private arbitrage strategy",
})

await session.run(async () => {
  const sensitivePayload = {
    strategy: "triangular-arb",
    targetProfit: 0.015,
    walletBalance: 42.5,
  }

  const encrypted = encryptPayload(sensitivePayload)

  const event = session.beginEvent("custom", encrypted ?? sensitivePayload)
  // ... do the work
  event.complete()
})
```

<Note>
  When `MORTEM_MASTER_KEY` is set and valid, the SDK automatically encrypts event payloads in the built-in instrumentation wrappers. You only need to call `encryptPayload` manually when constructing payloads yourself before passing them to `beginEvent` or `complete`.
</Note>

## Security checklist

* Generate the key with `openssl rand -base64 32` — do not use passwords or weak entropy sources.
* Store the key in your secrets manager or deployment platform, not in committed files.
* Never rotate the key without first backing up and decrypting all existing payloads you care about.
* The key is exactly 32 bytes (256 bits) after base64 decoding. An incorrectly sized key causes `encryptPayload` to return `undefined` silently.
