> ## Documentation Index
> Fetch the complete documentation index at: https://docs.everabyte.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js quickstart

> Encrypt files client-side in Node.js with the prebuilt WASM binding and store them zero-knowledge through the Everabyte REST API — no Rust toolchain needed.

Node.js consumes the **prebuilt WASM binding** committed in the [everabyte/crypto-sdk](https://github.com/everabyte/crypto-sdk) repository — no Rust toolchain, no native library.

Run this in a trusted Node.js service, CLI or worker process. Keep the Everabyte secret API key in server-side secret storage; do not reuse these API calls in browser code. The `seal_all` and `open_all` WASM helpers operate on complete buffers, so they require memory for the input and output file.

## 1. Install

```bash theme={null}
# npm ≥ 9.4 — direct from GitHub, prebuilt artifacts included
npm install everabyte/crypto-sdk#path:bindings/wasm/pkg-node

# or clone + vendor
git clone https://github.com/everabyte/crypto-sdk.git
cp -r crypto-sdk/bindings/wasm/pkg-node ./vendor/evb-e2ee-wasm
```

```js theme={null}
import fs from "node:fs/promises";
import init, {
  generate_umk,
  umk_public_keys,
  wrap_umk_with_mnemonic,
  wrap_dek_to_umk,
  unwrap_dek_from_umk,
  evb1_seal_all,
  evb1_open_all,
  generate_random_12,
  generate_random_32,
  derive_wire_alias,
  encrypt_file_name,
  decrypt_file_name,
} from "evb-e2ee-wasm";

await init();
```

```js theme={null}
const headers = {
  "X-Public-Key": process.env.EVB_PUBLIC_KEY,
  "X-Secret-Key": process.env.EVB_SECRET_KEY,
};
async function checkedFetch(url, options = {}) {
  const response = await fetch(url, { ...options, headers: { ...headers, ...options.headers } });
  if (!response.ok) throw new Error(`Everabyte request failed: HTTP ${response.status}`);
  return response;
}
```

## 2. One-time: generate the account key and enroll

```js theme={null}
const umk = generate_umk(); // 96 bytes — store ONLY in your own secure storage
const publics = JSON.parse(umk_public_keys(umk.subarray(0, 32), umk.subarray(32)));
const mnemonic = /* fresh 12-word BIP-39 phrase shown to the user ONCE */;
const stepUpToken = await obtainStepUpTokenFromYourApplication(); // account step-up flow
const blob = wrap_umk_with_mnemonic(umk.subarray(0, 32), umk.subarray(32), mnemonic);

const registerResponse = await checkedFetch("https://api.everabyte.io/api/e2ee/keys/register", {
  method: "POST",
  headers: {
    "X-Public-Key": process.env.EVB_PUBLIC_KEY,
    "X-Secret-Key": process.env.EVB_SECRET_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    publicKeyX25519: publics.publicKeyX25519,
    publicKeyMlkem: publics.publicKeyMlkem,
    wraps: [{ wrapKind: "mnemonic", wrappedBlob: blob }],
    stepUpToken, // account step-up flow
  }),
});
const registerResult = await registerResponse.json();
```

## 3. Encrypt → upload → attach envelope

```js theme={null}
const plaintext = await fs.promises.readFile("report.pdf");
const dek = generate_random_32();
const fileNonce = generate_random_12();
const payload = evb1_seal_all(dek, fileNonce, plaintext);

const wireName = derive_wire_alias(umk.subarray(0, 32), umk.subarray(32), "report.pdf");
const encryptedName = encrypt_file_name(dek, "report.pdf", generate_random_12());
const envelope = JSON.parse(wrap_dek_to_umk(
  dek,
  Buffer.from(publics.publicKeyX25519, "base64"),
  Buffer.from(publics.publicKeyMlkem, "base64"),
));

const form = new FormData();
form.append("files", new Blob([payload]), wireName);
const bucketId = "BUCKET_ID";
await checkedFetch(`https://api.everabyte.io/api/files/upload/${bucketId}`, {
  method: "POST",
  body: form,
});

const attach = await checkedFetch("https://api.everabyte.io/api/e2ee/files/by-path/envelope", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ bucketId, fileName: wireName, encryptedName, ...envelope }),
});
const { fileId } = (await attach.json()).data; // returned by the by-path envelope endpoint
```

The upload and envelope attachment are two separate requests. If the request times out, first reconcile whether it succeeded with `GET /e2ee/files/{fileId}/envelope` when you have a file ID. If attachment definitively fails, the E2EE upload contract requires deleting the orphaned ciphertext row. For the by-path flow, resolve its `fileId` from the bucket's file listing using the opaque wire name, confirm that no envelope is attached, then hard-delete it with the file API. Retention policy may prevent deletion; handle that response explicitly. Never blindly delete after a timeout because the attach may have committed.

## 4. Download → decrypt

```js theme={null}
const env = (await (await checkedFetch(`https://api.everabyte.io/api/e2ee/files/${fileId}/envelope`)).json()).data;
const stored = new Uint8Array(await (await checkedFetch(`https://api.everabyte.io/api/files/${fileId}/stream?download=true`)).arrayBuffer());

const dek = unwrap_dek_from_umk(JSON.stringify(env), umk.subarray(0, 32), umk.subarray(32));
const original = evb1_open_all(dek, stored); // === plaintext, byte-identical
const realName = decrypt_file_name(dek, env.encryptedName);
```

## Next

* [TypeScript quickstart](https://docs.everabyte.com/sdk/quickstart-typescript.md) — browsers, bundlers, Workers.
* [API reference](https://docs.everabyte.com/sdk/api-reference.md) — every export.
* Remember the [mandatory-SDK rule](https://docs.everabyte.com/sdk/overview.md#mandatory-for-new-accounts): on zero-knowledge-default accounts, uploads without an envelope attached within the grace window are swept.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.