arkiv-images 1.0.2 → 1.0.4

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/AGENTS.md CHANGED
@@ -2,14 +2,14 @@ Edit instructions only in AGENTS.md; CLAUDE.md only imports this file.
2
2
 
3
3
  # Integrating arkiv-images
4
4
 
5
- Read [README.md](README.md) explicitly for the public API, runnable local example, data model, limits, signing and recovery. Install version 1.0.2 with `npm install arkiv-images@1.0.2 @arkiv-network/sdk@1.0.0 viem@2.56.3`.
5
+ Read [README.md](README.md) explicitly for the public API, runnable local example, data model, limits, signing and recovery. Install version 1.0.4 with `npm install arkiv-images@1.0.4 @arkiv-network/sdk@1.0.0 viem@2.56.3`.
6
6
 
7
7
  - Ask whether the task needs local inspection, retrieval or a new public upload. Inspection requires no network. Retrieval needs an image key, Rabdi and optionally an independently trusted SHA-256. Writes additionally require a locally configured signing wallet and test funds.
8
8
  - Ask whether original bytes, filename and metadata are safe to publish. EXIF/GPS is preserved. Stop if confidentiality is required; encryption is a different tool.
9
9
  - Use Rabdi chain 7794095 with SDK 1.0.0. Ask for an explicit Entity Expiration within the admitted block range. Access keys and test funds come from the Hub links in README. Never request a private key in chat.
10
10
  - Import only public package APIs. Reuse arkiv-chunking's publicly exported write policy; no private dependency imports or copied storage/chunking/gas logic. Bytes use non-indexed bytes columns; query only indexed typed metadata with SDK predicates.
11
11
  - Preserve MIME/size/network/integrity/decode checks and public TypeScript types. A browser must decode before upload and after retrieval, render only PNG/JPEG, treat filenames as text and revoke Blob URLs.
12
- - Every write uses unsigned latest-state estimation, explicit gas/price/latest nonce and at least twice estimate, below half the head block gas limit. Do not use unlimited lifetime or historical state reads. Never automatically retry successful or uncertain writes.
12
+ - Every write uses unsigned latest-state estimation, explicit gas/price/latest nonce and at least twice estimate, at or below half the head block gas limit. Do not use unlimited lifetime or historical state reads. Never automatically retry successful or uncertain writes.
13
13
  - Serialize account writes. Recheck browser wallet identity/network before each signature, including the final root. Confirm completion only after storeImage resolves. Preserve error imageKey/manifestKey/transactionHashes, including CONFIRMATION_PENDING. Never display raw provider errors or causes.
14
14
  - No independent part mutation, transfer or extension. Root existence does not prove chunk availability; expiration does not erase historical copies.
15
15
  - Sample reads keep the access key server-side in its own endpoint. Browser writes use the visitor's configured wallet RPC; no shared server signer.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.4
4
+
5
+ - Pin arkiv-chunking 1.1.3 so every inherited write enforces half the fresh latest block gas limit, including the single allowed retry.
6
+
7
+ ## 1.0.3
8
+
9
+ - Depends on arkiv-chunking 1.1.2 for the corrected shared write policy, which preserves an uncertain submission hash without resending.
10
+
3
11
  ## 1.0.2
4
12
 
5
13
  - Use the long-lived JPEG example, filter expired examples by current block, align VERSION guidance and verify archives using package.json.
package/README.md CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  Store a public PNG or JPEG as queryable Arkiv entities, then retrieve its original bytes with size, MIME, dimensions and SHA-256 checks. Small images use one entity with a non-indexed `bytes()` column. Larger images use `arkiv-chunking`: an image entity references a file manifest and ordered immutable chunks.
4
4
 
5
- **arkiv-images 1.0.2 targets Rabdi testnet.** It stores PNG/JPEG binary data in `bytes` columns. See [verification](docs/verification.md) for reproducible checks and separately labelled historical observations.
5
+ **arkiv-images 1.0.4 targets Rabdi testnet.** It stores PNG/JPEG binary data in `bytes` columns. See [verification](docs/verification.md) for reproducible checks and separately labelled historical observations.
6
6
 
7
7
  ## Install and inspect
8
8
 
9
- ESM, Node 22.10+ with Web Crypto, or a browser on HTTPS/localhost. Tested dependencies: SDK 1.0.0, viem 2.56.3, arkiv-chunking 1.1.0.
9
+ ESM, Node 22.10+ with Web Crypto, or a browser on HTTPS/localhost. Dependencies: SDK 1.0.0, viem 2.56.3, arkiv-chunking 1.1.3.
10
10
 
11
11
  ```sh
12
12
  mkdir image-consumer
13
13
  cd image-consumer
14
14
  npm init -y
15
15
  npm pkg set type=module
16
- npm install arkiv-images@1.0.2 @arkiv-network/sdk@1.0.0 viem@2.56.3
16
+ npm install arkiv-images@1.0.4 @arkiv-network/sdk@1.0.0 viem@2.56.3
17
17
  ```
18
18
 
19
19
  Save `inspect.mjs`:
@@ -76,7 +76,7 @@ Run `node roundtrip.mjs` after configuring those environment variables locally.
76
76
  | `ImageSchema`, `IMAGE_TYPE` | SDK 1.0 named schema `arkiv-images/v2`. Binary `data` is stored without an index. Indexed type/mode/mime/sha256/manifest support discovery. Filename, size and dimensions are stored typed columns. |
77
77
  | `ImageStorageError`, `ChunkingError` | Safe code/message; preserve imageKey/manifestKey and transactionHashes on failure. Raw causes can include provider details: never display or log them. |
78
78
  | `TransactionParameterPolicy`, `Progress` | Public TypeScript integration types re-exported from chunking. |
79
- | `VERSION`, `INLINE_MAX_BYTES`, `MAX_IMAGE_BYTES`, `MAX_IMAGE_PIXELS`, `MAX_EXPIRATION_BLOCKS`, `RABDI_MAX_LIFETIME_BLOCKS`, `EXPIRATION_INCLUSION_MARGIN_BLOCKS` | Implementation version (`1.0.2`) and enforced limits. |
79
+ | `VERSION`, `INLINE_MAX_BYTES`, `MAX_IMAGE_BYTES`, `MAX_IMAGE_PIXELS`, `MAX_EXPIRATION_BLOCKS`, `RABDI_MAX_LIFETIME_BLOCKS`, `EXPIRATION_INCLUSION_MARGIN_BLOCKS` | Implementation version (`1.0.4`) and enforced limits. |
80
80
 
81
81
  Import types such as `ImageInfo`, `ImagePlan`, `StoreImageOptions`, `StoredImage`, `RetrieveImageOptions`, `RetrievedImage`, `ImageContentType` and `ImageErrorCode` directly from `arkiv-images`.
82
82
 
@@ -88,7 +88,7 @@ Entity Expiration is whole blocks, at least `max(64, transactionCount + 32)` and
88
88
 
89
89
  Every part and the final root reuse the public `arkiv-chunking` write policy: an unsigned SDK request is estimated at `latest`; gas is at least `max(1_000_000, 2 × estimate)`, with explicit network gasPrice and a fresh latest nonce. The image API additionally stops before signing if that budget exceeds half the latest block gas limit. Longer expiration can substantially increase gas, so shorten Entity Expiration or choose a smaller image if the estimate exceeds that budget. Admitted byte/lifetime limits do not guarantee every combination fits the network's gas budget. `txParams` can increase gas or change fees; it cannot remove required headroom or override the latest nonce. Wallet account/network are checked before every signature.
90
90
 
91
- Serialize writes from the same account. A retry is permitted only once after an authoritative gas-exhausted reverted receipt and only within the half-block gas ceiling. Successful or uncertain writes are never automatically resent. `CONFIRMATION_PENDING` preserves SDK-success hashes and root/manifest keys if a later receipt lookup is unresolved or disagrees. The operation enters `submitted` before that lookup. Browser consumers must keep a `createImageWriteOperation` object across renders and later clicks; `storeImage` is a one-call convenience for an intentional new upload. Inspect preserved receipts before explicitly creating another operation. The sample resets the operation only when you choose a file again. Other failures can also leave confirmed entities. There is no multi-transaction atomicity, rollback, resumable upload, independent part mutation/transfer/extension or automatic cleanup.
91
+ Serialize writes from the same account. A retry is permitted only once after an authoritative gas-exhausted reverted receipt and only within the half-block gas ceiling. Successful or uncertain writes are never automatically resent. `CONFIRMATION_PENDING` preserves a known submission hash, including one recovered by the shared policy from a wrapped SDK error, plus any available root/manifest keys. SDK-success results enter `submitted` before the receipt recheck; a known-hash SDK error enters `submitted` when the shared policy returns its unresolved confirmation. Observed hashes are recovery evidence, not proof of successful receipts. Browser consumers must keep a `createImageWriteOperation` object across renders and later clicks; `storeImage` is a one-call convenience for an intentional new upload. Inspect preserved receipts before explicitly creating another operation. The sample resets the operation only when you choose a file again. Other failures can also leave confirmed entities. There is no multi-transaction atomicity, rollback, resumable upload, independent part mutation/transfer/extension or automatic cleanup.
92
92
 
93
93
  Retrieval uses latest-state keyset pages and verifies immutable chunks, owner/creator, sequence, lengths, metadata and digest; the manifest is rechecked before return. It does not claim a historical snapshot. An independently trusted digest pins bytes, not the publisher's identity. Missing/corrupt chunks never return a partial image.
94
94
 
@@ -109,7 +109,7 @@ The package checks containers, not full PNG CRC/JPEG entropy decoding or malware
109
109
 
110
110
  ## Sample and development
111
111
 
112
- The [sample README](sample/README.md) explains the existing `arkiv-images-example` project and visitor-wallet setup. Reads use its own bounded `/api/rpc` endpoint and a server-side `ARKIV_ACCESS_KEY`; the visitor configures their own wallet RPC. No server signer. The sample pins the released registry package `arkiv-images@1.0.0`.
112
+ The [sample README](sample/README.md) explains the existing `arkiv-images-example` project and visitor-wallet setup. Reads use its own bounded `/api/rpc` endpoint and a server-side `ARKIV_ACCESS_KEY`; the visitor configures their own wallet RPC. No server signer. The sample pins the released registry package `arkiv-images@1.0.3`.
113
113
 
114
114
  Give consumer agents [AGENTS.md](AGENTS.md) and [sample/AGENTS.md](sample/AGENTS.md) explicitly. The corresponding CLAUDE.md files only import those guides.
115
115
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type Column, type Schema, type TypeTag } from '@arkiv-network/sdk/schema';
2
2
  import { type TransactionParameterPolicy, type ChunkingPublicClient, type ChunkingWalletClient, type Progress } from 'arkiv-chunking';
3
3
  import { type Hex } from 'viem';
4
- export declare const VERSION = "1.0.2";
4
+ export declare const VERSION = "1.0.4";
5
5
  export declare const IMAGE_TYPE = "arkiv-images/v2";
6
6
  type Stored<T extends TypeTag> = Column<T, false, false, false>;
7
7
  type Indexed<T extends TypeTag> = Column<T, true, false, false>;
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { bytes as binary, defineSchema, key, str, u64 } from '@arkiv-network/sdk
3
3
  import { eq } from '@arkiv-network/sdk/query';
4
4
  import { uploadFile, downloadFile, hashFile, ChunkingError, DEFAULT_CHUNK_BYTES, MAX_EXPIRATION_BLOCKS, writeWithPolicy, WriteConfirmationPendingError } from 'arkiv-chunking';
5
5
  import { hexToBytes } from 'viem';
6
- export const VERSION = '1.0.2';
6
+ export const VERSION = '1.0.4';
7
7
  export const IMAGE_TYPE = 'arkiv-images/v2';
8
8
  export const ImageSchema = defineSchema(IMAGE_TYPE, {
9
9
  type: str().indexed(), mode: str().indexed(), mime: str().indexed(), sha256: str().indexed(),
@@ -234,6 +234,12 @@ async function executeStoreImage(options, submitted, observed) {
234
234
  return { ...info, imageKey: stored.entityKey, manifestKey, mode: plan.mode, sha256, transactionHashes };
235
235
  }
236
236
  catch (cause) {
237
+ // The shared policy can recover a submission hash from a wrapped SDK error.
238
+ // Retain that typed pending evidence without treating it as a successful receipt.
239
+ if (cause instanceof WriteConfirmationPendingError || (cause instanceof ChunkingError && cause.code === 'CONFIRMATION_PENDING')) {
240
+ if (hex32(cause.txHash))
241
+ rememberHash(cause.txHash);
242
+ }
237
243
  if (cause instanceof ChunkingError) {
238
244
  manifestKey = cause.manifestKey ?? manifestKey;
239
245
  cause.transactionHashes.forEach(rememberHash);
@@ -241,7 +247,7 @@ async function executeStoreImage(options, submitted, observed) {
241
247
  if (cause instanceof ImageStorageError && !transactionHashes.length)
242
248
  throw cause;
243
249
  const pending = cause instanceof WriteConfirmationPendingError || (cause instanceof ChunkingError && cause.code === 'CONFIRMATION_PENDING');
244
- throw new ImageStorageError(pending ? 'CONFIRMATION_PENDING' : 'UPLOAD_FAILED', pending ? 'The SDK reported a successful write, but receipt confirmation is pending. Preserve the image/manifest keys and hashes; inspect them using reads and never resend automatically.' : 'Upload did not complete. Inspect the wallet and available transaction receipts before retrying; any previously written entities remain.', { cause, manifestKey, imageKey, transactionHashes });
250
+ throw new ImageStorageError(pending ? 'CONFIRMATION_PENDING' : 'UPLOAD_FAILED', pending ? 'A submitted write has unresolved receipt confirmation. Preserve the image/manifest keys and hashes; inspect them using reads and never resend automatically.' : 'Upload did not complete. Inspect the wallet and available transaction receipts before retrying; any previously written entities remain.', { cause, manifestKey, imageKey, transactionHashes });
245
251
  }
246
252
  }
247
253
  function attr(attributes, name, type) {
@@ -1,4 +1,4 @@
1
- # Verify arkiv-images 1.0.2
1
+ # Verify arkiv-images 1.0.4
2
2
 
3
3
  Use Node 22.10+ on the DGX. From this checkout run `npm ci --no-audit --no-fund`, then `node scripts/verify-candidate.mjs`. The script reads package.json, runs library gates, packs that exact version, installs it in a private clean consumer, checks runtime VERSION and public TypeScript imports, and builds an isolated copy of the sample using the same archive. It preserves the sample registry pin and lockfile.
4
4
 
@@ -6,7 +6,7 @@ To check a registry installation of the same version, run `node scripts/verify-c
6
6
 
7
7
  The public example selects the longer-lived inline JPEG `0x2989bda8bbadfe7e22a0b1e3a435aa3b266106adbbca3f97c162ee4eccca8488` and filters entries using the current block. The short-lived PNG and chunked examples in the old measurements have expired; those measurements are not an availability promise.
8
8
 
9
- > Historical 1.0.0 measurements and procedure below are preserved from 51f4905; use the current procedure above for 1.0.2.
9
+ > Historical 1.0.0 measurements and procedure below are preserved from 51f4905; use the current procedure above for 1.0.4.
10
10
 
11
11
  # Rabdi release verification
12
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkiv-images",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Store public PNG and JPEG images as Arkiv entities and retrieve verified original bytes.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -41,7 +41,7 @@
41
41
  "prepack": "npm run build"
42
42
  },
43
43
  "dependencies": {
44
- "arkiv-chunking": "1.1.0"
44
+ "arkiv-chunking": "1.1.3"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@arkiv-network/sdk": "^1.0.0",
package/sample/AGENTS.md CHANGED
@@ -4,7 +4,7 @@ Edit instructions only in AGENTS.md; CLAUDE.md only imports this file.
4
4
 
5
5
  Read README.md here and the package README/AGENTS.md before changing the sample. Preserve its existing visual design and English Arkiv copy.
6
6
 
7
- - Run `npm ci` in sample; it pins the registry release arkiv-images 1.0.0, SDK 1.0.0 and chunking 1.1.0. Never import sibling source or duplicate package storage, chunking or transaction policy.
7
+ - Run `npm ci` in sample; it pins the registry release arkiv-images 1.0.3, SDK 1.0.0 and chunking 1.1.2. Never import sibling source or duplicate package storage, chunking or transaction policy.
8
8
  - Retrieval is read-only through /api/rpc. Set ARKIV_ACCESS_KEY server-side only; no browser access key, arbitrary RPC destination, signing method, batch request, historical-state query or raw provider error may pass the proxy.
9
9
  - Browser writes use MetaMask/Rabby and the visitor's account/Rabdi RPC. Ask the visitor to configure their own key in their wallet if Rabdi is missing. Never request a private key or add a server signer. Guard account/network before each signature; never switch halfway through a write.
10
10
  - Keep the selected file's createImageWriteOperation across clicks/re-renders, including failed or uncertain results. Setup runs after its sending guard is stored. Only an explicit new file choice creates another upload operation. Preserve submitted hashes before receipt rechecks; only an authoritative gas-exhausted revert permits the shared policy's one bounded retry.
package/sample/README.md CHANGED
@@ -1,9 +1,11 @@
1
1
  # Image sample — Rabdi
2
2
 
3
- This sample uses the registry release **arkiv-images 1.0.0** and keeps the image picker, local-date Entity Expiration, byte verification, download, entity inspector, dark/light themes and wallet selection. All copy is English. It targets the existing **arkiv-images-example** Vercel project at https://arkiv-images-example.vercel.app.
3
+ This sample uses the registry release **arkiv-images 1.0.3** and keeps the image picker, local-date Entity Expiration, byte verification, download, entity inspector, dark/light themes and wallet selection. All copy is English. It targets the existing **arkiv-images-example** Vercel project at https://arkiv-images-example.vercel.app.
4
4
 
5
5
  The UI uses the package public API. Reads go through its own bounded `/api/rpc` function with server-only `ARKIV_ACCESS_KEY`. Only chain ID, current block number, block timing and bounded image/chunk queries are accepted; no signer, access key, upstream URL or raw provider error is sent to the browser. Root and manifest reads are latest state; chunks use keyset pages. Set a free [Hub access key](https://hub.arkiv.network/access-keys) in the Vercel preview environment.
6
6
 
7
+ The read function rejects a foreign Origin host or cross-site Fetch Metadata before contacting Rabdi. POST requests require `Content-Type: application/json`; JSON clients without Origin remain supported. These checks limit browser cross-origin quota use; they do not authenticate arbitrary HTTP clients.
8
+
7
9
  Writes use the visitor's MetaMask/Rabby account and configured Rabdi RPC, chain 7794095. If Rabdi is missing, configure it in the wallet using your own access key, then connect again. The app switches to an already configured Rabdi network; it never installs a server key in a wallet. Obtain [test funds](https://hub.arkiv.network/faucet). Every actual signature is guarded for account/network changes and uses the package's explicit gas policy.
8
10
 
9
11
  ## Build the sample
@@ -15,7 +17,20 @@ npm ci
15
17
  npm run build
16
18
  ```
17
19
 
18
- The sample pins `arkiv-images` to exact registry version `1.0.0`; its lock verifies the published archive integrity. No local tarball is used or uploaded. Deploy the sample folder to the existing linked project; its publication allowlist includes the application source and API function.
20
+ The sample pins `arkiv-images` to exact registry version `1.0.3`; its lock verifies the published archive integrity. Its direct chunking dependency is pinned to registry version 1.1.2. No local tarball is used or uploaded.
21
+
22
+ ## Deploy to the existing project
23
+
24
+ Use the existing **santiago-hobby/arkiv-images-example** Vercel project. The CLI uploads **sample/** as the project root; the project Root Directory setting is **.** relative to that upload. From the repository root:
25
+
26
+ ```sh
27
+ vercel link --cwd sample --project arkiv-images-example --scope santiago-hobby
28
+ vercel deploy --cwd sample --prod --scope santiago-hobby
29
+ ```
30
+
31
+ Vercel builds remotely from the uploaded sample source. Its `vercel.json` selects Vite, runs `npm ci` and `npm run build`, and serves `dist/` together with the Node function `api/rpc.mjs`. This route does not use `--prebuilt` or upload a local `.vercel/output`. The anchored `.vercelignore` allowlist includes the manifest, genuine lock, TypeScript config, HTML, Vercel config, `src/**`, `public/**` and `api/**`; it excludes local environment files and dependency/build directories.
32
+
33
+ Configure `ARKIV_ACCESS_KEY` only in this existing project's server environment, using the sample read key. It is read by `api/rpc.mjs`; Vite receives no access key. Versioned [package documentation](https://github.com/SantiagoDevRel/arkiv-images/tree/v1.0.3#readme) and the [package agent guide](https://github.com/SantiagoDevRel/arkiv-images/blob/v1.0.3/AGENTS.md) match the installed release. `node scripts/sample-release-guard.mjs` from the repository root checks versioned sample links and the remote-build upload contract; `npm test` executes its negative mutations.
19
34
 
20
35
  The Vite development server alone does not serve Vercel functions. Use a Vercel preview for the complete real-network read flow; controlled browser tests route `/api/rpc` to the in-memory harness. Never put an access key in Vite environment variables.
21
36
 
@@ -30,3 +45,5 @@ The existing layout mirrors the project's Arkiv token/type contract. This migrat
30
45
  The locked build tools have two high-severity npm audit findings: `vite` and `source-map-js`, both development dependencies. See the [Windows editor middleware advisory](https://github.com/advisories/GHSA-587h-7wmj-h5pm) and [source-map offset advisory](https://github.com/advisories/GHSA-68fv-2mgg-jv7q). The deployment serves built assets and a Node read function, not a Vite development server. These existing tool versions were not upgraded in the migration; the full dependency audit is not clean. Update the affected build tools before exposing a development server or accepting untrusted source maps.
31
46
 
32
47
  The public example defaults to the longer-lived JPEG, entity `0x2989bda8bbadfe7e22a0b1e3a435aa3b266106adbbca3f97c162ee4eccca8488`. The example list reads the current Rabdi block and skips expired entries. Its button stays unavailable if timing/configuration fails or no active example exists. A second head read rejects expiry before retrieval. No seed or write is performed.
48
+
49
+ Origin must be its exact canonical `scheme://host[:port]` serialization, with no credentials, path, query or fragment. The expected origin uses the request Host and HTTPS; only `localhost` and `127.0.0.1` development hosts use HTTP. Forwarded scheme/host headers cannot change this policy. Cross-site Fetch Metadata and POST JSON checks still apply.