Skip to content
Merged
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: 2 additions & 0 deletions docs/concepts/canisters.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ 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).

## Memory model

Each canister has two storage regions:
Expand Down
59 changes: 53 additions & 6 deletions docs/references/ic-interface-spec/abstract-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -4574,14 +4574,56 @@ 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 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 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`.

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.
```
management_canister_query(S, Caller, Method_name, Arg) =
let Cycles_used = <implementation-specific>
if Method_name = 'canister_status' and Arg = candid(A) and
S.canister_subnet[A.canister_id].subnet_id = S.canister_subnet[Caller].subnet_id and
((Caller = A.canister_id)
or
(Caller ∈ 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 Caller ∈ S.controllers[A.canister_id])
or
(S.canister_status_visibility[A.canister_id] = AllowedViewers Principals and (Caller ∈ S.controllers[A.canister_id] or Caller ∈ Principals)))
then
Return (Reply (candid(canister_status(S, A.canister_id))), Cycles_used)
if Method_name = 'canister_metrics' and Arg = candid(A) and
S.canister_subnet[A.canister_id].subnet_id = S.canister_subnet[Caller].subnet_id and
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 = '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)
or
(S.canister_log_visibility[A.canister_id] = Controllers and Caller ∈ S.controllers[A.canister_id])
or
(S.canister_log_visibility[A.canister_id] = AllowedViewers Principals and (Caller ∈ S.controllers[A.canister_id] or Caller ∈ Principals)))
then
Return (Reply (candid(S.canister_logs[A.canister_id])), Cycles_used)
if Method_name = 'list_canisters' and
Caller ∈ S.subnet_admins[S.canister_subnet[Caller]]
then
// CanisterIdRanges is the list of all canister IDs on the subnet S.canister_subnet[Caller]
// encoded as consecutive canister ID ranges (excluding deleted canisters)
Return (Reply (candid({canisters: CanisterIdRanges})), Cycles_used)
Return (Reject (<implementation-specific>, <implementation-specific>), Cycles_used)
```

We define an auxiliary method that handles calls from composite query methods by performing a call graph traversal. It can also be (trivially) invoked for query methods that do not make further calls.
```
composite_query_helper(S, Cycles, Depth, Root_canister_id, Caller, Caller_info_data, Caller_info_signer, Canister_id, Method_name, Arg) =
Expand Down Expand Up @@ -4641,12 +4683,17 @@ composite_query_helper(S, Cycles, Depth, Root_canister_id, Caller, Caller_info_d
Return (Reject (CANISTER_ERROR, <implementation-specific>), Cycles, S) // max call graph depth exceeded
let Calls' · Call · Calls'' = Calls
Calls := Calls' · Calls''
if S.canister_subnet[Canister_id].subnet_id ≠ S.canister_subnet[Call.callee].subnet_id
if Call.callee = ic_principal
then
Return (Reject (CANISTER_ERROR, <implementation-specific>), Cycles, S) // calling to another subnet
let (Response', Cycles', S') = composite_query_helper(S, Cycles, Depth + 1, Root_canister_id, Canister_id, "", "", Call.callee, Call.method_name, Call.arg)
Cycles := Cycles'
S := S'
let (Response', Cycles_used') = management_canister_query(S, Canister_id, Call.method_name, Call.arg)
Cycles := Cycles - Cycles_used'
else
if S.canister_subnet[Canister_id].subnet_id ≠ S.canister_subnet[Call.callee].subnet_id
then
Return (Reject (CANISTER_ERROR, <implementation-specific>), Cycles, S) // calling to another subnet
let (Response', Cycles', S') = composite_query_helper(S, Cycles, Depth + 1, Root_canister_id, Canister_id, "", "", Call.callee, Call.method_name, Call.arg)
Cycles := Cycles'
S := S'
if Cycles < MAX_CYCLES_PER_RESPONSE
then
Return (Reject (CANISTER_ERROR, <implementation-specific>), Cycles, S) // composite query out of cycles
Expand Down
5 changes: 5 additions & 0 deletions docs/references/ic-interface-spec/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ sidebar:
(the time at which the canister's code was most recently deployed or a snapshot was loaded onto it),
both expressed in nanoseconds since 1970-01-01. Both can be requested via `read_state` if
`<canister_id>` matches the effective canister id of the request.
* Composite query methods and their callbacks can call the management canister query methods
`canister_status`, `canister_metrics`, `fetch_canister_logs`, and `list_canisters`.
Such a call is always executed against the state of the subnet hosting the calling canister
and it is subject to the same access control as the corresponding query call submitted by a user,
with the calling canister as the caller. Calls to all other management canister methods are rejected.

### 0.65.0 (2026-08-03) {$0_65_0}
* New canister setting `status_visibility` controlling who can read a canister's status via the
Expand Down
12 changes: 8 additions & 4 deletions docs/references/ic-interface-spec/management-canister.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ The optional `sender_canister_version` parameter can contain the caller's canist
### IC method `canister_status` {#ic-canister_status}

This method can be called by canisters as well as by external users via ingress messages.
This method can also be called by external users via non-replicated (query) calls, but it cannot be called from composite query calls.
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.

Indicates various information about the canister. It contains:

Expand Down Expand Up @@ -336,7 +337,8 @@ All sizes are expressed in bytes.
### IC method `canister_metrics` {#ic-canister_metrics}

This method can be called by canisters as well as by external users via ingress messages.
This method can also be called by external users via non-replicated (query) calls, but it cannot be called from composite query calls.
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.

This method returns a set of canister related metrics for the requested canister, like cycles consumed by different use cases. These metrics should be counters (i.e. monotonically increasing values) that report the accumulated respective amount since the canister was created for new canisters or since the metrics introduction for existing canisters.

Expand Down Expand Up @@ -948,7 +950,8 @@ A snapshot may be deleted only by the controllers of the canister that the snaps

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

This method can only be called by external users via non-replicated (query) calls, i.e., it cannot be called by canisters, cannot be called via replicated calls, and cannot be called from composite query calls.
This method can only be called via non-replicated (query) calls: by external users directly and by canisters from composite query methods and their callbacks, i.e., it cannot be called via replicated calls.
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.

Given a canister ID as input, this method returns a vector of logs of that canister including its trap messages.
The canister logs are *not* collected in canister methods running in non-replicated mode (NRQ, TQ, CQ, CRy, CRt, CC, and F modes, as defined in [Overview of imports](./canister-interface.md#system-api-imports)) and the canister logs are *purged* when the canister is reinstalled or uninstalled.
Expand Down Expand Up @@ -977,7 +980,8 @@ Replica-signed queries may improve security because the recipient can verify the

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

This method can only be called by external users with subnet admin privileges via non-replicated (query) calls, i.e., it cannot be called by canisters, cannot be called via replicated calls, and cannot be called from composite query calls.
This method can only be called by subnet admins via non-replicated (query) calls: by external users directly and by canisters from composite query methods and their callbacks, i.e., it cannot be called via replicated calls.
A call from a composite query returns the canisters on the subnet hosting the calling canister.

This method returns the list of all canisters on the subnet as consecutive canister ID ranges. Deleted canisters are not included in the result.

Expand Down
12 changes: 6 additions & 6 deletions docs/references/management-canister.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar:

The management canister provides access to system features on the Internet Computer: creating and managing canisters, chain-key signing, HTTPS outcalls, randomness, and Bitcoin integration. It is not a real canister with its own state or Wasm module. It is a virtual canister implemented as part of the IC protocol itself.

The management canister address is `aaaaa-aa` (the empty blob). It is present on every subnet. When you call `aaaaa-aa`, the IC routes the request to the appropriate subnet transparently.
The management canister address is `aaaaa-aa` (the empty blob). It is present on every subnet. When you call `aaaaa-aa`, the IC routes the request to the appropriate subnet transparently. Calls made from a composite query are the exception: they are always answered by the calling canister's own subnet.

Most methods require the caller to be a **controller** of the target canister. Some methods (such as `raw_rand` and `deposit_cycles`) can only be called by canisters, not by external users. When an external user calls the management canister, the cost is charged to the managed canister.

Expand Down Expand Up @@ -119,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:** 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)
- **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, including from composite queries)
- **Parameters:**
- `canister_id` (`principal`)
- **Returns:** A record containing:
Expand All @@ -141,7 +141,7 @@ By default, only controllers can read a canister's status. The `status_visibilit

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).

- **Caller:** Controllers or subnet admins (canisters or external users; also available as a query call, but query responses come from a single replica and are not suitable for security-sensitive use)
- **Caller:** Controllers or subnet admins (canisters or external users; also available as a query call, including from composite queries, but query responses come from a single replica and are not suitable for security-sensitive use)
- **Parameters:**
- `canister_id` (`principal`)
- **Returns:** A record containing:
Expand Down Expand Up @@ -536,7 +536,7 @@ For Bitcoin integration patterns, see the [Bitcoin guide](../guides/chain-fusion

Returns the most recent log entries for a canister. Logs are produced by `ic0.debug_print` and trap messages. Logs persist across upgrades but are purged on reinstall or uninstall. Total log size is capped at 4 KiB.

- **Caller:** External users only (query call; not callable by canisters)
- **Caller:** External users via query calls, or canisters from composite queries (not callable via replicated calls)
- **Parameters:** `canister_id` (`principal`)
- **Returns:**
- `canister_log_records` (`vec record { idx : nat64; timestamp_nanos : nat64; content : blob }`)
Expand Down Expand Up @@ -575,9 +575,9 @@ Returns metadata about a subnet.

### `list_canisters`

Returns all canisters hosted on the caller's subnet as a list of consecutive canister ID ranges. Deleted canisters are not included. Only callable by subnet admins as a query call; not callable by canisters, via replicated calls, or from composite query calls.
Returns all canisters hosted on the caller's subnet as a list of consecutive canister ID ranges. Deleted canisters are not included. Only callable by subnet admins as a query call, either by an external user directly or by a canister from a composite query; not callable via replicated calls.

- **Caller:** Subnet admins only (query call; not callable by canisters)
- **Caller:** Subnet admins only (query call, including from composite queries)
- **Parameters:** none
- **Returns:**
- `canisters` (`vec record { start : principal; end : principal }`): contiguous ranges of canister IDs where `start` and `end` are both inclusive
Expand Down
Loading