Prompt: serve versioned static assets from your own hostname over IPFS
Have an agent serve your build's static assets (CSS, JS, fonts, images)
from https://<hostname> on the ird IPFS gateway, unbranded — every
build a new immutable root, filenames versioned so browsers can cache
forever, old roots retired on a schedule you set — and tell you what it
costs to store and serve.
Needs: ird access (MCP server or the ird CLI, logged in); the hostname must be on a zone hosted on ird DNS; local access to the built assets folder
Costs: hourly per MB pinned plus per MB served through the gateway (credits_prices → recurring.ipfs-pin-hourly → per_mb_hour, per_mb_served); attaching the domain has no price-book line (only gateway traffic on it bills); its A record rides on the zone's recurring.dns-zone-hourly → per_record_hour
Group: hosting
Paste everything below the line into a fresh conversation. Fill in the
FILL IN block first.
Task
Serve FOLDER at https://HOST the way MODE says. First run attaches the
hostname; every later run pins the new build, exposes it, verifies it,
and retires roots beyond KEEP_LAST. Nothing is pinned before I confirm
the cost.
FILL IN
HOST: assets.example.ro # hostname on a zone hosted on ird DNS (no other site on it)
FOLDER: ./dist/assets # built assets with hashed filenames (app.3f9c2a.js)
MODE: pins # pins = https://HOST/ipfs/<cid>/app.3f9c2a.js | ipns = https://HOST/app.3f9c2a.js
VERSION: 2026-09-09-a1b2c3 # build id (date + commit) — goes in the pin name
KEEP_LAST: 2 # roots that stay pinned; pages still deployed may reference older ones
Non-negotiable rules
- Cost before pinning.
du -sh FOLDER; show MB × per_mb_hour × 730
per root (× KEEP_LAST for the steady state) plus per_mb_served × the
monthly traffic I expect. Wait for a yes.
- Never edit in place. A CID is the content: a changed byte is a new
CID. Filenames must carry a hash or version — a build that overwrites
app.js under the same name is refused; tell me to fix the bundler.
- No AAAA on HOST. The gateway is IPv4-only and certificate issuance
prefers IPv6, so a stale AAAA record blocks the certificate
(
ipfs_domain_add description). Check dns_get_zone and delete one
with dns_delete_record(domain, name="HOST.", type="AAAA").
- Only attached pins serve. In
pins mode every other /ipfs path
404s and only attached pins' traffic bills. Detach and unpin a root
only when no deployed page still references it — that is what
KEEP_LAST protects.
Steps
A. Check the build. Confirm hashed filenames, compute the cost,
stop for confirmation.
B. Pin. ipfs_add(path=<absolute FOLDER>, name="assets:HOST:VERSION")
(CLI: ird ipfs add FOLDER --name assets:HOST:VERSION). Blocks until
pinned; keep the root CID and gateway URL. Same CID as the previous
root means nothing changed — say so and stop.
C. Attach HOST (first time only). ipfs_domains — skip if HOST is
listed. Otherwise, per MODE:
pins: ipfs_domain_add(domain=HOST, pins=[<CID>]) (CLI:
ird ipfs domains add HOST --pin <CID>). The A record is created
automatically; assets serve at https://HOST/ipfs/<CID>/<file>. The
CID in the path is the version, so URLs change every build.
ipns: ipns_create(hostname=HOST, serve=true) (CLI:
ird ipfs ipns create HOST --serve) creates the k51… name, attaches
HOST and writes the A + _dnslink records. The root is whatever the
name points at, so assets serve at https://HOST/<file> and the
hashed filenames are what keeps old and new apart. (An existing name
links with ipfs_domain_add(domain=HOST, ipns_name=<label>).)
Both need HOST's zone in dns_list_zones; otherwise stop and point me
to the register-domain or bring-domain-dns prompt.
D. Expose the new root (every later build). pins:
ipfs_domain_pins_update(domain=HOST, add=[<CID>]) (CLI:
ird ipfs domains pins HOST --add <CID>) — an idempotent delta, never
the full set; the pin must be stored on the domain's node; a refusal is
reported, not retried. ipns: ipns_publish(name=HOST, cid=<CID>) (CLI:
ird ipfs ipns publish HOST <CID>); propagates within minutes, and HOST
keeps the old root until its DNSLink cache expires.
E. Verify. curl -sI three real files — a CSS, a JS, a font — at
their final URL: HTTP 200, the right Content-Type, size matching the
local file. Report the Cache-Control and Access-Control-Allow-Origin headers
as observed, assuming neither (fonts and cross-origin fetch() need CORS).
In pins mode, a CID that is not attached must 404 on HOST.
F. Give me the URLs. A list <local file> → <URL> for the build, and
the base prefix to put in the bundler's public path
(https://HOST/ipfs/<CID>/ or https://HOST/).
G. Retire old roots. ipfs_domain_pins(domain=HOST) (paged; CLI:
ird ipfs domains pins HOST) and ipfs_pins(search="assets:HOST:").
Sort by VERSION, keep the newest KEEP_LAST, and for each older root —
after I confirm the list — ipfs_domain_pins_update(domain=HOST,
remove=[<CID>]) then ipfs_unpin(pin=<CID>) (CLI: ird ipfs unpin
<CID>). In ipns mode only the unpin applies. Billing stops at unpin.
H. Cost recap. ipfs_usage (CLI: ird ipfs usage) — bytes pinned,
bytes served this period, hourly and monthly cost.
Deliverable
HOST, MODE, the new root CID, the pin name, the URL prefix and the
file → URL list, the headers seen in step E (content types,
cache-control, CORS), the roots still pinned with their sizes, and the
monthly cost from ipfs_usage.