用你的容器Your container
网盘、内容平台Cloud drives, content platforms
- 用户Users
- 不用钱包No wallet needed
- 文件记在Owner
- 你的容器Your container
- 谁签名Signer
- 后台,用专用钱包Your backend, with a dedicated wallet
- 你要有You run
- Node.js 后台A Node.js backend
给开发者 · 网盘 · 视频站 · 网页应用For developers · drives · video sites · web apps
中继网关替你存文件、传文件 · 不用 API keyRelay gateways store and deliver your files · no API keys
同时传 3 家 · 存好 2 家就能用 · 第 3 家自动重试Upload to 3 at once · usable once 2 have it · the 3rd retries automatically
协议和客户端库都一样,区别在文件记在谁的容器名下、由谁签名。Same protocol, same library. They differ in whose container owns the files and who signs.
网盘、内容平台Cloud drives, content platforms
DeWEB 视频站、个人网站DeWEB video sites, personal sites
两种可以混用。You can mix both.
模版 A 要用专用钱包:只当这一个容器的持有人,只放少量 BNB。后台被攻破,攻击者能做持有人能做的一切。Template A needs a dedicated wallet that holds only this container and a little BNB — a breached backend can do anything the holder can.
先跑测试:只连本机,不要钱包。Start with the tests — local only, no wallet.
要 Node.js 20.19 以上 · 也可以直接下载Needs Node.js 20.19+ · or download the archive
curl -fsSL https://www.taperelay.com/download/examples | tar -xz
cd taperelay-examples/TapeRelay && npm ci --ignore-scripts起 3 台本机测试服务器,走一遍上传、删除、补副本。Starts 3 local test servers and runs upload, delete and repair.
node examples/template-a/test/test-template-a.mjs用专用钱包持有一个容器;到 3 家不同运营商的发布站,给它的电路各买一个存储套餐,发布站会把这家的服务器写进发现文件。Hold a container with a dedicated wallet. At the release sites of 3 different operators, buy a storage package for its circuit at each; each release site writes its servers into the discovery file.
TapeRelay 目录里的 .env 加 TEMPLATE_A_WALLET_KEY;config.json 填 account、servers。Add TEMPLATE_A_WALLET_KEY to .env in the TapeRelay folder; set account and servers in config.json.
node examples/template-a/server.mjs # 打开 http://127.0.0.1:8280/# open http://127.0.0.1:8280/上线时放在 HTTPS 反向代理后面。In production, put it behind an HTTPS reverse proxy.
起 4 台本机测试服务器,用假链、假钱包走一遍挑 3 家、授权、上传、删除。Starts 4 local test servers with a fake chain and wallet: pick 3, authorize, upload, delete.
node examples/template-b/test/test-template-b.mjspublic/site-config.json 填 app(你的电路的链上名字,例如 4246.0.tape)和 appName。In public/site-config.json, set app (your circuit's on-chain name, such as 4246.0.tape) and appName.
node examples/template-b/serve.mjs # 预览 http://127.0.0.1:8290/# preview at http://127.0.0.1:8290/
node examples/template-b/serve.mjs --out # 导出到 examples/template-b/out/# export to examples/template-b/out/把 out/ 放到任意 HTTPS 静态网站。用户要有状态为 ok 的 TapeOut 容器和少量 BNB。Host out/ on any HTTPS static host. Users need a TapeOut container in the ok state and a little BNB.
Claude Code、Codex、Cursor 都行。手册要求它不碰密钥、不替你发链上交易。Claude Code, Codex or Cursor. The runbook forbids touching keys or sending on-chain transactions for you.
请照这份手册,帮我基于 TapeRelay 做一个能上传、播放、删除文件的产品:Follow this runbook to help me build a product on TapeRelay that uploads, plays and deletes files: https://www.taperelay.com/docs/DEVELOPERS-FOR-AI.md存几份、谁能写、怎么读、怎么改删、花多少钱。Copies, permissions, reading, edits and deletes, costs.
随机挑 3 家不同运营方,同时上传。存好 2 家就能用,第 3 家自动重试,缺了再补。Pick 3 different operators at random and upload to all at once. Usable once 2 have it; the 3rd retries, and gaps get repaired.
pickOperatorsuploadToServersrepairReplicas
服务器只认容器持有人的钱包,和它授权的会话密钥(写明服务器和范围,最长 24 小时)。Servers accept only the container holder's wallet, or a session key it authorizes for named servers and scopes, up to 24 hours.
authorizeSessionremoteAuth
按文件读取的网址不要签名,直接放进 <video>、<img>,能拖动进度。浏览器不核对内容,要核对用 readFile。File URLs need no signature: use them in <video> or <img>, and seeking works. Browsers don't verify them — use readFile when you must.
fileUrlreadFile
内容不能原地修改:传新版本,再改指向(你的数据库,或签名指针)。Content never changes in place: upload a new version, then repoint (your database or a signed pointer).
publishPointerreadPointer
只认持有人的钱包。别的文件还在用的块不能释放;释放后运营方还会保留一段时间。Holder's wallet only. Don't release chunks other files still use; operators keep the bytes for a while afterwards.
fileObjectsclient.release
存储按容器买套餐,每家有免费额度;写发现文件付少量 BNB 手续费;下载流量目前不收费。Storage is a per-container plan, with a free quota at each operator. Writing the discovery file costs a little BNB. Downloads are free for now.
/v1/info → quota
每台中继网关都提供这些接口。一般不用直接调:客户端库会签名、核对。Every relay gateway serves these endpoints. You rarely call them directly — the client library signs and verifies for you.
| 接口Endpoint | 做什么Purpose | 客户端库Library | 权限Access |
|---|---|---|---|
| 服务器Server | |||
GET/v1/info | 服务器信息:配额、价格、支持的功能Server info: quota, pricing, features | client.info | 公开Public |
GET/v1/usage/{account} | 用量和配额Usage and quota | client.usage | objects |
| 对象Objects | |||
POST/v1/objects | 上传一个对象Upload an object | client.upload | objects |
POST/v1/objects/claim | 认领服务器上已有的对象Claim objects the server already has | client.claim | objects |
GET/v1/objects | 列出记在名下的对象List your objects | client.list | objects |
GET/v1/objects/{sha256} | 读一个对象,支持 RangeRead an object (Range supported) | client.getObject | 公开Public |
HEAD/v1/objects/{sha256} | 查是否存在、多大Check existence and size | client.has | 公开Public |
POST/v1/objects/release | 释放(删除),一次最多 1,000 个Release (delete), up to 1,000 at a time | client.release | 仅钱包Wallet only |
| 文件与指针Files and pointers | |||
GET/v1/files/{account}/{manifest} | 读整个文件,可以直接播放Read a whole file; plays directly | fileUrl | 公开Public |
PUT/v1/pointers/{account}/{name} | 写签名指针Write a signed pointer | client.putPointer | pointers |
GET/v1/pointers/{account}/{name} | 读签名指针Read a signed pointer | client.getPointer | 公开Public |
| 通知Notices | |||
POST/v1/outbox | 发通知,由自己的服务器转交Send a notice; your server forwards it | client.submitNotice | send |
GET/v1/outbox/{id} | 查投递状态Delivery status | client.outboxStatus | 公开Public |
GET/v1/inbox/{account} | 拉取收到的通知Pull received notices | client.inbox | inbox |
POST/v1/inbox/{account}/read | 标记已读Mark as read | client.markRead | inbox |
| 授权Authorization | |||
POST/v1/revocations | 撤销会话密钥Revoke a session key | client.revoke | 仅钱包Wallet only |
{account} 是电路的链上名字,例如 4246.0.tape(其他链如 1.3.1.tape);{manifest} 是清单对象的 SHA-256。{account} is the circuit's on-chain name, such as 4246.0.tape (on other chains e.g. 1.3.1.tape); {manifest} is the manifest object's SHA-256./v1/files 和按范围授权,只在 /v1/info 的 features 列出 files、scopes 的服务器上能用。/v1/files and scoped sessions work only where features in /v1/info lists files and scopes.POST /v1/inbox/{account}、给存储抽查挑网站用的 GET /v1/stats/sites,和运营方自己用的 /v1/admin/*,开发者用不到。Server-to-server POST /v1/inbox/{account}, GET /v1/stats/sites used to pick sites for storage audits and the operator-only /v1/admin/* aren't for apps.client/src/index.mjs:浏览器和 Node.js 通用,只依赖 @noble。client/src/index.mjs works in browsers and Node.js and depends only on @noble.
import * as T from '/TapeRelay/client/src/index.mjs';
// account:用户的电路(链上名字);servers:发现文件里的 3 台;app:你的电路// account: the user's circuit (on-chain name); servers: the 3 in its discovery file; app: your circuit
const client = T.createClient({ fetch });
const [address] = await window.ethereum.request({ method: 'eth_requestAccounts' });
const wallet = T.eip1193Wallet(window.ethereum, address); // 钱包要在 BNB Smart Chain 上// the wallet must be on BNB Smart Chain
// 授权一次:钱包签名,写明应用和范围// Authorize once: one wallet signature naming the app and scopes
const session = await T.authorizeSession({
wallet, account, app,
scopes: ['objects', 'pointers'],
servers,
now: Math.floor(Date.now() / 1000),
seconds: 8 * 3600,
});
const auth = T.sessionAuth(session);
// 模版 A 换成:T.remoteAuth({ account, endpoint })// Template A instead: T.remoteAuth({ account, endpoint })
// 同时传 3 台,存好 2 台就返回// Upload to all 3; returns once 2 have it
const plan = await T.planPublicFile(file, {
name: file.name, contentType: file.type, withManifest: true,
});
const up = await T.uploadToServers(client, servers, auth, file, { plan });
// 播放:按文件读取的网址,不要签名// Play: a file URL, no signature needed
video.src = T.fileUrl(up.ok[0], account, plan.manifestObject.sha256);checkOperatorpickOperatorsdiscoveryJsonparseDiscoverywriteSiteFileeip1193WalletkeyWalletauthorizeSessionsessionAuthwalletAuthremoteAuthcreateRemoteSignerplanPublicFileuploadToServersfileUrlreadFilereadObjectcheckReplicasrepairReplicasmakePointersignPointerpublishPointerreadPointerfileObjectsclient.release