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

# Opt in to lockfile-hash substrate attribution

> Set the X-Mnemom-Lockfile-Hash header on outbound requests so each integrity checkpoint records which deployed dependency graph produced it. SHA-256 digest of your resolved manifest; the hash is sent, the manifest is not.

The default [substrate fingerprint](/concepts/substrate-fingerprint) recorded on each integrity checkpoint covers `<provider>:<model>`. Setting the `X-Mnemom-Lockfile-Hash` header (and, optionally, the companion `X-Mnemom-Sdk-Version` header) extends it to a four-component identifier that includes a hash of *your* deployed dependency graph. Each checkpoint then records exactly which build of your stack produced it, which you can use as evidence in your own audits and incident reviews.

This guide is the runnable how-to. The conceptual surface is [substrate fingerprint](/concepts/substrate-fingerprint).

## What you'll change

You will:

1. Compute a stable SHA-256 digest of your resolved package manifest at deploy time.
2. Set `X-Mnemom-Lockfile-Hash: <hex>` on every outbound request from your agent to the Mnemom gateway.
3. (Optionally) set `X-Mnemom-Sdk-Version: <package>@<version>` to tighten SDK attribution past the gateway's User-Agent fallback.
4. Verify the substrate is recorded as the four-component form via the integrity-checkpoint read path.

The two headers compose into the `substrate_id` stamped on every `integrity_checkpoints` row per the production trigger (migration 252). The four collapse forms are documented at [Substrate fingerprint — the four collapse forms](/concepts/substrate-fingerprint#the-four-collapse-forms).

## Step 1 — Compute the digest

The hash is SHA-256, 64 hex chars, lowercase canonical on the wire (mixed case is accepted and normalized; malformed input is rejected at the gateway with `400` and `X-Mnemom-Error: invalid-lockfile-hash`).

Compute the digest over whatever stable serialization of your resolved manifest best represents the deployed dependency graph. For most ecosystems the lockfile-on-disk bytes are the natural input.

<CodeGroup>
  ```bash bash (sha256sum) theme={null}
  # Most lockfile types: hash the file bytes as-shipped.
  LOCKFILE_HASH=$(sha256sum pnpm-lock.yaml | awk '{print $1}')
  echo "$LOCKFILE_HASH"
  # → e.g. 9e8a4f3c2b1d0e7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a
  ```

  ```python Python (hashlib) theme={null}
  import hashlib

  with open("pnpm-lock.yaml", "rb") as f:
      lockfile_hash = hashlib.sha256(f.read()).hexdigest()
  print(lockfile_hash)
  # → e.g. 9e8a4f3c2b1d0e7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a
  ```

  ```typescript TypeScript (node:crypto) theme={null}
  import { readFileSync } from "node:fs";
  import { createHash } from "node:crypto";

  const lockfileHash = createHash("sha256")
    .update(readFileSync("pnpm-lock.yaml"))
    .digest("hex");
  console.log(lockfileHash);
  // → e.g. 9e8a4f3c2b1d0e7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a
  ```
</CodeGroup>

Compute the digest at deploy time (CI / image build) and inject it into the agent's runtime environment as `MNEMOM_LOCKFILE_HASH`. Recomputing at every request adds I/O without changing the value between deploys.

## Step 2 — Set the headers on outbound requests

Both headers travel with the request to the Mnemom gateway. Workers handles header names case-insensitively; the casing below matches the gateway's `LOCKFILE_HASH_HEADER` and `SDK_VERSION_HEADER` constants in `mnemom-platform/gateway/src/substrate-fingerprint.ts`.

<CodeGroup>
  ```bash curl theme={null}
  curl -sS \
    -H "X-Mnemom-Api-Key: $MNEMOM_API_KEY" \
    -H "X-Mnemom-Sdk-Version: @anthropic-ai/sdk@0.65.0" \
    -H "X-Mnemom-Lockfile-Hash: $LOCKFILE_HASH" \
    -H "anthropic-version: 2023-06-01" \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"model":"claude-sonnet-4-6","messages":[...]}' \
    https://gateway.mnemom.ai/anthropic/v1/messages
  ```

  ```python Python (requests) theme={null}
  import os
  import requests

  response = requests.post(
      "https://gateway.mnemom.ai/anthropic/v1/messages",
      headers={
          "X-Mnemom-Api-Key": os.environ["MNEMOM_API_KEY"],
          "X-Mnemom-Sdk-Version": "@anthropic-ai/sdk@0.65.0",
          "X-Mnemom-Lockfile-Hash": os.environ["MNEMOM_LOCKFILE_HASH"],
          "anthropic-version": "2023-06-01",
          "Content-Type": "application/json",
      },
      json={"model": "claude-sonnet-4-6", "messages": [...]},
      timeout=60,
  )
  response.raise_for_status()
  ```

  ```typescript TypeScript (fetch) theme={null}
  const response = await fetch(
    "https://gateway.mnemom.ai/anthropic/v1/messages",
    {
      method: "POST",
      headers: {
        "X-Mnemom-Api-Key": process.env.MNEMOM_API_KEY!,
        "X-Mnemom-Sdk-Version": "@anthropic-ai/sdk@0.65.0",
        "X-Mnemom-Lockfile-Hash": process.env.MNEMOM_LOCKFILE_HASH!,
        "anthropic-version": "2023-06-01",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ model: "claude-sonnet-4-6", messages: [/* ... */] }),
    },
  );
  ```
</CodeGroup>

The two headers are independent opt-ins. Setting only the lockfile-hash header produces a 4-component fingerprint with an empty SDK slot (`<provider>:<model>::<lockfile-hash>`); setting only the SDK header produces a 3-component fingerprint (`<provider>:<model>:<sdk@ver>`); setting both produces the full 4-component form.

### What `X-Mnemom-Sdk-Version` adds

The SDK version is sourced from three places, in order:

1. The `X-Mnemom-Sdk-Version` header value, if present (this is the preferred path).
2. A User-Agent dispatch over seven known SDK families (`@anthropic-ai/sdk`, OpenAI Python, Anthropic Python, etc.), if the request's `User-Agent` matches one.
3. `null` (collapsing to the 3- or 2-component fingerprint form).

Setting `X-Mnemom-Sdk-Version` explicitly is the way to get reliable SDK attribution when you're sending requests from custom or non-canonical clients.

## Step 3 — What gets stored

| Attribute | What the gateway records |
| - | - |
| Hash | Stored verbatim in `integrity_checkpoints.substrate_id` as the fourth component. |
| Manifest contents | **Never sent, never stored.** Only the hash crosses the wire. |
| Validation | Malformed (not 64 hex chars after normalization) → 400 + `X-Mnemom-Error: invalid-lockfile-hash`; request rejected before reaching the agent. |
| Visibility | Returned on your own checkpoints only. Mnemom does not compare it across customers. |

### Privacy posture

The hash is a deterministic function of your lockfile contents. Reversing it to recover the lockfile requires brute-forcing the lockfile-content space, which is computationally infeasible for non-trivial lockfiles. For very small / trivial lockfiles (e.g., a single pinned dependency) the hash is in principle reversible to that content; if that matters for your threat model, do not opt in.

## Step 4 — Verify the substrate is being recorded

The canonical record is on `integrity_checkpoints.substrate_id`, readable via the customer-facing checkpoints endpoint:

```bash theme={null}
curl -sS \
  -H "X-Mnemom-Api-Key: $MNEMOM_API_KEY" \
  "https://api.mnemom.ai/v1/agents/$AGENT_ID/checkpoints?limit=1" \
  | jq '.checkpoints[0].substrate_id'
```

After issuing one request with both headers set, the latest checkpoint's `substrate_id` should be the four-component form, for example:

```
"anthropic:claude-sonnet-4-6:@anthropic-ai/sdk@0.65.0:9e8a4f3c2b1d0e7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a"
```

There is no dedicated self-test endpoint — the checkpoint read is the verification path.

## When to use this

| Scenario | Recommendation |
| - | - |
| You want each checkpoint to record which deployed dependency graph produced it, for your own audit trail. | Opt in. |
| You only want SDK-level attribution and the User-Agent dispatch handles your client. | Skip. The default `<provider>:<model>` plus User-Agent-derived `<sdk@ver>` is sufficient. |
| Your lockfile changes on every deploy. | Opt in if you want per-deploy attribution; the value changes with each deploy. |
| Your lockfile is trivial enough that the hash is reversible. | Skip if the reversal matters for your threat model. |

The lockfile hash is a record of what you deployed. It does not detect a compromised dependency; use package-layer provenance for that. See [Supply-chain trust](/guides/supply-chain-trust).

## See also

* [Substrate fingerprint](/concepts/substrate-fingerprint) — the conceptual page
* [Supply-chain trust](/guides/supply-chain-trust) — package-layer provenance
* [AEGIS](/concepts/aegis) — runtime screening


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.