Getting a canister's controllers from rust

I’m trying to cover all the bases of canisters and controllers, making sure that I don’t overlook a simple way of achieving the same results.

When working with rust canisters and spawning new canisters from an existing one, I’d like to better understand how to access a list of controllers from inside the spawned canister. I’ll use [I] for Index and [B] for Bucket.

[I] → ic_cdk::api::call::call_with_payment ( create_canister ) → [B]

In this call, we can set the controllers via “pub controllers: Option<Vec>,”. Adding both [I]'s canister_id and a hardcoded principal-id is verified to work, by issuing a “dfx canister status [B]” command. We get both the canister’s and the hardcoded principal-ids listed as Controllers.

Is there an API that we could use on [B] to get access to this list?

Alternative ways to get the list that I’ve found:

A) we can get access to one of the controllers, in this case the [I] canister by calling " ic_cdk::api::caller()" inside the init() function. This will give us one of the controllers, but not the hardcoded one (it basically gives us the spawner of the bucket canister, which is still good. I’d just want to also have a dfx-based controller in case manual intervention is needed).

B) we can get access to a list of controllers by calling “canister_status” on the management canister. This would return a “definite_canister_settings” that has “controllers : vec principal;”. Sweet, but this would require an async call to obtain.

Is there a way of getting a list of controllers from a rust canister, that doesn’t involve calling the management canister at least once?

1 Like

Found another potential workaround that doesn’t imply any async calls:

When calling “install_code” we can pass some args, and they will be received in init() on the [B] side.

// on [I]:

#[derive(CandidType, Deserialize)]
    struct SendArgs {
        greet: String,
        controllers: Vec<Principal>,

    let send_arg = Encode!(&SendArgs {
        greet: "Hello from Index".to_string(),
        controllers: vec![Principal::from_text(

// on [B]:

    let call_arg = ic_cdk::api::call::arg_data::<(Option<SendArgs>,)>().0;

    ic_cdk::print(format!("{:?}", call_arg));

And we get this in the console after every [B] spawns:

[Canister rrkah-fqaaa-aaaaa-aaaaq-cai] creating bucket...
[Canister rrkah-fqaaa-aaaaa-aaaaq-cai] rkp4c-7iaaa-aaaaa-aaaca-cai
[Canister rkp4c-7iaaa-aaaaa-aaaca-cai] Bucket spawned by rrkah-fqaaa-aaaaa-aaaaq-cai
[Canister rkp4c-7iaaa-aaaaa-aaaca-cai] Some(SendArgs { greet: "Hello from Index", controllers: [Principal(PrincipalInner { len: 29, bytes: [163, 18, 218,

So as long as the [I] canister keeps track of hard-coded principals and sends them in both canister_create and install_code calls, all should be good.

1 Like