MemoryId Inventory
This document is a committed architectural contract. It is the authoritative
allocation table for every ic-stable-structures MemoryId across all OhShii
canisters (launcher repo + locker repo). Read it — and update it in the same
commit — before any change that adds, removes, or relocates a stable structure
(StableBTreeMap / StableCell / StableVec / StableLog).
It exists because a single, authoritative table is the only reliable defence
against reusing a MemoryId: ic-stable-structures’ MemoryManager never
reclaims a bucket, so binding a new, layout-incompatible structure to a
previously-used id makes the library decode the old bytes — which traps
(fixed-size cells / primitives) or silently mis-decodes (BTree node layout).
The hard rule (why this document exists)
Section titled “The hard rule (why this document exists)”ic-stable-structures’ MemoryManager never reclaims a bucket. When a stable
structure is removed, its MemoryId keeps the removed structure’s bytes on every
canister that was deployed during that structure’s lifetime. Binding a new,
layout-incompatible structure to that same id makes the library decode the
stale bytes — which traps (fixed-size cells / primitives, below your
Storable::from_bytes, so a decode fallback cannot help) or silently
mis-decodes (BTree node layout). Therefore:
- A removed MemoryId is RESERVED FOREVER. Never reassign it.
- New structures take the lowest NEVER-USED id (per the per-canister “next-free” line below), never a reserved/removed one.
- Never tighten a value/key type or shrink a
Boundedmax_sizeon an existing id (re-encodes old bytes — treat as a destructive migration). - New persisted record fields are
Option<T>, append-last (Candid tolerates missing trailingoptfields; bare-Tappends wipe config on upgrade). - One
MemoryManagerper canister. Never construct a secondMemoryManager::init(DefaultMemoryImpl::default())— two managers on the sameDefaultMemoryImplalias the same bucket table (a latent corruption hazard). - Any layout/MemoryId change requires an UPGRADE-PATH test (deploy the
current wasm → populate →
--mode upgradeto the new wasm → exercise every public query) as a deploy gate — unit tests and diff-reading cannot catch this class.
Launcher repo canisters
Section titled “Launcher repo canisters”1. launcher backend (src/backend/src/memory.rs)
Section titled “1. launcher backend (src/backend/src/memory.rs)”type Memory = VirtualMemory<DefaultMemoryImpl> over the single MEMORY_MANAGER.
Currently bound:
| id | static | type |
|---|---|---|
| 0 | LAUNCHED_TOKENS | StableBTreeMap<u64, LaunchedToken> |
| 1 | TOKEN_ID_COUNTER | StableCell<u64> |
| 2 | ICO_CREATION_STATUS | StableCell<FeatureStatus> |
| 3 | ICO_PARTICIPATION_STATUS | StableCell<FeatureStatus> |
| 4 | TOKEN_CREATION_STATUS | StableCell<FeatureStatus> |
| 5 | INDEX_CREATION_STATUS | StableCell<FeatureStatus> |
| 6 | SUBACCOUNT_COUNTER | StableCell<u32> |
| 8 | CRITICAL_LOGS | StableBTreeMap<u64, CriticalLogEntry> |
| 9 | CRITICAL_LOG_COUNTER | StableCell<u64> |
| 10 | LAST_PUBLIC_TIMER_RESTART | StableCell<u64> |
| 11 | TIMER_RESTART_IN_PROGRESS | StableCell<bool> |
| 12 | BURNING_FEE_CONFIG | StableCell<BurningFeeConfig> |
| 13 | ICO_FEE_CONFIG | StableCell<IcoFeeConfig> |
| 14 | LAST_HOUSEKEEPING_CHECK | StableCell<u64> |
| 15 | BITCOIN_GATEWAY_STATUS | StableCell<FeatureStatus> — Bitcoin cross-chain Gateway toggle (frontend-only enforcement) |
| 16 | SOLANA_GATEWAY_STATUS | StableCell<FeatureStatus> — Solana (Menese) Gateway toggle (also enforced in register_menese_user) |
| 17 | GUEST_FEE_CUSTODY_V2 | StableBTreeMap<String, ()> — OHSHII guest-fee v2 custody marker (campaign_id present ⇒ per-campaign sub, not MAIN); virgin bucket, SAFE |
| 18 | GUEST_FEE_FORWARD_DUE | StableBTreeMap<String, u64> — Completed-campaign guest-pot forward schedule/latch (campaign_id → due ns = completion+7d); schedule ts, NOT a ledger created_at_time pin; virgin bucket, SAFE |
| 19 | TREASURY_LIQ_OPS | StableBTreeMap<u64, StorableTreasuryLiquidityOp> — treasury-funded ICPSwap liquidity op journal (ONS proposals; persisted before any fund movement, resumed by proposal_retry_treasury_liquidity); virgin bucket, SAFE (2026-07) |
| 20 | TREASURY_LIQ_OP_COUNTER | StableCell<u64> — monotonic id counter for TREASURY_LIQ_OPS; virgin bucket, SAFE (2026-07) |
| 21 | FINALIZE_DRAIN_PINS | StableBTreeMap<String, StorableFinalizeDrainPin> — LGE finalize LP-drain pin (C1/GAP-1/GAP-2 hardening). Keyed by campaign_id, written BEFORE the finalize campaign-sub → pool_manager MAIN LP transfer: pins the drawn AMOUNT and a FIXED created_at_time so a retry re-sends the byte-identical ledger tx (dedup via TxDuplicate→Ok) instead of recomputing a draw from an already-drained subaccount; drained=true = transfer provably landed (retry skips the transfer). Virgin bucket, SAFE (2026-07) |
| 22 | PENDING_POOL_JOBS | StableBTreeMap<String, StorablePendingPoolJob> — detached LGE pool-creation jobs (2026-07 creation-pipeline hardening). Keyed by campaign_id; persisted BEFORE the zero-delay timer that runs the pool phase after create_ico_with_icrc2_payment returns; re-armed from post_upgrade (timers do not survive upgrades); carries canister ids to reveal, liquidity params, at-most-once fund_transfer_started marker and bounded attempts; removed on completion or terminal Frozen. Virgin bucket, SAFE (2026-07) |
| 30 | USED_RELAUNCH_MEMOS | StableBTreeMap<u64, u64> |
| 31 | PENDING_RELAUNCH_COMMITS | StableBTreeMap<String, StorableRelaunchUpdate> |
| 36 | SHIELD_PREAUTH_MODE | StableCell<u8> (relocated from 34) |
| 37 | USED_POW_NONCES | StableBTreeMap<UsedPowNonceKey, ()> (relocated from 32) |
| 38 | BACKEND_WORKFLOW_RECORDS | StableBTreeMap<String, StorableBackendWorkflowRecord> (relocated from 33; folded into the single main MEMORY_MANAGER) |
| 39 | REFUND_CREATED_AT | StableBTreeMap<String, u64> (F2(b) per-(campaign,user) deterministic refund created_at_time) |
RESERVED — NEVER reuse: 7 (CAMPAIGN_ID_COUNTER), 32 (PENDING_TOKEN_PURCHASES),
33 (RECOVERY_QUEUE), 34 (PENDING_CLEANUP_CURSOR), 35 (RECENT_APPLIED_CONTRIBUTIONS),
40 (REFERRAL_REFUND_COVERED — removed in the FIX-2 treasury-cover removal, 2026-06).
Each held a removed structure; the bucket still carries that structure’s stale bytes,
so none of these ids may ever be rebound.
Next free id: 23–29, 41+ (use the lowest for the next new structure; 17 GUEST_FEE_CUSTODY_V2 + 18 GUEST_FEE_FORWARD_DUE bound by the OHSHII guest-fee v2 custody work, 2026-06; 19 TREASURY_LIQ_OPS + 20 TREASURY_LIQ_OP_COUNTER bound by the treasury-liquidity governance work, 2026-07; 21 FINALIZE_DRAIN_PINS bound by the LGE finalize LP-drain-pin hardening, 2026-07; 22 PENDING_POOL_JOBS bound by the LGE creation detached-pool work, 2026-07).
2. dao_storage (src/storage/src/memory.rs)
Section titled “2. dao_storage (src/storage/src/memory.rs)”Currently bound: 0 CAMPAIGNS, 1 CONTRIBUTIONS, 2 TOKENS, 3 SPONSOR_INFO,
4 USER_SPONSORS, 5 WHALE_PASSES, 6 REFERRALS, 7 CAMPAIGN_BANNERS,
8 SPONSOR_CASE_INSENSITIVE_INDEX, 9 PRINCIPAL_SPONSOR_INDEX, 10 SPONSOR_USERS_INDEX,
11 CREATOR_TOKEN_INDEX, 12 CAMPAIGN_STATE_INDEX, 13 HIGHLIGHTED_CAMPAIGNS_INDEX,
14 TOKEN_ID_COUNTER, 15 CONTRIBUTION_ID_COUNTER, 16 SPONSOR_REGISTRATION_LIMIT,
17 RUGGYS_CHOICE_CACHE, 18 STORAGE_EVENT_LOG, 19 STORAGE_EVENT_COUNTER,
20 LEDGER_TO_CAMPAIGN_INDEX, 22 CONTRIBUTION_ID_INDEX, 24 CONTRIBUTORS_BY_CAMPAIGN,
25 SHIELD_PREAUTH_MODE (StableCell<u8> — bound on a virgin bucket, SAFE),
26 CONTRIBUTION_LIFECYCLE, 27 GUEST_FEE_PAID,
28 GUEST_FEE_AMOUNT (guest-fee gross snapshot for the failure refund, 2026-06),
29 GUEST_FEE_REFUNDED (per-(campaign,user) guest-fee refunded marker, 2026-06),
30 CAMPAIGN_SWEEP_DONE (per-campaign 7-day residual-ICP sweep marker, 2026-06).
RESERVED — NEVER reuse: 21 (DAO_MIGRATION_DONE, StableCell<u64>; migration code removed, const reserved), 23 (CONTRIB_INNER_MIGRATION_FLAG, one-shot flag; migration code removed, const reserved).
Next free id: 31+.
3. ohshii_governance / ONS (src/ohshii_governance/src/memory.rs)
Section titled “3. ohshii_governance / ONS (src/ohshii_governance/src/memory.rs)”The most-migrated canister. A past proposals migration is the canonical correct
pattern: it moved active maps to fresh high slots (PROPOSALS 0→43,
EXECUTED_PROPOSALS 2→44, VOTER_HISTORY 3→46, WASM_CHUNKS 4→42) rather than
reusing the vacated low ids — with an in-code comment “Using a fresh slot prevents
reads against legacy/corrupted node layout.”
Currently bound (selected; full set in code): 1 PROPOSAL_COUNTER, 5 UPLOAD_SESSIONS,
6 SESSION_COUNTER, 7 EVENT_LOG, 8 TIMER_STATE, 9 EVENT_COUNTER,
10 PENDING_SELF_UPGRADE, 11 PROPOSAL_CREATION_FEE, 12 REJECTION_COST,
13 GUARDIAN_PRINCIPAL, 14 MODERATED_PROPOSAL_DESCRIPTIONS,
15 PENDING_DAPP_SELF_STEP (StableCell<StorablePendingDappSelfStep> — Candid
Option<PendingDappSelfStep>, Unbounded, decode-hardened → None; virgin, SAFE;
UpgradeDappBatch self-last step), 16 PENDING_ASSET_REVOKES
(StableBTreeMap<StorableAssetRevokeKey, u64> — asset-Prepare revoke retry queue,
key {of_principal, asset_target} natural dedup, value enqueued_at ns,
Bound::Bounded{64}, decode-hardened → anonymous sentinel; virgin, SAFE), 40 DECODE_ERROR_COUNT,
41 LAST_DECODE_ERROR_TS, 42 WASM_CHUNKS, 43 PROPOSALS, 44 EXECUTED_PROPOSALS,
45 PROPOSAL_MAP_MIGRATION_STATE, 46 VOTER_HISTORY, 47 PROPOSAL_VOTES,
48 LAST_PUBLIC_TIMER_RESTART, 49 TIMER_RESTART_IN_PROGRESS, 50 PROPOSAL_RATE_LIMITS,
51 VOTING_PARAMS_CONFIG, 53 VERIFIED_USERS, 54 USED_NULLIFIERS,
55–58 World-ID, 59–69 DecideID, 70 ANY_METHOD_VERIFIED_USERS, 71–73/76–78 delegation,
74 REBUILD_STATE, 79 USED_POW_NONCES, 80 OVERRIDE_PROPOSAL_VOTES,
81 SHIELD_PREAUTH_MODE (StableCell<u8> — virgin bucket, SAFE),
82 OVERRIDE_FRESH_ATTESTED (StableBTreeMap<Vec<u8>, u64> — B1 override
fresh-personhood marker, value = override_expiry_ns for GC; virgin, SAFE; holds
TWO disjoint key shapes in the ONE bucket — the caller-driven SONS-routed path
REUSED this id, adding NO new MemoryId: (a) the ONS-self marker
"{proposal_id}:{voter}" (leading ASCII digit 0x30..=0x39) read by ONS vote();
(b) the DAO-DIMENSIONED marker [dao_len][dao][proposal_id:8be][voter] from
make_override_marker_key (leading length-byte 0..=29) written by the
caller-driven attest path and read by the did_attest_override query a SONS
pulls — the leading-byte ranges can never overlap → zero collision even when
dao == canister_self()), 83 OVERRIDE_PERSONHOOD_NULLIFIERS
(StableBTreeMap<Vec<u8>, StorableOverrideNullifierClaim> — B1 one-human →
one-ballot authority, key (dao, proposal_id, nullifier), value {voter, override_expiry_ns}; disjoint from 53/54; virgin, SAFE),
84 OVERRIDE_OUTCALL_GLOBAL_CAP (StableCell<u64> — A3 runtime-tunable
canister-wide override-outcall cap, default 240/hr, floored on read AND write at
OVERRIDE_OUTCALL_GLOBAL_CAP_FLOOR; virgin, SAFE).
LEGACY-ACTIVE (bound; do NOT reuse, do NOT remove without care): 0 LEGACY_PROPOSALS,
2 LEGACY_EXECUTED_PROPOSALS — rebound from the 0→43/2→44 migration with the
identical StableBTreeMap<u64, StorableProposal> type (Candid-compatible), read
only by the one-time migration fn. Safe because the type is unchanged.
RESERVED — NEVER reuse: 3 (VOTER_HISTORY, abandoned by the proposals migration), 4 (WASM_CHUNKS,
abandoned by the proposals migration — has the documented “fresh slot” comment), 52 (DAO_MIGRATION_FLAG,
binding removed, const reserved).
Next free id: 17–39, 75, 85. (82 OVERRIDE_FRESH_ATTESTED + 83 OVERRIDE_PERSONHOOD_NULLIFIERS + 84 OVERRIDE_OUTCALL_GLOBAL_CAP bound by the override fresh-personhood re-verification work, 2026-06.)
4. sons_governance / SONS (src/sons_governance/src/memory.rs) — DYNAMIC, per-campaign
Section titled “4. sons_governance / SONS (src/sons_governance/src/memory.rs) — DYNAMIC, per-campaign”Currently bound (selected): 0 PROPOSALS … 20 UPLOAD_SESSIONS, 22 PENDING_SELF_UPGRADE,
23 DECODE_ERROR_COUNT, 24 LAST_DECODE_ERROR_TS, 25 WASM_CHUNKS (relocated from 21
because the bounds changed — move-to-fresh-id), 26 PROPOSAL_VOTES,
27–32 timer/fee/session, 34 PENDING_DAPP_SELF_STEP (StableCell<StorablePendingDappSelfStep>
— Candid Option<PendingDappSelfStep>, Unbounded, decode-hardened → None; virgin, SAFE;
UpgradeDappBatch self-last step), 71–78 delegation, 79 SONS_DELEGATION_REQUIRES_VERIFICATION,
80 PERMANENT_VERIFIED_USERS, 81 LGE_COMPLETED_LOCKED, 82 USED_POW_NONCES,
83 OVERRIDE_PROPOSAL_VOTES, 84 SHIELD_PREAUTH_MODE (StableCell<u8> — virgin, SAFE),
85 LAST_PULL_RECOVERY_TIMESTAMP, 86 OVERRIDE_FRESH_ATTESTED
(StableBTreeMap<Vec<u8>, u64> — B1 SONS-local override fresh-personhood marker,
key "<proposal_id>:<voter>", value = override_expiry_ns; the one-ballot
nullifier authority lives on ONS, not here; virgin, SAFE), 35 PENDING_ASSET_REVOKES (StableBTreeMap<StorableRevokeKey, u64> — asset-Prepare revoke retry queue; key (of_principal, asset_target), value enqueued_at ns; virgin bucket, SAFE),
36 REFERRAL_CLAIMS (F4e — StableBTreeMap<Principal, StorableReferralClaim>, deferred-referral per-sponsor claims; Bounded 128; decode-hardened placeholder earned_e8s==0; virgin bucket, SAFE),
37 REFERRAL_COMPLETION_TS (F4e — StableCell<u64>, LGE completion ts = 7-day claim window start; virgin bucket, SAFE),
38 CUSTODY_OPS (per-lock custody isolation, 2026-07 — StableBTreeMap<String, StorableCustodyOp>, fund/sweep/refund queue keyed "F:{lock_id}"/"S:"/"R:", insert-if-absent preserves the pinned dedup key; Unbounded, decode-hardened placeholder lock_id __decode_error__ removed by the processor; virgin bucket, SAFE),
39 LOCK_SUBACCOUNTS (per-lock custody isolation, 2026-07 — StableBTreeMap<String, String>, APPEND-ONLY registry hex(subaccount 32B) → lock_id; treasury-integrity guard consumed by execute_treasury_withdraw; entries never removed; virgin bucket, SAFE).
RESERVED — NEVER reuse: 21 (former WASM_CHUNKS Unbounded; relocated to 25, abandoned
bucket holds stale legacy chunk bytes on live per-campaign canisters), 33 (DAO_MIGRATION_FLAG,
reserved, no live static).
Next free id: 42–70, 87–89 (40 POOL_POSITIONS + 41 POOL_OP_INTENT bound by the treasury-liquidity governance work, 2026-07). Ids 74 and 75 are intentionally SKIPPED in SONS to keep
the ONS/SONS MemoryId layout aligned (ONS binds them for its VERIFIED_USERS-family slots,
which SONS does not replicate) — do NOT allocate 74/75 in SONS even though they are
per-canister-unbound. (Note: per-campaign SONS canisters are upgraded in place — apply the
reserve rule to SONS wasm history exactly as for static canisters.)
5. pool_manager (src/pool_manager/src/memory.rs)
Section titled “5. pool_manager (src/pool_manager/src/memory.rs)”Currently bound: 0–20 (fully append-only; POOLS, POOL_CANISTER_IDS, POSITION_IDS,
POOL_TOKENS, CAMPAIGN_SUBACCOUNTS, CAMPAIGN_ACCOUNT_IDS, TOKEN_LEDGER_IDS,
TOKEN_SYMBOLS, TOTAL_FEES_COLLECTED, FEE_DISTRIBUTIONS_COMPLETED, OPERATION_STATE,
BACKEND_CANISTER, ADMIN_PRINCIPAL, COLLECTED_FEES_TOTAL, MIGRATION_LOG,
MIGRATION_COMPLETED, CLOWN_APPROVALS_GIVEN, FEE_WITHDRAWALS, POOL_EVENT_LOG,
POOL_EVENT_COUNTER, PASSCODE_PRICE).
RESERVED — NEVER reuse: 21 (DAO_MIGRATION_DONE, StableCell<u64>; migration code
removed, const reserved).
Also bound (stale-line fix, 2026-07 audit): 22 MINT_ATTEMPTS, 23 REFERRAL_DRAIN_TS
(F4 — per-campaign deterministic drain created_at_time), 24 REFERRAL_DRAIN_INITBAL
(F4 — stable initial balance for the underfunded-clamp) — verified against
src/pool_manager/src/memory.rs:63,70,78. Plus 25 TREASURY_POSITIONS
(StableBTreeMap<String, StorableTreasuryPosition>), 26 TREASURY_OPS
(StableBTreeMap<u64, StorableTreasuryOp>), 27 REMOVAL_OPS
(StableBTreeMap<u64, StorableRemovalOp>), 28 REMOVAL_OP_COUNTER (StableCell<u64>)
— treasury-liquidity governance work, 2026-07; and 29 POOL_CREATING_STARTED_AT
(StableBTreeMap<String, u64>, campaign_id → ns when PoolState::Creating began;
the 30-minute staleness escape that lets a wedged pool-creation retry, 2026-07 —
src/pool_manager/src/memory.rs:105,506).
Next free id: 30 (25–28 bound by the treasury-liquidity governance work, 29 by the pool-creation staleness-escape hardening, all 2026-07).
Locker repo canisters (canonical here; mirrored to dev-ohshii-locker/docs/MEMORY_ID_INVENTORY.md)
Section titled “Locker repo canisters (canonical here; mirrored to dev-ohshii-locker/docs/MEMORY_ID_INVENTORY.md)”6. locker backend (dev-ohshii-locker/src/backend/src/memory.rs)
Section titled “6. locker backend (dev-ohshii-locker/src/backend/src/memory.rs)”Currently bound: 0 SUPPORTED_TOKENS, 1 TRANSFER_MONITORS, 2 LOCK_CHECKS,
3 TEXT_LOGS, 4 CIRCUIT_BREAKERS, 5 CONFIG, 6 PENDING_RECLAIM_FIXUPS,
7 PENDING_UNLOCK_CONFIRMATIONS, 8 USED_POW_NONCES, 9 RECOVERED_ORPHAN_POSITIONS,
10 SHIELD_PREAUTH_MODE (StableCell<u8> — virgin, SAFE), 11 ORPHAN_PENDING_LP
(StableBTreeMap<u128, OrphanPendingRecord> — pending-orphan LP journal, complement of
id 9; appended on a virgin bucket, SAFE), 12 LOCK_CREATION_STATUS
(StableCell<LockCreationStatus> — public lock-creation toggle, gates create_lock_v2;
virgin, SAFE), 13 LP_SAGA_FUND_EVENTS (StableBTreeMap<u64, LpSagaFundEventRecord> —
LP-lock saga fund-event journal; DEFINITE/AMBIGUOUS-classified money legs append here
instead of a blind rollback, queryable via get_lp_saga_fund_events; virgin, SAFE),
14 LP_MINT_INTENTS (StableBTreeMap<StableString, LpMintIntentRecord> — write-ahead LP
mint intent, written BEFORE the pool mint so a lost-response mint is recoverable; virgin,
SAFE), 15 LP_SAGA_FUND_EVENT_SEQ (StableCell<u64> — monotonic id counter for
LP_SAGA_FUND_EVENTS, ids never reused; virgin, SAFE), 16 ORPHAN_PENDING_LP_V2
(StableBTreeMap<StableString, OrphanPendingRecord> — composite (pool, position_id)
orphan-pending keyspace; new writes land here, reads fall back to id 11; virgin, SAFE),
17 RECOVERED_ORPHAN_POSITIONS_V2 (StableBTreeMap<StableString, OrphanRecoveryRecord> —
composite-key recovered-orphan dedup, complement of id 9; virgin, SAFE). Fully append-only;
single MemoryManager. (13–17 all bound by the LP-lock saga hardening, 2026-07.)
RESERVED: none. Next free id: 18+.
7. lock_storage (dev-ohshii-locker/src/storage/src/stable_memory.rs)
Section titled “7. lock_storage (dev-ohshii-locker/src/storage/src/stable_memory.rs)”Currently bound: 0 = MEMORY_MANAGER own bookkeeping id (not a data bucket),
1 LOCKS, 2 USER_LOCKS_INDEX, 3 TOKEN_LOCKS_INDEX, 5 UNLOCK_TIME_INDEX,
6 SUBACCOUNTS, 7 SUBACCOUNT_INDEX, 8 CONFIG, 10 STATUS_INDEX,
11 RETRY_QUEUE (bound but never populated), 12 CREATION_TIME_INDEX,
13 STORAGE_EVENT_LOG, 14 STORAGE_EVENT_COUNTER, 15 BACKED_UP_TOKENS,
16 TOKEN_BACKUP_COUNTER, 17 ARCHIVED_LOCKS, 18 ARCHIVED_USER_INDEX,
19 UNLOCK_SCHEDULES, 20 SHIELD_PREAUTH_MODE (StableCell<u8> — virgin, SAFE),
21 MIN_LOCK_DURATION_DAYS (StableCell<u64>, default 45 — ONS-synced VP-eligibility floor; virgin, SAFE).
StorableLockRecord evolves only by append-last Option<T> fields.
RESERVED / virgin (never bound to a static): 4 (ACTIVE_LOCKS_INDEX_ID, const-only),
9 (TOKEN_CONFIG_ID, const-only). Next free id: 4, 9, 22–24 (4 and 9 are virgin
reserved consts — safest; id 21 is now bound to MIN_LOCK_DURATION_DAYS).
8. locker proxy + strongbox
Section titled “8. locker proxy + strongbox”proxy (dev-ohshii-locker/src/proxy/src/memory.rs): 0 TRANSFERS, 1 STABLE_LOGS,
2 CONFIG (gained a bare u64 pending_transfer_count with #[serde(default)] — VALUE-struct
evolution on the same binding, not a collision; recomputed in post_upgrade). Next free: 3–6.
strongbox (dev-ohshii-locker/src/strongbox/src/memory.rs): 0 TRANSFER_LOGS,
1 AUTHORIZED_PROXIES (StableBTreeMap<Principal, ()> — bound on the formerly
reserved-UNUSED LOG_COUNTER_ID id; SAFE because it was never bound to any structure (virgin
bucket), unlike a removed-structure bucket which is reserved forever), 2 CONFIG.
RESERVED: none (id 1’s reserved-unused status was resolved by the virgin-bucket bind).
Next free: 3+.
Allocation policy
Section titled “Allocation policy”- Adding a structure: assign the lowest never-used id from the canister’s “Next free id” line. Update this document + the per-canister “Next free” line in the SAME commit.
- Removing a structure: move its id to that canister’s RESERVED table with the
reason. Leave a
// RESERVED — never reuse: …comment at the const inmemory.rs. Do NOT delete the const if other code still references it; if you delete it, the id is still reserved. - Relocating a structure (e.g. to escape a collision): bind it to a fresh never-used id, mark the old id RESERVED, and run the upgrade-path test.
- Never: reuse a reserved id; construct a second
MemoryManageron the sameDefaultMemoryImpl; tighten a type or shrink aBoundedmax_sizeon a live id; append a bare-T(non-Option) field to a persisted record.
Audit procedure (validate this inventory against the code)
Section titled “Audit procedure (validate this inventory against the code)”# 1. Every MemoryId::new( site in both repos (must all map to a canister section above):rg -n "MemoryId::new\(" --type rust src/ # run in each repo root
# 2. Per canister, full allocation history of its memory file (catches rebindings):git -C <repo> log --all -p --follow -- <memory_file>git -C <repo> log --all -p -S 'MemoryId::new(<N>)' -- <src_dir> # per suspicious id
# 3. Reserved-id check: confirm no RESERVED id is bound to a live static today.# 4. Single-manager check: exactly ONE MemoryManager::init per canister.rg -n "MemoryManager::init" --type rust src/