ZeroForgeLive on 0G Galileo

@sovereignclaw/memory

Sovereign, encrypted, revocable memory primitives for SovereignClaw agents. Storage backends are pluggable (InMemory for tests, OG_Log for 0G Storage) and any backend can be wrapped with encrypted(...) to get authenticated client-side encryption (AES-256-GCM, KEK derived from an EOA signer).

Install

pnpm add @sovereignclaw/memory ethers

10-line quickstart

import { JsonRpcProvider, Wallet } from 'ethers';
import { OG_Log, encrypted, deriveKekFromSigner } from '@sovereignclaw/memory';

const signer = new Wallet(process.env.PRIVATE_KEY!, new JsonRpcProvider(process.env.RPC_URL!));
const kek = await deriveKekFromSigner(signer, 'my-agent');
const memory = encrypted(
  OG_Log({
    namespace: 'my-agent',
    rpcUrl: process.env.RPC_URL!,
    indexerUrl: process.env.INDEXER_URL!,
    signer,
  }),
  { kek },
);
await memory.set('greeting', new TextEncoder().encode('hello from sovereignclaw'));
const value = await memory.get('greeting'); // plaintext Uint8Array; ciphertext on the log

API

ExportKindPurpose
MemoryProviderinterfaceget / set / delete / list / flush / close over keyed byte values.
InMemory(opts)adapterProcess-local backend. Namespaced. Used by tests and coordination.
OG_Log(opts)adapter0G Storage Log backend. Writes envelopes; keeps a pointer tree.
encrypted(provider, opts)wrapperAES-256-GCM around any provider. KEK in, keys/ciphertext out.
deriveKekFromSignerfnEIP-191 sign a namespaced message, keccak256 → 32-byte KEK.
encryptValue / decryptValuefnLower-level GCM helpers with AAD binding.
buildAadfnBuilds the canonical AAD for a (namespace, key, version) tuple.
TOMBSTONE / isTombstoneconst/fnDelete marker sentinel for append-only stores.
readEnvelopeByRootfnLow-level: read a single envelope by its 0G root hash.

Errors

All typed; all extend MemoryError:

ErrorWhen
DecryptionErrorEnvelope failed to decrypt (wrong KEK, wrong AAD, corrupt data).
TamperingDetectedErrorGCM tag mismatch — the envelope was tampered with in transit.
MalformedCiphertextErrorEnvelope header didn’t match the expected v1 layout.
StorageErrorBackend rejected a read/write for a non-SDK reason.
StorageSdkError0G SDK threw or the indexer returned a transient fee/node error.
InvalidKeyErrorKEK is not 32 bytes / AAD is malformed.
KeyDerivationErrorderiveKekFromSigner failed (usually the signer refused).
ProviderClosedErrorOperation after provider.close().

What’s sovereign about this

  • Plaintext never leaves the wallet. encrypted() encrypts before any

network call. Anyone with only the indexer can read ciphertext.

  • The KEK is reproducible. Derived from an EOA signature over a

namespaced message, so the same wallet + namespace always gets the same KEK — no key storage service.

  • Append-only with tombstones. delete(key) writes a tombstone, not

a physical erase. Combined with AgentNFT.revoke() and the oracle’s revocation registry, this is how iNFT memory becomes unreadable.

Further reading

License

MIT — see the repo root.