Skip to content
IC Reactor

ClientManager

Defined in: core/src/client.ts:170

ClientManager is a central class for managing the Internet Computer (IC) agent.

It initializes the agent (connecting to local or mainnet) and integrates with TanStack Query’s QueryClient for state management. Use this as a singleton shared by all reactors in an app.

import { ClientManager } from "@ic-reactor/core";
import { QueryClient } from "@tanstack/query-core";
const queryClient = new QueryClient();
const clientManager = new ClientManager({
queryClient,
agentOptions: { host: "http://127.0.0.1:4943" },
});
await clientManager.initialize();
// Reuse the same ClientManager across multiple canisters. `canisterId` is
// required in Node, where the constructor otherwise throws; a browser page
// may omit it only where the ic_env cookie is trusted (a local replica, or
// `allowEnvConfig: true`).
const backend = new Reactor<BackendService>({
clientManager,
idlFactory: backendIdl,
name: "backend",
canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
})
const ledger = new Reactor<LedgerService>({
clientManager,
idlFactory: ledgerIdl,
name: "ledger",
canisterId: "ryjl3-tyaaa-aaaaa-aaaba-cai",
})

new ClientManager(parameters): ClientManager

Defined in: core/src/client.ts:214

Creates a new instance of ClientManager.

ClientManagerParameters

Configuration options for the agent and network environment.

ClientManager

queryClient: QueryClient

Defined in: core/src/client.ts:202

The TanStack QueryClient used for managing cached canister data and invalidating queries on identity changes.


agentState: AgentState

Defined in: core/src/client.ts:206

Current state of the HttpAgent, including initialization status, network, and error information.

get agent(): HttpAgent

Defined in: core/src/client.ts:489

The underlying HttpAgent managed by this class.

HttpAgent


get agentHost(): URL | undefined

Defined in: core/src/client.ts:496

The host URL of the current IC agent.

URL | undefined


get agentHostName(): string

Defined in: core/src/client.ts:503

The hostname of the current IC agent.

string


get trustsEnvConfig(): boolean

Defined in: core/src/client.ts:523

Whether the configuration carried by the ic_env cookie may be trusted for this agent’s host.

true when BOTH the agent host and the page origin are unambiguously a local replica (loopback, localhost and its subdomains), or when the caller passed allowEnvConfig. The page counts because the page is what decides who can write the cookie. A Codespaces or Gitpod domain shares its parent with other users’ workspaces, so it needs allowEnvConfig. Every consumer of that cookie reads this one decision, so the root key, the Internet Identity provider and a reactor’s canister ID cannot disagree about whether the environment is trustworthy.

The deprecated allowEnvRootKey is not enough on its own: it granted the root key alone, and is honoured for the root key alone.

boolean


get explicitRootKey(): Uint8Array<ArrayBufferLike> | undefined

Defined in: core/src/client.ts:533

The root key the caller passed as agentOptions.rootKey, which the agent keeps: on a local host initializeAgent does not replace it with the key the host serves. undefined when none was passed, also when the agent took one from the ic_env cookie.

Uint8Array<ArrayBufferLike> | undefined


get isLocal(): boolean

Defined in: core/src/client.ts:540

Returns true if the agent is connecting to a local environment.

boolean


get network(): "local" | "remote" | "ic"

Defined in: core/src/client.ts:547

Returns the current network type (‘ic’ or ‘local’).

"local" | "remote" | "ic"


get identity(): Identity | undefined

Defined in: core/src/client.ts:559

The identity currently installed on the agent, if one was set explicitly.

Calls capture this at submit time and pass it back on every request they make, so a sign-in or sign-out part-way through cannot re-sign a request that is already in flight.

Identity | undefined

initialize(): Promise<ClientManager>

Defined in: core/src/client.ts:380

Orchestrates the complete initialization of the ClientManager. This method awaits the agent’s core initialization (e.g., fetching root keys) Authentication session restoration is handled by AuthenticationManager.

Promise<ClientManager>

A promise that resolves to the ClientManager instance when core initialization is complete.


initializeAgent(): Promise<void>

Defined in: core/src/client.ts:399

Specifically initializes the HttpAgent. On local networks, this includes fetching the root key for certificate verification. That covers every host whose network isn’t "ic", Codespaces and Gitpod included. The fetched key replaces the key the agent holds: one from the ic_env cookie, or mainnet’s when shouldFetchRootKey is false.

A key passed as agentOptions.rootKey is kept instead: nothing is fetched, so /api/v2/status is not requested and need not be reachable, and the agent verifies against that key before and after this call alike. On a mainnet host nothing is fetched either way.

Promise<void>

A promise that resolves when the agent is fully initialized.


getUserPrincipal(): Promise<Principal>

Defined in: core/src/client.ts:566

Returns the current user’s Principal identity.

Promise<Principal>


registerCanisterId(canisterId, name?): void

Defined in: core/src/client.ts:574

Registers a canister ID that this agent will interact with. This is used for informational purposes and network detection.

string

string

void


connectedCanisterIds(): string[]

Defined in: core/src/client.ts:597

Returns a list of all canister IDs registered with this agent.

string[]


getSubnetIdFromCanister(canisterId): Promise<Principal>

Defined in: core/src/client.ts:604

Get the subnet ID for a canister.

string

Promise<Principal>


syncTimeWithSubnet(subnetId): Promise<void>

Defined in: core/src/client.ts:611

Sync time with a specific subnet.

Principal

Promise<void>


subscribe(callback): () => void

Defined in: core/src/client.ts:627

Subscribes to identity changes (e.g., after login/logout).

Callbacks run in the order they subscribed, after the agent already holds the new identity. A callback that throws does not stop the others; the first error is rethrown from updateAgent once they have all run. When a callback itself calls updateAgent, the identity that replaced this one is the last each callback hears.

(identity) => void

Function called with the new identity.

An unsubscribe function.

() => void


subscribeAgentState(callback): () => void

Defined in: core/src/client.ts:650

Subscribes to changes in the agent’s initialization state.

Delivery works as for subscribe: every callback runs even when one throws, and a newer state set from inside a callback is the last state each callback hears.

(state) => void

Function called with the updated agent state.

An unsubscribe function.

() => void


updateAgent(identity): void

Defined in: core/src/client.ts:688

Installs identity on the agent, cleans the cached queries of every registered canister when the principal changes, and notifies identity subscribers.

Query keys carry no principal, so on a switch to another principal (signing in, signing out, switching users) the cache is swept: queries in flight are cancelled, inactive entries are removed, and the rest are invalidated, so mounted queries refetch as the new principal.

When identity has the principal already installed (a renewed delegation, a sign-in while signed in, an identity attribute request), the cached answers are already that principal’s and are kept: nothing is cancelled or removed, and no query that succeeded refetches. Only queries whose last fetch failed are invalidated, since the old identity may be why they failed (an expired delegation, or one not valid for that canister), and so is a query whose fetch was in flight and fails afterwards. The first call after construction always sweeps.

The comparison sees only the principal. An identity that changes what a canister is told under the same principal, such as an AttributesIdentity adding signed sender_info to each request, keeps answers computed without it. After installing or removing one, invalidate the affected queries yourself, e.g. with reactor.invalidateQueries().

Identity

The new identity to use.

void


fetchAcrossIdentitySwitch<T>(fetch): Promise<T>

Defined in: core/src/client.ts:893

Runs fetch, a fetch of a registered canister’s query through queryClient, and runs it again when the principal switches while it runs.

updateAgent cancels those queries when the principal changes, so an answer fetched for the previous principal never reaches the cache. A query with a mounted observer refetches afterwards. An imperative fetch has no observer: TanStack rejects its promise with a CancelledError, which is neither a CallError nor a CanisterError, or, when the entry held data before the fetch began, resolves it with that data, which is the previous principal’s. Through this method it resolves with the answer for the identity installed now, as a mounted query would show; the previous identity’s answer is still never cached.

Reactor.fetchQuery, and with it the query factories’ fetch(), runs through here. Wrap any other fetch of a canister query the same way.

Whatever fetch settles with while the principal stays is passed on as is, including a cancellation from queryClient.cancelQueries(). After three runs again, each overtaken by another switch, it rejects with a CallError whose cause is what the last run settled with, if it rejected, so the loop always ends and never returns an answer that may be another principal’s.

T

() => Promise<T>

Starts the fetch. It is called once per run, so it has to start a fetch each time rather than return one promise it kept.

Promise<T>

What fetch resolved with.

const pages = await clientManager.fetchAcrossIdentitySwitch(() =>
clientManager.queryClient.fetchInfiniteQuery(options)
)