From e0c77a5d1e652550a64d0a5023f37c21dc1d97bf Mon Sep 17 00:00:00 2001 From: Martin Raszyk Date: Fri, 17 Jul 2026 08:18:45 +0000 Subject: [PATCH] docs: add status_visibility canister setting to canister_status docs Reflect the new status_visibility canister setting (dfinity/ic#10667), which governs who can read a canister's status via canister_status: controllers (default), public, or allowed_viewers. Mirrors the existing log_visibility and snapshot_visibility settings. - ic.did: add status_visibility type and fields - interface spec: settings description, canister_status access rules, formal model (state, create/update, delete, migration, access guards, and the ingress-inspection message-acceptance guard), changelog - hand-written management canister reference: settings table and method Co-Authored-By: Claude Opus 4.8 (1M context) --- .../ic-interface-spec/abstract-behavior.md | 56 +++++++++++++++++-- .../references/ic-interface-spec/changelog.md | 7 +++ .../ic-interface-spec/management-canister.md | 21 ++++++- docs/references/management-canister.md | 5 +- public/references/ic.did | 10 +++- 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/docs/references/ic-interface-spec/abstract-behavior.md b/docs/references/ic-interface-spec/abstract-behavior.md index a99ca398..3cf4d5da 100644 --- a/docs/references/ic-interface-spec/abstract-behavior.md +++ b/docs/references/ic-interface-spec/abstract-behavior.md @@ -462,6 +462,10 @@ CanisterSnapshotVisibility = Controllers | Public | AllowedViewers [Principal] +CanisterStatusVisibility + = Controllers + | Public + | AllowedViewers [Principal] CanisterLog = { idx : Nat; timestamp_nanos : Nat; @@ -518,6 +522,7 @@ S = { canister_history: CanisterId ↦ CanisterHistory; canister_log_visibility: CanisterId ↦ CanisterLogVisibility; canister_snapshot_visibility: CanisterId ↦ CanisterSnapshotVisibility; + canister_status_visibility: CanisterId ↦ CanisterStatusVisibility; canister_logs: CanisterId ↦ [CanisterLog]; query_stats: CanisterId ↦ [QueryStats]; system_time : Timestamp @@ -628,6 +633,7 @@ The initial state of the IC is canister_history = (); canister_log_visibility = (); canister_snapshot_visibility = (); + canister_status_visibility = (); canister_logs = (); query_stats = (); system_time = T; @@ -801,7 +807,19 @@ liquid_balance(S, E.content.canister_id) ≥ 0 E.content.arg = candid({canister_id = CanisterId, …}) E.content.sender ∈ S.controllers[CanisterId] ∪ S.subnet_admins[S.canister_subnet[CanisterId]] E.content.method_name ∈ - { "start_canister", "stop_canister", "uninstall_code", "delete_canister", "canister_status", "canister_metrics" } + { "start_canister", "stop_canister", "uninstall_code", "delete_canister", "canister_metrics" } +) ∨ ( + E.content.canister_id = ic_principal + E.content.arg = candid({canister_id = CanisterId, …}) + (E.content.sender ∈ S.subnet_admins[S.canister_subnet[CanisterId]]) + or + (S.canister_status_visibility[CanisterId] = Public) + or + (S.canister_status_visibility[CanisterId] = Controllers and E.content.sender ∈ S.controllers[CanisterId]) + or + (S.canister_status_visibility[CanisterId] = AllowedViewers Principals and (E.content.sender ∈ S.controllers[CanisterId] or E.content.sender ∈ Principals)) + E.content.method_name ∈ + { "canister_status" } ) ∨ ( E.content.canister_id = ic_principal E.content.sender ∈ S.subnet_admins[S.canister_subnet[ECID]] @@ -1745,6 +1763,11 @@ if A.settings.snapshot_visibility is not null: New_canister_snapshot_visibility = A.settings.snapshot_visibility else: New_canister_snapshot_visibility = Controllers + +if A.settings.status_visibility is not null: + New_canister_status_visibility = A.settings.status_visibility +else: + New_canister_status_visibility = Controllers ``` State after @@ -1774,6 +1797,7 @@ S' = S with canister_history[Canister_id] = New_canister_history canister_log_visibility[Canister_id] = New_canister_log_visibility canister_snapshot_visibility[Canister_id] = New_canister_snapshot_visibility + canister_status_visibility[Canister_id] = New_canister_status_visibility canister_logs[Canister_id] = [] messages = Older_messages · Younger_messages · ResponseMessage { @@ -1916,6 +1940,8 @@ S' = S with canister_log_visibility[A.canister_id] = A.settings.log_visibility if A.settings.snapshot_visibility is not null: canister_snapshot_visibility[A.canister_id] = A.settings.snapshot_visibility + if A.settings.status_visibility is not null: + canister_status_visibility[A.canister_id] = A.settings.status_visibility messages = Older_messages · Younger_messages · ResponseMessage { origin = M.origin @@ -1927,7 +1953,8 @@ S' = S with #### IC Management Canister: Canister status -The controllers of a canister can obtain detailed information about the canister. +Detailed information about a canister can be obtained by the callers permitted by the canister's `canister_status_visibility` setting. +The canister itself and subnet admins can always obtain this information, regardless of the setting. Given a state `S` and `Canister_id`, we define @@ -1991,7 +2018,13 @@ S.messages = Older_messages · CallMessage M · Younger_messages M.callee = ic_principal M.method_name = 'canister_status' M.arg = candid(A) -M.caller ∈ S.controllers[A.canister_id] ∪ {A.canister_id} ∪ S.subnet_admins[S.canister_subnet[A.canister_id]] +(M.caller ∈ {A.canister_id} ∪ S.subnet_admins[S.canister_subnet[A.canister_id]]) + or + (S.canister_status_visibility[A.canister_id] = Public) + or + (S.canister_status_visibility[A.canister_id] = Controllers and M.caller ∈ S.controllers[A.canister_id]) + or + (S.canister_status_visibility[A.canister_id] = AllowedViewers Principals and (M.caller ∈ S.controllers[A.canister_id] or M.caller ∈ Principals)) ``` @@ -2033,7 +2066,13 @@ is_effective_canister_id(E.content, ECID) S.system_time <= Q.ingress_expiry or Q.sender = anonymous_id Q.arg = candid(A) A.canister_id ∈ verify_envelope(E, Q.sender, S.system_time) -Q.sender ∈ S.controllers[A.canister_id] ∪ S.subnet_admins[S.canister_subnet[A.canister_id]] +(Q.sender ∈ S.subnet_admins[S.canister_subnet[A.canister_id]]) + or + (S.canister_status_visibility[A.canister_id] = Public) + or + (S.canister_status_visibility[A.canister_id] = Controllers and Q.sender ∈ S.controllers[A.canister_id]) + or + (S.canister_status_visibility[A.canister_id] = AllowedViewers Principals and (Q.sender ∈ S.controllers[A.canister_id] or Q.sender ∈ Principals)) ``` @@ -2942,6 +2981,7 @@ S with canister_history[A.canister_id] = (deleted) canister_log_visibility[A.canister_id] = (deleted) canister_snapshot_visibility[A.canister_id] = (deleted) + canister_status_visibility[A.canister_id] = (deleted) canister_logs[A.canister_id] = (deleted) query_stats[A.canister_id] = (deleted) chunk_store[A.canister_id] = (deleted) @@ -3182,6 +3222,11 @@ if A.settings.snapshot_visibility is not null: New_canister_snapshot_visibility = A.settings.snapshot_visibility else: New_canister_snapshot_visibility = Controllers + +if A.settings.status_visibility is not null: + New_canister_status_visibility = A.settings.status_visibility +else: + New_canister_status_visibility = Controllers ``` State after @@ -3209,6 +3254,7 @@ S' = S with canister_history[Canister_id] = New_canister_history canister_log_visibility[Canister_id] = New_canister_log_visibility canister_snapshot_visibility[Canister_id] = New_canister_snapshot_visibility + canister_status_visibility[Canister_id] = New_canister_status_visibility canister_logs[Canister_id] = [] query_stats[CanisterId] = [] messages = Older_messages · Younger_messages · @@ -4191,6 +4237,8 @@ S with canister_log_visibility[Canister_id] = (deleted) canister_snapshot_visibility[New_canister_id] = S.canister_snapshot_visibility[Canister_id] canister_snapshot_visibility[Canister_id] = (deleted) + canister_status_visibility[New_canister_id] = S.canister_status_visibility[Canister_id] + canister_status_visibility[Canister_id] = (deleted) canister_logs[New_canister_id] = S.canister_logs[Canister_id] canister_logs[Canister_id] = (deleted) query_stats[New_canister_id] = S.query_stats[Canister_id] diff --git a/docs/references/ic-interface-spec/changelog.md b/docs/references/ic-interface-spec/changelog.md index 86e2fcfb..e0907f81 100644 --- a/docs/references/ic-interface-spec/changelog.md +++ b/docs/references/ic-interface-spec/changelog.md @@ -8,6 +8,13 @@ sidebar: ## Changelog {#changelog} + +### Unreleased +* New canister setting `status_visibility` controlling who can read a canister's status via the + `canister_status` endpoint: `controllers` (default) restricts access to the canister's controllers, + `public` allows anyone, and `allowed_viewers` grants access to a list of up to 10 principals in addition + to the controllers. The canister itself and subnet admins can always read the status. + ### 0.64.0 (2026-07-06) {$0_64_0} * New optional `permissions` field in request delegations restricting the kinds of requests the delegation applies for: the value `"queries"` restricts the delegation to query calls diff --git a/docs/references/ic-interface-spec/management-canister.md b/docs/references/ic-interface-spec/management-canister.md index 386f669c..21a83f36 100644 --- a/docs/references/ic-interface-spec/management-canister.md +++ b/docs/references/ic-interface-spec/management-canister.md @@ -119,6 +119,17 @@ The optional `settings` parameter can be used to set the following settings: Default value: `controllers`. +- `status_visibility` (`status_visibility`) + + Controls who can access the canister's status through the `canister_status` endpoint of the management canister. Can be one of: + - `controllers`: Only the canister's controllers can read its status + - `public`: Anyone can read the canister's status + - `allowed_viewers` (`vec principal`): Only principals in the provided list and the canister's controllers can read its status, the maximum length of the list is 10 + + Regardless of this setting, subnet admins and the canister itself can always read the canister's status. + + Default value: `controllers`. + - `wasm_memory_threshold` (`nat`) Must be a number between 0 and 248, inclusively, and indicates the threshold on the remaining wasm memory size of the canister in bytes: @@ -264,6 +275,8 @@ Indicates various information about the canister. It contains: - The visibility of the canister's snapshots. + - The visibility of the canister's status. + - The WASM heap memory limit of the canister in bytes (the value of `0` means that there is no explicit limit). - The "low wasm memory" threshold, which is used to determine when the [canister_on_low_wasm_memory](./canister-interface.md#on-low-wasm-memory) function is executed. @@ -292,7 +305,13 @@ Indicates various information about the canister. It contains: * `response_payload_bytes_total`: the total number of query and composite query response payload (reply data or reject message) bytes. -Only the controllers of the canister or the canister itself or subnet admins can request its status. +Who can request a canister's status is governed by the `status_visibility` field of `canister_settings` and can be one of the following variants: + +- `controllers`: only the canister's controllers can request the status (default); +- `public`: everyone can request the status; +- `allowed_viewers` (`vec principal`): only principals in the provided list and the canister's controllers can request the status, the maximum length of the list is 10. + +Regardless of this setting, the canister itself and subnet admins can always request the canister's status. #### Memory Metrics {#ic-canister_status-memory_metrics} diff --git a/docs/references/management-canister.md b/docs/references/management-canister.md index 7fb925df..b33270f7 100644 --- a/docs/references/management-canister.md +++ b/docs/references/management-canister.md @@ -28,6 +28,7 @@ Several methods accept or return a `canister_settings` record. The fields are: | `wasm_memory_threshold` | `nat` | `0` | Remaining Wasm memory threshold that triggers the low-memory hook | | `log_visibility` | `log_visibility` | `controllers` | Who can read canister logs: `controllers`, `public`, or `allowed_viewers(vec principal)` | | `snapshot_visibility` | `snapshot_visibility` | `controllers` | Who can list and read canister snapshots: `controllers`, `public`, or `allowed_viewers(vec principal)` | +| `status_visibility` | `status_visibility` | `controllers` | Who can read the canister status: `controllers`, `public`, or `allowed_viewers(vec principal)` | | `environment_variables` | `opt record` | `null` | Key-value pairs accessible during canister execution | For practical guidance on configuring these, see the [canister settings guide](../guides/canister-management/settings.md). @@ -118,7 +119,7 @@ Removes a canister's code and state, making it empty. Outstanding calls are reje Returns detailed information about a canister: status, settings, module hash, cycle balance, memory usage, and query statistics. -- **Caller:** Controllers, the canister itself, or subnet admins (canisters or external users; also available as a query call) +- **Caller:** Governed by the `status_visibility` setting (see below); the canister itself and subnet admins can always call it (canisters or external users; also available as a query call) - **Parameters:** - `canister_id` (`principal`) - **Returns:** A record containing: @@ -134,6 +135,8 @@ Returns detailed information about a canister: status, settings, module hash, cy - `idle_cycles_burned_per_day` (`nat`): daily idle burn rate - `query_stats`: query call statistics (total calls, instructions, request/response bytes) +By default, only controllers can read a canister's status. The `status_visibility` setting relaxes this: set it to `public` to let anyone read the status, or `allowed_viewers` to grant access to a specific list of up to 10 principals (in addition to the controllers). The canister itself and subnet admins can always read the status regardless of this setting. + ### `canister_metrics` Returns cycle consumption metrics for a canister broken down by use case. Metrics are monotonically increasing counters accumulating since canister creation (or since the metrics feature was introduced for existing canisters). diff --git a/public/references/ic.did b/public/references/ic.did index 8d6fab7a..23c9e10b 100644 --- a/public/references/ic.did +++ b/public/references/ic.did @@ -14,7 +14,13 @@ type snapshot_visibility = variant { allowed_viewers : vec principal; }; -type environment_variable = record { +type status_visibility = variant { + controllers; + public; + allowed_viewers : vec principal; +}; + +type environment_variable = record { name: text; value: text; }; @@ -28,6 +34,7 @@ type canister_settings = record { minimum_incoming_canister_call_cycles : opt nat; log_visibility : opt log_visibility; snapshot_visibility : opt snapshot_visibility; + status_visibility : opt status_visibility; wasm_memory_limit : opt nat; wasm_memory_threshold : opt nat; environment_variables : opt vec environment_variable; @@ -42,6 +49,7 @@ type definite_canister_settings = record { minimum_incoming_canister_call_cycles : nat; log_visibility : log_visibility; snapshot_visibility : snapshot_visibility; + status_visibility : status_visibility; wasm_memory_limit : nat; wasm_memory_threshold : nat; environment_variables : vec environment_variable;