Agent-js metadata request path

Follow-up of Read+write canister wasm module metadata - #3 by peterparker

I try to query wasm metadata using agent-js and I do not understand what value of CustomPath or Metadata should be provided to agent-js because it seems that any combination just works out.

e.g given such metadata:

await spawn({
    command: 'ic-wasm',
    args: [
      OUTPUT,
      '-o',
      OUTPUT,
      'metadata',
      'hello:world', // <-------- here hello:world === metadata key
      '-d',
      '"yolo"', // <-------- here value === yolo
      '-v',
      'public'
    ]
  });

I try to request the medata as following:

const agent = new HttpAgent({
    host: import.meta.env.VITE_HOST,
  });

  await agent.fetchRootKey();

  const request = await CanisterStatus.request({
    canisterId: Principal.from(import.meta.env.VITE_CANISTER_ID),
    agent,
    paths: [
      {
        kind: "metadata",
        key: "",
        path: "hello:world",
        decodeStrategy: "utf-8",
      },
    ],
  });

  console.log(request);

Above works out but also

{
        key: "",
        path: "hello:world",
        decodeStrategy: "utf-8",
      },

or even

{
        kind: "metadata",
        key: "whatever-this-does-not-matter",
        path: "hello:world",
        decodeStrategy: "utf-8",
      },

So I’m curious what’s correct, what’s the key and what’s the path because unless I missed something it isn’t that obvious from the ts doc:

**
 * Interface to define a custom path. Nested paths will be represented as individual buffers, and can be created from text using {@link TextEncoder}
 */
export interface CustomPath {
    key: string;
    path: ArrayBuffer[] | string;
    decodeStrategy: 'cbor' | 'hex' | 'leb128' | 'utf-8' | 'raw';
}
/**
 * Interface to request metadata from the icp:public or icp:private sections.
 * Similar to {@link CustomPath}, but accepts a simple string argument.
 * Private metadata will require the ${@link Identity} used by the ${@link HttpAgent} will need to be requested using an identity that controlls the canister.
 */
export interface MetaData {
    kind: 'metadata';
    key: string;
    path: string | ArrayBuffer;
    decodeStrategy: 'cbor' | 'hex' | 'leb128' | 'utf-8' | 'raw';
}

Maybe I’m dumb but, I cannot understand the TypeScript definition I shared above and the relation with the effective implementation (source) in agent-js.

// Check for CustomPath signature
      if ('key' in path && 'path' in path) {
        // For simplified metadata queries
        if (typeof path['path'] === 'string' || path['path'] instanceof ArrayBuffer) {
          const metaPath = path.path;
          const encoded = typeof metaPath === 'string' ? encode(metaPath) : metaPath;

          return [encode('canister'), canisterBuffer, encode('metadata'), encoded];

          // For non-metadata, return the provided custompath
        } else {
          return path['path'];
        }
      }

Looks like the key can indeed be anything and kind: 'metadata' just doesn’t matter but, I’m probably missing something.

Anyone has use this? Can anyone provide an example or explain what at the correct parameters to query a custom metadata on a wasm module?

An example of metadata is the predefined candid, path, which is an alias for:

case 'candid':
    return [encode('canister'), canisterBuffer, encode('metadata'), encode('candid:service')];

It’s not the friendliest API - I’m happy to add any known metadata fields as known paths to query, since custom paths are difficult to intuit.

There are multiple ways to query paths, though. CustomPath is the most broad utility, because it can request paths outside of metadata. metadata is simply a shorthand, skipping part of the definition. Whatever suits you is the “right” way to query, from my POV

What metadata field were you originally trying to query?

That’s the thing, a custom metadata as in the example I shared above.

ic-wasm test.wasm -o test.wasm metadata hello:world -d "yolo" -v public

Also all the example above using agent-js are able to read this custom metadata: Agent-js metadata request path

But because they can all read the data, I’m really confused about what is the actually proper way.

I wasn’t aware that the metadata type was superfluous - maybe I’ll deprecate that in favor of the

{
    key: "",
    path: "hello:world",
    decodeStrategy: "utf-8",
},

pattern just because it’s shorter.

As for the key, the request can send multiple paths, and will return a map of all the paths you requested. The key, is just what you’ll use to look up the value in the returned map

1 Like

I was really confused by the key, make sense now that you explains it. Thanks a lot!

I’ll be modifying this feature today, so this was great timing! I’ll add some more documentation

1 Like