关于 static-site
面向 Internet Computer 项目的静态站点部署配方,将构建后的文件上传到 certified-assets canister 并通过 HTTP 提供。它对每个响应进行认证,供支持验证的网关检查内容证明。
主要功能
- 01部署单一目录中的构建文件
- 02为 HTTP 响应生成认证证明
- 03配置构建与同步前命令
- 04使用 _redirects 设置路由规则
- 05使用 _headers 配置响应头
适用场景
- 01在 Internet Computer 上托管前端
- 02为单页应用配置深层路由重写
- 03通过自定义域名提供站点
- 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-canisterrecipe(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-cliskill。- 前端的构建工具链(例如 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 插值,因此可以使用 $(…)。 |
常见问题
-
在 static-site 配方中使用
.ic-assets.json5。.ic-assets.json5是旧版 asset canister 的配置文件。certified-assets canister 不会读取它——而且由于插件会跳过所有点文件和点目录(仅.well-known/例外,见问题 12),dir中的.ic-assets.json5甚至不会上传。它会悄无声息地缺失,因此 SPA 回退、标头和安全策略都不会生效。请改用_redirects和_headers配置此 canister(见下文)。 -
SPA 回退规则错误。 对于客户端路由,回退规则应是在
_redirects中设置的重写:/* /index.html 200。状态码200表示这是重写(在请求的 URL 提供 Shell 内容,不会显示重定向),这样深层链接在首次加载和重新加载时才能正常工作。不要使用301/302重定向,也不要使用enable_aliasing——这是旧版 asset canister 的设置,在此处不起作用。 -
SPA 中使用相对资源路径。 请使用绝对路径链接资源(
/assets/app.js),不要使用相对路径(assets/app.js)。在/*重写规则下,相对 URL 会相对于客户端路由解析:在/dashboard/settings页面,浏览器会请求/dashboard/settings/assets/app.js,而/*会对此请求返回 HTML Shell——导致令人困惑的 MIME 类型错误,而不是加载脚本。 -
误以为
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 没有可直接替代的 JSAssetManager。(如果你需要以编程方式上传,几乎可以确定你使用的是旧版 canister——请参阅旧版参考。) -
_headers/_redirects放错位置。 这两个文件必须位于dir的根目录(例如dist/_redirects)。它们会作为配置读取,绝不会作为资源提供。请将它们放在public/(Vite)或static/文件夹中,以便构建过程自动将其复制到dir——如果文件留在项目根目录且没有复制到dir,部署时就会缺失。 -
在
_headers中设置保留标头。 同步插件会在部署时拒绝这些标头,并给出说明性错误:Content-Length、Content-Encoding、ETag、Transfer-Encoding、Accept-Ranges、Content-Range、IC-Certificate、IC-CertificateExpression、Location。要设置资源的媒体类型,请使用不带值的Content-Type:形式(它会写入资源元数据,而非响应标头)。要重定向,请使用_redirects——在_headers中设置Location标头不会触发重定向(状态仍为200)。 -
假定存在默认安全标头。 与旧版 canister 的
security_policy: "standard"不同,认证资源 canister不会添加任何默认标头——没有Cache-Control、CSP 或X-Frame-Options。如果需要这些标头,请在_headers中声明(下方提供基准配置)。它自行管理的标头只有认证和服务相关标头,以及其ic_envCookie。 -
将
404/410规则指向大型文件。 错误页目标必须是较小的单分块文件(小于约 1.9 MB)。大型文件会作为经过认证的206范围响应提供,无法携带 4xx 状态,因此插件会在部署时拒绝此类规则,并指出对应文件名。(将200重写到大型文件则没有问题。) -
期待动态重定向捕获。 没有
:splat或:placeholder——无法转发捕获的路径段(/old/:rest → /new/:rest)。每个可认证的响应都必须预先列举,因此_redirects/_headers仅支持精确路径、以/*结尾的子树通配符和固定目标。 -
将现有项目从旧版资产 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 仍在运行却不提供任何内容。请参阅迁移参考。 -
假定升级 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。 -
.well-known/会自动上传——无需配置。 插件会跳过点文件和点目录,但.well-known/除外,会照常遍历。因此,dir/.well-known/ic-domains会以/.well-known/ic-domains路径提供,无需额外设置。(这与旧版 canister 恰好相反;旧版需要在.ic-assets.json5中明确添加取消忽略规则。) -
访问保护顺序。 配方中的
icp deploy会同时安装 canister 并同步资源,因此先执行deploy再执行enable_protection,会导致内容短暂公开。对于全新的私有应用,请在同步实际资源之前启用保护——先部署一个只包含login.html的dir,再执行enable_protection,然后部署完整站点——这样资源就不会对所有人可见。登录页面必须完全自包含(内联 CSS/JS、data:URI)——它是唯一不受访问保护限制的路径,引用的任何外部子资源本身都会受到保护限制。请参阅访问保护。 -
分享或链接到
raw.icp.netURL。<canister-id>.raw.icp.net提供与<canister-id>.icp.net相同的站点,但网关会转发响应,而不验证证书,因此访客得到的保证并不比普通 Web 主机更多。canister 没有可拒绝raw请求的设置(不同于旧版 canister 的allow_raw_access):唯一的线索是未经身份验证的Host标头,而哪些主机名会通过验证属于网关部署细节,并非协议规定。请链接到https://<canister-id>.icp.net(或使用支持验证的网关上的自定义域名),并仅将raw用作调试工具。启用访问保护后,rawURL 会带来实际风险:重定向和登录页面未经验证,因此不支持验证的网关可能会提供伪造的登录表单并窃取令牌。访问 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_envCookie,作为两个名称和值相同的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_envCookie — 在 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_envCookie(控制器可通过环境变量更改前端的后端 Canister ID,即使构建的字节完全相同)以及访问保护(哈希匹配只能说明 Canister 持有你的构建,不能说明访客能够访问它)。
旧版 Asset Canister 与迁移
- 维护现有的
@dfinity/asset-canister项目(.ic-assets.json5、AssetManager上传、grant_permission角色、allow_raw_access):请参阅references/legacy-asset-canister.md。 - 将现有 asset-canister 项目迁移到 certified-assets / static-site:请参阅
references/migrating-from-asset-canister.md,了解配置映射和重新安装模式的注意事项。
更多参考资料
- 加载
icp-cli,了解配方系统、icp.yaml结构、Canister ID 注入,以及ic_envCookie /safeGetCanisterEnv()模式。 - 加载
custom-domains,了解自定义域名的 DNS 记录、ACME 挑战和 TLS 配置。 - 加载
internet-identity,了解如何在前端从ic_env读取根密钥和 Canister ID。 - 上游完整用户文档:certified-assets 文档。