[RFC] tech_stack - Canister Metadata Standard extension

I was expecting dfx canister metadata [canister_name] tech_stack, and also dfx canister metadata [canister_name] tech_stack:cdk, dfx canister metadata [canister_name] tech_stack:lanuage, etc

There is a limit on the number of metadata sections. So in dfx, we deliberated to group all dfx-specific metadata into a “dfx” metadata which has a JSON content.

Then the clients can get the JSON content and extract interesting value from it.

In CLI, you can:

dfx canister metadata [canister_name] dfx | jq ".tech_stack.cdk"
2 Likes

For anyone reading @lwshang and I had a call, and the situation is not perfect but due to the various constraints on Wasm metadata, the current solution should work fine.

I still think a name other than dfx would be nice, but at least we have a standard that should work well. We can always create our own custom metadata sections and standards if we’d really like to, but this is fine for now.

3 Likes

FYI, icpp-pro 3.15.3 was released, which adds the custom section dfx.tech_stack to the .wasm file according to the standard spec.

I am not using dfx to do this addition, but just add it to the wasm as part of the build process, using LLVM.

3 Likes

The next versions of Azle and Kybra will both have this implemented, waiting on releases, possibly waiting for a stable version of dfx extensions with custom canister type.

2 Likes

@icpp @lastmjs could you share a canister id with tech_stack metadata?

1 Like

@anon74414410 or @lwshang is there an example of hitting the metadata endpoint with agent-js (without dfx)?

I spent a few minutes hacking around in the dark, but am wondering how close I am

async function getState() {
  const identity = localIdentity();
  let agent = HttpAgent.createSync({
    host: "https://icp0.io",
    identity
  });

  const canisterId = <canister_id>;
  const paths = ["canister", <canister_id>, "metadata", "motoko:compiler"]
  try {
    const request = await agent.createReadStateRequest(
      { paths : convertToBuffer(paths) },
      identity
    );
    console.log("read state request", request);

    const response = await agent.readState(
      canisterId,
      { paths : convertToBuffer(paths) },
      identity,
      request
    );

    console.log("response", response)

  } catch (err) {
    console.log("read state request rejected with error", err)
  }
}

and convertToBuffer() is doing this

function convertToBuffer (strings: string[]): ArrayBuffer[][] {
  // Create an instance of TextEncoder
  const encoder = new TextEncoder();

  // Convert each string to an ArrayBuffer
  const arrayBuffers = strings.map(str => encoder.encode(str).buffer);

  // Wrap each ArrayBuffer into its own array, forming a 2D array
  return arrayBuffers.map(buffer => [buffer]);
};

oh yeah, there’s a utility for it: https://agent-js.icp.xyz/agent/modules/CanisterStatus.html

import { CanisterStatus } from '@dfinity/agent';
const { request, encodePath} = CanisterStatus;

const customPaths = [
  {
    key: 'metadata',
    path: [encodePath('metadata')],
    decodeStrategy: 'utf-8'
  },
  {
    key: 'motoko:compiler',
    path: [encodePath('motoko:compiler')],
    decodeStrategy: 'utf-8'
  },
];

const agent = await HttpAgent.create();
const statusMap = await request({
  canisterId,
  paths,
  agent
});
3 Likes

Not that it’s massively less verbose, but I’m always open to adding more “canonical” path aliases to check for if there’s a corresponding ICRC standard for them!

If we added those and knew how to decode them, it could be simplified to:

const customPaths = [ 'metadata', 'motoko:compiler'];

@ZenVoich ,
I don’t believe I ever provided you with an example canister id for C++.
You can use this one, which is a deployment of llama_cpp_canister:

6uwoh-vaaaa-aaaag-amema-cai

% dfx canister --ic metadata 6uwoh-vaaaa-aaaag-amema-cai dfx       
{ "tech_stack": { "language": {"c++": {} }, "cdk": {"icpp-pro": {} } } }

Is tech_stack metadata inherited to child canisters?

For example in Motoko:

let child = await ChildActorClass();

Will the child canister have the same tech_stack metadata as the parent Motoko canister?

I would be extremely surprised. Reason being that dfx modifies the wasm custom data section during the build. The child actor wasm is baked into the parent wasm, so dfx cannot modify it there. So unless the Motoko compiler adds the tech stack the metadata won’t make it to the child canister