Skip to content

Latest commit

 

History

History
588 lines (373 loc) · 23.6 KB

File metadata and controls

588 lines (373 loc) · 23.6 KB

Asset Canister Interface

Introduction

The asset canister interface provides for storage and retrieval of static assets, such as HTML, CSS, JavaScript, images, and other media files. It can store different content encodings of an asset's contents, such as identity and gzip.

A canister that implements this interface can also return dynamic results from the http_request method.

This document is meant to describe the interface with enough detail to aid in understanding how the asset canister works and in interacting with the asset canister at the code level. It does not describe the interface in sufficient detail to rise to the level of a specification.

This document describes an interface, not an implementation. The IC SDK bundles one such possible implementation, the IC Frontend Canister.

For brevity, this document does not reproduce the candid signatures for every method.

Table of Contents

Section
Retrieving Assets
Storing Assets
Type Reference
Method Reference
Batch Operation Reference
Configuration Reference
Permission Reference

Retrieving Assets

Asset retrieval begins with a call to either get() or http_request().

Asset Lookup

The asset canister looks up assets by key. An asset key is a unique, case-sensitive identifier. Asset keys are usually pathnames, and by convention begin with a forward slash.

Examples:

  • /index.html
  • /img/how-it-works/chain-key-signature.jpg

Aliasing

If no asset with the requested key exists, the asset canister will look for an asset with a different key, where the alternate asset has the enable_aliasing field set to true.

The aliasing rules are as follows:

  • an attempt to retrieve {some key}/ can instead retrieve {some key}/index.html
  • an attempt to retrieve {some key}, where {some key} does not end with .html, can instead retrieve either {some key}.html or {some key}/index.html

Examples:

  • an attempt to retrieve / can instead retrieve /index.html
  • an attempt to retrieve /docs/language-guide/about-this-guide/ can instead retrieve /docs/language-guide/about-this-guide/index.html
  • an attempt to retrieve /docs/language-guide/about-this-guide can instead retrieve /docs/language-guide/about-this-guide/index.html or /docs/language-guide/about-this-guide.html

Content Encoding Selection

When retrieving an asset, the caller specifies a list of acceptable content encodings. The asset canister will select the first suitable[^1] content encoding from this list.

Large Assets

While the size of any given asset content encoding is limited only by the canister's available memory, the amount of data that can be passed or returned in a single method call is limited. For this reason, the interface provides for data upload and retrieval in smaller pieces, called "chunks".

The size of each chunk is limited by the message ingress limit.

Storing Assets

Batch Updates

The usual method of updating data in the asset canister is by calling the following methods:

  1. create_batch() once.
  2. create_chunk() one or more times, which can occur concurrently.
  3. commit_batch() zero or more times with batch_id: 0.
  4. commit_batch() once with the batch ID from step 1, which indicates the batch is complete.

The reason for multiple rather than single calls to [commit_batch][#method-commit_batch] is that certificate computation for an entire batch may exceed per-message computation limits.

Batch Updates By Proposal

If a Service Nervous System (SNS) controls an asset canister, it can update the assets by proposal. In this scenario, there are two principals:

  • The Preparer, which must have the Prepare permission. This principal prepares the proposal by uploading data and proposing changes to be committed.
  • The Committer, which must have the Commit permission. This principal commits the previously-proposed changes.

In this scenario, the Preparer calls the following methods:

  1. create_batch() once.
  2. create_chunk() one or more times, which can occur concurrently.
  3. propose_commit_batch() once.
  4. compute_evidence() until the method returns Some(evidence).

The Preparer then furnishes the Committer with the following information:

  • the batch ID
  • the computed evidence

The Committer then calls the following method upon approval of the proposal:

  1. commit_proposed_batch()

If the proposal is not approved, the Preparer must call delete_batch(). Until this is done, all calls to create_batch() will fail.

Individual Updates

It is also possible to upload a single content encoding of a single asset by calling the store() method. The size of the content encoding must not exceed the message ingress limit.

Type Reference

Asset

Key

The key is a case-sensitive string that identifies the asset. By convention, all asset keys begin with a forward slash. For example, /index.html.

Content Type

The content_type field is a string that identifies the type of the asset, such as text/plain or image/jpeg. It is used to set the Content-Type header when serving the asset over HTTP.

Content Encodings

The asset canister can store and serve multiple encodings of the same asset. Each encoding is identified by a content_encoding string, such as identity or gzip. It is used to set the Content-Encoding header when serving the asset over HTTP.

The identity encoding corresponds to the original, unencoded asset contents.

Content Chunks

Each encoding contains one or more "chunks" of data. The size of each chunk is limited by the message ingress limit.

Content chunks can have any size that fits within the message ingress limit, but for a given asset encoding, all chunks except the last must have the same size.

Content Hash

The sha256 field contains the SHA-256 hash of the entire asset encoding. It is used to set the ETag header when serving the asset over HTTP, and to ensure coherence when retrieving a content encoding with more than one query call.

Max Age

The max_age field is the maximum number of seconds that the asset can be cached by a browser or CDN. It is used to set the max-age value of the Cache-Control header when serving the asset over HTTP.

Headers

The headers field is a list of additional headers to set when serving the asset over HTTP.

Enable Aliasing

This field enables retrieval of this asset by a different key, according to the aliasing rules.

NOTE The interface uses more than one name for this field:

In all cases, it indicates that the asset's key might be an alias for another asset, not that it is definitely the case for the asset in question. It will often be true for assets which are not an alias for another asset.

Raw Access

The allow_raw_access field controls whether an asset can be retrieved from raw.ic0.app or raw.icp0.io. If false (which is the default), then the asset canister will redirect any such attempts to the non-raw URL.

Batch

The asset canister holds related changes in a batch before committing those changes to assets in its state. The asset canister must retain all data in a batch for at least the Minimum Batch Retention Duration after creation of the batch itself or creation of any chunk in the batch.

Chunk

A chunk is sequence of bytes comprising all or part of a content encoding for an asset.

The size of any chunk cannot exceed the message ingress limit.

Method Reference

Method: init and post_upgrade

service: (asset_canister_args: variant {
  Init: record {};
  Upgrade: record {
    set_permissions: opt record {
      prepare: vec principal;
      commit: vec principal;
      manage_permissions: vec principal;
    };
  };
})

The methods init and post_upgrade are called automatically by the system after new code is installed in the canister.

Both methods take the same argument type by definition. Therefore, to be able to have different arguments for the two cases, an enum is used to make the distinction. If init is called with the Upgrade variant or if post_upgrade is called with the Init variant the asset canister traps and thereby reverts the code changes.

In Upgrade, the field set_permissions can be used to (re)set the list of principals with the listed permissions. If set_permissions that is not null, then all permissions are set to the newly provided list of principals and the previous lists of principals are discarded.

Method: get

  get: (record {
    key: Key;
    accept_encodings: vec text;
  }) -> (record {
    content: blob; // may be the entirety of the content, or just chunk index 0
    content_type: text;
    content_encoding: text;
    sha256: opt blob; // sha256 of entire asset encoding
    total_length: nat; // all chunks except last have size == content.size()
  }) query;

This method looks up the asset with the given key, using aliasing rules if the key is not found.

Then, it searches the asset's content encodings in the order specified in accept_encodings. If none are found, it returns an error. A typical value for accept_encodings would be ["gzip", "identity"].

Finally, it returns the first chunk of the content encoding.

If total_length exceeds the length of the returned content blob, this means that there is more than one chunk. The caller can then call get_chunk() to retrieve the remaining chunks. Note that since all chunks except the last have the same length as the first chunk, the caller can determine the number of chunks by dividing total_length by the length of the first chunk.

The sha256 field is opt only because it was added after the initial release of the asset canister. It must always be present in the response.

Method: get_chunk

  get_chunk: (record {
    key: Key;
    content_encoding: text;
    index: nat;
    sha256: opt blob;
  }) -> (record { content: blob }) query;

This method looks up the asset with the given key, using aliasing rules if the key is not found.

It returns the chunk with the given index of the specified content encoding of the asset.

The asset canister returns an error if the sha256 field is not present, or if the sha256 field does not match the hash of the content encoding. This protects against changes to the content encoding in between calls to get() and get_chunk().

Method: list

  list : (record {}) -> (vec record {
    key: Key;
    content_type: text;
    encodings: vec record {
      content_encoding: text;
      sha256: opt blob; // sha256 of entire asset encoding
      length: nat;
      modified: Time;
    };
  }) query;

This method returns a list of all assets.

The sha256 field is opt only because it was added after the initial release of the asset canister. It must always be present in the response.

Method: http_request

This method returns an HTTP response for the given HTTP request.

Method: http_request_streaming_callback

If the response to an http_request call includes a streaming_strategy, then this will be the value of the callback.

Method: create_batch

This method creates a new batch and returns its ID.

Preconditions:

  • No batch exists for which propose_commit_batch() has been called.
  • Creation of a new batch would not exceed batch creation limits.

Required Permission: Prepare

Method: create_chunk

  create_chunk: (
    record {
      batch_id: BatchId;
      content: blob
    }
  ) -> (record {
    chunk_id: ChunkId
  });

This method stores a content chunk and extends the batch expiry time.

When creating chunks for a given content encoding, the size of each chunk except the last must be the same.

The asset canister must retain all data related to a batch for at least the Minimum Batch Retention Duration after creating a chunk in a batch.

Preconditions:

  • The batch exists.
  • Creation of the chunk would not exceed chunk creation limits.

Required Permission: Prepare

Method: create_chunks

  create_chunks: (
    record {
      batch_id: BatchId;
      content: vec blob;
    }
  ) -> (
    chunk_ids: vec ChunkId;
  );

This method stores a number of chunks and extends the batch expiry time.

When creating chunks for a given content encoding, the size of each chunk except the last must be the same.

The asset canister must retain all data related to a batch for at least the Minimum Batch Retention Duration after creating a chunk in a batch.

Preconditions:

  • The batch exists.
  • Creation of the chunk would not exceed chunk creation limits.

Required Permission: Prepare

Method: commit_batch

  commit_batch: (record {
    batch_id: BatchId;
    operations: vec BatchOperationKind
  }) -> ();

The commit_batch method executes the specified batch operations in the order listed. The method traps if there is an error executing any operation, so either all or none of the operations will be applied.

After executing the operations, this method deletes the batch associated with batch_id. It is valid to pass 0 for batch_id, in which case this method does not delete any batch. This allows multiple calls to commit_batch to execute operations from a large batch, such that no call to commit_batch exceeds per-call computation limits. The final call to commit_batch should include the batch ID, in order to delete the batch.

Operation Description
CreateAsset Creates a new asset.
SetAssetContent Adds or changes content for an asset.
SetAssetProperties Changes properties for an asset.
UnsetAssetContent Removes content for an asset.
DeleteAsset Deletes an asset.
Clear Deletes all assets.

Required Permission: Commit

Method: delete_batch

The delete_batch method deletes a single batch and any related chunks.

Required Permission: Prepare

Method: propose_commit_batch

This method takes the same arguments as commit_batch, but does not execute the operations. Instead, it stores the operations in a "proposed batch" for later execution by the commit_proposed_batch method.

Required permission: Prepare

Method: compute_evidence

The compute_evidence method computes a hash over the proposed commit batch arguments.

Since calculation of this hash may exceed per-message computation limits, this method computes the hash iteratively, saving its work as it goes. Once it completes the computation, it saves the hash as evidence to be checked later.

The method will return None if the hash computation has not yet completed, or Some(evidence) if the hash computation has been completed.

The returned evidence value must be passed to the commit_proposed_batch method.

After the hash computation has completed, the batch will no longer expire. The batch will remain until one of the following occurs:

  • a call to [commit_proposed_batch()]
  • a call to [delete_batch()]
  • the canister is upgraded

Required permission: Prepare

Method: commit_proposed_batch

This method executes the operations previously supplied by propose_commit_batch(), and deletes the batch.

Preconditions:

  • The batch exists.
  • The batch has proposed commit batch arguments.
  • Evidence computation has completed.
  • Evidence passed in the arguments matches the evidence previously computed by compute_evidence().

Required permission: Commit

Method: grant_permission

This method grants a permission to a principal.

Callable by: Principals with ManagePermissions permission, and canister controllers.

Method: revoke_permission

This method revokes a permission from a principal.

Callable by: Principals with ManagePermissions permission, and canister controllers. Also, any principal can revoke any of its own permissions.

Method: list_permitted

This method returns a list of principals that have the given permission.

Method: authorize

NOTE: This method is deprecated. Use grant_permission() instead.

This method grants the Commit permission to a principal.

Callable by: Principals with ManagePermissions permission, and canister controllers.

Method: deauthorize

NOTE: This method is deprecated. Use revoke_permission() instead.

This method revokes the Commit permission from a principal.

Callable by: Principals with ManagePermissions permission, and canister controllers. Also, any principal can revoke anny of its own permissions.

Method: list_authorized

NOTE: This method is deprecated. Use list_permitted() instead.

This method returns a list of principals that have the Commit permission.

Method: configure

This method configures the asset canister. See Configuration Reference for details.

Method: get_configuration

This method returns the configuration of the asset canister.

Method: get_asset_properties

This method returns the properties of the asset with the given key.

Method: certified_tree

This method returns the certified tree.

Convenience Methods

Each of these methods is the equivalent of its respective batch operation.

NOTE: While they are provided for "convenience," some don't actually make sense. For example, set_asset_content() requires chunk ids, but create_chunk() requires a batch.

These methods may be deprecated in the future. It is recommended to instead call commit_batch() with a single operation, specifying batch ID 0.

Method Operation
create_asset() CreateAsset
delete_asset() DeleteAsset
set_asset_content() SetAssetContent
set_asset_properties() SetAssetProperties
unset_asset_content() UnsetAssetContent
clear() Clear

Each of these methods requires permission: Commit

Validation Methods

These methods validate the arguments for the corresponding methods. They are required for the SNS to be able to call the corresponding methods.

  • validate_grant_permission()
  • validate_revoke_permission()
  • validate_take_ownership()
  • validate_commit_proposed_batch()
  • validate_configure()

Batch Operation Reference

Operation: CreateAsset

type CreateAssetArguments = record {
  key: Key;
  content_type: text;
  max_age: opt nat64;
  headers: opt vec HeaderField;
  enable_aliasing: opt bool;
  allow_raw_access: opt bool;
};

This operation creates a new asset. An asset with the given key must not already exist.

Operation: SetAssetContent

type SetAssetContentArguments = record {
  key: Key;
  content_encoding: text;
  chunk_ids: vec ChunkId;
  sha256: opt blob;
};

This operation adds or changes a single content encoding for an asset. It also updates the modification time of the content encoding.

If sha256 is not passed, the asset canister will compute the hash of the content.

Operation: SetAssetProperties

type SetAssetPropertiesArguments = record {
  key: Key;
  max_age: opt opt nat64;
  headers: opt opt vec HeaderField;
  allow_raw_access: opt opt bool;
  is_aliased: opt opt bool;
};

This operation sets some or all properties of an asset.

Operation: UnsetAssetContent

type UnsetAssetContentArguments = record {
  key: Key;
  content_encoding: text;
};

This operation removes a single content encoding for an asset.

Operation: DeleteAsset

type DeleteAssetArguments = record {
  key: Key;
};

This operation deletes a single asset.

Operation: Clear

type ClearArguments = record {};

This operation deletes all assets.

Configuration Reference

These are set by the configure() method. All limits default to unlimited.

Configuration Description
max_batches The maximum number of batches being uploaded at one time.
max_chunks The maximum number of chunks across all batches being uploaded.
max_bytes The maximum number of total size of content bytes across all chunks being uploaded.

API Versions

API Version 1

This version added SetAssetProperties to BatchOperationKind.

Permissions

Permission: Commit

Permits changes to the assets served by the asset canister.

Any principal with this permission can also call any method callable with the Prepare permission.

Permission: Prepare

Permits upload of data to the canister to be committed later by a principal with the Commit permission.

Permission: ManagePermissions

Permits a principal to grant and revoke permissions to other principals.

Constants

Constant: Minimum Batch Retention Duration

The asset canister must retain all data related to a batch for at least 5 minutes after creating that batch or creating a chunk within it.

[^1] This term is intentionally vague.