Keys & encryption
The crypto module is a hybrid integrated-encryption scheme (ephemeral key, then
ECDH, then HKDF-SHA256, then AEAD), built directly on @noble/curves,
@noble/ciphers, and @noble/hashes. It is the basis for sealing every
encrypted prism slot. The central type is DataPrismKey.
DataPrismKey
The cryptographic identity of a prism. Your application supplies it separately from the account that signs writes to the public registry. The dashboard derives its key from an account signature; SDK integrations can also supply an independently generated secret. Neither path sends the secret to DataPrism.
Constructors (static)
| Method | Description |
|---|---|
DataPrismKey.fromSecret(secret, config) | from a raw 32-byte secret (reduced to a valid scalar). The standard integration path. |
DataPrismKey.fromSignature(signature, config) | derives the secret from an account signature, hashed and reduced to a valid scalar. Deterministic. |
DataPrismKey.fromPublicKey(publicKey, config) | a public-only key: can encrypt, cannot open |
Connecting to a dashboard-created prism
Open your project's SDK tab in the dashboard and copy the Secret key (and the Project ID next to it). Use it to construct the encryption key:
import { DataPrismKey } from "@dataprism/sdk";
const key = DataPrismKey.fromSecret(secretKey, {
curve: "secp256k1", // shown in the SDK tab
algorithm: "aes-256-gcm", // shown in the SDK tab
});Whoever holds the Secret key can decrypt the prism's sealed slots, including sealed indexes. Scoped file contents additionally require their derivation scope keys. Keep the Secret key in your secret manager, never in source control or in browser-exposed configuration. Copying a dashboard-derived secret into a secret manager does not make it independent of the signing account or remove the original derivation path.
Supplying an independent prism key
For a new SDK-created prism, your application can supply a separately generated
32-byte secret from customer-controlled secret storage. Pass that key to
CloudStandard, and pass the transaction-signing account to the client:
import { CloudStandard, DataPrismClient, DataPrismKey } from "@dataprism/sdk";
const key = DataPrismKey.fromSecret(independentSecret, {
curve: "secp256k1",
algorithm: "aes-256-gcm",
});
const standard = new CloudStandard(key);
const client = new DataPrismClient(network, { accountClient });
const prism = client.prism(standard);
const { cloudId } = await prism.create({ fundAmount, salt, metadata });Here, independentSecret is not derived from accountClient. Possession of the
signing account alone does not reproduce that secret. Your application must
retain the prism key to read its sealed slots. For an existing prism, load its
existing key; generating a replacement does not decrypt or re-encrypt earlier
payloads.
An authorized writer can use a different execution account while sealing data for the same prism public key. This does not change prism ownership or rotate the encryption key. Keeping the signing account and prism secret in the same process also does not isolate them from a compromise of that process.
The derived path: from an account signature
fromSignature is how the dashboard itself produces the key, and your app can
do the same when the user's account is present, for example to avoid storing
any secret at all:
const sig = await accountClient.signMessage({ message: `dataprism:key:${cloudId}` });
const key = DataPrismKey.fromSignature(sig, { curve: "secp256k1", algorithm: "aes-256-gcm" });fromSignature produces the same key from the same signature bytes. Reproducing
the key therefore requires a signing implementation that returns the same
signature for the same message; not every account provider guarantees this.
Derive the message from the cloudId (default): it
is public and reproducible from the prism's id, so nothing per-prism is
stored. For extra security, mix in a password
(`dataprism:key:${cloudId}:${password}`): without it, anyone who steals
the account's private key could reproduce the signature and decrypt; with it,
decryption also requires the password, which is never stored.
Properties
| Property | Type | Note |
|---|---|---|
config | EncryptionConfig | curve plus algorithm |
curve | Curve | |
canOpen | boolean | false for a public-only key |
publicKeyHex | Hex | |
secretHex | Hex | throws if public-only |
Methods
| Method | Returns | Note |
|---|---|---|
seal(data, aad?) | Uint8Array | encrypt for this prism |
open(blob, aad?) | Uint8Array | decrypt (requires the secret) |
sealJson(value, aad?) | Hex | JSON to sealed hex |
openJson<T>(blob, aad?) | T | sealed hex to JSON |
toKeySlot() | KeySlot | { public, private: "" }; never serializes the secret |
const sealed = key.sealJson({ secret: "value" }); // Hex, safe to store on the network
const data = key.openJson<{ secret: string }>(sealed);Key-slot writes are public-key-only. The private member remains in KeySlot
so older slots can still be read, but the SDK no longer provides a publication
flag. Calls using the removed positional boolean throw instead of silently
changing behaviour.
Low-level functions
seal(config: EncryptionConfig, recipientPublicKey: Uint8Array, data: Uint8Array, aad?): Uint8Array
open(config: EncryptionConfig, recipientSecret: Uint8Array, blob: Uint8Array, aad?): Uint8ArrayThe blob layout is ephemeralKey ‖ nonce ‖ tag ‖ ciphertext. Because it is
AEAD, any tampering with a sealed payload is detected: open fails rather
than returning corrupt data.
Configuration
type Curve = "secp256k1" | "x25519" | "ed25519";
type Algorithm = "aes-256-gcm" | "xchacha20";
interface EncryptionConfig { curve: Curve; algorithm: Algorithm }
const DEFAULT_ENCRYPTION = { curve: "secp256k1", algorithm: "aes-256-gcm" };
assertEncryptionConfig(config): void // validates, or throwsAll six curve and algorithm combinations are supported and round-trip tested.
The chosen config is stored, in clear, in the prism's
dataprism.encryption slot so any reader
knows how to decrypt the rest.
Access control
A public-only key (fromPublicKey) can seal but cannot open: attempting
to decrypt a sealed slot throws. A writer can therefore encrypt new payloads
without holding the prism secret, although it still sees the plaintext it
submits. Opening sealed slots through
CloudReader requires the secret.
The public key does not grant permission to write to the public registry. The execution account must be the owner or an authorized writer. Conversely, a writer grant does not supply the prism secret or grant decryption access.

