Grant a Sui address permission to decrypt your encrypted blobs. Two
granularities: store-level (every blob in the BlobStore) or per-blob
(one specific blob). The SDK reads both ACLs but doesn't write them yet —
build the PTBs yourself with @mysten/sui + the exported PACKAGE_ID.
Sharing only matters for encrypted blobs
Plaintext blobs are public-readable by blob_id — anyone with the id can
fetch the bytes. Sharing is a SEAL-decryption gate, so it only applies
to blobs uploaded with encrypted: true.
Option A — Bundle viewers into the upload tx
Cheapest. One transaction grants access at the moment of registration.
Each viewer becomes an add_blob_share(store, GlobalConfig, marker, viewer)
Move call appended to the same PTB as register_blob. Atomic — either
everything lands or nothing does.
Option B — Per-blob share after the fact
Build the PTB yourself:
import { Transaction } from "@mysten/sui/transactions";import { bcs } from "@mysten/sui/bcs";import { PACKAGE_ID, SHARED_OBJECTS } from "@waldrop/sdk";function hexToBytes(hex: string): Uint8Array { const clean = hex.startsWith("0x") ? hex.slice(2) : hex; const out = new Uint8Array(clean.length / 2); for (let i = 0; i < out.length; i++) { out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16); } return out;}const tx = new Transaction();tx.setSender(senderAddress);tx.moveCall({ target: `${PACKAGE_ID}::storage::add_blob_share`, arguments: [ tx.object(blobStoreId), tx.object(SHARED_OBJECTS.globalConfig), tx.pure(bcs.vector(bcs.u8()).serialize(Array.from(hexToBytes(sealMarker)))), tx.pure.address(viewerAddress), ],});await signer.signAndExecuteTransaction({ transaction: tx });
sealMarker is the 16-byte hex value from UploadBlobResult.sealMarker
(or BlobRef.sealMarker if you fetched the blob first). The contract uses
it as the allowlist key — the viewer can decrypt this blob but not the
rest of the store.
Option C — Store-level share (whole BlobStore)
Use this when a viewer needs access to every blob you ever store:
Per-blob shares aren't exposed via a dedicated method yet — they're stored
as a separate table keyed by seal_marker. Query the BlobStore object's
per_blob_shares field directly if you need it.
Revoking a viewer prevents futureseal_approve calls, but a viewer
who already pulled the key from the SEAL servers can keep decrypting. If
you need true revocation, re-encrypt under a fresh identity and upload
a new blob.
Granularity at a glance
01
Per-blob share
Keyed by 16-byte SEAL marker. Viewer can decrypt one specific blob.
02
Store-level share
Stored on the BlobStore. Viewer can decrypt every blob you'll ever
put there — past + future.