@feralfile/cli 2.10.1 → 3.0.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.
package/docs/README.md CHANGED
@@ -29,7 +29,10 @@ ff-cli setup --non-interactive --key-file /run/secrets/ff-signing.key \
29
29
  ```
30
30
 
31
31
  `--key` and `--key-file` together is an error, and an empty or unreadable key file is refused rather
32
- than quietly generating a new identity for the machine. See
32
+ than quietly generating a new identity for the machine. Setup also never replaces a signing key that is
33
+ already configured: `--generate-key`, `--key`, and `--key-file` refuse when a different key is in place,
34
+ because that key owns every playlist you published (`publish --replace` and `unpublish` accept only it).
35
+ Add `--force` to replace it anyway. See
33
36
  [Where the signing key can come from](./CONFIGURATION.md#where-the-signing-key-can-come-from).
34
37
 
35
38
  See the full configuration reference here: `./CONFIGURATION.md`.
@@ -105,8 +108,9 @@ Both paths run the same deterministic pipeline: fetch metadata, assemble a DP-1
105
108
 
106
109
  ## Commands (cheat sheet)
107
110
 
108
- - `build [params.json]` – Deterministic build from JSON or stdin
111
+ - `build [params.json]` – Deterministic build from JSON or stdin. `ff-cli build --help` lists the requirement types and settings
109
112
  - Options: `-o, --output <file>`, `-v, --verbose`
113
+ - Exits 1 when any requirement finds nothing or fails, when named `tokenIds` do not all resolve, or when the send to `playlistSettings.deviceName` fails. The playlist is still saved when anything was collected; when nothing was, no file is written
110
114
  - `validate <file-or-url>` – Validate playlist structure only
111
115
  - `verify <file-or-url>` – Validate structure and verify signatures. On failure, the CLI labels structure issues separately from signature verification. dp1-js uses `--public-key` (or a key derived from `playlist.privateKey` / `PLAYLIST_PRIVATE_KEY` when omitted) **only** for legacy flat `signature` verification; DP-1 v1.1.0 `signatures[]` envelopes are verified without relying on that argument. If deriving or normalizing key material fails, the CLI prints a warning on stderr and continues without it (legacy verification still requires a usable key when the playlist uses a flat `signature`). The derived key is emitted as PEM. Supported key forms: hex with optional `0x`, PEM, or 32-byte raw public key as hex or base64
112
116
  - `sign <file>` – Sign playlist with a DP-1 v1.1.0 multi-signature envelope (private key string is forwarded to **`dp1-js`**; same hex or base64 PKCS#8 DER forms as `playlist.privateKey` in `./CONFIGURATION.md`). The command verifies the final envelope before writing output and refuses to persist tampered or otherwise unverifiable `signatures[]`.
@@ -116,14 +120,19 @@ Both paths run the same deterministic pipeline: fetch metadata, assemble a DP-1
116
120
  - Signing **appends** by default, so repeated runs accumulate endorsements. That is only correct while the signed content is unchanged: editing a signed playlist moves bytes the earlier signatures cover, and the envelope check then refuses to write the result. `--replace-signatures` discards every existing entry — including a feed's own co-signature — and signs the document as it stands. It is the required step before `publish --replace`
117
121
  - **An in-place run refuses when it would discard a still-valid signature from another key**, since only its holder could make that signature again: write the result elsewhere with `-o, --output`, which leaves the original where it is. The refusal is about what cannot be recovered — an in-place run still proceeds when the only entries dropped are your own, or ones that no longer verify
118
122
  - `--replace-signatures` names each discarded entry by role and the last 8 characters of its `kid`, and verifies it against the document as it now stands. Your own earlier signature is replaced by the same run. Another key's entry that **still verifies** — which is every entry when the content was not edited, since the signing payload excludes `signatures` — is reported as removed; keep the previous file if you want it back. An entry that does **not** verify is reported as exactly that and no more: the command has only the document as it stands, so it cannot tell an edit from an entry that was never valid, and it does not guess. Roles are carried through as found (`agent`, `institution` and `licensor` are as valid as `curator`), and a flat legacy `signature` is reported as not checkable, since it carries no key or role at all
119
- - `play <source>` – Play a playlist file, playlist URL, or media URL on an FF1 device (runs `verify` before sending; only the CLI-synthesized media URL fallback is auto-signed when a signing key is configured; use `--skip-verify` to bypass the gate)
120
- - Options: `-d, --device <name>`, `--skip-verify` (skip signature verification; not recommended)
123
+ - `play <source>` – Play a playlist file, playlist URL, media URL, or web page on an FF1 device (runs `verify` before sending; only the CLI-synthesized media/web URL fallback is auto-signed when a signing key is configured; use `--skip-verify` to bypass the gate)
124
+ - Any URL that does not load as a playlist is sent as a single item: a media file plays as media, and anything else plays as a web page
125
+ - A URL that answers 404 or 410, a host that does not resolve, and a `.json` URL that does not load as a playlist are refused, so a missing page is never cast as an error page. `--web` checks the first two as well. Other failures to read the URL (a 403 from bot protection, a 5xx, a timeout) still send it, with a note, because the device fetches it itself
126
+ - Marketplace links (Art Blocks, OpenSea, objkt, Feral File, …) are sent to `find`, which resolves the artwork. `--web` plays the page itself instead
127
+ - Options: `-d, --device <name>`, `--web` (play the URL as a web page, skipping the marketplace redirect), `--skip-verify` (skip signature verification; not recommended)
121
128
  - `find <input>` – Resolve a marketplace URL, raw `chain:contract:tokenId`, or wallet address into a playable DP-1 playlist
122
129
  - Sources: Art Blocks, Objkt, fxhash, OpenSea, SuperRare, Feral File, Neort, Verse, raw on-chain coordinates, and wallet addresses
123
130
  - OpenSea forms: `/assets/ethereum/{contract}/{tokenId}`, `/item/ethereum/{contract}/{tokenId}`, and `/collection/{slug}` (Ethereum collections; the slug is resolved from the public collection page, no API key needed)
124
131
  - Verse forms: `/items/ethereum/{contract}/{tokenId}` and `/series/{slug}`
125
132
  - Options: `-o, --output <path>`, `-l, --limit <n>`, `-p, --play`, `--no-play` (never cast; combine with `-y` to build and save without playing), `-d, --device <name>`, `--publish`, `-s, --server <index>`, `-y, --yes` (skip prompts; without `-o` or `--publish` it plays the playlist on a device, so add `--no-play` to skip casting), `--skip-verify`
126
- - `enrich <file>` – Add missing artist, title, and thumbnail metadata to a playlist that already exists. Resolves each item by its `provenance.contract` and writes an inline Ref Manifest; `source`, `duration`, `title`, and `id` are never touched. Drops the signature when it changes anything, so re-sign afterwards
133
+ - A wallet address picks one work by the artist at that address. For what a wallet owns, use `build` with a `query_address` requirement
134
+ - Without a terminal, pass `-y`: a question with no terminal to answer it fails with exit 1 and names the flag, rather than waiting or exiting 0 with nothing built
135
+ - `enrich <file>` – Add missing artist, title, and thumbnail metadata to a playlist that already exists. Resolves each item by its `provenance.contract` and writes an inline Ref Manifest; `source`, `duration`, `title`, and `id` are never touched. Drops the signature when it changes anything, so re-sign afterwards. With the opt-in Raster token source no still image is supplied, so `enrich` adds artist and description but no thumbnail; thumbnails come from the default indexer source
127
136
  - DP-1 `evm` names a chain family rather than a network, so those items are skipped unless `--assume-ethereum` asserts which one they are. Getting that wrong attaches another artwork's metadata
128
137
  - Options: `-o, --output <file>`, `--force` (replace existing manifests), `--assume-ethereum`, `-v, --verbose`
129
138
  - `publish <file>` – Publish a playlist to a feed server (runs `verify` before upload and rejects unsigned or broken playlists)
@@ -160,7 +169,8 @@ ff-cli find https://objkt.com/tokens/hicetnunc/111068 --play
160
169
  # Save without playing
161
170
  ff-cli find ethereum:0xababababab20053426ad1c782de9ea8444358070:5001410 -o send-receive.json
162
171
 
163
- # Wallet address (owner lookup); cap the number of tokens with --limit
172
+ # Wallet address: one work by the artist at that address; cap the number of tokens with --limit
173
+ # (for what a wallet owns, use build with a query_address requirement)
164
174
  ff-cli find 0xaeE022552B539dB18297D7481b6D547C622488B3 -l 10 -o wallet.json
165
175
 
166
176
  # Build and publish to a configured feed server
@@ -178,7 +188,7 @@ cat params.json | ff-cli build -o playlist.json
178
188
 
179
189
  `params.json` should include `requirements` and optional `playlistSettings`. See `examples/params-example.json`. Each requirement has a `type`:
180
190
 
181
- - `build_playlist` — `blockchain`, `contractAddress`, `tokenIds`, optional `quantity`
191
+ - `build_playlist` — `blockchain`, `contractAddress`, `tokenIds`, optional `quantity`. Omitting `tokenIds` asks for random tokens from the contract, which only the default indexer token source supports.
182
192
  - `feral_file_artwork` — `artworkId` (a Feral File public artwork id or `/exhibitions/artwork/{id}` URL)
183
193
  - `query_address` — `ownerAddress`, optional `quantity` (random selection)
184
194
  - `fetch_feed` — `playlistName`, `quantity`
@@ -211,8 +221,8 @@ copied from another document, or written before inline manifests existed has no
211
221
  and an empty tile in the app's grid — the app can draw a thumbnail from an
212
222
  image, video, or SVG source, but not from a live HTML work.
213
223
 
214
- `ff-cli enrich` fills that in from the indexer, keyed on each item's
215
- `provenance.contract`:
224
+ `ff-cli enrich` fills that in from the configured token source (the Feral
225
+ File indexer by default), keyed on each item's `provenance.contract`:
216
226
 
217
227
  ```bash
218
228
  ff-cli enrich playlist.json --assume-ethereum
@@ -222,8 +232,8 @@ ff-cli enrich playlist.json --assume-ethereum --force # replace existing manif
222
232
 
223
233
  `--assume-ethereum` is needed for most playlists. DP-1 records an EVM
224
234
  coordinate as `chain: "evm"`, which names Ethereum, Polygon, Arbitrum,
225
- Optimism, Base and Zora alike, and the indexer's answer collapses back to the
226
- same word. Enrichment will not pick one for you, because the wrong member
235
+ Optimism, Base and Zora alike, and the token source's answer collapses back to
236
+ the same word. Enrichment will not pick one for you, because the wrong member
227
237
  returns a different artwork's artist and still — into a document you then sign.
228
238
 
229
239
  It writes `inlineManifest` and nothing else. Items with no
@@ -265,13 +275,28 @@ npm run dev -- play playlist.json -d "Living Room Display"
265
275
  # Play a hosted DP-1 playlist
266
276
  npm run dev -- play "https://cdn.example.com/playlist.json"
267
277
 
268
- # Play a media URL directly
278
+ # Play a media URL or any web page directly
269
279
  npm run dev -- play "https://example.com/video.mp4"
280
+ npm run dev -- play "https://example.com/"
281
+
282
+ # Play a page on a marketplace site as a page (play would otherwise send it to find)
283
+ npm run dev -- play --web "https://feralfile.com/"
270
284
 
271
285
  # Skip signature verification only if you must send a non-conformant payload (not recommended)
272
286
  npm run dev -- play playlist.json --skip-verify
273
287
  ```
274
288
 
289
+ ### Owner remote maintenance
290
+
291
+ FFOS images containing `feral-tailscale` support opt-in persistent owner SSH on
292
+ port 2222 through Tailscale. It requires the owner's authorized computer and SSH
293
+ key and remains available independently of controld. The procedure checks for
294
+ a complete installation. If support is absent, check the normal firmware release
295
+ channel; enrollment waits until a version containing it is published and installed. Follow the [owner-access procedure](https://github.com/feral-file/ffos/blob/36e25cec84505271a7232d6936bb46a19fab5cfd/docs/OWNER_TAILSCALE.md)
296
+ for enrollment, diagnostics, revocation and validation. The normal `ff-cli ssh`
297
+ commands below continue to manage temporary support SSH on port 22; disabling
298
+ support SSH does not revoke persistent owner access.
299
+
275
300
  ### SSH access
276
301
 
277
302
  ```bash
@@ -287,8 +312,8 @@ ff-cli ssh disable -d "Living Room Display"
287
312
  ### Publish to feed server
288
313
 
289
314
  ```bash
290
- # Publish to first configured feed server
291
- npm run dev -- publish playlist.json
315
+ # Publish to the first configured feed server (-s is required when more than one is configured)
316
+ npm run dev -- publish playlist.json -s 0
292
317
 
293
318
  # Publish to specific server (if multiple configured)
294
319
  npm run dev -- publish playlist.json -s 0
@@ -442,7 +467,7 @@ Setup preserves existing devices when adding new ones. See selection rules and e
442
467
 
443
468
  ## Constraints
444
469
 
445
- - Max 20 items total across all requirements
470
+ - Per-requirement caps: `build_playlist` up to 20 with `tokenIds` (100 without), `query_address` up to 100 when `quantity` is a number (omitted or `"all"` takes the whole wallet), `fetch_feed` up to 20. Requirements add up; there is no total cap beyond DP-1's 1024 items
446
471
  - Per-source caps enforced in utilities
447
472
  - Per-item timing: when no duration is given, video/audio items carry **no** `duration` and `display.loop: false`, so the player advances at end-of-stream (DP-1 §4.1) — the media plays its natural length. Static items use `defaultDuration` (10s unless configured). An explicit duration always wins.
448
473
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "2.10.1",
3
+ "version": "3.0.0",
4
4
  "description": "CLI for building, signing, and playing DP-1 playlists of digital art on Feral File devices",
5
5
  "repository": {
6
6
  "type": "git",