mcpField
S
static-site

static-site

github.com/dfinity云与 DevOps3993 次安装今天更新
代码仓库

关于 static-site

面向 Internet Computer 项目的静态站点部署配方,将构建后的文件上传到 certified-assets canister 并通过 HTTP 提供。它对每个响应进行认证,供支持验证的网关检查内容证明。

主要功能

  1. 01部署单一目录中的构建文件
  2. 02为 HTTP 响应生成认证证明
  3. 03配置构建与同步前命令
  4. 04使用 _redirects 设置路由规则
  5. 05使用 _headers 配置响应头

适用场景

  1. 01在 Internet Computer 上托管前端
  2. 02为单页应用配置深层路由重写
  3. 03通过自定义域名提供站点
  4. 04部署时同步构建产物

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dfinity/icskills --skill static-site

安装后在 Claude Code 中输入/static-site确认已加载。

附带文件references/legacy-asset-canister.mdreferences/migrating-from-asset-canister.md

详细介绍

静态站点(已认证资源)

这是什么

@dfinity/static-site recipe 会将静态站点——构建后的前端、文档或任意文件夹中的文件——部署到 Internet Computer 上的 certified-assets canister,由其通过 HTTP 提供服务,并进行响应认证。canister 会认证每一个响应(仅使用认证 v2,绝不使用 no_certification 变通方式,也无法关闭认证),而验证型 HTTP 网关会在将响应交给浏览器前检查证明:访客获得的是 canister 可证明已承诺的内容,而不是传输途中被篡改的内容。证明是否会被检查取决于访客所使用的网关:https://<canister-id>.icp.net 会验证,https://<canister-id>.raw.icp.net 则不会(陷阱 14)。

今后,在 IC 上托管前端的推荐方式就是这种方法。 此 recipe 将一对匹配的组件——canister 和同步插件——打包在一起,并由同一个版本固定。指定构建目录后,icp deploy 会上传、认证并提供服务。

较早的 @dfinity/asset-canister recipe(SDK 资产 canister,通过 .ic-assets.json5 配置)仍支持现有项目,但已不再是推荐方式。它是一个不同的 canister,使用不同的配置格式和 API——请参阅 references/legacy-asset-canister.md。若要迁移现有项目,请参阅 references/migrating-from-asset-canister.md。

前置条件

  • icp-cli — npm install -g @icp-sdk/icp-cli。此 recipe 会固定 canister 与 sync-plugin 的配对版本,两者均为预构建,因此仅部署 dir/build/presync 不需要其他依赖。
  • ic-wasm — npm install -g @icp-sdk/ic-wasm。这是一个独立二进制文件,不包含在 icp-cli 中。仅当你设置 metadata 字段时,此 recipe 才会调用它(生成的构建会通过 command -v ic-wasm 检查是否存在,找不到时会报错 "ic-wasm not found");其他官方 recipe 都无条件需要它,因此预先安装这两个工具是稳妥的默认做法——请参阅 icp-cli skill。
  • 前端的构建工具链(例如 Vite/React 应用需要 Node.js >= 22)。

Canister ID 和 URL

静态站点 canister 按项目创建——没有全局 canister ID。部署后,canister ID 会存储在 .icp/<cache|data>/mappings/<environment>.ids.json 中。托管网络(本地副本)属于 cache——.icp/cache/mappings/local.ids.json;连接的网络(主网 ic)属于 data——.icp/data/mappings/ic.ids.json。

环境浏览器 URL
本地http://<canister-name>.<environment>.localhost:8000 — 中间标签是环境名称,默认值为 local,因此 URL 为 http://frontend.local.localhost:8000(这是 icp deploy 打印的 URL;http://<canister-id>.localhost:8000 也可用——不带环境标签的 <canister-name>.localhost 则不可用)
主网https://<canister-id>.icp.net
自定义域名https://yourdomain.com(需配置 DNS)

icp.yaml 配置

canisters:
  - name: frontend
    recipe:
      type: "@dfinity/[email protected]"
      configuration:
        build:
          - npm ci
          - npm run build
        dir: dist

查看 static-site releases 获取最新版本,并将其固定在 type 字段中。由于此配方固定了一组匹配的 canister + 插件,因此无需单独选择 canister 版本——配方版本就是 canister 版本。

此配方包含四个配置字段:

字段必填说明
dir是要提供服务的已构建文件所在的单个目录。canister 拥有整个 URL 空间,因此这里只能指定一个目录,而不是列表。Vite → dist,Next.js 导出 → out。
build否在 canister 创建之前运行的 Shell 命令,用于生成 dir(例如 npm run build)。此时还没有可用的 canister ID。
presync否在同步时、canister 创建之后运行的 Shell 命令,已部署的 canister ID 会作为环境变量导出。可用它将 canister ID 写入前端构建中(参见下文)。
metadata否通过 ic-wasm 写入 canister wasm 的 name/value/visibility 条目。visibility 为可选项,可设为 public 或 private;省略时默认为 private(ic-wasm 的默认值),只有 public 部分可供任何人通过 icp canister metadata 读取。值会在构建时通过 Shell 插值,因此可以使用 $(…)。

常见问题

  1. 在 static-site 配方中使用 .ic-assets.json5。 .ic-assets.json5 是旧版 asset canister 的配置文件。certified-assets canister 不会读取它——而且由于插件会跳过所有点文件和点目录(仅 .well-known/ 例外,见问题 12),dir 中的 .ic-assets.json5 甚至不会上传。它会悄无声息地缺失,因此 SPA 回退、标头和安全策略都不会生效。请改用 _redirects 和 _headers 配置此 canister(见下文)。

  2. SPA 回退规则错误。 对于客户端路由,回退规则应是在 _redirects 中设置的重写:/* /index.html 200。状态码 200 表示这是重写(在请求的 URL 提供 Shell 内容,不会显示重定向),这样深层链接在首次加载和重新加载时才能正常工作。不要使用 301/302 重定向,也不要使用 enable_aliasing——这是旧版 asset canister 的设置,在此处不起作用。

  3. SPA 中使用相对资源路径。 请使用绝对路径链接资源(/assets/app.js),不要使用相对路径(assets/app.js)。在 /* 重写规则下,相对 URL 会相对于客户端路由解析:在 /dashboard/settings 页面,浏览器会请求 /dashboard/settings/assets/app.js,而 /* 会对此请求返回 HTML Shell——导致令人困惑的 MIME 类型错误,而不是加载脚本。

  4. 误以为 AssetManager / @icp-sdk/canisters 能正常工作。 certified-assets canister 的上传 API 是 upload_chunks + execute_operations,不是 SDK asset canister 的 store/create_batch/commit_batch。@icp-sdk/canisters/assets 中的 AssetManager 面向旧版 canister,无法用于 static-site。上传通过 icp deploy 时配方的同步插件完成;此 canister 没有可直接替代的 JS AssetManager。(如果你需要以编程方式上传,几乎可以确定你使用的是旧版 canister——请参阅旧版参考。)

  5. _headers / _redirects 放错位置。 这两个文件必须位于 dir 的根目录(例如 dist/_redirects)。它们会作为配置读取,绝不会作为资源提供。请将它们放在 public/(Vite)或 static/ 文件夹中,以便构建过程自动将其复制到 dir——如果文件留在项目根目录且没有复制到 dir,部署时就会缺失。

  6. 在 _headers 中设置保留标头。 同步插件会在部署时拒绝这些标头,并给出说明性错误:Content-Length、Content-Encoding、ETag、Transfer-Encoding、Accept-Ranges、Content-Range、IC-Certificate、IC-CertificateExpression、Location。要设置资源的媒体类型,请使用不带值的 Content-Type: 形式(它会写入资源元数据,而非响应标头)。要重定向,请使用 _redirects——在 _headers 中设置 Location 标头不会触发重定向(状态仍为 200)。

  7. 假定存在默认安全标头。 与旧版 canister 的 security_policy: "standard" 不同,认证资源 canister不会添加任何默认标头——没有 Cache-Control、CSP 或 X-Frame-Options。如果需要这些标头,请在 _headers 中声明(下方提供基准配置)。它自行管理的标头只有认证和服务相关标头,以及其 ic_env Cookie。

  8. 将 404/410 规则指向大型文件。 错误页目标必须是较小的单分块文件(小于约 1.9 MB)。大型文件会作为经过认证的 206 范围响应提供,无法携带 4xx 状态,因此插件会在部署时拒绝此类规则,并指出对应文件名。(将 200 重写到大型文件则没有问题。)

  9. 期待动态重定向捕获。 没有 :splat 或 :placeholder——无法转发捕获的路径段(/old/:rest → /new/:rest)。每个可认证的响应都必须预先列举,因此 _redirects/_headers 仅支持精确路径、以 /* 结尾的子树通配符和固定目标。

  10. 将现有项目从旧版资产 canister 切换到 static-site。 将 recipe: 改为 @dfinity/static-site,然后直接运行 icp deploy,会在安装任何内容之前失败:这是两个互不相关的 canister,Candid 接口也互不兼容,因此 icp-cli 的安装前检查会中止,并显示 Candid interface compatibility check failed: '<canister>' … You are making a BREAKING change。请改为运行 icp deploy --mode reinstall。这会用认证资源 canister 替换 wasm,丢弃旧的稳定内存——所有旧版资源、权限和 .ic-assets.json5 派生设置都会消失——并使 canister 变为空状态,之后同步插件会从头上传整个 dir。将 static-site 部署为全新的 canister 则完全不会有这个问题。请勿使用 --yes 跳过检查:这会强行执行原位升级,将 certified-assets 无法读取的数据留在稳定内存中,导致 canister 仍在运行却不提供任何内容。请参阅迁移参考。

  11. 假定升级 recipe 版本会自动重新安装。 在认证资源自身的不同版本之间升级,比上述旧版切换更容易——同一发布系列中的 Candid 接口保持稳定,因此不会阻止部署——但即使是重大版本升级,也仍需自行执行重新安装。canister 与插件的版本必须匹配,因此升级 recipe 后,icp deploy 会执行原位升级,随后同步插件会拒绝操作,并报告 assets canister version mismatch: canister is X, this plugin is Y,同时给出修复方法:对于补丁版本升级,运行 icp canister install --mode upgrade(保留状态);对于重大版本升级(1.0 之前是次要版本,1.0 之后是主要版本),运行 icp canister install --mode reinstall,这会清除状态,使下一次同步重新上传所有资源和重定向规则。(v0.3.x → v0.4.0 属于重大升级:重新安装,然后运行 icp deploy。)版本升级后同步失败是预期行为,并非 Bug。

  12. .well-known/ 会自动上传——无需配置。 插件会跳过点文件和点目录,但 .well-known/ 除外,会照常遍历。因此,dir/.well-known/ic-domains 会以 /.well-known/ic-domains 路径提供,无需额外设置。(这与旧版 canister 恰好相反;旧版需要在 .ic-assets.json5 中明确添加取消忽略规则。)

  13. 访问保护顺序。 配方中的 icp deploy 会同时安装 canister 并同步资源,因此先执行 deploy 再执行 enable_protection,会导致内容短暂公开。对于全新的私有应用,请在同步实际资源之前启用保护——先部署一个只包含 login.html 的 dir,再执行 enable_protection,然后部署完整站点——这样资源就不会对所有人可见。登录页面必须完全自包含(内联 CSS/JS、data: URI)——它是唯一不受访问保护限制的路径,引用的任何外部子资源本身都会受到保护限制。请参阅访问保护。

  14. 分享或链接到 raw.icp.net URL。 <canister-id>.raw.icp.net 提供与 <canister-id>.icp.net 相同的站点,但网关会转发响应,而不验证证书,因此访客得到的保证并不比普通 Web 主机更多。canister 没有可拒绝 raw 请求的设置(不同于旧版 canister 的 allow_raw_access):唯一的线索是未经身份验证的 Host 标头,而哪些主机名会通过验证属于网关部署细节,并非协议规定。请链接到 https://<canister-id>.icp.net(或使用支持验证的网关上的自定义域名),并仅将 raw 用作调试工具。启用访问保护后,raw URL 会带来实际风险:重定向和登录页面未经验证,因此不支持验证的网关可能会提供伪造的登录表单并窃取令牌。访问 Cookie 仅限当前主机,因此在已认证主机上的会话不会延续到 raw。

SPA 路由与重定向:_redirects

将 _redirects 文件添加到 dir 的根目录。语法遵循 Netlify 的 _redirects——每行一条规则,包含三个以空白分隔的字段(from、to、status);空行和 # 注释会被忽略。

# Single-page-app fallback: serve the shell for any unmatched path (a rewrite, status 200)
/*               /index.html      200

# Permanent redirect to a new internal path
/old-blog        /blog            301

# Redirect to an external site
/discord         https://discord.gg/example   302

# Rewrite: serve /content/article.html at a pretty URL, no visible redirect
/article         /content/article.html         200

# Subtree redirect: everything under /docs/v1/ moves to /docs/v2/
/docs/v1/*       /docs/v2/        301

# Custom error pages (target must be a small single-chunk file)
/secret          /403.html        404
/retired-feature /sunset.html     410

状态码:301/302/307/308 用于重定向;200 表示重写;404/410 用于提供错误页面。from 始终为绝对路径,唯一支持的通配符是末尾的 /*。优先级按以下顺序排列,先匹配者优先:实际文件 > 自动简洁 URL规则 > 你的 _redirects(按文件中的顺序)> 404 回退规则。由于实际文件始终优先,/* SPA 规则只会匹配未被其他规则处理的路径。

SPA 注意事项——缺失资源会返回 HTML。 使用 /* /index.html 200 时,拼错的 /assets/app-old.js 也会匹配 /*,并以 text/html 提供页面框架。若要让构建输出目录下的路径真正返回 404,请在兜底规则之前添加范围更窄的规则(规则按文件中的顺序匹配):

/assets/*  /404.html    404
/*         /index.html  200

这需要在 dir 中放置 404.html;声明 /* 后,系统不会添加内置的默认 404 页面。

自定义标头:_headers

在 dir 的根目录中添加 _headers 文件。语法遵循 Netlify 的 _headers:单独一行写路径模式,后续缩进的行写 Name: value;空行或 # 会结束一个区块。模式为绝对路径,可包含一个可选的 * 通配符(也会匹配 /;不支持 ** 或 ?)。

一个实用的基础配置——像资深安全工程师那样,为你的应用收紧 CSP:

/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'

# Fingerprinted build assets never change — cache them hard
/assets/*
  Cache-Control: public, max-age=31536000, immutable

# HTML should revalidate so deploys are picked up
/*.html
  Cache-Control: public, max-age=0, must-revalidate

关键规则:

  • 模式匹配文件(资源键),而不是访问者的 URL。 写 /index.html,不要写 /。对于 SPA,设置在 /index.html(或 /*.html)上的 Cache-Control 会应用于所有被重写到 /* 的客户端路由;针对 /dashboard/* 这类路由编写的区块不会匹配任何文件,因此不起作用。
  • 所有匹配的区块都会生效——匹配多个区块的文件会获得其中所有区块的标头(同名值会以逗号分隔合并;Set-Cookie 除外)。
  • Content-Type 比较特殊——单独的 Content-Type: <type> 格式会覆盖匹配文件已存储的媒体类型(可用于 /llms.txt 这类没有扩展名的文件);此标头只能设置一个值,采用首个匹配项,且不会作为普通标头发出。
  • 允许使用 Set-Cookie,但不要使用 ic_env 这个名称。 Canister 会在 HTML 响应中添加自己的 ic_env Cookie,作为两个名称和值相同的 Set-Cookie 标头:一个为 SameSite=Lax,另一个为 SameSite=None; Partitioned(使其能在跨站 iframe 中保留)。看到两个 ic_env 标头是预期行为,并非重复标头错误。
  • 部署时会拒绝保留标头——参见陷阱 6。

简洁 URL 和 404 页面

你无需链接到 .html 文件——canister 会将每个 HTML 文件映射到简洁、不带扩展名的规范 URL,并将其他形式 307 重定向到该 URL:

你的文件规范 URL也会处理(→ 307)
dist/index.html//index
dist/about.html/about/about/、/about/index
dist/blog/index.html/blog//blog、/blog/index

直接请求底层的 /about.html 当前会返回 200(文件始终优先于路由规则)。要自定义 404 页面,请在 dir 的根目录中放置 404.html;否则会提供经过认证的默认页面。SPA 的 /* 规则会接管整个路径空间,因此会用你的外壳页面替代默认 404 页面。

自定义域名

若要从自己的域名提供服务,请在 dir 中添加 .well-known/ic-domains 文件,每行填写一个域名:

example.com
www.example.com

.well-known/ 会自动上传(陷阱 12),因此 IC 边界节点可以在相应位置获取该文件。域名本身的注册(DNS 记录、ACME 验证、TLS 配置)是 IC 平台上的独立步骤——参见 custom-domains 技能。

访问保护(私有应用)

在开发中或预览中的应用前设置登录门禁。未经身份验证的访客会收到经过认证的 307 → <login_page>(HTML)或 401(其他资源)。通过仅限控制器调用的 canister 方法进行配置:

# 1. Add a self-contained /login.html to your dir and deploy.
icp deploy

# 2. Turn the gate on, naming your login page.
icp canister call frontend enable_protection '("/login.html")'

# 3. Mint a credential (here: a chosen passphrase valid ~1 year). Pass value = null for a random token.
#    issue_token takes a record (IssueTokenArgs), not positional args.
icp canister call frontend issue_token '(record { label = "owner"; ttl_secs = 31536000 : nat32; value = opt "my-passphrase" })'

顺序与公开窗口。 static-site 配方中的 icp deploy 会在一个步骤中安装 canister 并同步你的资源,因此从首次部署到 enable_protection 之间的短暂窗口内,你的内容会公开提供——对于已有应用或预览应用的门禁设置来说没有问题。若要避免全新私有应用出现任何公开暴露,应在同步实际资源之前启用保护:首次运行 icp deploy 时,使用仅包含 login.html 的 dir,然后运行 enable_protection '("/login.html")',再使用完整站点运行 icp deploy。登录页面不受门禁限制,因此未经身份验证的用户永远无法获取任何私有内容(陷阱 13)。(对真正空的 canister 启用保护也可行——canister 会报告 EnabledLoginPageMissing,并在同步 login.html 后自行修复为 Enabled。)

方法效果
issue_token '(record { label = "<label>"; ttl_secs = <secs> : nat32; value = opt "<value>" })'生成一个令牌并返回其值。value = null → 高熵随机令牌。
revoke_token '("<label>")'实时移除所有带有该标签的令牌。
list_tokens '()'实时令牌 { label; expires_at }(仅限控制器)。
check_protection_status '()'Disabled、Enabled 或 EnabledLoginPageMissing。
disable_protection '()'关闭门禁并删除所有令牌。

始终显式传递参数——不接受参数的方法传入 '()'。不带参数调用时,icp canister call 会打开交互式提示,而不是发送空参数。

这是一种访问门禁,而非保密机制:节点运营方可以读取资源字节和令牌存储,没有速率限制,并且依赖诚实副本/诚实网关假设。门禁会在每个主机名上由 canister 端运行,但其重定向和登录页面仅通过验证网关验证,因此切勿将受保护应用的 raw URL 发给他人(陷阱 14)。分享链接应使用高熵随机令牌;对于新的私有应用,应在首次同步之前启用保护(陷阱 13)。详情请参阅认证资源访问保护文档。

授权上传者

上传(同步)由 canister 控制器和一组独立的获授权同步器主体执行。若要允许 CI/部署主体同步,但不授予其完整的 canister 控制权,请使用 authorize 授权它——不要使用 --add-controller,因为这会授予远超上传权限的升级/设置/删除权限。

# Allow a principal to sync assets (upload), without making it a controller
icp canister call frontend authorize '(principal "<principal-id>")'

# List authorized syncers
icp canister call frontend list_authorized '()'

# Revoke
icp canister call frontend deauthorize '(principal "<principal-id>")'

安全警告:icp canister settings update frontend --add-controller <principal-id> 会授予完整的 canister 控制权(升级 wasm、更改设置、删除 canister、耗尽 cycles),而不只是上传权限。部署流水线应优先使用 authorize。

针对 Canister ID 构建(presync 与 build)

canister 尚不存在时就会运行 build,因此它无法获知任何 canister ID。如果客户端应用必须预先写入其调用的 canister ID,请在 presync 中构建——它会在同步时运行,此时 ID 已存在,并会导出这些 ID:

canisters:
  - name: frontend
    recipe:
      type: "@dfinity/[email protected]"
      configuration:
        dir: dist
        presync:
          - npm ci
          # $ICP_CLI_CID_BACKEND is the `backend` canister's principal.
          - VITE_CANISTER_ID_BACKEND=$ICP_CLI_CID_BACKEND npm run build

presync 可用的变量:ICP_CLI_CID(当前 canister)、ICP_CLI_CID_<NAME>(项目中的每个 canister——名称转为大写,非字母数字字符替换为 _,例如 backend → ICP_CLI_CID_BACKEND)、ICP_CLI_NETWORK、ICP_CLI_ENVIRONMENT。

或者,在浏览器中运行时从 canister 在每个 HTML 响应中设置的 ic_env cookie 读取 canister ID(本地和主网均可使用,无需按环境分支)——有关 safeGetCanisterEnv() 模式,请参阅 internet-identity 和 icp-cli 技能。优先使用 cookie,而不是 fetchRootKey()。

自动获得的功能

无需配置,默认启用:

  • 响应认证 — 每个响应都经过认证;验证网关(而非 raw)会检查证明。
  • 简洁 URL — 307 规范化(如上所述)。
  • 压缩 — 可压缩资源会以 gzip 和 Brotli 格式与原文件一同存储,并通过每个请求的 Accept-Encoding 进行协商。可压缩资源包括:所有 text/*;以 +json 或 +xml 结尾的资源(例如 image/svg+xml、application/xhtml+xml);application/javascript、application/json、application/xml、application/wasm;以及除 woff/woff2(已压缩)以外的所有 font/*。只有实际比原文件更小的编码才会保留。
  • ETag / 304 Not Modified — 内容哈希 ETag;未更改的文件不会重新下载。
  • 默认的认证 404 — 可用自定义 /404.html 替换。
  • ic_env Cookie — 在 HTML 响应中发送(作为两个 Set-Cookie 标头,分别为 Lax 和 None; Partitioned),携带 PUBLIC_* 环境变量(Canister ID)以及前端所需的根密钥。

部署与验证

# Start the local network
icp network start -d

# Build + deploy everything (or just the frontend)
icp deploy
icp deploy frontend

# Mainnet (needs cycles)
icp deploy -e ic frontend

重新运行 icp deploy 会重新同步:插件会比较你的目录与 Canister,并且只上传发生变化的内容。

# Canister is running
icp canister status frontend            # Status: Running, non-zero memory

# Get the canister ID (there is no `icp canister id`; use status --id-only)
icp canister status frontend --id-only

# Fetch the index page (certified)
icp canister call frontend http_request '(record {
  url = "/"; method = "GET"; body = vec {}; headers = vec {};
  certificate_version = opt 2;
})'                                       # → status_code = 200

# SPA fallback returns the shell, not 404
icp canister call frontend http_request '(record {
  url = "/dashboard/settings"; method = "GET"; body = vec {}; headers = vec {};
  certificate_version = opt 2;
})'                                       # → 200 (index.html), NOT 404

# Open in a browser (this is the URL `icp deploy` prints)
# Local:   http://frontend.local.localhost:8000
# Mainnet: https://<frontend-canister-id>.icp.net

验证已部署内容(状态哈希)

认证可证明响应与 Canister 提交的内容一致;状态哈希可证明 Canister 提交的内容 与根据公开源代码复现的构建一致。icp deploy 打印的哈希(canister reports state hash <hex>)来自 Canister 本身——这是部署自检,不是第三方验证。验证 Canister 的方法如下:

# 1. Reproduce the build from source at the deployed version → ./dist (incl. _headers/_redirects)

# 2. Ask the canister its release, then build the verifier from THAT tag.
#    Not published as a binary or on crates.io. --locked is required: Cargo.lock pins the
#    compressor builds whose output bytes the hash covers; without it the hash differs.
icp canister call <canister-id> version '()' -n ic --query
# (record { major = 0 : nat32; minor = 4 : nat32; patch = 0 : nat32 })
cargo install --git https://github.com/dfinity/certified-assets --tag v0.4.0 --locked state-hash-cli

# 3. Compute the hash locally (prints 64 hex chars)
state-hash ./dist

# 4. Read the canister's hash (an update call, so consensus-backed); the hash is the last 32 bytes
icp canister call <canister-id> state_hash '()' -n ic -o hex | tail -c 65
  • 使用 -n <network>,通过 principal 指定目标 Canister;-e <environment> 会从本地项目解析 Canister 名称,第三方验证者无法获取该项目。请显式传入 '()'。
  • 32 个零字节不是哈希:Canister 从未完成同步,或同步仍在进行中(同步开始时会清除缓存的哈希)。部署完成后请重新读取。
  • 哈希不匹配意味着内容、标头或重定向与源代码不同——也可能是 Canister 使用了非标准压缩器构建(嵌入这些 crate 的平台可能会这样做;该工具只会复现 icp deploy 的行为)。
  • 哈希不涵盖以下内容: ic_env Cookie(控制器可通过环境变量更改前端的后端 Canister ID,即使构建的字节完全相同)以及访问保护(哈希匹配只能说明 Canister 持有你的构建,不能说明访客能够访问它)。

旧版 Asset Canister 与迁移

更多参考资料

  • 加载 icp-cli,了解配方系统、icp.yaml 结构、Canister ID 注入,以及 ic_env Cookie / safeGetCanisterEnv() 模式。
  • 加载 custom-domains,了解自定义域名的 DNS 记录、ACME 挑战和 TLS 配置。
  • 加载 internet-identity,了解如何在前端从 ic_env 读取根密钥和 Canister ID。
  • 上游完整用户文档:certified-assets 文档。

评论

支持 Markdown