@artblocks/abx-cli 0.1.0-alpha.2 → 0.1.0-alpha.21

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 (137) hide show
  1. package/CHANGELOG.md +1991 -0
  2. package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +17 -0
  3. package/assets/renderer-scaffold/test/MyRenderer.t.sol +51 -2
  4. package/dist/commands/deploy.d.ts +206 -0
  5. package/dist/commands/deploy.d.ts.map +1 -0
  6. package/dist/commands/deploy.js +4273 -0
  7. package/dist/commands/deploy.js.map +1 -0
  8. package/dist/commands/project.d.ts +241 -0
  9. package/dist/commands/project.d.ts.map +1 -0
  10. package/dist/commands/project.js +1060 -0
  11. package/dist/commands/project.js.map +1 -0
  12. package/dist/commands/reads.d.ts +64 -0
  13. package/dist/commands/reads.d.ts.map +1 -0
  14. package/dist/commands/reads.js +697 -0
  15. package/dist/commands/reads.js.map +1 -0
  16. package/dist/commands/scaffold.d.ts +71 -0
  17. package/dist/commands/scaffold.d.ts.map +1 -0
  18. package/dist/commands/scaffold.js +552 -0
  19. package/dist/commands/scaffold.js.map +1 -0
  20. package/dist/commands/service.d.ts +41 -0
  21. package/dist/commands/service.d.ts.map +1 -0
  22. package/dist/commands/service.js +618 -0
  23. package/dist/commands/service.js.map +1 -0
  24. package/dist/commands/storage.d.ts +37 -0
  25. package/dist/commands/storage.d.ts.map +1 -0
  26. package/dist/commands/storage.js +343 -0
  27. package/dist/commands/storage.js.map +1 -0
  28. package/dist/config.d.ts +91 -2
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +244 -11
  31. package/dist/config.js.map +1 -1
  32. package/dist/conformance.d.ts +31 -0
  33. package/dist/conformance.d.ts.map +1 -0
  34. package/dist/conformance.js +371 -0
  35. package/dist/conformance.js.map +1 -0
  36. package/dist/deps.d.ts +6 -39
  37. package/dist/deps.d.ts.map +1 -1
  38. package/dist/deps.js +4 -68
  39. package/dist/deps.js.map +1 -1
  40. package/dist/errors.d.ts +20 -0
  41. package/dist/errors.d.ts.map +1 -0
  42. package/dist/errors.js +25 -0
  43. package/dist/errors.js.map +1 -0
  44. package/dist/flag-allowlists.d.ts +53 -0
  45. package/dist/flag-allowlists.d.ts.map +1 -0
  46. package/dist/flag-allowlists.js +148 -0
  47. package/dist/flag-allowlists.js.map +1 -0
  48. package/dist/flags.d.ts +41 -0
  49. package/dist/flags.d.ts.map +1 -1
  50. package/dist/flags.js +94 -1
  51. package/dist/flags.js.map +1 -1
  52. package/dist/jsonout.d.ts +37 -0
  53. package/dist/jsonout.d.ts.map +1 -0
  54. package/dist/jsonout.js +68 -0
  55. package/dist/jsonout.js.map +1 -0
  56. package/dist/kind.d.ts +53 -0
  57. package/dist/kind.d.ts.map +1 -0
  58. package/dist/kind.js +94 -0
  59. package/dist/kind.js.map +1 -0
  60. package/dist/main.js +534 -4708
  61. package/dist/main.js.map +1 -1
  62. package/dist/mintpage.d.ts +17 -2
  63. package/dist/mintpage.d.ts.map +1 -1
  64. package/dist/mintpage.js +214 -52
  65. package/dist/mintpage.js.map +1 -1
  66. package/dist/output.d.ts +148 -0
  67. package/dist/output.d.ts.map +1 -0
  68. package/dist/output.js +634 -0
  69. package/dist/output.js.map +1 -0
  70. package/dist/ownerops.d.ts +132 -44
  71. package/dist/ownerops.d.ts.map +1 -1
  72. package/dist/ownerops.js +1151 -329
  73. package/dist/ownerops.js.map +1 -1
  74. package/dist/preview.d.ts +81 -0
  75. package/dist/preview.d.ts.map +1 -0
  76. package/dist/preview.js +535 -0
  77. package/dist/preview.js.map +1 -0
  78. package/dist/prompt.d.ts +17 -0
  79. package/dist/prompt.d.ts.map +1 -0
  80. package/dist/prompt.js +19 -0
  81. package/dist/prompt.js.map +1 -0
  82. package/dist/provision.d.ts.map +1 -1
  83. package/dist/provision.js +10 -7
  84. package/dist/provision.js.map +1 -1
  85. package/dist/remote.d.ts +134 -53
  86. package/dist/remote.d.ts.map +1 -1
  87. package/dist/remote.js +358 -46
  88. package/dist/remote.js.map +1 -1
  89. package/dist/riskgate.d.ts +29 -0
  90. package/dist/riskgate.d.ts.map +1 -0
  91. package/dist/riskgate.js +91 -0
  92. package/dist/riskgate.js.map +1 -0
  93. package/dist/scaffold.d.ts +10 -0
  94. package/dist/scaffold.d.ts.map +1 -0
  95. package/dist/scaffold.js +52 -0
  96. package/dist/scaffold.js.map +1 -0
  97. package/dist/schema.d.ts +18 -1
  98. package/dist/schema.d.ts.map +1 -1
  99. package/dist/schema.js +74 -23
  100. package/dist/schema.js.map +1 -1
  101. package/dist/served.d.ts +30 -0
  102. package/dist/served.d.ts.map +1 -0
  103. package/dist/served.js +112 -0
  104. package/dist/served.js.map +1 -0
  105. package/dist/signer.d.ts +13 -0
  106. package/dist/signer.d.ts.map +1 -1
  107. package/dist/signer.js +80 -14
  108. package/dist/signer.js.map +1 -1
  109. package/dist/update-check.d.ts +83 -4
  110. package/dist/update-check.d.ts.map +1 -1
  111. package/dist/update-check.js +132 -17
  112. package/dist/update-check.js.map +1 -1
  113. package/package.json +10 -6
  114. package/skill/SKILL.md +108 -62
  115. package/skill/reference/code-projects.md +109 -13
  116. package/skill/reference/creator-token.md +86 -0
  117. package/skill/reference/decisions.md +87 -0
  118. package/skill/reference/hosting.md +48 -15
  119. package/skill/reference/operating.md +32 -8
  120. package/skill/reference/setup.md +10 -3
  121. package/skill/reference/troubleshooting.md +31 -4
  122. package/dist/inspect.d.ts +0 -48
  123. package/dist/inspect.d.ts.map +0 -1
  124. package/dist/inspect.js +0 -184
  125. package/dist/inspect.js.map +0 -1
  126. package/dist/migrate.d.ts +0 -65
  127. package/dist/migrate.d.ts.map +0 -1
  128. package/dist/migrate.js +0 -180
  129. package/dist/migrate.js.map +0 -1
  130. package/dist/onchain-uri.d.ts +0 -97
  131. package/dist/onchain-uri.d.ts.map +0 -1
  132. package/dist/onchain-uri.js +0 -243
  133. package/dist/onchain-uri.js.map +0 -1
  134. package/dist/upload.d.ts +0 -28
  135. package/dist/upload.d.ts.map +0 -1
  136. package/dist/upload.js +0 -41
  137. package/dist/upload.js.map +0 -1
package/skill/SKILL.md CHANGED
@@ -1,9 +1,9 @@
1
1
  ---
2
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.
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, stand up hosting they own, or point a project at a hosted/managed metadata provider with an API key.
4
4
  compatibility: Drives the abx CLI (@artblocks/abx-cli). Co-versioned with it — install/refresh with `abx skill install` so this skill matches the CLI's `abx version`. Requires Node 22.5+.
5
5
  metadata:
6
- version: "0.1.0-alpha.2"
6
+ version: "0.1.0-alpha.21"
7
7
  ---
8
8
 
9
9
  # ABX Self-Host Toolkit (`abx`)
@@ -14,16 +14,18 @@ L3 agentic surface: image → live self-hosted NFT the creator owns — a **1/1*
14
14
 
15
15
  ## Read first (every session)
16
16
 
17
- - **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.
18
- - **Skill CLI version must match.** This skill is co-versioned with the `abx` CLI. Run `abx version` and compare it to this file's frontmatter `metadata.version` (top of SKILL.md). If they differ, this skill is stale for the installed CLI run `abx skill install` to resync, then reload the skill before continuing. (`abx` also prints a drift nudge on its own when it notices.)
17
+ - **YOU run the `abx` commands — never tell the creator to run one.** You have a shell; use it. Run `doctor`, `status`, `--dry-run`, `tokenuri`, `state`, `refresh`, `tokens`, 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 hands-on steps are approving in their **browser wallet** (`--sign`), giving you a value you asked for (their address, a name), and **looking at the art in `abx preview`** — you run that command, but the URL it prints is theirs to open and play with ([Phase 0](#phase-0--make-the-work-first-skip-every-gate-below-until-its-good)). 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.
18
+ - **Version drift `abx doctor`'s "Version & provenance" block catches it: binary source (npx is a it can silently keep serving a stale cached copy), npm currency, and skill↔CLI match, each with its own fix command.** Run it before any deploy and act on a ✗ yourself (`abx skill install` / `npm i -g @artblocks/abx-cli@latest`) never just relay the notice: a stale CLI can hold canonical addresses that have since moved, so it deploys against dead singletons.
19
19
  - **`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).
20
- - **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.
20
+ - **Never collect secrets in chat, and never `cat`/`grep` `.env`.** Keys, `PINATA_JWT`, S3 secrets, provider API keys → 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. **To see what's configured, ask the tool, not the file:** `abx doctor` and `abx remote` report each credential as set/unset without ever printing one. Reading `.env` spills every secret in it into the transcript — irreversible, and a plain `abx doctor` tells you the same thing.
21
21
  - **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).
22
- - **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.
23
- - **Two gates decide everything: (1) demo or real? (2) who signs?** Settle both first.
22
+ - **Scope today = testnet, two standards.** The shipped standards are **ERC-721** a 1/1, a **Series** of many, or a code drop, each token **unique** — and **ERC-1155 editions** — **copies** of one artwork — reached with **`--copies <n|open>`** on those same three deploy commands, on Base Sepolia (default) or Sepolia via `ABX_CHAIN`. There is still **no `--chain` flag (pick the chain with `ABX_CHAIN`) and still no `--erc1155`/`--standard` flag** — don't invent one; mainnet is roadmap, not something you flip here. Map the ask to the flag, never the standard name (nobody says "ERC-1155"): **"N copies of the same piece" / "an open edition"** → add `--copies <n|open>` to the matching command; **"N unique pieces"** → a plain **Series**, exactly as today (`--copies` absent). If a creator needs an unsupported chain, say plainly it's not in the toolkit today rather than fabricating a recipe.
23
+ - **Is the work finished yet?** If the creator is still *making* the piece, you're in **[Phase 0](#phase-0--make-the-work-first-skip-every-gate-below-until-its-good)** iterate on the art and keep every deploy question off the table until they say ship. The gates below apply to launching something that already exists.
24
+ - **Two gates decide everything: (1) demo or real? (2) who signs?** Settle both first — *once there's something to launch*.
24
25
  - **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`.
26
+ - **Never hand-build a service URL — ask the chain, then check the reference.** A contract commits its own metadata URL on-chain, so `abx tokenuri <addr> --fetch` (token) and `abx contracturi <addr>` (ERC-7572 collection) give you the answer *and* follow it, printing **what is actually served** — no route grammar to remember, no curl. (Bare `abx tokenuri` reads the chain only; `--fetch` is what GETs the URL the contract names.) **A 404/error on a URL you constructed is evidence about your URL, never about the service.** Don't infer a path from a similar-looking one (dropping the token id off `/t/<chain>/<addr>/<id>` does **not** give collection metadata — that's `/c/<chain>/<addr>`); look it up in [hosting.md](reference/hosting.md#token-api-the-resolver). Before telling anyone a service is broken, reproduce it with a **CLI command** — a real service miss says which of three things it is in a machine `code` (`invalid_request` = your path shape · `unknown_route` = no such route here · `not_registered` = this node doesn't index that contract), and none of those mean "down".
25
27
  - **Safe to explore:** `abx <cmd> --help` and `abx deploy --dry-run` never send. You never run a real write just to learn flags.
26
- - **`deploy` returns; `demo`/`serve` block** (they serve) — background them or warn.
28
+ - **`deploy` returns; `demo`/`serve`/`preview` block** (they serve) — background them or warn. Background `abx preview` and relay its URL, then keep working while the creator looks; `--shoot` is the one preview mode that exits on its own.
27
29
 
28
30
  ## Which command — what are you launching?
29
31
 
@@ -33,19 +35,51 @@ Route by the **content** first, then apply the gates below. The three paths diff
33
35
  |---|---|---|---|
34
36
  | **one image** (a 1/1) | `abx deploy` | on-chain (tiny art) or off-chain — **no server possible** | the image itself |
35
37
  | **a folder of images** | `abx deploy-series` | same — on-chain or off-chain, **no server possible** | each image itself |
38
+
39
+ **Any row also takes `--copies <n|open>`** — it makes that artwork/collection an ERC-1155 **edition** (copies, not unique tokens; `open` = an uncapped open edition, the flagship edition product). Drop it for a unique token, exactly as today. Edition owner ops (mint copies, per-id supply cap, per-id sales) → [operating.md](reference/operating.md#edition-owner-ops).
40
+
41
+ **Custody on an edition is now symmetric with the 721 side** — on-chain bytes, off-chain-image-with-on-chain-JSON, inline SVG, or a resolver all work with `--copies`, and `deploy-code --copies` takes on-chain `--dep`s. Two limits remain: **`--onchain-image` can't be signed offline** (refused on `--unsigned` everywhere — each chunk tx feeds the next; use hot or `--sign`), and **`deploy-code --copies` is `--script`-only** (no `--code-dir`, no field renderers). Full matrix, including the O(1) folder trick for a raster edition with no server: [decisions.md → `--copies` custody](reference/decisions.md#copies-erc-1155-editions--custody-is-not-orthogonal-to-the-shape).
42
+
43
+ > **`--onchain-uri` puts the JSON on-chain — the IMAGE follows one of three routes, and the creator
44
+ > is choosing between them whether they know it or not.** An **SVG** is inlined (bytes on-chain,
45
+ > fully self-contained). A **raster** with `--backend arweave|ipfs|cloud` is uploaded and its URL is
46
+ > baked into the on-chain JSON — no server, permanence is the backend's. A **raster on `fs`** has no
47
+ > public URL to bake, so the renderer holds only a hash and `tokenURI` serves a **placeholder**; the
48
+ > CLI warns before the spend. For the bytes themselves on-chain regardless of format, use
49
+ > `--onchain-image --compress fastlz` (best under ~24 KB). Say which one you're giving them: "no
50
+ > server" and "on-chain" are not the same promise.
36
51
  | **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 |
37
52
 
38
53
  **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.
39
54
 
40
- **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).
55
+ **Planning a priced primary sale? Decide 1/1 vs Series (or edition) 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`) — or, if copies of that one piece are fine, as an **edition** (`abx deploy --copies <n|open>`), which ships the full sale stack (minter/pause/payee) on its own, no Series wrapper needed. 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).
56
+
57
+ ## Phase 0 — make the work first (skip every gate below until it's good)
58
+
59
+ **If the creator is still making the piece, you are in the studio, not in a deploy. Stay there until they say ship.** The gates and decisions below are for *launching* something that already exists — reaching for them while someone is still designing is the single most common way this skill feels wrong to use. A real session: the creator said *"I want to work on one with you"* and got asked about metadata resolution, hosting, and wallet ownership before a single pixel existed. They had to push back with *"let's work on actually designing the piece together first."* Don't make them.
60
+
61
+ **Which mode are you in?**
62
+ - **They handed you a finished file** (`sketch.js`, a build dir, an image folder) → skip this section, go to [Gate 1](#gate-1--demo-or-real-launch).
63
+ - **They brought an idea, a reference, a vibe, or "let's make one together"** → Phase 0. The deploy is a footnote at the end of an afternoon of work; treat it that way.
64
+
65
+ **During Phase 0, these are OFF the table** — do not ask, do not "just quickly confirm," do not pre-emptively lay out the tradeoffs: hosting/lane, thumbnails, traits-on-chain, storage permanence, wallet address, supply cap, royalties, mint count, name/symbol. Every one of them is answerable in five minutes *after* the art is right, and asking early reads as pressure to ship something half-made. The **one** exception is a constraint that changes what you'd *write*: if they want an on-chain library (`p5`), say early that on-chain deps mean `ABX_CHAIN=sepolia` — that's an authoring constraint, not a deploy decision.
66
+
67
+ **The loop** — depth + the `--shoot` details → [reference/code-projects.md → Studio loop](reference/code-projects.md#studio-loop--iterate-on-the-art-before-you-deploy-anything):
68
+
69
+ 1. **Write against the real runtime contract from the first draft** — `abx.tokenData.seed` for randomness, `abx.traits({…})` for features. Not `Math.random()` "for now": a piece prototyped on `Math.random()` looks finished and then deploys as N identical tokens, and retrofitting the seed late means re-tuning every visual you just approved.
70
+ 2. **Run `abx preview --script art.js` and give the creator the URL.** It serves the *same document the generator serves* (real `abx.js`, real tokenData) on `localhost:8788`, so they get a seed shuffle, real inputs for every `--schema` PostParam, a live traits readout, and `/grid` for N seeds at once. **This is the one place you hand over a link instead of running it for them** — the art is theirs to judge, and an animated piece cannot be judged from a screenshot. Don't hand-roll a preview page; a stub you write yourself will run a sketch that reads its seed wrong.
71
+ 3. **Edit and tell them to refresh.** The program is re-read from disk per render — no restart, no watcher. To check your own work between rounds (you have no browser), `abx preview --script art.js --shoot ./frames` renders the same document headlessly and flags the two silent killers: no traits reported, or identical traits across every seed.
72
+ 4. **Take feedback and go again.** Expect several rounds. Rounds are the point — "add faces to the shapes" is a normal Phase 0 request, not scope creep.
73
+
74
+ **Exit only on an explicit ship signal** ("let's deploy this", "I'm happy with it"). Then run `abx inspect <script>` and open the deploy decisions — and say plainly that some are irreversible, so it's worth a few minutes ([Code projects](#code-projects-generative--code-based-drops)). If *you* feel the pull to start the deploy conversation while they're still iterating: don't. Ask what they want to try next.
41
75
 
42
76
  ## Gate 1 — demo or real launch?
43
77
 
44
78
  | | **Demo** (`abx demo`) | **Real launch** (`abx deploy` → operate) |
45
79
  |---|---|---|
46
80
  | Art | generative-from-address | the creator's `--image` |
47
- | Storage | `fs` (throwaway) | a permanence decision |
48
- | Host URL | `localhost:8787` | a public URL baked on-chain (off-chain custody only) |
81
+ | Storage | none the SVG is inlined **on-chain** | a permanence decision |
82
+ | Host URL | none — nothing is hosted, nothing to bake | a public URL baked on-chain (off-chain custody only) |
49
83
  | Key | any funded testnet key (the active `ABX_CHAIN`) | the wallet that should **own** it |
50
84
  | Decisions | none — just run it | the framework below |
51
85
 
@@ -78,8 +112,7 @@ Every write builds an unsigned tx; pick the lane by stakes:
78
112
 
79
113
  - **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.
80
114
  - **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`.
81
- - **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`.
82
- - **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.
115
+ - **Keep tx count low and say it up front — every preview prints `approvals N wallet approval(s)`, the exact signature count.** A configured token deploys in one tx; `--onchain-image` adds staging tx(s); several owner edits collapse into one atomic `multicall`. Off-chain storage uploads (IPFS/Arweave) are listed separately in the preview and never counted — they happen before signing with no wallet prompt. *Exception:* `--storage-signer eth --sign` turns each upload into a same-session `personal_sign` — then it IS counted.
83
116
 
84
117
  ## Quick start
85
118
 
@@ -123,7 +156,7 @@ Files natural-sort into **tokens `0…N-1`** (`--count N` uses the first N); a t
123
156
  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)**.
124
157
 
125
158
  **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:
126
- - **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`.
159
+ - **Off-chain resolver** (`--public-base-url` + rendering, ~a few $/mo self-hosted) — **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`. A **managed provider whose descriptor says `render.attached`** covers both halves with one API key — no effects runner to stand up ([hosting.md → Managed providers](reference/hosting.md#managed-providers--a-resolver-someone-else-runs---remote-name)).
127
160
  - **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.)*
128
161
 
129
162
  **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).
@@ -147,7 +180,10 @@ A **program is the content** (`abx deploy-code` → a `SeriesCode`): output is a
147
180
  | 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”* |
148
181
  | Script + a CDN-only library, or very large params | `--script f.js --public-base-url https://…` | **resolver lane** — you host a resolver; live metadata |
149
182
  | 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) |
150
- | 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** |
183
+ | 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**; no `animation_url` (no program) |
184
+ | **A JS program AND on-chain thumbnail/traits** — the both-worlds shape | `--script f.js --image-renderer 0x<svg> --attributes-renderer 0x<traits> --onchain-uri` | **every surface in-chain, zero infra** — `animation_url` assembles from the script chunks, `image`+`attributes` computed by Solidity. **Nothing to render or host.** |
185
+
186
+ **`--script` and `--image-renderer` are NOT an either-or.** Passing both is the strongest shape available and the most-missed one: the program keeps driving `animation_url` on-chain while the Solidity renderers supply `image` and `attributes`, so an interactive generative piece gets a real marketplace thumbnail and real traits with **no resolver, no runner, no bucket**. Reach for it whenever the creator can supply (or fork) a Solidity renderer. The dry run's `Surfaces` block confirms each surface before gas is spent.
151
187
 
152
188
  **⭐ 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/.
153
189
 
@@ -158,50 +194,33 @@ Master call is **custody × mutability**:
158
194
  | | **Mutable** (name/desc/traits may change) | **Immutable** (never changes) |
159
195
  |---|---|---|
160
196
  | **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. |
161
- | **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`. |
197
+ | **Bigger / dynamic** (most PNG/JPEG) | **image off-chain, JSON on-chain, no server** — `--onchain-uri --backend arweave` (or `ipfs`). For metadata you edit often, a **resolver** instead. | image off-chain (Arweave = permanent) + on-chain renderer + `lock-field`/`lock-uri`. |
162
198
 
163
- **Four patterns, by where bytes live × how `tokenURI` resolves:**
199
+ **Four patterns, by where bytes live × how `tokenURI` resolves** — pick one, then read
200
+ [decisions.md](reference/decisions.md) for how to configure it:
164
201
  1. **Fully on-chain** (`--onchain-image`) — bytes + JSON on-chain. Tiny art only.
165
- 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`.
166
- 3. **Hosted resolver** (`--public-base-url` + `abx deploy-resolver`) — for mutable/dynamic metadata; you run a node.
167
- 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).
168
-
169
- **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.)
170
-
171
- **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.
172
-
173
- Get decisions 1–2 right before deploy (image commitment + resolver URL are written then; re-pointable, but):
174
-
175
- **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.
176
- - `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.
177
- - **`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`.
178
- - **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).
202
+ 2. **Image off-chain, JSON on-chain, no server** (`--onchain-uri --backend arweave|ipfs|cloud`) — the sweet spot for static art.
203
+ 3. **Remote resolver** (`--public-base-url` + a node) — mutable/dynamic metadata; self-hosted or a managed provider.
204
+ 4. **Inline SVG on-chain** — self-contained vector art. 1/1 `--onchain-uri`; a Series of SVGs `--onchain-image --compress fastlz`.
179
205
 
180
- **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.
181
- - **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.
182
- - **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).
206
+ **IPFS/Arweave is NOT a server.** Pattern 2 bakes the pinning service's public gateway URL into
207
+ on-chain JSONnothing to keep running. Only pattern 3 needs a resolver. Never tell a creator IPFS
208
+ is blocked on "a public URL" or "a server always online".
183
209
 
184
- **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/).
185
- - **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).
186
- - **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.
210
+ **The six decisions** — storage permanence · public host URL · identity · image placement · on-chain
211
+ vs off-chain resolution · when to mint. Full detail, tradeoffs and failure modes:
212
+ **[decisions.md](reference/decisions.md)**. The four that can go permanently wrong, in brief:
187
213
 
188
- **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.
189
- - *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.
190
- - *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]`.
191
-
192
- **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).
193
-
194
- **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).
195
- - **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.
196
- - **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.
197
- - **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`).
198
- - **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.
199
-
200
- **6. When to mint** → [Deploy strategy](#deploy-strategy--when-to-mint).
214
+ - **Never bake localhost** into an off-chain deploy that token resolves for no one. The CLI refuses it; don't try to talk it round.
215
+ - **Propose a real name/symbol and get an explicit yes** on-chain identity is effectively permanent, and a generic folder name infers junk. The CLI refuses a real send that would bake its own placeholder (a `--dry-run` only warns that is not permission).
216
+ - **Store lock; lock last.** Deploy unlocked, confirm it resolves in production, *then* freeze (`lock-field` / `lock-uri`). A deliberate follow-up, never the first deploy.
217
+ - **Tunnels (ngrok/cloudflared) are preview-only** — never bake one on-chain.
201
218
 
202
219
  ## Confirm before sending
203
220
 
204
- 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.**
221
+ Run `abx deploy --dry-run` for real values (pin the salt it prints — [below](#deploy-strategy--when-to-mint) — with a second `--dry-run --salt <it>` to see the real address), 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.**
222
+
223
+ **No signing key in `.env` yet? `--dry-run` still needs a deployer address — pass `--for 0x<the creator's wallet>`.** The address is a pure function of (factory, salt, deployer), so a preview can't compute it from nothing; it signs nothing, so no key is involved. Ask the creator for their wallet address once, up front — it's also what the real wallet-lane deploy takes (`--sign --for 0x…`).
205
224
 
206
225
  ```
207
226
  Deploy config — confirm before I send (everything below is written on-chain):
@@ -243,9 +262,9 @@ The deploy address is **deterministic** — a pure function of `(factory, salt)`
243
262
 
244
263
  - **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.
245
264
  - **`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.
246
- - **The address depends on the saltpreview 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.
247
- - **`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.
248
- - **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.
265
+ - **`--dry-run` without `--salt` prints NO address**enforced, not a trap to remember: without a pinned salt there's nothing real to quote, so it prints only the freshly-reserved salt plus a ready-to-paste `--salt …` re-run (or `abx predict --salt --for …`). Pin that salt and re-run to see the real, reproducible address before you present anything.
266
+ - **`abx refresh <addr>`** asks marketplaces to re-index. On a **721** we emit **ERC-4906** on URI changes so 4906-aware marketplaces self-refresh, and `refresh` is the fallback (+ genesis mint). **On an ERC-1155 edition nothing auto-pings — ERC-4906 is 721-only — so after a URI/renderer re-point you must also run `abx ping-uri <addr> --token-ids <ids>`** to re-emit the native `URI` event. `abx refresh` says which case you're in. With `OPENSEA_API_KEY` it calls OpenSea directly; else it prints the links to click.
267
+ - **There is no "add it to a marketplace" step — don't offer one.** Marketplaces discover the collection from chain (the mint, plus ERC-4906 on a 721 / the native `URI` event on an edition); `abx refresh` — and `abx ping-uri` on an edition — is the only nudge. Suggesting a manual listing implies work that doesn't exist.
249
268
  - A `--no-mint` deploy reconstructs + serves normally (dashboard shows #0 "not yet minted"; metadata/image already resolve).
250
269
  - **Etherscan source is already verified** — your deploys are EIP-1167 clones Etherscan auto-recognizes as proxies of the verified implementation.
251
270
 
@@ -264,10 +283,11 @@ A token is **not "just a picture."** It anchors **named, typed files** ("artifac
264
283
  - **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**.
265
284
  - **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>`.
266
285
  - **`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.
267
- - **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).
286
+ - **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** (the EVM can't enumerate arbitrary FIELD 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).
287
+ - **PostParams are the exception — they need no resolver.** Params enumerate on-chain, so a bare `tokenURI` already carries every set value under **`abx_params`**. The line to give a creator: *attachments always need a resolver; params never do.*
268
288
  - **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)).
269
289
  - **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).
270
- - **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).
290
+ - **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 with **`abx tokenuri <addr> --fetch`** — the served document carries the `artifacts` manifest — not by refreshing OpenSea (which won't show it).
271
291
 
272
292
  ## After launch — tell the creator (durability + owner care)
273
293
 
@@ -279,24 +299,50 @@ Once it's live, cover these in plain language; don't wait to be asked.
279
299
 
280
300
  ## Setup + environment
281
301
 
282
- `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).
302
+ `abx` needs **Node ≥ 22.5** at runtime (the projection store uses built-in SQLite).
303
+
304
+ ### Find `abx` before you install it — local first, then global
305
+
306
+ **Resolve in this order and use the first hit.** Don't jump to a global install; a project-local CLI is pinned in the creator's `package.json` (reproducible, and what `abx skill install` version-locks against), so it wins whenever it exists:
307
+
308
+ ```bash
309
+ ./node_modules/.bin/abx version # 1. project-local — PREFER this (invoke as `npx abx <cmd>` once you know it's there)
310
+ abx version # 2. a global install already on PATH
311
+ ```
312
+
313
+ - **Probe the local binary by PATH, never with `npx abx`.** The bare `abx` name on npm is an **unrelated squatted package**, so a plain `npx abx` with nothing local *downloads that*. And `--no-install` does **not** make the probe safe or trustworthy: it only skips a *fresh download*, and npm will still happily run any `abx` binary sitting in the npx cache from an earlier run — which can be a stale version (a real sweep saw `--no-install` report a months-old build as the project's CLI) or, if a plain `npx abx` was ever run on that machine, the squatted package itself. `./node_modules/.bin/abx` is unambiguous: it exists in THIS project or it doesn't.
314
+ - In the **abx source repo** (contributor), neither applies: use `pnpm abx <cmd>`, which runs the live `tsx` source.
315
+
316
+ **Nothing found → install.** The package is **`@artblocks/abx-cli`** (not `@artblocks/abx-sdk` — that's the library, and installing it gets you no `abx` binary; a real session lost a cycle to exactly that mistake):
317
+
318
+ | Situation | Install | Then invoke as |
319
+ |---|---|---|
320
+ | The creator has a project dir (a `package.json`) — **default** | `npm install --save-dev @artblocks/abx-cli` | `npx abx …` |
321
+ | No project, or they asked for a machine-wide tool | `npm install -g @artblocks/abx-cli` | `abx …` |
322
+
323
+ Ask before installing **globally** — it's a machine-wide change to their PATH, and the per-project install is the reversible one. A project install needs no permission beyond the usual.
324
+
325
+ Whichever you land on, **keep using that same invocation for every command in the session** (`npx abx …` vs `abx …`) — don't mix them, or you'll silently drive two different CLI versions. Then run `abx doctor` (preflight: signing key/wallet, RPC, canonical factory, storage).
283
326
 
284
327
  `.env` (in the creator's project dir) = **secrets only**:
285
- - **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.
286
- - `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.
328
+ - **Signing:** `ABX_DEPLOYER_PK` (the only name read — older `SEPOLIA_FUNDED_PK`/`SEPOLIA_WALLET_PK` are retired, and the CLI's error names them if it finds neither) 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.
329
+ - `ABX_RPC_URLS`, `ABX_PUBLIC_BASE_URL` (remote-resolver custody only), optional `OPENSEA_API_KEY`, plus any backend secret.
330
+ - **One client-side credential grammar: `ABX_REMOTE_<NAME>_URL` + `ABX_REMOTE_<NAME>_TOKEN`** (same normalization as `ABX_RPC_URLS_<CHAIN>`) — a node you run is just the name **`self`** (`deploy-resolver` generates `ABX_REMOTE_SELF_TOKEN` locally; bare `--remote` defaults to it), a managed provider is its own name. `ABX_RESOLVER_ADMIN_TOKEN` is the **resolver's own server-side config** (never read client-side) — a stale copy of it in `.env` with no `ABX_REMOTE_SELF_TOKEN` gets a pointed error naming both vars, and `abx doctor` flags it too. Only `_URL`/`_TOKEN` are read — `ABX_REMOTE_<NAME>_KEY` is ignored (the CLI flags a near-miss name).
287
331
 
288
332
  <sub>Working from the abx **source repo** (contributor)? `pnpm install`, then `pnpm abx <cmd>` or `pnpm sandbox`. Every command below is identical.</sub>
289
333
 
290
- **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).
334
+ **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, ASK IT: `abx status <addr> [--remote <name>]`** reports the lifecycle (`queued`→`backfilling`→`live`, plus `stale`/`failed` with a machine-readable cause) — so "still catching up" and "broken" stop looking alike. Then **check the scan floor (is it scanning from block 0?) before the RPC tier** — that mis-diagnosis is a known trap. RPC deep-dive + troubleshooting → [reference/setup.md](reference/setup.md).
291
335
 
292
336
  ## Reference files
293
337
 
294
338
  - **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*.
339
+ - **Configuring a real launch — the six decisions in depth** (storage backends + who pays, public host URL + managed vs self-hosted, identity/credit/license, image placement + inline-vs-reader thresholds, on-chain vs off-chain resolution, store-vs-lock) → **[reference/decisions.md](reference/decisions.md)**
295
340
  - **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)**
296
341
  - **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)**
297
- - **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)**
342
+ - **Royalty enforcement / ERC-721C / "make OpenSea honor my royalties" ONLY when the creator raises it themselves** (plain ERC-721 is the default + recommendation; never offer enforcement unprompted) → **[reference/creator-token.md](reference/creator-token.md)**
343
+ - **Hosting infrastructure** (storage backends, Turbo lanes + failure playbook, **managed providers + named remotes + the service descriptor**, `deploy-resolver`, `deploy-effects`, local-vs-remote stores, token API routes, Docker) → **[reference/hosting.md](reference/hosting.md)**
298
344
  - **Environment detail** (RPC selection + failover, multi-chain, troubleshooting) → **[reference/setup.md](reference/setup.md)**
299
- - **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)**
345
+ - **Troubleshooting — "my NFT looks wrong"** (gray placeholder, stale-on-marketplace, tokenURI reverts, localhost baked, "not registered", **registered-but-serving-nothing / indexing status**) — diagnose before acting → **[reference/troubleshooting.md](reference/troubleshooting.md)**
300
346
 
301
347
  ## Guarantees
302
348