Skip to content

Logging System

significant events only

Canister Code

log_and_store(msg)

log_and_store_with_level(msg, level)

log_event(event)

Tier 1: IC system log (println + trap messages)

Size set by log_memory_limit (default 4096 bytes)

Survives upgrades · fetch_canister_logs API

Tier 2: Heap Logs (RefCell/thread_local)

Lost on upgrade · variable capacity

ICO_LOGS, RECENT_LOGS, TOKEN_CREATION_LOGS

Tier 3: Stable Memory Logs (StableBTreeMap)

Persistent across upgrades · 10K entries max (50K backend)

FIFO rotation · public query API

CanisterMemIdLog typeCapacity / rotationSize
Backend8,9CriticalLogEntry50K max (MAX_CRITICAL_LOGS), FIFO~10-25 MB
Proposal Mgr7EventRecord10K max, prune~2-5 MB
LGE Gov4,5EventRecord10K max, prune~2-5 MB
Pool Mgr18,19PoolEventRecord10K max, FIFO~2-5 MB
DAO Storage18,19StorageEventRecord10K max, FIFO~2-5 MB

Total stable log overhead: ~18-45 MB across all 5 canister types (under 0.01% of the 500 GiB stable memory limit of any one canister).

Canister method call

log_and_store(msg)

println!(msg)

IC system log

infer log level

extract error code

error/warn/success/info

(explicit: also critical)

TRANSFER_FAILED, SECURITY_*

store_critical_log()

(skip if stable mem > ~3.8 GiB)

StableBTreeMap (if level

qualifies & mem available)

route_to_heap_logs()

(match on msg prefix)

ICO_LOGS or RECENT_LOGS

(based on [prefix] pattern)

/ons-voting (governance logs)

Summary

Stable

Native IC

/system-status

Backend Logs

LGE Stor.

Pool Mgr Events

Rate Limit

Propos. Mgr Events

Native IC Logs

Three-tier logging across all canisters:

TierPersistenceCapacityPurpose
IC system logKept by the network; survives upgrades, deleted on reinstall or uninstalllog_memory_limit per canister (network default 4096 bytes; oldest lines dropped first)Diagnostics: every println! and every trap message, read through the management canister fetch_canister_logs
Heap LogsLost on upgradeVariable per canisterReal-time campaign/operation tracking
Stable Memory LogsPersistent across upgrades10,000 entries FIFO per canister (50,000 on the backend)Audit trail, monitoring, DAO transparency

All stable log query endpoints are public (no caller restriction on query calls) for DAO transparency. The IC system log of every canister listed here has its log_visibility set to public, so anyone can read it through the IC management canister; see The IC system log below.


The IC system log (also called the canister log, or “Native IC Logs” on the status pages) is kept by the Internet Computer itself, outside the canister’s own memory. Its rules come from the network, not from OhShii code:

  • What goes in: every ic_cdk::println! line and every trap message. A trap is recorded as [TRAP]: <message>; a Rust panic! leaves two lines, Panicked at … and [TRAP]: Panicked at ….
  • What does not go in: anything printed while a method runs in non-replicated mode — a plain query call, a composite query and its callbacks, inspect_message, and cleanup callbacks. A println! inside a query is recorded only when that query runs replicated (called as an update, or by another canister). A call refused in inspect_message therefore leaves no line in any log.
  • Writes from non-replicated code are discarded: a query or inspect_message cannot change canister state, so a stable log row or a counter written from one of them is dropped with the rest of that message’s state.
  • Size: set per canister by the log_memory_limit setting (0, or 4096 bytes up to 2 MiB). The network default is 4096 bytes and the oldest lines are dropped first, so at the default a busy canister keeps only minutes of lines and a quiet one keeps days. The memory reserved for the log counts toward the canister’s memory usage.
  • Lifetime: the log survives upgrades; it is deleted when the canister is reinstalled or uninstalled (a canister that runs out of cycles is uninstalled).
  • Visibility: the log_visibility setting is controllers (the network default), public, or a list of up to 10 allowed viewers. The launcher canisters (backend, OHSHII Governance, Pool Manager, DAO Storage) have it set to public in their canister settings, and each LGE Governance canister sets its own log to public after every upgrade. Because these logs are public, the text a canister prints or traps with is public text.
  • Reading: the management canister’s fetch_canister_logs query (filterable by index or by timestamp), for example dfx canister logs <canister-id> --network ic --identity anonymous for a public log. Each line carries a monotonic index that keeps growing across upgrades. The status pages show the same lines under “Native IC Logs”.

What each launcher canister writes there: the backend prints every message it also stores through log_and_store (the stable critical log below), plus diagnostic lines of its own; OHSHII Governance, LGE Governance, the Pool Manager and DAO Storage print diagnostic lines tagged with the canister or function name. Because the log is public, it carries only real events, refusals, errors and trap messages: no debug lines, no line of their own from queries or inspect_message, and no line for a timer tick that found no work, except a skip caused by a real condition (low cycles, the previous tick still running). A new token’s ledger, index and LGE Governance (SONS) ids are withheld from the public records from creation or relaunch until the ICPSwap pool is created (the SONS id too, because its public configuration query returns the ledger id). In that window the lines the creation flow writes in the backend, the Pool Manager, DAO Storage and OHSHII Governance carry no such id: where a line names one of these canisters (the owners of the new token’s initial balances, the archive controllers), it shows only the shortened <ledger-redacted:…> form. The IC system log is a diagnostic window, not a record: the stable logs below and the canisters’ own records are the durable trail.

Replicated query refusal: every public query, including the log readers on this page, answers query calls from anyone. When another canister calls it from an update method or a timer (a replicated call), it answers only the OhShii canisters that read it; any other caller is refused with a trap whose message starts with OHSHII_QUERY_REPLICATED_REFUSED canister=<id> method=<name> caller=<principal>, so the refusal is visible in the IC system log of the refusing canister. An ingress update call naming a query method is refused earlier, in inspect_message, and leaves no line.


Every canister records each of its upgrades in the stable log the System Status page shows: the launcher backend, ONS, pool_manager and dao_storage (like the Locker’s canisters) as a row UPGRADE v=<canister_version>, and each campaign governance canister through its own SystemLifecycle event with event_type = "upgraded" (this event carries no version number and an empty campaign_id). canister_version is the system counter of the canister: it rises with each upgrade, but also with other changes to the canister such as its settings or controllers, so the number is not a count of upgrades.

  • Storage: StableBTreeMap (MemoryId 8), counter in StableCell (MemoryId 9)

  • Capacity: 50,000 entries max (MAX_CRITICAL_LOGS)

  • Rotation: once the ring reaches 50,000 rows, the next insert removes the oldest rows above the cap plus 100 more

  • Safety: Skips the stable write (the line still goes to the IC system log) when the canister’s stable memory exceeds 95% of 4 GiB (about 3.8 GiB) — a fixed threshold, not a fraction of the 500 GiB limit

  • Timer: 300s (5 min) housekeeping cycle

  • Fields: timestamp, payment_memo, campaign_id, user_principal, message, error_code, log_level

  • Levels: error, warn, success, info (auto-inferred from message keywords by log_and_store). Call sites that pass the level explicitly (log_and_store_with_level, log_escrow_latch) can also write critical (money in an ambiguous state, or a broken invariant that needs attention) and, at some older sites, warning (read as warn)

  • Error codes: Auto-extracted from message patterns (GOVERNANCE_CREATION_FAILED, TRANSFER_FAILED, SECURITY_*, etc.). The purchase flow emits one explicit forensic code:

    CodeWritten byMeaningAction
    ICP_RECEIVEDpurchase_tokens_icrc2 after icrc2_transfer_from Ok and BEFORE dao_storage.add_contribution_atomic (src/backend/src/purchase.rs)ICP has landed in the backend canister. Message body carries block=N amount_e8s=X contribution_id=Y. The contribution_id is deterministic on (user, campaign, amount, client_nonce) and matches dao_storage::Contribution.contribution_id once applied.None on the happy path — the entry is observability only. Recovery is fully on-chain (see below); there is no off-chain reconciler.

    Orphan recovery is on-chain. Purchases use per-contribution escrow custody, so a deposit that landed but never became a contribution is recoverable on-chain without any external job: finalize reconciles unforwarded escrows into the campaign sub-account, and a deposit still held in its escrow is returned to the buyer by sweep_escrow_refund (see WORKFLOWS.md). The ICP_RECEIVED log is purely an audit/observability aid — anyone can match a block / contribution_id against dao_storage.lookup_contribution(contribution_id) (a free query) — it is not consumed by any off-chain server. On-chain escrow state is the source of truth; there is no off-chain reconciler (see WORKFLOWS.md and ARCHITECTURE.md).

  • Upgrade row: every upgrade writes one info row UPGRADE v=<canister_version> into the critical log the System Status page shows (get_public_logs)

  • Key functions (in lib.rs / memory.rs):

    • log_and_store(msg) — println + infer level + store stable + route to heap
    • log_and_store_with_level(msg, level) — same with explicit level
    • store_critical_log(entry) — direct stable insert (used from modules outside lib.rs, e.g. security.rs)
    • log_security_event(type, details) — security audit with SECURITY_* error codes, persists to stable
    • route_to_heap_logs(msg, campaign_id) — routes to ICO_LOGS or RECENT_LOGS based on message prefix

API endpoints:

MethodSignatureAccessNotes
get_critical_logs(limit: opt nat64) -> vec CriticalLogEntryPublicMost recent N (at most 3000), oldest first; no limit (null) returns the newest 3000 (older rows: get_public_logs_paginated)
get_critical_log_count() -> nat64PublicTotal stable log count
get_public_logs(limit: opt nat64) -> vec CriticalLogEntryPublicMost recent N (default 50, max 3000)
get_public_log_count() -> nat64PublicSame as get_critical_log_count
get_public_logs_paginated(start: nat64, limit: nat64) -> (vec CriticalLogEntry, nat64)PublicPaginated (max 3000/page), returns (entries, total)
get_campaign_finalize_logs(campaign_id: text, limit: opt nat64) -> vec CriticalLogEntryPublicNewest-first finalization timeline for one campaign (filters the stable store to the [finalize_step] / [finalize_ico] / [post_finalization] tags; default 50, max 200)
get_rate_limit_events(limit: nat64) -> RateLimitEventPagePublicHeap storage, newest N (max 500), plus total_count (rows the ring holds), truncated and ring_capacity
get_campaign_logs(campaign_id: text, user: opt principal) -> vec IcoLogEntryPublicHeap storage: the campaign’s bucket (at most 100 rows, kept for one hour), optionally filtered by user

Finalization step telemetry ([finalize_step]): every LGE finalization pass writes one stable entry per pipeline milestone — state flip to Finalizing, escrow reconcile, existing-position pre-gate, pool verify-or-create (the pool itself is born at LGE creation; at finalization the pool_create milestone reuses it and runs the liquidity leg — pool_manager deposits the raise from the campaign subaccount inside this step; there is no separate drain step) / add-liquidity on an existing pool, pool_id persistence, position verification, post-finalization (vesting locks, position transfer, governance handoff), terminal Completed commit, rollback to Frozen, and the Failed flip — successes AND failures. Message format: [finalize_step] campaign_id: <id> step=<step> target=<canister> outcome=<ok|err> <detail>; campaign_id is set explicitly on the entry, and failures carry an error_code of FINALIZE_STEP_<STEP>. The frontend “LGE Finalization Status” box on the campaign page consumes this feed via get_campaign_finalize_logs.


Read budget on the event readers. The event readers of OHSHII Governance, LGE Governance, the Pool Manager and DAO Storage walk the store by key and decode only the rows they return. A read that would not fit in one query (a budget of 3 billion instructions) stops at the budget instead of failing: a “recent” reader returns the NEWEST rows that fit, in the same order, and get_proposal_events stops its newest-first scan at the same budget; a paginated reader (get_events, get_pool_events, get_storage_events_query) returns the rows from its start position that fit. So an answer can hold fewer rows than the limit asked for; ask again with a smaller limit, or continue a paginated read from the next position.

OHSHII Governance (EventRecord + GovernanceEvent)

Section titled “OHSHII Governance (EventRecord + GovernanceEvent)”
  • Storage: StableBTreeMap (MemoryId 7), counter in StableCell
  • Capacity: 10,000 events max
  • Rotation: once the ring holds more than 10,000 events, the next insert removes the oldest events above the cap plus 100 more
  • Timer: 300s (5 min) for proposal expiry, execution, refunds
  • Event types — 54 variants. The groups below are illustrative, not exhaustive; the authoritative enumeration is the GovernanceEvent type in the canister’s .did.
    • Proposal lifecycle: ProposalCreated, VoteCast, ProposalApproved, ProposalExpired, ProposalRejected, ProposalExecutionStarted, ProposalExecutionFailed
    • Execution results: ChunksTransferredToStore, UpgradeCompleted, AssetUpgradeCompleted, AdminMethodExecuted, FeeCollected, CustomPoolCreated
    • Moderation: GuardianVeto, ProposalDescriptionModerationUpdated
    • Upload: UploadStarted, UploadCompleted, UploadSessionExpired, AssetLeaseTakenOver, FeeForfeited
    • Operational: CanisterManagementAction, ControllerChanged, TimerCycleSummary, TransferResult, SystemLifecycle
  • Upgrade row: every upgrade writes one SystemLifecycle event (event_type = "UPGRADE", details = "v=<canister_version>"), which the System Status page renders as UPGRADE v=<canister_version>
  • Key function: log_event(event) in memory.rs

API endpoints:

MethodSignatureAccessNotes
get_events(start: nat64, limit: nat64) -> vec EventRecordPublicPaginated from the OLDEST stored event, max 3000/page
get_recent_events(limit: opt nat64) -> vec EventRecordPublicThe newest N (default 100, max 3000), returned oldest first
get_events_count() -> nat64PublicNumber of events currently stored
get_proposal_events(proposal_id: nat64) -> vec EventRecordPublicEvents for one proposal, max 500

LGE Governance (EventRecord + GovernanceEvent)

Section titled “LGE Governance (EventRecord + GovernanceEvent)”

One instance per LGE campaign (dynamic canister, not in dfx.json).

  • Storage: StableBTreeMap (MemoryId 4), counter in StableCell (MemoryId 5)
  • Capacity: 10,000 events max
  • Rotation: once the ring holds more than 10,000 events, the next insert removes the oldest events above the cap plus 100 more
  • Timer: 300s (5 min) for lock unlocks and proposal checks (proposals are checked on every tick)
  • Event types — 50 variants. The groups below are illustrative, not exhaustive; the authoritative enumeration is the GovernanceEvent type in that campaign’s canonical .did.
    • Proposal lifecycle: ProposalCreated, VoteCast, ProposalApproved, ProposalExpired, ProposalRejected, ProposalExecutionStarted, ProposalExecuted, ProposalExecutionFailed
    • Config: ConfigUpdatedByAdmin, ConfigUpdated, AdminAdded, AdminRemoved, GovernanceFinalized
    • Upload: UploadStarted, UploadCompleted, UploadSessionExpired, AssetLeaseTakenOver, FeeForfeited
    • Execution results: UpgradeCompleted, ChunksTransferredToStore, AssetUpgradeCompleted
    • Operational: LockStateChanged, TreasuryOperation, TimerCycleSummary, ErrorOccurred, SystemLifecycle, CanisterManagementAction
  • Upgrade row: every upgrade writes one SystemLifecycle event with event_type = "upgraded" and an empty campaign_id; it carries no version number
  • Key function: add_event(event) in memory.rs

API endpoints:

MethodSignatureAccessNotes
get_recent_events(limit: nat64) -> vec EventRecordPublicMost recent N (max 1000), returned newest first
get_events_count() -> nat64PublicNumber of events ever written (the id counter), not the number currently stored
get_proposal_events(proposal_id: nat64) -> vec EventRecordPublicEvents for one proposal, max 500

Pool Manager (PoolEventRecord + PoolEvent)

Section titled “Pool Manager (PoolEventRecord + PoolEvent)”
  • Storage: StableBTreeMap (MemoryId 18), counter in StableCell (MemoryId 19)
  • Capacity: 10,000 events max (MAX_POOL_EVENTS)
  • Rotation: FIFO — once the ring holds more than 10,000 events, the next insert removes the oldest events above the cap plus 100 more
  • Timer: None (event-driven only — logs on pool creation, fee collection, buyback, etc.)
  • Event types — 21 variants: PoolCreated, FeesCollected, BuybackExecuted, FeeCollectionFailed, ProposalExecuted, LiquidityAdded, TransferCompleted, OhshiiSwapExecuted, AdminBuybackExecuted, CustomPoolCreated, CustomPoolLiquidityAdded, PoolStateResetForRelaunch, LedgerRelinked, PoolRedeployed, OperationStep, RateLimitSaturated, TreasuryLiquidityAdded, TreasuryLiquidityRemoved, CampaignPoolReconciled, PoolUnusedBalanceSwept, Error
  • Key function: log_pool_event(event) in memory.rs
  • Migration log: Separate StableBTreeMap (MemoryId 14) for upgrade migration messages. It has no row cap; its reader get_migration_status returns the first (oldest) 1,000 entries
  • Upgrade row: every upgrade writes one row UPGRADE v=<canister_version> (an upgrade operation step) into the pool event log the System Status page shows

API endpoints:

MethodSignatureAccessNotes
get_pool_events(start: nat64, limit: nat64) -> vec PoolEventRecordPublicPaginated, max 3000/page
get_recent_pool_events(limit: opt nat64) -> vec PoolEventRecordPublicMost recent N (default 50, max 3000)
get_pool_events_count() -> nat64PublicNumber of events currently stored
get_logs(campaign_id: opt text) -> vec PoolCreationLogPublicHeap (lost on upgrade), pool creation logs, at most 1,000
get_operation_logs(campaign_id: text) -> vec OperationLogPublicHeap (lost on upgrade), max 100/campaign — do not rely on it for durable history; use get_pool_events instead
clear_logs() -> ResultAuthorizedUpdate call, clears heap logs

DAO Storage (StorageEventRecord + StorageEvent)

Section titled “DAO Storage (StorageEventRecord + StorageEvent)”
  • Storage: StableBTreeMap (MemoryId 18), counter in StableCell (MemoryId 19)
  • Capacity: 10,000 events max (MAX_STORAGE_EVENTS)
  • Rotation: FIFO — once the ring holds more than 10,000 events, the next insert removes the oldest events above the cap plus 100 more
  • Timer: None (event-driven only — logs on guarded state changes, contribution modifications, etc.)
  • Event types — 9 variants: CampaignStateChanged, CampaignParamsUpdated, ContributionModified, CreatorUpdated, SponsorModified, TokenModified, RateLimitSaturated, Error, SystemLifecycle
  • Upgrade row: every upgrade writes one SystemLifecycle event (event_type = "UPGRADE", details = "v=<canister_version>"), which the System Status page renders as UPGRADE v=<canister_version>
  • Caller tracking: the state-change and modification events include an admin: String field capturing the caller principal
  • Key function: log_storage_event(event) in memory.rs

API endpoints:

MethodSignatureAccessNotes
get_storage_events_query(start: nat64, limit: nat64) -> vec StorageEventRecordPublicPaginated, max 3000/page
get_recent_storage_events_query(limit: opt nat64) -> vec StorageEventRecordPublicMost recent N (default 100, max 3000)
get_storage_events_count_query() -> nat64PublicNumber of events currently stored

StoreCapacityTTLPurposeRead by
ICO_LOGS100 entries/campaign, 200 campaign buckets max1 hourCampaign operations (purchase, finalize, refund, relaunch); levels info / success / warning / errorget_campaign_logs, get_ico_creation_logs, get_imported_dao_creation_logs, get_relaunch_logs, get_ico_logs
RECENT_LOGS50 entriesNoneTreasury/conversion operationsget_recent_conversion_logs
TOKEN_CREATION_LOGS100 entries/user, 500 users max (LRU eviction)NoneToken creation progressget_token_creation_progress (the caller’s own rows)
RATE_LIMIT_EVENTS1,000 entriesNoneDoS/cycle-drain monitoringget_rate_limit_events
RECENT_SHIELD_EVENTS100 entriesNoneShield refusals and state changesget_shield_rate_limit_status

Routing: route_to_heap_logs(msg, campaign_id) checks the message to decide which heap store receives the entry. RECENT_LOGS receives messages containing FUNDS_ALLOCATION, TREASURY_TRANSFER, CMC_TRANSFER or CYCLES_CONVERSION, or starting with [SECURITY]. ICO_LOGS receives messages starting with [create_ico_campaign], [finalize_ico], [check_expired_icos], [refund_campaign], [purchase_tokens_icrc2], [ICP_RECEIVED], [ESCROW_, [REFUND_, [REFERRAL_, [admin_upgrade_index, [admin_create_governance, [post_upgrade], [relaunch_ico], [admin_clear_stuck_relaunch], [admin_retry_relaunch_step] or [internal_create_ledger_and_index].

StoreCapacityTTLPurpose
PoolCreationLog (in State)1,000 entriesNonePool creation success/failure records
OperationLog (per campaign)100 per campaignNonePool operation logs (fee collection, buyback, etc.)

Only significant events go to stable memory. This prevents filling the stable rings (10K entries, 50K on the backend) with routine noise.

LevelWhat gets persistedExamples
errorAlwaysTransfer failures, governance creation failures, inter-canister call errors
warnAlwaysLow cycles, expired campaigns updated, security events, lock frozen
successOperation milestonesFinalize completed, upgrade completed, lock unlocked, transfer sent
infoNon-repetitive milestones onlyGuarded actions, lifecycle events (init, post_upgrade), config changes
criticalAlways (backend, explicit level only)A payment debit whose outcome is ambiguous, a broken invariant the flow refuses to pass

The level is a field only in the backend critical log. OHSHII Governance, LGE Governance, the Pool Manager and DAO Storage store typed events with no level field; the status pages derive a level from the event type.

What does NOT go to stable memory (IC system log only):

  • Progress lines of a flow that did work (for example a refund pass that processed refunds, an unlock pass that unlocked)
  • Timer skips caused by a real condition (for example the previous tick still running, or LGE Governance’s low-cycles skip; the backend’s low-cycles skip is also stored in the critical log as a warn)
  • Error lines that the flow also reports to its caller

A timer tick that finds no work writes no line at all, not even to the IC system log, and debug diagnostics are not printed.


CanisterMax entriesRotation strategy
Backend50,000At the cap, removes the oldest rows above the cap plus 100
OHSHII Governance10,000Above the cap, removes the oldest rows above the cap plus 100
LGE Governance10,000Above the cap, removes the oldest rows above the cap plus 100
Pool Manager10,000Above the cap, removes the oldest rows above the cap plus 100
DAO Storage10,000Above the cap, removes the oldest rows above the cap plus 100

Assumes a busy day with active LGE campaigns, frequent proposals, pool operations, and guarded recovery actions.

CanisterPessimistic daily entriesDays until fullReasoning
Backend~300-500100-166 days (50K ring)Multiple active campaigns with purchases + finalization + security events + privileged operations + hourly housekeeping cleanup events
OHSHII Governance~50-15066-200 daysActive proposal creation/voting/execution + canister management + timer summaries. No per-tick logs.
LGE Governance (per instance)~100-30033-100 daysActive lock unlock processing (up to 30 locks per 5-minute cycle, logged only on an actual unlock) + proposal lifecycle + treasury ops. Worst case: 300 locks unlocking over a few days.
Pool Manager~20-80125-500 daysEvent-driven only (no timer). Pool creation, fee collection, buyback — all infrequent.
DAO Storage~10-50200-1000 daysEvent-driven only. Campaign state changes, contribution modifications — mostly during active LGE periods.

Worst case (all canisters under sustained heavy load simultaneously): the backend fills its 50K ring in ~100 days and LGE Governance its 10K ring in ~33 days; from then on FIFO rotation keeps the most recent entries, a rolling window of the last few weeks (10K rings) to months (backend) of significant operations.

Each stable log entry is ~200-500 bytes serialized. At 10,000 entries:

  • ~2-5 MB per canister for the log StableBTreeMap
  • Well within the 500 GiB stable memory limit (typically <0.001% of capacity)

IC memory limits (per the canonical IC resource limits):

ResourceLimitNotes
Stable memory per canister500 GiBPer-canister cap; the actually-usable amount is also bounded by the subnet’s shared memory capacity (see “Subnet capacity” below)
Wasm heap (Wasm32)4 GiBHard limit of the 32-bit address space. OhShii canisters build to wasm32-unknown-unknown, so this 4 GiB heap ceiling is the one that applies to them.
Wasm heap (Wasm64)6 GiBThe opt-in wasm64-unknown-unknown target (not used by OhShii); an ICP platform limit, expected to grow toward stable-memory capacity over time
Per-message stable memory access2 GiB read / 2 GiB writePer replicated message; upgrades: 8 GiB read/write
Subnet capacity2 TiBShared across all canisters on the subnet

The 10K-entry log buffers use ~2-5 MB each — negligible relative to any of these limits.

Sources: Canister Resource Limits, Canister Storage


Public dashboard with tabs for:

  • Backend Stable Memory Logs: Paginated CriticalLogEntry list with level filtering (error/warn/success/info; rows with the critical level are listed under All)
  • Pool Manager Events: Paginated PoolEventRecord list
  • OHSHII Governance Events: Paginated EventRecord list with event type formatting
  • DAO Storage: Summary + paginated StorageEventRecord list
  • Rate Limit Events: Heap-based DoS monitoring events

The Backend, Pool Manager, OHSHII Governance and DAO Storage tabs also show Native IC Logs: the canister’s IC system log, read through the IC management canister fetch_canister_logs (see The IC system log).

Expandable “Governance Canister Status & Logs” section with three sub-tabs:

  • Summary: Canister ID, total events count, loaded events count
  • Stable Events: Paginated event list with level filtering (error/warn/success/info), formatted per GovernanceEvent variant
  • Native IC Logs: the raw IC system log from the management canister, sorted by timestamp descending

  • ARCHITECTURE.md — Canister map and governance scope table.
  • WORKFLOWS.md — Create LGE, token, vote, create/execute proposal, backend vs frontend upgrade, self-snapshot flows.
  • GOVERNANCE.md — ONS vs SONS, proposal categories, snapshot/restore.
  • OHSSHII_LOCKER_INTEGRATION.md — OhShii Locker token list and lock management via ONS proposals, SONS vesting vs Locker.
  • GOOGLE_VERIFICATION.md — Google verification (Standard): the Internet-Identity attribute-bundle mechanism, one Google account per wallet, the address-normalisation rules, privacy model, governance controls and escape hatches.
  • TELEGRAM_VERIFICATION.md — Telegram verification (Strong): the flow, the privacy model, and the veto-override attestation it now carries.
  • WORLD_ID_VERIFICATION.md — World ID voter verification (Strong, legacy): flow, privacy model, configuration states including the accepts_new_proofs retirement flag, threshold-ECDSA RP signatures, rate limits and governance controls.
  • DECIDEID_VERIFICATION.md — DecideID voter verification (Strong, legacy): configuration states including the accepts_new_proofs retirement flag, the VC and OIDC paths, privacy model, rate limits and pause levers.
  • FRONTEND.md — Frontend architecture: React + JSX + JSDoc @ts-check type-checking, the canister-types.js + utils/canister.js bridges, Candid wire conventions, auth providers, and the TypeScript version policy.
  • Deploy/VOTER_VERIFICATION_GUIDE.md — Verifying WASM hashes for DAO proposals.