Prisms & the Cloud Standard
A prism is an on-chain, namespaced vault dedicated to the index of your files (the reconstruction "routes"), not to the files themselves. The heavy data lives at your cloud providers; the prism only holds lightweight, encrypted, tamper-proof metadata.
Identity
A user's DataPrism identity is an Ethereum wallet address. Two entry points:
- Existing wallet: connect MetaMask (or any compatible wallet) through the frontend (viem / wagmi).
- PrismKey: for users without a wallet, DataPrism generates and manages one for them. The user never has to understand the blockchain.
That address serves as identity (prism ownership, request signatures), as the destination for
purchased credits, and, crucially, as the source of the encryption key (see
dataprism.key below).
Identifiers: protocolId & cloudId
Each prism has a bytes32 identifier, the protocolId: derived deterministically as
keccak256(DataPrismCloud address, salt). On the application layer, a shortened cloudId
(bytes16, the first 16 bytes of the protocolId) is used to reduce calldata cost.
Because the derivation is deterministic, the SDK can compute the cloudId offline
(computeCloudId(address, salt)). This is what lets a prism created through the gasless API
path know its ID immediately, without waiting for the relayer's confirmation.
Creating a prism
Creation goes through the SDK, which calls the on-chain creation function
(DataPrismCloud.createPrism). Two paths, depending on whether the user holds gas:
┌──────── does the user hold ETH? ────────┐
│ YES NO │
▼ ▼
direct contract call call to dataprism-api
(DataPrismCloud.createPrism) with a signed request
user pays the gas (EIP-712, gasless)
│ │
│ dataprism-relayer executes
│ the transaction on-chain
▼ ▼
prism created on-chain ◄───────────────────────┘The SDK's execution layer makes both paths transparent, the calling code is identical. At creation, a prism is provisioned with a set of standardized data slots.
When a prism is created with an initial balance (fundAmount > 0), funding and creation happen
in a single transaction: the SDK signs an EIP-2612 permit instead of a separate approve
(createPrismWithPermit on the wallet path, createPrismFor with PermitParams on the API
path). See Single-transaction funding.
Creation can also write an initial batch of data in the same atomic transaction, so a
prism is created and populated in one signature and one nonce increment (equivalent to a
createPrism immediately followed by batchWriteData, but atomic). The contract exposes:
createPrismWithData(fundAmount, salt, dataIds, payloads), on the wallet path.createPrismWithDataAndPermit(fundAmount, salt, dataIds, payloads, deadline, v, r, s), which also bundles the EIP-2612permitso a funded, populated prism is created with no separateapprove.createPrismWithDataFor(...)on the Forwarder for the gasless (API) path, and thecreatePrismWithDataAscallback that the Forwarder invokes.
Here fundAmount must cover the creation fee plus the per-entry data fees. The cloudId
derivation is unchanged and stays deterministic, so it is known offline immediately. In the SDK
this is the default behaviour of PrismManager.create(): it provisions the prism and writes the
standard slots (key, encryption, metadata, providers) in one transaction, and any extra
initialData entries are written in the same call. See the
execution layer for the createPrismWithData method.
The Cloud Standard
The Cloud Standard is a naming convention for the reserved slots of a prism, the same idea as
Ethereum's EIP/ERC standards, applied to cloud storage. Each slot is identified by a dataId =
keccak256("dataprism.xxx") (a bytes32). The stored value is always JSON serialized to
bytes; it is encrypted unless noted otherwise.
| Slot | dataId | Encrypted? | Holds |
|---|---|---|---|
dataprism.key | keccak256("dataprism.key") | clear | the prism's public key |
dataprism.encryption | keccak256("dataprism.encryption") | clear | curve + algorithm |
dataprism.metadata | keccak256("dataprism.metadata") | sealed | name, description, member addresses |
dataprism.providers | keccak256("dataprism.providers") | sealed | managed-storage service marker |
| file index | keccak256(filename) | sealed | per-file reconstruction root |
dataprism.key
{ "public": "", "private": "" }public: the prism's public key. Readable by anyone (it is used to encrypt for the prism), so this slot is stored in clear.private: the private key. Empty by default and never written on-chain.
The private key is not stored, it is derived on demand, client-side, from a wallet signature:
connected wallet
│ the user signs a deterministic message:
│ • dataprism:key:${cloudId} (default)
│ • dataprism:key:${cloudId}:${password} (extra security)
▼
signature (deterministic)
▼
= private key used by the SDK
▼
public key derived → written into dataprism.key.publicWhy the cloudId? It is public and known from the prism's id, so the key is
reproducible from the prism alone (no per-prism secret to store; works when listing
prisms by cloudId). Why the optional password? Deriving from the cloudId alone means
anyone who steals the wallet's private key could reproduce the signature and decrypt.
Adding a password (dataprism:key:${cloudId}:${password}) makes it a second factor:
decryption then also requires the password. The password is never stored, only mixed into
the signed message.
The exact signed message is dataprism:key:${cloudId} (default) or
dataprism:key:${cloudId}:${password} (with a password). The same message must be used
at creation and on every read, or the derived key will not match.
Optional public mode: if the user accepts that anyone can read their data, they may choose to write
privateon-chain. This is an explicit, opt-in "public" mode.
dataprism.encryption
{ "curve": "secp256k1", "algorithm": "aes-256-gcm" }curve:"secp256k1"|"x25519"|"ed25519"algorithm:"aes-256-gcm"|"xchacha20"
These parameters define how the key (above) encrypts and decrypts everything else (metadata, providers, file indexes). Stored in clear so a reader knows how to decrypt the rest.
dataprism.metadata
{
"name": "",
"description": "",
"addresses": { "0x...": "human-readable name" }
}name:description, prism information.addresses: the list of authorized owner / writer addresses, each mapped to a readable name.
This JSON is sealed (encrypted) with the parameters from dataprism.encryption.
dataprism.providers
A sealed JSON keyed by provider ID. New projects store only a public marker for DataPrism managed storage:
{
"dataprism_managed": { "service": "dataprism" }
}This marker contains no credentials. The signed-in user authorizes individual storage operations through the API, which returns short-lived URLs scoped to the project and object.
File index slots
In addition to the reserved slots above, each uploaded file creates a slot whose dataId is
the hashed filename. Its value is the (sealed) JSON of the root reconstruction index:
quorum, provider list, and a Merkle root (BlobRef) that points to the chunks/manifests
stored at the providers. This root stays tiny (≈ a few hundred bytes) regardless of file
size. See File Storage & Resilience.
Working with prisms in code
The SDK turns these concepts into a typed API, CloudStandard (encode/seal each slot),
CloudReader (read + decrypt in one step), and PrismManager (single-transaction creation that
also writes the standard slots, plus dedicated slot editors with a guard against overwriting
reserved slots). See the SDK Cloud Standard page.

