ic-memory
ic-memory helps Internet Computer canisters avoid opening the wrong stable
memory after an upgrade.
It remembers this mapping forever:
logical store -> physical stable-memory slot
If a future version tries to move that store to a different slot, or reuse that
slot for a different store, ic-memory rejects the layout before stable-memory
handles are opened.
Why Use It?
Use ic-memory when a canister has more than one stable store and the layout
can change over time.
It is most useful for frameworks, generated canisters, multi-store apps, plugin systems, and canister families that evolve across releases.
You probably do not need it for a tiny canister with one hand-written stable structure and a fixed layout.
The Bug
Version 1 ships with:
app.users.v1 -> MemoryManager ID 100
app.orders.v1 -> MemoryManager ID 101
A later upgrade accidentally ships with:
app.users.v1 -> MemoryManager ID 101
app.orders.v1 -> MemoryManager ID 100
That can still compile. It can even install.
But now the canister may open orders data as users data, and users data as
orders data. ic-memory catches that mismatch first.
Quick Start
Declare the MemoryManager IDs your crate owns:
ic_memory_range!;
Open stable structures through ic_memory_key!:
use RefCell;
thread_local!
Bootstrap once before touching stable data:
That is the normal path.
Multi-Crate Composition
Every crate registers into the same linked ic-memory runtime. Crates do not
need to import or name each other:
Bootstrap validates the complete layout from every linked crate, commits the
allocation ledger, and publishes validated allocations. TLS-backed stores open
when your code first touches the thread_local!.
Duplicate stable keys, duplicate MemoryManager IDs, overlapping ranges, and out-of-range declarations fail before stable structures open.
Range claims are authoritative in the default runtime. If a crate registers
ic_memory_range!, its declared memories must stay inside that range. Framework
adapters that want their own range policy, such as Canic, should register only
the ranges they want ic-memory to enforce and put the rest in their policy
adapter.
The validated allocation state is an in-memory capability produced by bootstrap; it is not a serde payload and should not be treated as configuration.
Stable Keys
Stable keys are permanent logical store names. They should describe ownership and purpose, not the current memory ID.
namespace.component.store_or_role.vN
Examples:
use StableKey;
parse.expect;
parse.expect;
parse.expect;
Changing a key creates a new logical allocation identity. If the durable store is the same, keep the stable key and update schema metadata instead.
More Detail
The short version:
declare ranges
register stable stores
bootstrap once
only then open stable memory
Framework authors and policy adapters should read ADVANCED.md. The non-negotiable invariants are recorded in SAFETY.md.
ic-memory is early infrastructure extracted from Canic. It owns allocation
governance, not schema migration, endpoint routing, authorization, or data
semantics.