Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/concepts/canisters.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Key constraints:
- **Ingress only**: only external clients (browsers, CLI tools) can invoke composite queries. Other canisters cannot call them.
- **No replicated mode**: unlike regular queries, composite queries cannot be executed as update calls for stronger authenticity.

A composite query can also call the [management canister](../references/management-canister.md) query methods `canister_status`, `canister_metrics`, `fetch_canister_logs`, and `list_canisters`. These calls are answered from the state of the calling canister's own subnet, so they can only target canisters on that subnet, and the calling canister must be permitted to read that data (for example, be a controller of the target canister).
A composite query can also call the [management canister](../references/management-canister.md) query methods `canister_status`, `canister_metrics`, `canister_info`, `fetch_canister_logs`, and `list_canisters`. These calls are answered from the state of the calling canister's own subnet, so they can only target canisters on that subnet, and the calling canister must be permitted to read that data (for example, be a controller of the target canister). The exception is `canister_info`, which returns public data and is not subject to any access control.

## Memory model

Expand Down
2 changes: 2 additions & 0 deletions docs/guides/canister-management/lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ icp canister status my-canister -e ic

The status output includes the module hash and controller list. For full change history, use the `canister_info` management canister call.

Canister history is public, so `canister_info` is not subject to any access control. External users can read it directly with a query call to the management canister (update calls to `canister_info` are rejected), and canisters can call it either as an inter-canister call or from a composite query method. See the [interface specification](../../references/ic-interface-spec/management-canister.md#ic-canister_info) for details.

## Trapping and error handling

A **trap** is an unrecoverable error during WebAssembly execution: caused by panics, division by zero, out-of-bounds memory access, or explicit trap calls. When a canister traps:
Expand Down
98 changes: 82 additions & 16 deletions docs/references/ic-interface-spec/abstract-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -2176,9 +2176,32 @@ verify_response(Q, R, Cert) ∧ lookup(["time"], Cert) = Found S.system_time //
```


#### IC Management Canister: Canister information
#### IC Management Canister: Canister information {#ic-management-canister-canister-information}

Every canister can retrieve the canister history, current module hash, and current controllers of every other canister (including itself).
Every canister and every external user can retrieve the canister history, current module hash, and current controllers of every canister (including, for a canister caller, itself).
This information is public: retrieving it is not subject to any access control.

Given a state `S`, a `Canister_id`, and `Num_requested_changes`, we define

```html

canister_info(S, Canister_id, Num_requested_changes) =
let Recent_changes = S.canister_history[Canister_id].recent_changes
let From = if Num_requested_changes = null
then |Recent_changes|
else max(0, |Recent_changes| - Num_requested_changes)
let End = |Recent_changes| - 1
{
total_num_changes = S.canister_history[Canister_id].total_num_changes;
recent_changes = Recent_changes[From..End];
module_hash =
if S.canisters[Canister_id] = EmptyCanister
then null
else opt (SHA-256(S.canisters[Canister_id].raw_module));
controllers = S.controllers[Canister_id];
}

```

Conditions

Expand All @@ -2189,9 +2212,6 @@ S.messages = Older_messages · CallMessage M · Younger_messages
M.callee = ic_principal
M.method_name = 'canister_info'
M.arg = candid(A)
if A.num_requested_changes = null then From = |S.canister_history[A.canister_id].recent_changes|
else From = max(0, |S.canister_history[A.canister_id].recent_changes| - A.num_requested_changes)
End = |S.canister_history[A.canister_id].recent_changes| - 1

```

Expand All @@ -2203,20 +2223,60 @@ S with
messages = Older_messages · Younger_messages ·
ResponseMessage {
origin = M.origin
response = candid({
total_num_changes = S.canister_history[A.canister_id].total_num_changes;
recent_changes = S.canister_history[A.canister_id].recent_changes[From..End];
module_hash =
if S.canisters[A.canister_id] = EmptyCanister
then null
else opt (SHA-256(S.canisters[A.canister_id].raw_module));
controllers = S.controllers[A.canister_id];
})
response = candid(canister_info(S, A.canister_id, A.num_requested_changes))
refunded_cycles = M.transferred_cycles
}

```

The IC method `canister_info` can also be invoked via management canister query calls.
They are calls to `/api/v3/canister/<ECID>/query`
with CBOR content `Q` such that `Q.canister_id = ic_principal`.

Submitted request to `/api/v3/canister/<ECID>/query`

```html

E : Envelope

```

Conditions

```html

E.content = CanisterQuery Q
Q.canister_id = ic_principal
Q.method_name = 'canister_info'
|Q.nonce| <= 32
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)
A.canister_id ∈ dom(S.canisters)

```

Query response `R`:

```html

{status: "replied"; reply: {arg: candid(canister_info(S, A.canister_id, A.num_requested_changes))}, signatures: Sigs}

```

where the query `Q`, the response `R`, and a certificate `Cert` that is obtained by requesting the path `/subnet` in a **separate** read state request to `/api/v3/canister/<ECID>/read_state` satisfy the following:

```html

verify_response(Q, R, Cert) ∧ lookup(["time"], Cert) = Found S.system_time // or "recent enough"

```

Like the other management canister methods that can be invoked via query calls, `canister_info`
can also be called from composite query methods (see [Query call](#query-call)).
Unlike them, it is not subject to any access control.

#### IC Management Canister: Canister metadata

Every canister can retrieve public metadata of every other canister (including itself)
Expand Down Expand Up @@ -4577,15 +4637,15 @@ for calls to `/api/v3/subnet/<ESID>/read_state`.

#### Query call {#query-call}

This section specifies query calls `Q` whose `Q.canister_id` is a non-empty canister `S.canisters[Q.canister_id]`. Query calls to the management canister, i.e., `Q.canister_id = ic_principal`, are specified in Sections [Canister status](#ic-management-canister-canister-status), [Canister metrics](#ic-management-canister-canister-metrics), [Canister logs](#ic-mgmt-canister-fetch-canister-logs), and [List canisters](#ic-mgmt-canister-list-canisters).
This section specifies query calls `Q` whose `Q.canister_id` is a non-empty canister `S.canisters[Q.canister_id]`. Query calls to the management canister, i.e., `Q.canister_id = ic_principal`, are specified in Sections [Canister status](#ic-management-canister-canister-status), [Canister metrics](#ic-management-canister-canister-metrics), [Canister information](#ic-management-canister-canister-information), [Canister logs](#ic-mgmt-canister-fetch-canister-logs), and [List canisters](#ic-mgmt-canister-list-canisters).

Canister query calls to `/api/v3/canister/<ECID>/query` can be executed directly. They can only be executed against non-empty canisters which have a status of `Running` and are also not frozen.

In query and composite query methods evaluated on the target canister of the query call, a certificate is provided to the canister that is valid, contains a current state tree (or "recent enough"; the specification is currently vague about how old the certificate may be), and reveals the canister's [Certified Data](./canister-interface.md#system-api-certified-data).

Composite query methods can call query methods and composite query methods up to a maximum depth `MAX_CALL_DEPTH_COMPOSITE_QUERY` of the call graph. The total amount of cycles consumed by executing a (composite) query method and all (transitive) calls it makes must be at most `MAX_CYCLES_PER_QUERY`. This limit applies in addition to the limit `MAX_CYCLES_PER_MESSAGE` for executing a single (composite) query method and `MAX_CYCLES_PER_RESPONSE` for executing a single callback of a (composite) query method.

Composite query methods and their callbacks can also call the management canister query methods `canister_status`, `canister_metrics`, `fetch_canister_logs`, and `list_canisters`. Unlike calls to the management canister in replicated mode, such a call is not routed based on the method name and the argument: it is always executed against the state of the subnet hosting the calling canister and can thus only target canisters hosted by that subnet. Who is allowed to call these methods is determined in the same way as for the corresponding query call submitted by a user, with the calling canister as the caller. Calls to all other management canister methods are rejected. Calls to the management canister do not contribute to the depth of the call graph, but the cycles consumed while producing their responses count towards `MAX_CYCLES_PER_QUERY`.
Composite query methods and their callbacks can also call the management canister query methods `canister_status`, `canister_metrics`, `canister_info`, `fetch_canister_logs`, and `list_canisters`. Unlike calls to the management canister in replicated mode, such a call is not routed based on the method name and the argument: it is always executed against the state of the subnet hosting the calling canister and can thus only target canisters hosted by that subnet. Who is allowed to call these methods is determined in the same way as for the corresponding query call submitted by a user, with the calling canister as the caller. Calls to all other management canister methods are rejected. Calls to the management canister do not contribute to the depth of the call graph, but the cycles consumed while producing their responses count towards `MAX_CYCLES_PER_QUERY`.

We define an auxiliary function that handles calls from composite query methods to the management canister. It returns the response to the call and the amount of cycles consumed while producing that response. The reject code and reject message of a reject response are implementation-specific.
```
Expand All @@ -4609,6 +4669,12 @@ management_canister_query(S, Caller, Method_name, Arg) =
Caller ∈ S.controllers[A.canister_id] ∪ S.subnet_admins[S.canister_subnet[A.canister_id]]
then
Return (Reply (candid(<implementation-specific>)), Cycles_used)
if Method_name = 'canister_info' and Arg = candid(A) and
A.canister_id ∈ dom(S.canisters) and
S.canister_subnet[A.canister_id].subnet_id = S.canister_subnet[Caller].subnet_id
then
// retrieving canister information is not subject to any access control
Return (Reply (candid(canister_info(S, A.canister_id, A.num_requested_changes))), Cycles_used)
if Method_name = 'fetch_canister_logs' and Arg = candid(A) and
S.canister_subnet[A.canister_id].subnet_id = S.canister_subnet[Caller].subnet_id and
((S.canister_log_visibility[A.canister_id] = Public)
Expand Down
6 changes: 6 additions & 0 deletions docs/references/ic-interface-spec/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ sidebar:

## Changelog {#changelog}

### 0.67.0 (2026-08-17) {$0_67_0}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The date should be updated to the date when this change has been rolled out to all ICP mainnet subnets.

* The management canister method `canister_info` can now also be invoked via non-replicated (query) calls
by external users and from composite query methods (it remains callable by canisters via inter-canister
calls and remains rejected for ingress messages). Retrieving canister information is not subject to any
access control, so any principal, including the anonymous one, can call it.

### 0.66.0 (2026-08-17) {$0_66_0}
* Two new paths in the certified state tree, `/canister/<canister_id>/canister_creation_timestamp`
(the time at which the canister was created) and `/canister/<canister_id>/last_install_timestamp`
Expand Down
1 change: 1 addition & 0 deletions docs/references/ic-interface-spec/https-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ See [The system state tree](./index.md#state-tree) for details on the state tree
A query call is a fast, but less secure way to call canister methods that do not change the canister state.
Only methods that are explicitly marked as "query methods" and "composite query methods" by the canister can be called this way.
In contrast to a query method, a composite query method can make further calls to query and composite query methods of canisters on the same subnet.
A composite query method can also call the query methods `canister_status`, `canister_metrics`, `canister_info`, `fetch_canister_logs`, and `list_canisters` of the Management Canister (`aaaaa-aa`); such a call is always answered by the calling canister's own subnet and no other Management Canister method can be called this way.

The following limits apply to the evaluation of a query call:

Expand Down
15 changes: 12 additions & 3 deletions docs/references/ic-interface-spec/management-canister.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The *IC management canister* is just a facade; it does not actually exist as a c

The IC management canister address is `aaaaa-aa` (i.e. the empty blob).

It is possible to use the management canister via external requests (a.k.a. ingress messages). The cost of processing that request is charged to the canister that is being managed. Most methods only permit the controllers to call them. Calls to `raw_rand` and `deposit_cycles` are never accepted as ingress messages.
It is possible to use the management canister via external requests (a.k.a. ingress messages). The cost of processing that request is charged to the canister that is being managed. Most methods only permit the controllers to call them. Calls to `raw_rand`, `deposit_cycles`, and `canister_info` are never accepted as ingress messages (but `canister_info` can be invoked via query calls, see [IC method `canister_info`](#ic-canister_info)).

### Interface overview {#ic-candid}

Expand Down Expand Up @@ -353,9 +353,11 @@ Replica-signed queries may improve security because the recipient can verify the

### IC method `canister_info` {#ic-canister_info}

This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages.
This method can be called by canisters, but it cannot be called by external users via ingress messages.
This method can also be called via non-replicated (query) calls: by external users directly and by canisters from composite query methods and their callbacks.
A call from a composite query is executed against the state of the subnet hosting the calling canister and can thus only target canisters hosted by that subnet.

Provides the history of the canister, its current module SHA-256 hash, and its current controllers. Every canister can call this method on every other canister (including itself). Users cannot call this method.
Provides the history of the canister, its current module SHA-256 hash, and its current controllers. This method is not subject to any access control: every canister and every external user can retrieve this information about every canister (including, for a canister caller, itself).

The canister history consists of a list of canister changes (canister creation, code uninstallation, code deployment, loading a snapshot, controllers change, canister renaming). Every canister change consists of the system timestamp at which the change was performed, the canister version after performing the change, the change's origin (a user or a canister), and its details. The change origin includes the principal (called *originator* in the following) that initiated the change and, if the originator is a canister, the originator's canister version when the originator initiated the change (if available).
- Canister creation is described by the full set of controllers along with a [hash of the environment variables](./https-interface.md#hash-of-map), if environment variables were specified. The order of controllers stored in the canister history may vary depending on the implementation.
Expand All @@ -382,6 +384,13 @@ The returned response contains the following fields:

- `controllers`: the current set of canister controllers. The order of returned controllers may vary depending on the implementation.

:::warning

The response of a query comes from a single replica, and is therefore not appropriate for security-sensitive applications.
Replica-signed queries may improve security because the recipient can verify the response comes from the correct subnet.

:::

### IC method `canister_metadata` {#ic-canister_metadata}

This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages.
Expand Down
4 changes: 2 additions & 2 deletions docs/references/management-canister.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ Returns cycle consumption metrics for a canister broken down by use case. Metric

### `canister_info`

Returns the history, current module hash, and controllers of any canister. Unlike `canister_status`, any canister can call this on any other canister.
Returns the history, current module hash, and controllers of any canister. Unlike `canister_status`, this information is public: it is not subject to any access control.

- **Caller:** Canisters only
- **Caller:** Anyone (canisters via inter-canister calls; external users via query calls only, not update calls; also callable from composite query methods. Query responses come from a single replica and are not suitable for security-sensitive use)
- **Parameters:**
- `canister_id` (`principal`)
- `num_requested_changes` (`opt nat64`): how many history entries to return (default `0`)
Expand Down
2 changes: 1 addition & 1 deletion public/references/ic.did
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ service ic : {
http_request : (http_request_args) -> (http_request_result);

// Public canister data
canister_info : (canister_info_args) -> (canister_info_result);
canister_info : (canister_info_args) -> (canister_info_result) query;
canister_metadata : (canister_metadata_args) -> (canister_metadata_result);

// Threshold ECDSA signature
Expand Down
Loading