@artblocks/abx-cli 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/assets/renderer-scaffold/README.md +59 -0
  3. package/assets/renderer-scaffold/foundry.toml +15 -0
  4. package/assets/renderer-scaffold/remappings.txt +3 -0
  5. package/assets/renderer-scaffold/script/Deploy.s.sol +23 -0
  6. package/assets/renderer-scaffold/src/MyRenderer.sol +123 -0
  7. package/assets/renderer-scaffold/src/MyTraits.sol +75 -0
  8. package/assets/renderer-scaffold/src/interfaces/IAbxFieldRenderer.sol +32 -0
  9. package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +26 -0
  10. package/assets/renderer-scaffold/test/MyRenderer.t.sol +110 -0
  11. package/dist/config.d.ts +69 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +184 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/deps.d.ts +46 -0
  16. package/dist/deps.d.ts.map +1 -0
  17. package/dist/deps.js +90 -0
  18. package/dist/deps.js.map +1 -0
  19. package/dist/flags.d.ts +25 -0
  20. package/dist/flags.d.ts.map +1 -0
  21. package/dist/flags.js +34 -0
  22. package/dist/flags.js.map +1 -0
  23. package/dist/inspect.d.ts +48 -0
  24. package/dist/inspect.d.ts.map +1 -0
  25. package/dist/inspect.js +184 -0
  26. package/dist/inspect.js.map +1 -0
  27. package/dist/main.d.ts +3 -0
  28. package/dist/main.d.ts.map +1 -0
  29. package/dist/main.js +5102 -0
  30. package/dist/main.js.map +1 -0
  31. package/dist/migrate.d.ts +65 -0
  32. package/dist/migrate.d.ts.map +1 -0
  33. package/dist/migrate.js +180 -0
  34. package/dist/migrate.js.map +1 -0
  35. package/dist/mintpage.d.ts +46 -0
  36. package/dist/mintpage.d.ts.map +1 -0
  37. package/dist/mintpage.js +461 -0
  38. package/dist/mintpage.js.map +1 -0
  39. package/dist/onchain-uri.d.ts +97 -0
  40. package/dist/onchain-uri.d.ts.map +1 -0
  41. package/dist/onchain-uri.js +243 -0
  42. package/dist/onchain-uri.js.map +1 -0
  43. package/dist/ownerops.d.ts +195 -0
  44. package/dist/ownerops.d.ts.map +1 -0
  45. package/dist/ownerops.js +1270 -0
  46. package/dist/ownerops.js.map +1 -0
  47. package/dist/provision.d.ts +86 -0
  48. package/dist/provision.d.ts.map +1 -0
  49. package/dist/provision.js +372 -0
  50. package/dist/provision.js.map +1 -0
  51. package/dist/remote.d.ts +58 -0
  52. package/dist/remote.d.ts.map +1 -0
  53. package/dist/remote.js +54 -0
  54. package/dist/remote.js.map +1 -0
  55. package/dist/schema.d.ts +15 -0
  56. package/dist/schema.d.ts.map +1 -0
  57. package/dist/schema.js +125 -0
  58. package/dist/schema.js.map +1 -0
  59. package/dist/series-traits.d.ts +30 -0
  60. package/dist/series-traits.d.ts.map +1 -0
  61. package/dist/series-traits.js +103 -0
  62. package/dist/series-traits.js.map +1 -0
  63. package/dist/signer.d.ts +80 -0
  64. package/dist/signer.d.ts.map +1 -0
  65. package/dist/signer.js +520 -0
  66. package/dist/signer.js.map +1 -0
  67. package/dist/upload.d.ts +28 -0
  68. package/dist/upload.d.ts.map +1 -0
  69. package/dist/upload.js +41 -0
  70. package/dist/upload.js.map +1 -0
  71. package/package.json +55 -0
  72. package/skill/SKILL.md +304 -0
  73. package/skill/reference/code-projects.md +211 -0
  74. package/skill/reference/hosting.md +138 -0
  75. package/skill/reference/operating.md +116 -0
  76. package/skill/reference/setup.md +36 -0
  77. package/skill/reference/troubleshooting.md +28 -0
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@artblocks/abx-cli",
3
+ "version": "0.1.0-alpha.0",
4
+ "license": "MIT",
5
+ "description": "ABX CLI ('abx') — the agentic UX surface of the Self-Host Toolkit (Layer 3). Deploy, index, serve, and demo a self-hosted ABX project end to end. Wraps the SDK; runs a different implementation and the protocol works identically.",
6
+ "type": "module",
7
+ "main": "./dist/main.js",
8
+ "exports": {
9
+ ".": "./dist/main.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "skill",
14
+ "assets",
15
+ "LICENSE"
16
+ ],
17
+ "engines": {
18
+ "node": ">=22.5.0"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/ArtBlocks/abx.git",
23
+ "directory": "packages/cli"
24
+ },
25
+ "homepage": "https://github.com/ArtBlocks/abx#readme",
26
+ "bugs": "https://github.com/ArtBlocks/abx/issues",
27
+ "keywords": [
28
+ "abx",
29
+ "nft",
30
+ "cli",
31
+ "erc721",
32
+ "ethereum",
33
+ "art-blocks",
34
+ "self-host"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "viem": "^2.21.0",
41
+ "@artblocks/abx-storage": "0.1.0-alpha.0",
42
+ "@artblocks/abx-sdk": "0.1.0-alpha.0",
43
+ "@artblocks/abx-indexer": "0.1.0-alpha.0",
44
+ "@artblocks/abx-token-api": "0.1.0-alpha.0"
45
+ },
46
+ "optionalDependencies": {
47
+ "@artblocks/abx-effects": "0.1.0-alpha.0"
48
+ },
49
+ "scripts": {
50
+ "build": "tsc -b tsconfig.build.json"
51
+ },
52
+ "bin": {
53
+ "abx": "./dist/main.js"
54
+ }
55
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,304 @@
1
+ ---
2
+ name: abx-self-host
3
+ description: Launch and operate a self-hosted ABX NFT end to end with the ABX CLI (`abx`) on testnet — a 1/1 (`abx deploy`), a multi-token Series from a folder of media (`abx deploy-series`), or a generative/code drop (`abx deploy-code`). Covers on-chain vs off-chain metadata, storage custody (local disk, S3/R2, IPFS, Arweave), deploy + mint (now or pre-warmed at a predicted address), rendered thumbnails and on-chain traits for code art, primary sales via the shared fixed-price minter, and owner ops (transfer, refresh, re-point URIs, royalties, lock fields, pause/unpause, supply cap, delegate minting). Use when the user wants to self-host an ABX project, take an image to an NFT on testnet, deploy a collection from a folder of images, launch generative/code art, mint or run a primary sale, refresh a listing, operate a project they launched, choose a storage backend, or stand up hosting they own.
4
+ ---
5
+
6
+ # ABX Self-Host Toolkit (`abx`)
7
+
8
+ L3 agentic surface: image → live self-hosted NFT the creator owns — a **1/1** (`abx deploy`) or a multi-token **Series** from a folder (`abx deploy-series`, see [Series](#series-multi-token-drops)) — and operate it after, with no central service in the loop. Wraps `@artblocks/abx-sdk`.
9
+
10
+ **You drive this for a creator.** Do the mechanical work yourself; surface only genuine decisions, each a simple choice + a recommendation. If *you* hold the signing wallet (an agent releasing its own work), you *are* the human — pick defaults, run the hot lane.
11
+
12
+ ## Read first (every session)
13
+
14
+ - **YOU run the `abx` commands — never tell the creator to run one.** You have a shell; use it. Run `doctor`, `ls`, `--dry-run`, `tokenuri`, `state`, `refresh`, `balance`, etc. yourself and read the output — don't paste a command and wait for them to run it or copy back results. The creator's *only* hands-on step is approving in their **browser wallet** (`--sign`) or giving you a value you asked for (their address, a name). Even "next steps" after a deploy: **run the read-only ones** (`tokenuri` to prove it resolves) and **offer to run** the actions (`refresh`, `unpause`, `mint`) — don't hand over a list of commands to run. Exceptions: a genuinely interactive/again-in-their-env command (an OS login, `gcloud auth`), and the **in-chain Solidity lane's Foundry step** (`forge build/test/deploy` a renderer — `abx` never compiles/deploys Solidity; see [Code projects](#code-projects-generative--code-based-drops)) — then run it if you have the tool, else hand it over.
15
+ - **`abx doctor` first, always** — full preflight (Node, pnpm, RPC, signing key, storage). Fix any ✗ before deploying ([Setup](#setup--environment)). A missing public-base-url is not a "set up IPFS" signal: for tiny art go on-chain, for larger art pick an off-chain backend — see [Quick start](#quick-start).
16
+ - **Never collect secrets in chat.** Keys, `PINATA_JWT`, S3 secrets → the project's `.env`. The Arweave/Turbo key is a CLI-managed file (`.abx-self-host/arweave-key.json`) — never paste it. Name the var/file; never take the value.
17
+ - **Testnet only today** — every launch is on a testnet: **Base Sepolia by default** (`ABX_CHAIN` unset), with **Sepolia** also shipped (`ABX_CHAIN=sepolia`). Say "testnet"; don't imply mainnet. **Testnet IS the preview + e2e environment**: it runs the *real* wiring (renderers, generator, on-chain tokenURI assembly), so a creator should deploy there, inspect the actual result (`abx tokenuri` / the live view / `abx verify`), confirm it looks right, and only *then* go to mainnet — no separate local "preview" is as faithful as the real testnet drop, and a testnet deploy is ~free + ~minutes. **One cross-chain gotcha: on-chain library deps (`--dep p5@…`) resolve to on-chain bytes only where an Art Blocks dependency registry exists — that's Sepolia, NOT Base Sepolia.** A no-dependency script (vanilla JS/GLSL) goes fully on-chain on either; a drop that needs a registry-hosted library on-chain must target `ABX_CHAIN=sepolia` (or run the resolver lane).
18
+ - **Scope today = ERC-721 on testnet.** The shipped token standard is **ERC-721** (a 1/1, or a **Series** for many tokens), on Base Sepolia (default) or Sepolia via `ABX_CHAIN`. There is **no `--chain` flag (pick the chain with `ABX_CHAIN`) and no `--erc1155`/`--standard` flag** — don't invent one; mainnet + ERC-1155 are roadmap, not something you flip here. Map the ask to what ships: **"an edition of N" / "N copies"** → an ERC-721 **Series** (`abx deploy-series`, N tokens; for a priced sale of one piece, a 1-token Series). If a creator needs a true ERC-1155 shared-supply edition or an unsupported chain, say plainly it's not in the toolkit today rather than fabricating a recipe.
19
+ - **Two gates decide everything: (1) demo or real? (2) who signs?** Settle both first.
20
+ - **Confirm the full config before any on-chain write** ([readout](#confirm-before-sending)); wait for go-ahead. Never invent a field silently (name/symbol from filename, an auto description) — show it, flag it `inferred`.
21
+ - **Safe to explore:** `abx <cmd> --help` and `abx deploy --dry-run` never send. You never run a real write just to learn flags.
22
+ - **`deploy` returns; `demo`/`serve` block** (they serve) — background them or warn.
23
+
24
+ ## Which command — what are you launching?
25
+
26
+ Route by the **content** first, then apply the gates below. The three paths differ most in what you have to *keep running* and where the thumbnail comes from:
27
+
28
+ | You have | Command | `tokenURI` resolves | Thumbnail (marketplace still) |
29
+ |---|---|---|---|
30
+ | **one image** (a 1/1) | `abx deploy` | on-chain (tiny art) or off-chain — **no server possible** | the image itself |
31
+ | **a folder of images** | `abx deploy-series` | same — on-chain or off-chain, **no server possible** | each image itself |
32
+ | **a program** (generative / code) | `abx deploy-code` | **a resolver you run** (live seed + PostParam injection) — OR `--onchain-uri` (tokenURI on-chain; the canonical generator computes the live view) | **rendered off-chain** by the effect runner, else a placeholder |
33
+
34
+ **The dividing line is static art vs a running program.** Static art is self-resolving (the file *is* the thumbnail, nothing to keep running); **a code project's thumbnail is *rendered* off-chain, so it ALWAYS needs a public home you provide (`--image-base` bucket, or a resolver) — settle that infra fork with the creator FIRST** (details in [Code projects](#code-projects-generative--code-based-drops)). Nail the project type before the gates.
35
+
36
+ **Planning a priced primary sale? Decide 1/1 vs Series BEFORE deploying — it's irreversible.** The shared fixed-price minter sells a **Series** (mint-on-purchase); a plain `abx deploy` **1/1 has no minter/pause/payee**, so its only post-mint move is `abx transfer` (settle an off-chain sale). To run a native fixed-price sale of even a *single* piece, deploy it as a **1-token Series** (`abx deploy-series --count 1`), not a 1/1. abx has **no secondary-listing feature** — reselling a held token means an external marketplace or a manual `transfer`. Full detail: [operating.md → Selling](reference/operating.md#selling--the-shared-fixed-price-minter).
37
+
38
+ ## Gate 1 — demo or real launch?
39
+
40
+ | | **Demo** (`abx demo`) | **Real launch** (`abx deploy` → operate) |
41
+ |---|---|---|
42
+ | Art | generative-from-address | the creator's `--image` |
43
+ | Storage | `fs` (throwaway) | a permanence decision |
44
+ | Host URL | `localhost:8787` | a public URL baked on-chain (off-chain custody only) |
45
+ | Key | any funded testnet key (the active `ABX_CHAIN`) | the wallet that should **own** it |
46
+ | Decisions | none — just run it | the framework below |
47
+
48
+ Just want to see it work? `abx demo`, skip the rest.
49
+
50
+ ## Gate 2 — who signs? (three lanes)
51
+
52
+ Every write builds an unsigned tx; pick the lane by stakes:
53
+
54
+ | Lane | Flag | Signs | Use when |
55
+ |---|---|---|---|
56
+ | **Hot** | default (or `--send`) | env key, in-process | autonomous agent · testnet · low value |
57
+ | **Wallet** | `--sign` | human's own wallet (MetaMask/Ledger) | real value · key shouldn't touch `.env` |
58
+ | **Cold** | `--unsigned` | multisig / offline signer | a Safe / advanced setup |
59
+
60
+ - **Lane detection:** you hold the key + low stakes → hot. Human owns the valuable wallet → wallet. Multisig → cold. Beyond throwaway testnet, lead with `--sign`.
61
+ - **Missing key is a fork, not a blocker.** If you'd pick hot but `.env` has no key, offer both: add a funded key, or `--sign` in a browser wallet.
62
+ - **On the wallet lane, ALWAYS ask "which wallet will you connect?" and pass `--for <addr>` — don't offer a "just connect whatever" path.** The connecting wallet becomes owner + mint recipient + royalty receiver, so `--for` pins it and makes the sign page + CLI refuse a mismatched wallet (a real session skipped this and let a random connected wallet own the collection). All three deploy commands **warn on `--sign` without `--for`** (*"whichever wallet connects becomes owner + royalty receiver + mint recipient — pass --for to PIN it"*); treat that as a prompt to get the address, not to proceed.
63
+ - **`--for` is a DEPLOY concern, not an owner-op one.** It pins who *becomes* the owner at deploy. Post-launch owner ops (`set-royalty`, `transfer`, `minter configure`, `set-minter`, `pause`, …) already sign as the contract's **current on-chain owner** — the wallet lane targets that automatically — so `--for` isn't needed there (passing it is harmless but ignored). Just connect the owner wallet.
64
+
65
+ **⚠ `--sign` BLOCKS until the human signs — ALWAYS background it with `--sign-url-file`, NEVER foreground.** Foreground hangs your whole turn (you can't read the URL or talk to the human) and looks frozen. The #1 way agents break the wallet lane. The flow:
66
+
67
+ 1. Background-run `--sign --sign-url-file <path>` (e.g. `/tmp/abx-sign-url`).
68
+ 2. Read the URL from that file (fallback: grep output for `ABX_SIGN_URL=`) **every time — never assume the port**. Each op gets a fresh server; if the friendly port (8799) is still busy it auto-falls back to a different one, so the file is the source of truth. Server binds in ~1–2s; poll a couple times.
69
+ 3. Relay: "Open `<url>`, connect your wallet, approve." Don't auto-open (node may be remote). Multi-tx → tell them it's N approvals in one session.
70
+ 4. Human approves; only the signed tx hash returns.
71
+ 5. Let the background command finish (it signs + confirms + re-indexes), then report. Don't kill it.
72
+
73
+ **⚠ Run wallet-lane ops ONE AT A TIME — never start the next `--sign` op until the previous background command has finished (step 5).** Each op is its own process serving its own page; overlapping them means the human can land on the *previous* action's page, which (having already completed) shows "done" instantly — that "done" is the last op, not the new one. If a session is genuinely stuck (human never signs), the next op will open a *fresh, different* URL — so relay the new URL from the file and tell them to open that one, not the old tab.
74
+
75
+ - **Sign page is operation-aware** — shows decoded intent ("Transfer #0 → 0x…"), gates on network + signing wallet + the tx, and the CLI refuses a mismatched signer server-side too.
76
+ - **Multi-tx signs in ONE session** (hot + wallet only). `--onchain-image` stages bytes before the deploy that references them; the human connects once and walks `Transaction 1 of N`. Cold can't (staging is interactive) — `--unsigned --onchain-image` errors; use `--send`/`--sign`.
77
+ - **Keep tx count low and say it up front.** A configured token deploys in one tx; `--onchain-image` adds staging tx(s); several owner edits collapse into one atomic `multicall`.
78
+ - **Off-chain storage uploads are NOT wallet approvals — never count them as signatures.** An IPFS upload (Pinata `PINATA_JWT`) or a managed-key Arweave upload happens *before* signing with **no wallet prompt**; only **on-chain staging + the deploy** are approvals. So an off-chain IPFS/Arweave deploy is **one** wallet approval (the deploy). Say it that way — *"1 approval (the deploy); your N images upload to IPFS first, no signature"* — don't fold uploads into the MetaMask count (the classic "2 txs, approve both" mistake). *Exception:* `--storage-signer eth --sign` makes each upload a `personal_sign` in the same session (no gas) — then, and only then, they're approvals too.
79
+
80
+ ## Quick start
81
+
82
+ **Custody is the master call; resolve it before signing/identity.** The rule, by size:
83
+
84
+ | Art size | Default | Why |
85
+ |---|---|---|
86
+ | **Tiny** (≲ 24 KB/file, ≲ 256 KB total) | fully **on-chain** (`--onchain-image --compress fastlz`) | no host, renders forever, cheaper *at this size* |
87
+ | **Bigger / photographic** (most PNG/JPEG) | image **off-chain**, JSON on-chain (`--onchain-uri --backend arweave`) | on-chain is ~200 gas/byte → far more expensive here; Arweave is pay-once permanent |
88
+
89
+ On-chain's edge past tiny is self-resolution/permanence, **never cost** — don't call it "cheaper" above the thresholds. The CLI warns when an on-chain image exceeds them.
90
+
91
+ **When the user names off-chain custody for tiny art ("deploy it as an IPFS NFT"), lead with the on-chain recommendation in your *first* reply** — don't bury it, and don't collect resolver-URL details for a path you're about to advise against. "For a 2.4 KB SVG I'd go fully on-chain — no server, renders forever, cheaper. Want that, or IPFS?" Then let them choose (you surface the better default; you don't override the request). Defaulting to "IPFS" for tiny art is what lands it at a broken localhost URI.
92
+
93
+ Tiny-art path:
94
+ 1. **Confirm identity** (name, symbol, `--description`, royalty, owner) via the [readout](#confirm-before-sending).
95
+ 2. **Deploy + mint in one go:**
96
+ ```bash
97
+ abx deploy --image art.svg --name "…" --symbol … --description "…" --onchain-image --compress fastlz [--sign --for 0x…]
98
+ ```
99
+ = staging tx(s) + one deploy-that-mints. **Mint at deploy — no `--no-mint`** (nothing to warm).
100
+ 3. **Prove + finish:** `abx tokenuri <addr>` (reads metadata straight from chain) · `abx refresh <addr>` (nudge marketplaces) · lock later once it resolves: `abx lock-field <addr> --field image` then `abx lock-uri <addr>`.
101
+
102
+ For large/dynamic media, off-chain custody, or operating an existing project, use the framework + reference files below.
103
+
104
+ ## Series (multi-token drops)
105
+
106
+ One contract, **N tokens**, static creator metadata — a folder of media → a collection. **Everything from the 1/1 applies per token** (custody, on-chain vs off-chain, signing lanes, identity, locking, warming); only the three points below are new. Reach for it when there's more than one piece; a single image is `abx deploy`.
107
+
108
+ ```bash
109
+ abx deploy-series --dir <media-dir> --name "…" --symbol … [--onchain-uri --backend arweave|ipfs|cloud | --onchain-image --compress fastlz | --onchain-uri | --public-base-url https://…] [--mint-all | --mint-count N | --no-mint]
110
+ ```
111
+ **Quick start — a folder of photos, permanent, no server:**
112
+ ```bash
113
+ abx deploy-series --dir ./photos --name "My Series" --symbol MS --onchain-uri --backend arweave --mint-all --sign
114
+ ```
115
+ Files natural-sort into **tokens `0…N-1`** (`--count N` uses the first N); a token's metadata is its token id. `--dry-run` and the [readout](#confirm-before-sending) work identically (token count + mint plan). **Content placement is per token — the same custody call as a 1/1 (see [Decisions](#decisions-real-launch)).** The one Series-specific win: a **same-extension folder** uploads as ONE directory/manifest → a single collection `url-template` (O(1) on-chain, any size); mixed extensions fall back to per-token `url` fields (still no server). `cloud` needs `--public-base`. **Per-token traits: `--attributes <file.json>`** — a JSON **array** indexed by token id, or an **object** keyed by filename / token id (each value an attributes array or a `{name:value}` map). **Lane-aware, exactly like the 1/1's traits**: off-chain operator metadata by default (resolver-served, editable later via `abx add <addr> --attributes`), inlined **on-chain** when the token resolves on-chain (`--onchain-uri`) or you pass `--traits-onchain` — small/medium collections; a huge series sets on-chain traits post-deploy via `set-field` under a gas budget. *(For **generative** traits computed from a seed, that's a code project — `abx.traits()` / `--attributes-renderer` — not a static Series.)*
116
+
117
+ ## Code projects (generative / code-based drops)
118
+
119
+ A **program is the content** (`abx deploy-code` → a `SeriesCode`): output is a function of live on-chain state (`tokenData`: coordinates + `seed` + PostParams), injected at view time. Everything from a [Series](#series-multi-token-drops) applies (mint order, lanes, identity, supply cap, minter, pause). This section is the **decision tree**; the operating depth — what to keep running, the resume loop, verify steps, render ops, lane internals, the arweave delay, selling — lives in **[reference/code-projects.md](reference/code-projects.md)**.
120
+
121
+ **Infra fork FIRST (before any lane talk): a code project's thumbnail is *rendered* off-chain, so it ALWAYS needs a PUBLIC home you provide — there is NO zero-infrastructure code drop, and "fully on-chain" does NOT mean "nothing to run."** Settle the shape with the creator up front:
122
+ - **Off-chain resolver** (`--public-base-url` + an effects runner, ~a few $/mo) — **the default for a drop you'll sell.** Auto-renders every mint + param change, serves traits with no Solidity, and stays **maneuverable** (metadata/serving evolve with no on-chain surgery) while marketplaces fetch a **small** `tokenURI`.
123
+ - **Fully on-chain** (`--onchain-uri --image-base <a bucket you own>`) — maximal durability, no always-on service. Trade-offs: the whole ~200KB+ doc rides each `tokenURI` (some marketplace/indexer reads choke), **manual** stills (`abx render`), on-chain traits need a deployed renderer, later changes are on-chain re-points. Choose it deliberately when permanence outweighs maneuverability. *(The one zero-infra-AND-on-chain exception: the in-chain **Solidity** lane below.)*
124
+
125
+ **Writing the program yourself (the creator brought an *idea*, not a file)? There's ONE runtime contract — get it right or the drop is silently broken** (seed never injects → every token identical; traits empty). The program reads state via **`abx.tokenData`** (a flat object: `.seed`, and each `--schema` key flat, e.g. `.palette`) and reports traits via **`abx.traits({…})`** — never an invented global (`window.tokenData`, `window.tokenTraits`) and never "defensively across variants." `abx.traits()` is the ONLY thing captured into `attributes`, on the resolver lane too. Verify with `abx inspect` (its **PostParams** + **Traits** lines reflect what the program actually reads/reports — if they're empty but you intended a param/traits, you read it the wrong way), THEN pick a lane. Full contract: [reference/code-projects.md → Authoring the program](reference/code-projects.md#authoring-the-program--the-abxjs-runtime-contract-get-this-right-first).
126
+
127
+ **Run `abx inspect <script>` before proposing any lane, then adopt the recommended lane it prints — don't hand-assemble a different flag set.** (Exception: the in-chain **Solidity-render** lane below has no JS script to inspect — go straight to it. `abx inspect` only analyzes a JS file.) A code project has surfaces that each must land *somewhere public* — the fatal mistake (seen in real sessions) is picking *“fully on-chain, no server!”* and only discovering, one at a time after deploy, that it carries no thumbnail, no traits, and dropped a PostParam. **These are all DEPLOY-TIME decisions — an on-chain field with no pointer CANNOT be backfilled** (least of all to localhost). `abx inspect` and `deploy-code --dry-run` print a **Surfaces block** grading each one; **every ⚠ there is a marketplace-facing hole you must close before deploy — trust it over your own read.** Resolve all of them into ONE coherent lane with the creator BEFORE you collect identity or show a config:
128
+
129
+ | Surface | On-chain | Off-chain |
130
+ |---|---|---|
131
+ | **tokenURI + animation** | `--onchain-uri` — *if* script + every dep fit one `tokenURI` eth_call (`abx inspect` estimates) | a resolver (`--public-base-url`) |
132
+ | **thumbnail (`image`)** | ❌ never computed on-chain — needs a public destination: `--image-base <S3/R2/CDN you own>`. **No destination ⇒ placeholder FOREVER + orphaned render, not backfillable. Never localhost.** | a resolver's `/image` |
133
+ | **traits (`attributes`)** | a Solidity `--attributes-renderer 0x…` you **DEPLOY** (fork `SeedTraitsRenderer`) — **not a free flag or guessable address** (`deploy-code` refuses a codeless one; "ports to Solidity" ≠ "deployed") | a resolver stitches the JS `abx.traits()` |
134
+ | **PostParams** | declare EVERY key the script reads: `--schema key:Type:Auth` (a palette collectors set = `palette:HexColor:TokenOwner`) — else silently dropped at render | — |
135
+
136
+ **The resolver is the pivot: choosing *no resolver* forecloses the off-chain column for thumbnail + traits.** (On `--onchain-uri`, an unauthenticated Etherscan "Read Contract" can hit its gas cap on the big doc and look like a revert — expected; verify with `abx tokenuri`, never `abx index --full` to "fix" it. Diagnosis depth → [reference/troubleshooting.md](reference/troubleshooting.md).)
137
+
138
+ **Pick your code lane** (all `abx deploy-code`):
139
+
140
+ | Your sketch | Flags | Result |
141
+ |---|---|---|
142
+ | Vanilla JS, no libraries | `--script f.js --onchain-uri` | **fully on-chain** — resolves from any RPC forever |
143
+ | Script + a library on-chain (e.g. `p5@1.0.0`, **Sepolia only** — needs the AB dependency registry, absent on Base Sepolia) | `--script f.js --onchain-uri --dep p5@1.0.0` | **fully on-chain** — *iff the dep report says “ON-CHAIN bytes available”* |
144
+ | Script + a CDN-only library, or very large params | `--script f.js --public-base-url https://…` | **resolver lane** — you host a resolver; live metadata |
145
+ | A pre-built app (webpack/vite output) | `--code-dir build/` (+ `--onchain-uri` or `--public-base-url`) | **no server to run**, gateway-served → **never** chain-complete (8KB param budget) |
146
+ | A **Solidity renderer** (SVG on-chain, from seed+params) | `--image-renderer 0x<svg> [--attributes-renderer 0x<traits>] --onchain-uri` (**no `--script`**) | **truly in-chain** — image + traits computed on-chain, tokenURI on-chain, **zero infra** |
147
+
148
+ **⭐ In-chain Solidity lane — recommend on-chain `tokenURI` without hesitation.** The artwork is a Solidity `IAbxFieldRenderer` returning an SVG from seed+params (not a JS program), wired `--image-renderer 0x… [--attributes-renderer 0x…] --onchain-uri` with **no `--script`/`--code-dir`**: image AND traits computed on-chain, `tokenURI` assembled on-chain — **genuinely nothing to run** (a small SVG, so the large-`tokenURI` caveat doesn't apply). **NOT no-code, though**: the renderer is a contract the creator compiles + deploys with Foundry. **`abx scaffold-renderer <dir>`** writes a ready-to-build project (worked renderer + coherent traits + the interfaces with invariants + never-revert tests + deploy script); they `forge test`, deploy, and hand the address to `--image-renderer 0x…` (which verifies code-at-address, never compiles/deploys Solidity). Not comfortable with Solidity? Steer to the resolver or (tiny static SVG) `--onchain-image`. Depth → [reference/code-projects.md](reference/code-projects.md#in-chain-solidity-svg--the-zero-dependency-lane) · interface/invariants → https://abx.docs.artblocks.io/protocol/renderers/.
149
+
150
+ ## Decisions (real launch)
151
+
152
+ Master call is **custody × mutability**:
153
+
154
+ | | **Mutable** (name/desc/traits may change) | **Immutable** (never changes) |
155
+ |---|---|---|
156
+ | **Tiny static** (≲ 24 KB/file, ≲ 256 KB total) | **on-chain renderer** — `--onchain-image --compress fastlz`. No host, mutable via `set-field`, permanent. | on-chain renderer + `lock-field` + `lock-uri` once it resolves. |
157
+ | **Bigger / dynamic** (most PNG/JPEG) | **image off-chain, JSON on-chain, no server** — `--onchain-uri --backend arweave` (or `ipfs`). Renderer assembles JSON pointing at the bytes; many files → one `url-template` (O(1)). For metadata you edit often, a **hosted resolver** instead (`abx deploy-resolver`, [hosting.md](reference/hosting.md)). Not fully on-chain (~200 gas/byte). | image off-chain (Arweave = permanent) + on-chain renderer + `lock-field`/`lock-uri`. Or a frozen `ipfs://` override + `lock-uri`. |
158
+
159
+ **Four patterns, by where bytes live × how `tokenURI` resolves:**
160
+ 1. **Fully on-chain** (`--onchain-image`) — bytes + JSON on-chain. Tiny art only.
161
+ 2. **Image off-chain, JSON on-chain, no server** (`--onchain-uri --backend arweave|ipfs|cloud`) — the sweet spot for static art. Arweave/IPFS (permanent, content-addressed) or your S3/CDN (`--backend cloud --public-base <url>`; centralized, mutable). Many files → one `url-template`.
162
+ 3. **Hosted resolver** (`--public-base-url` + `abx deploy-resolver`) — for mutable/dynamic metadata; you run a node.
163
+ 4. **Inline SVG on-chain** — self-contained vector art inlined into `tokenURI`. For a **1/1** that's `abx deploy … --onchain-uri`; for a **Series** of tiny SVGs use `abx deploy-series … --onchain-image --compress fastlz` (bare `--onchain-uri` on a folder does NOT inline the images — it's the image-custody flag `--onchain-image` that puts SVG bytes on-chain per token).
164
+
165
+ **Picking IPFS (or Arweave) does NOT mean running a server.** The `--onchain-uri --backend ipfs|arweave` path (pattern 2) bakes the image's public **gateway** URL into on-chain JSON — a pinning service's read endpoint (a *dedicated* Pinata gateway for IPFS), not a resolver you host. So when a creator chooses IPFS, **default to this no-server path** — image on IPFS, JSON on-chain, nothing to keep running (just keep the pin alive). You only need a **hosted resolver** (pattern 3) if they want *freely editable* metadata. Never present IPFS as blocked on "a public URL" or "a server always online": the gateway belongs to the pinning service and the JSON lives on-chain. (The one real input IPFS needs is `PINATA_JWT` in `.env` for pinning — that's an API upload, not a host.)
166
+
167
+ **No-server tradeoff (patterns 1, 2, 4):** with the on-chain renderer only the *image* is off-chain — any **description / traits / animation_url live on-chain** (gas to write, permanent, lockable), vs a hosted resolver where they're free to edit. Cheap (a shared value is **one collection-scope field**, not one per token — the renderer falls back token→collection), but the creator should choose "no server" knowing their text metadata is on-chain.
168
+
169
+ Get decisions 1–2 right before deploy (image commitment + resolver URL are written then; re-pointable, but):
170
+
171
+ **1. Storage permanence** — where bytes live. Not irreversible: bytes are content-addressed by their on-chain keccak, so start on one backend and move later (`abx verify` confirms the hash). Don't let it block a first deploy.
172
+ - `arweave` = pay-once permanent, no recurring fee. `cloud` (S3/R2) = durable, you maintain it. `ipfs` = decentralized, you pin it. `fs` = zero-config start, dies with the disk → move before it matters.
173
+ - **`arweave` is nearly as easy as `fs` for small art** — Turbo default: **under 100 KB free, no setup** (a managed `.abx-self-host/arweave-key.json` minted on first upload; back it up with `abx storage backup-key`). Choose per command with `--backend` (stateless, no config file); a backend missing its secret falls back to `fs`.
174
+ - **Who pays is a lane (`--storage-signer`)** — Turbo credits attach to an identity (managed key · `.env` key · browser wallet). **Before any top-up, check BOTH balances** (`abx storage balance --backend arweave` shows the managed key AND the wallet — spend the wallet's credits if present). On an upload error surface it verbatim — `…already been uploaded…` is *success* (dedup); don't reflexively top-up or switch to IPFS. Full lanes + failure playbook → [hosting.md](reference/hosting.md#arweave-via-turbo--the-easy-permanent-path-read-before-quoting-setup).
175
+
176
+ **2. Public host URL** — where the resolver runs (**off-chain custody only**). Baked into `tokenURI` at deploy, so the CLI **refuses an off-chain deploy without a public URL** (`ABX_PUBLIC_BASE_URL` or `--public-base-url https://…`) and **never bakes localhost** (that token resolves for no one). No exceptions.
177
+ - **First ask whether you need a host at all** — tiny art is cheaper and more durable on-chain (no host). For bigger art, Arweave (no host to run) beats a resolver unless you need mutability or serve many files.
178
+ - **Tunnels (ngrok/cloudflared) are preview-only — never bake one on-chain** (dies on sleep, rotates on restart). A real launch puts the resolver on a host you control under your own domain (move = a DNS re-point).
179
+
180
+ **3. Identity** — `--name`, `--symbol`, `--royalty-bps` (default 500 = 5%), `--description "…"`, `--external-url <url>` (both served in the metadata — set them or the description is boilerplate). Owner + royalty receiver = the deploying wallet. These default to off-chain operator metadata (editable via `abx add <addr> --description "…"`). For a description that should outlast any node, add `--description-onchain` (or later `abx set-field <addr> --field description --text "…"`) → on-chain, freezable via `lock-field`; the resolver prefers the on-chain value. This is the per-field on-chain model — any field on-chain or off, one active `representation` (inline · reader · keccak256 · arweave · ipfs · url). Background: [metadata model](https://abx.docs.artblocks.io/protocol/metadata/).
181
+ - **Credit + license** — deploy flags `--artist "…"` · `--license "…"` (also `--display-notes`, `--artist-links`) bake authorship + rights ON-CHAIN in the deploy tx (all three deploy commands); or set/change them later with `abx set-field <addr> --collection --field artist|license --text "…"`. Reserved collection fields served in `contractURI`, on any type (1/1 · Series · code). Detail: [operating.md → Authorship + rights](reference/operating.md#authorship--rights-credit--license).
182
+ - **Propose a real name/symbol and confirm — never silently bake a generic folder-name guess.** A folder called `series`/`images`/`photos` infers junk ("Series" / "SRS"), and the CLI *refuses* demo defaults without `--name`/`--symbol` precisely because on-chain identity is effectively permanent. Suggest a specific title + a short ticker-style symbol drawn from the actual work, and get an explicit yes before deploying. Inference is a suggestion to confirm, not a default to ship — if the folder name is generic, say so and ask rather than proposing it.
183
+
184
+ **4. Image placement** — `--image <path>` (png · jpg · gif · svg · webp). The on-chain keccak256 (`image` field) anchors integrity; size is bounded by the backend, not the chain.
185
+ - *Off-chain:* the served `image` is the backend's **gateway HTTPS URL** (`https://<gateway>/ipfs/<cid>`), not raw `ipfs://` (wallets/marketplaces can't render that). So off-chain needs a pinning service + a **public** gateway — with Pinata use a **dedicated** gateway (`--gateway https://<you>.mypinata.cloud`); a local kubo gateway is preview-only. The keccak stays the anchor → move gateways without a tx.
186
+ - *Fully on-chain:* `abx set-field <addr> --field image --file <path> [--compress fastlz]` splits into SSTORE2 chunks behind the shared reader; or bake it in with `abx deploy --image <path> --onchain-image [--compress fastlz]`.
187
+
188
+ **Inline vs reader — default to the reader for real artwork.** `--onchain-uri` alone inlines the SVG (1 tx, ~700 gas/byte); `--onchain-image --compress fastlz` stages via SSTORE2 + a small `reader` pointer (~200 gas/byte, +1 tx) — **cheaper above ~0.5 KB** and widening with size. So: tiny (<~0.5 KB, a one-line SVG/short text) → `--onchain-uri` inline; real artwork (a few KB+) → `--onchain-image --compress fastlz`. **Never `--compress gzip` for an on-chain-rendered token** — gzip decodes off-chain only, breaking `--onchain-uri`; use fastlz (it decodes *in* the reader).
189
+
190
+ **5. On-chain vs off-chain resolution** — by default `tokenURI`/`contractURI` point at your resolver. `--onchain-uri` = JSON assembled *on-chain* by the shared `AbxMetadataRenderer`, self-resolving forever — so it pairs with on-chain content, cost-effective only for tiny art (thresholds above).
191
+ - **Fully on-chain = no server.** Don't stand one up; never cite a localhost URL. **Prove it with `abx tokenuri <addr>`** (reads `tokenURI(0)` over RPC, no `serve`). `abx serve` is only for off-chain-resolving tokens.
192
+ - **The off-chain `tokenURI` is a base, not a per-token URL** — the contract stores a base and derives `{base}/{chainId}/{address}/{tokenId}`. Set via `--public-base-url` or `set-token-uri --uri <base>` later.
193
+ - **Don't default to a frozen `ipfs://` tokenURI** — every edit then = re-pin + on-chain re-point, and the event spine stops driving the token (exiting the spec). Right only for true immutability, then lock it (`set-token-uri --override ipfs://<cid>` then `lock-uri`).
194
+ - **Store ≠ lock; lock last.** On-chain ≠ frozen. Deploy unlocked, confirm it resolves in production, *then* freeze. Two locks: `lock-field <addr> --field <name>` (a value) + `lock-uri <addr>` (how it resolves). Both = provably immutable. A deliberate follow-up, not the first deploy.
195
+
196
+ **6. When to mint** → [Deploy strategy](#deploy-strategy--when-to-mint).
197
+
198
+ ## Confirm before sending
199
+
200
+ Run `abx deploy --dry-run` for real values, present **this exact shape** — one row per on-chain value — then wait for go-ahead. **Mirror the dry-run's values; don't compose your own.**
201
+
202
+ ```
203
+ Deploy config — confirm before I send (everything below is written on-chain):
204
+
205
+ Name Donuts & Cake ⚠ inferred from filename — confirm or rename
206
+ Symbol DONUTS ⚠ inferred — confirm
207
+ Description "<the creator's words>" ⚠ I have nothing from you — give me a line, or I deploy with none
208
+ Traits none ⚠ none written — add --traits "Key=Value" or skip
209
+ Image donuts-cake.svg · 2.4 KB → reader (fastlz, 1 chunk) · on-chain
210
+ Resolution on-chain via renderer 0x5F36…1829 — no server, no localhost
211
+ Royalty 5% (500 bps) → 0x0248…b13C (default; confirm rate + receiver)
212
+ Owner 0x0248…b13C (the deploying wallet)
213
+ Mint token #0 → owner, at deploy
214
+ Address 0x2619…9Da9 (salt-pinned: --salt 0x…)
215
+ Locking deploying UNLOCKED — lock later, after verifying it resolves
216
+ Transactions stage image (1) + deploy + mint (1) = 2
217
+
218
+ Reply to change anything, or say go.
219
+ ```
220
+
221
+ Rules:
222
+ - **The readout is a contract: what's shown is *exactly* what deploys.** Every value = a flag you pass. Not going on-chain → the row says `none`, never an invented placeholder.
223
+ - **Every line must be *verified*, never aspirational — a value that depends on an external contract that must already exist** (`--attributes-renderer`, `--minter`, a `0x…` `--dep`, a gateway) **may appear as committed only once the dry-run confirms it resolves.** Never bake in a guessed/placeholder address; if it's unverified, show it `⚠ requires <X> — not yet deployed/verified` or leave the surface out. (The real-session trap: presenting "Traits computed on-chain via `--attributes-renderer 0x…`" off a *guessed* address — `deploy-code` now refuses an address with no code, so trust the dry-run over the assumption.)
224
+ - **Flag every inferred/defaulted value** with `⚠` + where it came from (name/symbol from filename, royalty default, owner = signing key).
225
+ - **The description is the one people forget** — never deploy an auto-written or empty description quietly. State what's written and whether it's on-chain (`--description-onchain`) or off. For a real piece, ask for the creator's words.
226
+ - **Traits are the creator's** (the OpenSea `attributes` array) — ask (`--traits "Background=Blue"` or `--attributes file.json`); never invent traits, never put protocol facts there. Off-chain by default; `--traits-onchain` to commit them.
227
+ - **Show real values** — the real name, description, address + pinned salt, chunk/tx count from the dry-run.
228
+ - Adapt rows to the config (off-chain shows storage backend + host URL instead of the renderer; `--no-mint` shows a deferred mint) — but always one row per written value, always the ⚠ flags, always an explicit confirm.
229
+
230
+ ## Deploy strategy — when to mint
231
+
232
+ The deploy address is **deterministic** — a pure function of `(factory, salt)`, knowable before signing (`abx predict`). So you can warm the resolver at that exact address first → the moment a marketplace sees the mint it fetches live metadata, not a cached blank. Minting is optional at deploy (`--no-mint`); there's a one-shot `abx mint`.
233
+
234
+ | Path | When | Flow |
235
+ |---|---|---|
236
+ | **Express** *(default)* | demo, or pre-mint to your own wallet | `deploy` (mints #0) → `serve` → `refresh` |
237
+ | **Careful** *(real launch, off-chain resolver)* | metadata live the instant it's listable | `deploy --no-mint` → `serve` (warm) → verify → `mint` → `refresh` |
238
+ | **Primary sale** | token issued at point of sale | `deploy --no-mint` → settle off-chain → `mint --to <buyer>` → `refresh` |
239
+
240
+ - **Fully on-chain ⇒ mint at deploy; do NOT add `--no-mint`.** The careful path warms an *off-chain resolver*; a fully on-chain token has none (the renderer resolves the instant the contract exists). Deferring buys nothing and adds a tx. Defer an on-chain mint only for a genuine primary sale.
241
+ - **`abx predict [--salt 0x..] [--for 0x..]`** pre-computes the address. No `--salt` → reserves a fresh, front-run-proof salt to the deployer; pass `--salt` for a vanity/known address. The salt's leading 20 bytes are an access guard: zero ⇒ anyone may deploy; non-zero ⇒ only that signer.
242
+ - **The address depends on the salt — preview one, you MUST pin it.** Without `--salt`, every invocation (incl. `--dry-run`) reserves a *fresh* salt → a plain `deploy` lands at a different address than the dry-run showed. `--dry-run` prints the exact `--salt …` it used; pass that same salt to the real deploy. Never quote a previewed address without pinning its salt.
243
+ - **`abx refresh <addr>`** asks marketplaces to re-index. We emit **ERC-4906** on URI changes so 4906-aware marketplaces self-refresh; `refresh` is the fallback (+ genesis mint). With `OPENSEA_API_KEY` it calls OpenSea directly; else it prints the links to click.
244
+ - **There is no "add it to a marketplace" step — don't offer one.** Marketplaces discover the collection from chain (the mint + ERC-4906); `abx refresh` is the only nudge. Suggesting a manual listing implies work that doesn't exist.
245
+ - A `--no-mint` deploy reconstructs + serves normally (dashboard shows #0 "not yet minted"; metadata/image already resolve).
246
+ - **Etherscan source is already verified** — your deploys are EIP-1167 clones Etherscan auto-recognizes as proxies of the verified implementation.
247
+
248
+ ## What a token carries — files beyond the image (the data plane)
249
+
250
+ A token is **not "just a picture."** It anchors **named, typed files** ("artifacts"), and the served metadata JSON carries an **`artifacts`** list — the *complete* set of the token's files, each `{key, mimeType, uri}`. The `image` / `animation_url` are just reserved members of that same set; alongside them a token can carry a hi-res master, a certificate, source files, a README — any number of named files. (Background: [data plane](https://abx.docs.artblocks.io/protocol/data-plane/).)
251
+
252
+ ```json
253
+ "artifacts": [
254
+ { "key": "image", "mimeType": "image/svg+xml", "uri": "…/image" },
255
+ { "key": "print", "mimeType": "image/tiff", "uri": "ipfs://Qm…/master.tiff" },
256
+ { "key": "readme", "mimeType": "text/markdown; charset=utf-8", "uri": "ar://…/README.md" }
257
+ ]
258
+ ```
259
+
260
+ - **Attach a file:** `abx attach <addr> <key> <ipfs://… | ar://… | https://…>` — `<key>` is any name you choose (`print`, `certificate`, `stems`, `readme`) and becomes the manifest entry's key. The representation is **auto-detected** from the URI scheme; the `mimeType` is declared from the file **extension** (`…/master.tiff` → `image/tiff`), so point the URI at the file itself. Tiny bytes with no external host can go **on-chain** with `--file <path>`. Token scope by default; `--collection` for a collection-wide file. Any signing lane; `--dry-run` previews. It's one file per call, run **after deploy**.
261
+ - **Don't have a URL yet? Upload first.** `attach` takes a locator you already host. `abx storage upload <path> --backend arweave|ipfs` uploads one file and prints a locator that **keeps the filename** (so the declared type survives) plus the ready-to-run `attach` line (Arweave = pay-once permanent; the same backends `deploy` uses; `--dry-run` to preview without uploading). Full flow: `abx storage upload master.tiff --backend arweave` → copy the printed locator → `abx attach <addr> print <that-locator>`.
262
+ - **`artifacts` is COMPUTED, never a field you set.** The resolver/renderer assembles the list from your fields — setting a field literally named `artifacts` is refused. You attach one file per key; the manifest builds itself.
263
+ - **The complete listing is a resolver surface.** A resolver serves the full `artifacts` list at `/t/<chainId>/<addr>/<id>`, and `/data/<key>` fetches each file. The bare on-chain `tokenURI` enumerates **reserved fields only** (there's no on-chain enumeration of arbitrary keys) — so a project that must surface extra files to consumers today runs a resolver (attached files are still stored on-chain + keccak-anchored regardless).
264
+ - **Effect outputs are artifacts too.** A code project's effect runner publishes `render/image`, `render/traits`, and any extra declared output (e.g. a hi-res `render/print`) into the same manifest automatically, at the current settled state — files appear as tokens are minted and params change (see [code-projects](reference/code-projects.md)).
265
+ - **Not the same as a Series.** `deploy-series` makes **N separate tokens, one file each**. The data plane is how **one** token holds several named files. Depth (representations, verify, reserved keys, on-chain-vs-resolver) → [operating.md](reference/operating.md#attaching-files--the-data-plane).
266
+ - **Set expectations honestly (say it up front).** No mainstream marketplace (OpenSea/Blur) shows a "files" tab **today** — they render `image`/`animation_url` only. Attached files are a durable, cryptographically-anchored part of the token *now*, read by **data-plane-aware tools and any resolver**; broad marketplace display is future adoption. So a creator verifies an attach by **curling their resolver's listing**, not by refreshing OpenSea (which won't show it).
267
+
268
+ ## After launch — tell the creator (durability + owner care)
269
+
270
+ Once it's live, cover these in plain language; don't wait to be asked.
271
+
272
+ - **Durability depends on the backend.** Arweave = **pay once, kept for centuries** (a storage endowment funds ~200 years; resolves as long as the network + any gateway are up) — nothing to renew. IPFS/Pinata = **the creator must keep it pinned** — the image serves through their gateway, and if pinning lapses the bytes can disappear. `cloud`/`fs` = they maintain them. Say which one this drop uses and what it implies. For a real drop, lead toward Arweave (or start elsewhere and re-host later — bytes are keccak-anchored, so re-upload to the new backend + re-point with `set-field`, verified by `abx verify`).
273
+ - **Moving backend/gateway later is an owner-signed on-chain edit.** The on-chain keccak anchors the bytes so they're portable, but re-pointing (`set-field`/`migrate`) is a tx from the **owner** wallet — so that wallet must stay secure and reachable.
274
+ - **Owner-wallet hygiene (suggest as follow-ons).** The owner wallet controls mint, royalties, URIs, and ownership itself. Recommend: a hardware/dedicated wallet over a throwaway hot key; for anything valuable, hand ownership to a multisig (`abx set-admin <addr> --to <safe>`); and **back up `.abx-self-host/arweave-key.json`** — it holds any prepaid Turbo credits, lose it and they're stranded.
275
+
276
+ ## Setup + environment
277
+
278
+ `abx` is installed and on your PATH; it needs **Node ≥ 22.5** at runtime (the projection store uses built-in SQLite). Start with `abx doctor` (preflight: signing key/wallet, RPC, canonical factory, storage).
279
+
280
+ `.env` (in the creator's project dir) = **secrets only**:
281
+ - **Signing:** a key (`SEPOLIA_FUNDED_PK` / `ABX_DEPLOYER_PK` / `SEPOLIA_WALLET_PK`) is needed ONLY for hot/unattended signing. If the creator owns a wallet, prefer **`--sign`** — no key in `.env`. `doctor`'s missing-key ✗ is **not fatal** on the `--sign` path.
282
+ - `ABX_RPC_URLS`, `ABX_PUBLIC_BASE_URL` (hosted-resolver custody only), optional `OPENSEA_API_KEY`, optional `ABX_RESOLVER_ADMIN_TOKEN` (`deploy-resolver` generates it), plus any backend secret.
283
+
284
+ <sub>Working from the abx **source repo** (contributor)? `pnpm install`, then `pnpm abx <cmd>` or `pnpm sandbox`. Every command below is identical.</sub>
285
+
286
+ **Indexing reads the event log via `eth_getLogs` from the contract's deploy block** — the CLI records it at deploy and forwards it to a resolver on `add` (discovering it on-chain for a contract it didn't deploy here). So a normal deploy→index scans a small, recent window and is fast on **any** RPC, and re-index is incremental (resumes from the last block). It **auto-chunks**, so a range cap never yields *wrong* state — but don't wave a cap away: a **from-genesis** or long-span reconstruction, or a resolver serving many code projects under load, is slow and rate-limit-prone on a getLogs-**range-capped** endpoint. `abx doctor` rates each endpoint and flags a capped one — treat that as a real infra signal, and give a resolver you'll run under load a range-generous **archive** RPC. **If indexing is slow, or a hosted resolver won't serve, check the scan floor FIRST (is it scanning from block 0?), not the RPC tier** — that mis-diagnosis is a known trap. RPC deep-dive + troubleshooting → [reference/setup.md](reference/setup.md).
287
+
288
+ ## Reference files
289
+
290
+ - **Public docs — the human-facing companion** at **https://abx.docs.artblocks.io** (quickstart, guides, the CLI/SDK reference, the protocol model). This skill is YOUR operating manual and stays authoritative for how to drive the CLI; the docs site is what you **link the creator to** for background/onboarding, and a place you can read if you want the protocol rationale behind a command. Don't send the creator commands to run (you run them) — send them the docs to *read*.
291
+ - **Code projects — operating depth** (what to keep running, the resume loop, verify-it-resolves, render ops, `--onchain-uri`/`--image-base`/traits internals, arweave delay, `deploy-code` flags, mint timing/pause/supply) → **[reference/code-projects.md](reference/code-projects.md)**
292
+ - **Operating an existing project** (owner ops, **artist credit + license fields**, **attaching files / the data plane**, selling via the shared minter, `abx mint-page`, moving hosting, resolver→resolver `migrate`) → **[reference/operating.md](reference/operating.md)**
293
+ - **Hosting infrastructure** (storage backends, Turbo lanes + failure playbook, `deploy-resolver`, `deploy-effects`, local-vs-remote stores, token API routes, Docker) → **[reference/hosting.md](reference/hosting.md)**
294
+ - **Environment detail** (RPC selection + failover, multi-chain, troubleshooting) → **[reference/setup.md](reference/setup.md)**
295
+ - **Troubleshooting — "my NFT looks wrong"** (gray placeholder, stale-on-marketplace, tokenURI reverts, localhost baked, "not registered") — diagnose before acting → **[reference/troubleshooting.md](reference/troubleshooting.md)**
296
+
297
+ ## Guarantees
298
+
299
+ - **Reconstruction from chain alone** — deterministic, idempotent; delete the projection, replay yields identical state.
300
+ - **Trust = the factory** — a clone is canonical only when the ownerless factory's `isAbxClone` confirms it (the `AbxDeployed` beacon is discovery, and spoofable).
301
+ - **Content is verifiable** — the on-chain keccak256 lets anyone re-hash the served bytes (the `verify` route), zero trust in the node.
302
+ - **Keys stay with their owner** — the wallet lane signs in the user's own wallet; the CLI never sees the key. Runtime data lives in `./.abx-self-host/` (gitignored).
303
+
304
+ Protocol model + full docs → **https://abx.docs.artblocks.io** ([protocol](https://abx.docs.artblocks.io/protocol/), [using ABX](https://abx.docs.artblocks.io/using-abx/), [CLI/SDK reference](https://abx.docs.artblocks.io/reference/)).