Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions packages/memorypack/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,74 @@

All notable changes to `@clude/memorypack` are documented here. The package follows [Semantic Versioning](https://semver.org/).

## [0.6.0] — 2026-04-29

Chain-anchored revocations. Pin the `revoked_at` of a soft-deleted record to a Solana transaction so a producer can't backdate a deletion claim.

### Added

- `revocation_anchors.jsonl` file in the spec.
- `MemoryPackRevocationAnchor` type and `anchor_format: "memo-revoke-v1"`.
- `expectedRevocationMemo(record_hash, revoked_at)` — exposes the canonical on-chain memo string a producer should commit, mirroring `expectedMemoForRecordHash`.
- `appendRevocationAnchors(packDir, anchors)` — append-only writer (directory-only, mirroring `appendRevocations`).
- `verifyRevocationAnchors(anchors, opts)` — Solana RPC verifier. Same semantics as `verifyChainAnchors`: SPL Memo program required, exact-match the memo bytes, signer-binding via `expectedSigner`, optional `cluster` cross-check via `getGenesisHash`, sequential by default to keep RPC pressure low.
- `result.revocationAnchors` and `result.verifiedRevocationAnchors` on both `readMemoryPack` and `streamMemoryPack`.
- CLI surfaces a "Revocation anchors" panel when present, and verifies them under `--verify-chain`. `--strict-chain` makes any mismatch a fail.

### On-chain memo format

```
revoke:v1:sha256:<record_hex>:<revoked_at>
```

~95 bytes for SHA-256 + RFC3339, well under Solana's 566-byte memo cap.

### Reader semantics

- `revocation_anchors.jsonl` is loaded eagerly but **not RPC-verified by `readMemoryPack`** — chain verification is out of band, like `verifyChainAnchors`.
- The reader cross-checks that each anchor's `(record_hash, revoked_at)` pair matches a verified entry in `revocations.jsonl`. Anchors with no matching pair are **skipped with a warning** — they cannot prove anything if there's no signed revocation backing them.
- Anchors with unsupported `anchor_format` are skipped with a warning (forward-safe for future memo versions).
- Malformed JSON lines are skipped with a warning.
- One bad anchor never throws — keeps the audit trail intact.

### Writer hygiene

`writeMemoryPack` now also clears prior `revocation_anchors.jsonl` alongside `revocations.jsonl` and the rest. Re-export means "fresh pack."

### Tests

12 new tests in this PR (83 total in the package, all green):

- `expectedRevocationMemo` formatting + size bounds
- `appendRevocationAnchors`: shape, append-not-overwrite, tarball reject, missing-manifest reject, no-op on empty input
- Reader: exposes anchors, skips mismatched pair, skips unsupported format, skips malformed lines
- Stream: exposes anchors alongside the iterator
- Writer: wipes prior `revocation_anchors.jsonl` on re-export

Solana RPC verification (`verifyRevocationAnchors`) is structurally identical to the v0.2 `verifyChainAnchors` and shares its untested-with-mocks status. Mocking @solana/web3.js is a separate testing-infra PR.

### Producer flow

```ts
// 1. Soft-delete (already shipped in 0.4.0)
appendRevocations(dir, [{ record_hash, reason: 'gdpr' }], { secretKey, publicKey });

// 2. Send a Solana tx with memo `revoke:v1:sha256:<hex>:<rfc3339>`
// using whatever wallet/lib you prefer. Use expectedRevocationMemo()
// to get the exact bytes.

// 3. Record the chain anchor
appendRevocationAnchors(dir, [{
record_hash, revoked_at, chain: 'solana-mainnet', tx, slot,
}]);
```

### Limitations (deferred to v0.7)

- Tarball-aware `appendRevocationAnchors` — operators with `.tar.zst` packs still need extract / append / re-tarball.
- @solana/web3.js mocking infrastructure — adds confidence to RPC-touching code.
- Backdating-detection: today the verifier confirms the memo bytes but doesn't compare the chain block timestamp to `revoked_at`. A future option `maxClockSkew` could reject anchors whose block time is hours/days off from the signed timestamp.

## [0.5.0] — 2026-04-28

Standalone CLI verifier. Auditors can install `@clude/memorypack` alone (~30 KB) and run `npx @clude/memorypack verify <pack>` without touching the rest of Clude.
Expand Down
30 changes: 28 additions & 2 deletions packages/memorypack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,30 @@ Revocations are append-only and forward-only — once revoked, always revoked. T

Records remain in `result.records` after revocation; apps decide whether to surface as `[redacted]`, omit, or display with a flag.

### Anchor revocations on-chain (v0.6)

A signed `revoked_at` is just self-attestation — a producer could backdate. Pin the timestamp to a Solana transaction whose memo is `revoke:v1:sha256:<hex>:<rfc3339>`:

```ts
import { appendRevocationAnchors, expectedRevocationMemo } from '@clude/memorypack';

// 1. Get the canonical memo bytes for your tx
const memo = expectedRevocationMemo(recordHash, revokedAt);

// 2. Send a Solana tx with that memo via your preferred wallet/lib

// 3. Record the chain anchor
appendRevocationAnchors(packDir, [{
record_hash: recordHash,
revoked_at: revokedAt,
chain: 'solana-mainnet',
tx: signature,
slot,
}]);
```

`npx @clude/memorypack verify <pack> --verify-chain` checks both record anchors and revocation anchors against the RPC. Memo bytes must match exactly; the producer's wallet must be among the tx signers.

## What's in v0.2

| Feature | API |
Expand All @@ -153,6 +177,7 @@ Records remain in `result.records` after revocation; apps decide whether to surf
| Schema-evolution fallback (minimal-shape readers) | `result.minimalRecords` |
| **Streaming reader for large packs** | `streamMemoryPack` (async iterator) |
| **Signed revocations (soft-delete)** | `appendRevocations`, `result.revokedRecordHashes` |
| **Chain-anchored revocations** | `appendRevocationAnchors`, `verifyRevocationAnchors` |
| **Standalone CLI verifier** | `npx @clude/memorypack verify <pack>` |
| Reference test vectors (deterministic fixture) | `src/__tests__/fixtures.ts` |

Expand Down Expand Up @@ -186,5 +211,6 @@ Post-v0.2 (tracked in the [main repo](https://github.com/sebbsssss/clude)):
- Production IPFS / Arweave content anchoring
- Multi-chain anchors (Ethereum L2, Bitcoin OP_RETURN)
- True streaming through tar (today the reader extracts to a temp dir first)
- Tarball-aware `appendRevocations` (today only directory packs)
- Chain-anchored revocations (so revocation timestamps can't be backdated)
- Tarball-aware `appendRevocations` / `appendRevocationAnchors` (today both are directory-only)
- @solana/web3.js test mocks for `verifyChainAnchors` / `verifyRevocationAnchors`
- Backdating detection (compare on-chain block timestamp to signed `revoked_at`)
2 changes: 1 addition & 1 deletion packages/memorypack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@clude/memorypack",
"version": "0.5.0",
"version": "0.6.0",
"description": "Reference reader/writer for the MemoryPack spec \u2014 open, signed, chain-anchorable file format for portable AI agent memory.",
"license": "MIT",
"homepage": "https://github.com/sebbsssss/clude/blob/main/docs/memorypack.md",
Expand Down
Loading