What chain-of-custody signatures must accompany a record transferred between memory layers?
**Date of Record:** 2026 October 26 **Scope:** Applies when an operational record is transferred from one external memory layer to another (e.g., from a session-local cache to a shared agent database, from a hot index to a cold archive), and the receiving layer must be able to prove that the record's history of custody is intact. Does not apply to in-place records that never leave their origin layer, or records that are explicitly ephemeral.
Core Distinction
The separation between **record content hash** (proving the payload itself is intact) and **chain-of-custody signature** (proving which layers held the record, in what order, and with what authorizations). Failing to distinguish these creates the **Custody Gap**: the payload hash matches, but the record may have been copied to an unauthorized intermediate layer, modified and reverted, or transferred without authorization. The payload hash proves *what* the record is, but not *where it has been* or *who authorized its movement*.
What Was Observed
In multi-tier memory architectures, failing to preserve chain-of-custody produces three primary failure modes:
- **Authorized Copy, Unauthorized Read:** A record is legitimately transferred from hot to cold storage, but during the transfer, it was copied through an unauthorized intermediate layer that read its contents. The payload hash matches, but the custody chain is broken.
- **Rollback Forgery:** A record is transferred to a new layer, modified, then the modification is reverted before the receiving layer verifies it. The payload hash matches, but the record was tampered with during custody, and the receiving layer has no proof it was ever changed.
- **Cross-Tier Contamination:** A record intended for a high-privilege layer is accidentally routed through a low-privilege layer during a migration. The receiving layer trusts the record because the payload is intact, but the record was exposed to unauthorized viewers.
Recorded Elements
- **`custody_chain_signatures`:** An ordered list of cryptographic signatures from each layer that held the record, in the sequence of custody, binding the record hash to that layer's identity and the transfer epoch.
- **`transfer_authorization_receipt`:** The cryptographic proof that each transfer was authorized by the sending layer's policy engine, binding the transfer to a specific principal and policy.
- **`layer_privilege_tier`:** The privilege classification of each layer in the custody chain, allowing the successor to detect cross-tier contamination.
- **`payload_hash_history`:** The sequence of payload hashes at each transfer point, proving the payload was not modified in transit and detecting temporary modification-then-reversion.
Condition of Validity
* **Applies when:** The record moves between memory layers with different trust, privilege, or compliance classifications, and the receiving layer must verify not just content integrity but also the authorization and exposure history of the record. * **Does not apply when:** The record stays within a single trusted layer (no transfer), or the record is explicitly public and exposure history is irrelevant.
Decision Boundary
- **Does the record have a `custody_chain_signatures` list?** If no, the receiving layer cannot verify the record's history. It must reject the transfer.
- **Is every `transfer_authorization_receipt` valid and signed by an authorized principal?** If no, the transfer was unauthorized. The record must be quarantined.
- **Do the `payload_hash_history` entries all match?** If no, the payload was modified during custody. The record is `TAMPERED`.
- **Conversely, when can a receiving layer accept a record without a full custody chain?** Only when the sending layer is strictly trusted (same privilege tier, same authorization domain) and the transfer is a simple in-tier migration with no cross-boundary exposure.
Operational Effect
- **Tamper Detection:** Catches records that were modified during custody even if reverted, by preserving the full hash history.
- **Exposure Tracking:** Provides cryptographic proof of every layer that held the record, satisfying compliance requirements for data exposure tracking.
- **Privilege Enforcement:** Detects cross-tier contamination by verifying that every layer in the custody chain had sufficient privilege to hold the record.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class CustodyVerdict(Enum):
CUSTODY_VERIFIED = auto()
MISSING_CHAIN = auto()
UNAUTHORIZED_TRANSFER = auto()
PAYLOAD_TAMPERED = auto()
PRIVILEGE_LEAK_DETECTED = auto()
@dataclass(frozen=True)
class CustodyLink:
layer_id: str
privilege_tier: int
payload_hash: str
transfer_authorization_signature: str
transfer_epoch: float
@dataclass(frozen=True)
class CustodyContext:
trusted_layers: frozenset[str]
receiving_layer_privilege_tier: int
current_payload_hash: str
@dataclass(frozen=True)
class CustodyManifest:
verdict: CustodyVerdict
directive: str
manifest_digest: str
class ChainOfCustodyGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_custody(
cls, chain: tuple[CustodyLink, ...], ctx: CustodyContext
) -> CustodyManifest:
# Gate 1: Chain presence
if not chain:
return CustodyManifest(
verdict=CustodyVerdict.MISSING_CHAIN,
directive="HALT: No custody chain provided. Cannot verify record history.",
manifest_digest=cls._hash("NO_CHAIN")
)
# Gate 2: Payload hash consistency
for link in chain:
if link.payload_hash != ctx.current_payload_hash:
return CustodyManifest(
verdict=CustodyVerdict.PAYLOAD_TAMPERED,
directive=f"HALT: Payload hash mismatch at layer {link.layer_id}. Record was modified during custody.",
manifest_digest=cls._hash(f"TAMPER:{link.layer_id}")
)
# Gate 3: Authorization verification
for link in chain:
if not link.transfer_authorization_signature.strip():
return CustodyManifest(
verdict=CustodyVerdict.UNAUTHORIZED_TRANSFER,
directive=f"HALT: Layer {link.layer_id} transfer lacks authorization signature.",
manifest_digest=cls._hash(f"NO_AUTH:{link.layer_id}")
)
# Gate 4: Privilege tier check
for link in chain:
if link.privilege_tier < ctx.receiving_layer_privilege_tier and link.privilege_tier < 2:
return CustodyManifest(
verdict=CustodyVerdict.PRIVILEGE_LEAK_DETECTED,
directive=f"HALT: Record passed through low-privilege layer {link.layer_id} en route to privileged layer.",
manifest_digest=cls._hash(f"LEAK:{link.layer_id}")
)
# Gate 5: Verified
digest = cls._hash(json.dumps({
"chain_length": str(len(chain)),
"final_hash": ctx.current_payload_hash,
"receiving_tier": str(ctx.receiving_layer_privilege_tier)
}, sort_keys=True))
return CustodyManifest(
verdict=CustodyVerdict.CUSTODY_VERIFIED,
directive="PROCEED: Full chain of custody verified.",
manifest_digest=digest
)