Quickstart
This guide demonstrates how to build a passwordless notes application using nilDB.
We will be using secretvaults for private storage, MetaMask for identity, and Nillion's Network User Credentials (NUC) for secure, decentralized authentication.
What You'll Build
In this quickstart, you'll create a simple notes app that requires login via MetaMask and learn:
- Builder setup: You (as a builder/application) will register for a Nillion API key
- Create a standard collection: Define a standard collection with an encrypted content field
- Wallet-scoped data storage: Store and manage a user's notes with create, read, update, and delete (CRUD) operations
This showcases Nillion's core value: builders can preserve privacy by default while storing data securely across multiple nilDB nodes.
What You'll Learn
- How builders create standard collections with encrypted fields
- How users authenticate with MetaMask and get a DID-backed NUC session
- How sessions persist via root token + invocation tokens
- How to use
%share(schema) and%allot(write payload) correctly - How the @nillion/secretvaults library handles encryption and share distribution automatically
Code Reference
- The completed full tutorial code is here.
1. Prerequisites
- MetaMask browser extension
- Active
nilDBsubscription and API key - Node.js 20+ and pnpm
Get your nilDB subscription and API key
You only need an EVM wallet (MetaMask) for this flow.
2. Project Setup
Clone the demo repository.
git clone https://github.com/NillionNetwork/blind-module-examples
cd blind-module-examples/nildb/secretvaults-ts/standard-collection/nextjs-app-metamask-full
pnpm install
pnpm run dev
Project Structure
src/
app/
layout.tsx
globals.css
page.tsx
profile/page.tsx
components/
Notes.tsx
ProtectedRoute.tsx
context/
AuthFlowManager.tsx
LogContext.tsx
NillionContext.tsx
NillionState.ts
hooks/
useBuilderProfileQuery.ts
useProfile.ts
useNillion.ts
usePersistedConnection.ts
useSessionQuery.ts
useInitializeSessionMutation.ts
useLoginMutation.ts
useNillionClient.ts
useNotesCollection.ts
useNotes.ts
config.ts
This is the project structure you'll use.
Config file
This configuration is required to connect to the three nilDB nodes.
loading...
3. MetaMask + Session Flow
We subscribed to the nilPay platform using a MetaMask account, so now we want to sign in with that account. This requires interfacing with a web3 wallet and is primarily done in src/context/NillionContext.tsx.
We also use the viem library to detect and prove ownership of the account.
Key Concept:
Signer.fromWeb3() wraps MetaMask's signing capability so Nillion can use it for NUC token creation.
loading...
Relevant files:
src/hooks/useInitializeSessionMutation.ts:
initializeSession():- Creates
NilauthClient - Checks
subscriptionStatus - Builds
SecretVaultBuilderClient.from({ blindfold: { operation: "store" } }) - Calls
refreshRootToken() - Mints per-node invocation tokens
- Ensures builder profile exists (
readProfile, thenregisterif needed) - Returns
{ nillionClient, nilauthClient, rootToken, nildbTokens }. - On success, saves the session into React Query (
["session"]) and persists tokens viausePersistedConnection.
- Creates
src/context/AuthFlowManager.tsx
- Decides whether to initialize or log in based on MetaMask connection and whether a stored session already exists.
src/hooks/useLoginMutation.ts
- Rehydrates from localStorage (
rootToken,nildbTokens) - Validates the stored root token
- Reuses invocation tokens when available, otherwise mints fresh tokens
- Ensures builder profile exists
- Writes session back to React Query.
src/hooks/usePersistedConnection.ts
- Persists connection state and session credentials in localStorage
- Exposes
hasStoredSessionused byAuthFlowManager
Key concepts:
- Root token: Master authorization from nilauth
- Invocation Tokens: Node-specific tokens (one per nilDB node)
- Blindfold: Enables automatic encryption for %allot fields
4. Collection Creation (Standard Collection)
Before storing notes, we need a standard collection with a schema.
- In the schema, encrypted fields use
%share - In write payloads, encrypted values use
%allot
loading...
5. Create, Read, Update, Delete (CRUD) Operations
Key patterns
Notice how content is wrapped in { "%allot": value } when sending.
The SDK automatically:
- Detects the %allot marker
- Encrypts the value
- Splits it into secret shares
- Sends one share to each nilDB node
In this example:
- Notes are written with
createStandardData - Reads are filtered by
walletAddress - Updates and deletes use
_idfilters on the same collection
loading...
6. UI Layout
src/app/page.tsx:
useNillion()to connect to MetaMask, log out, and access state (address/DID)useSessionQuery()to know when the session is ready- Displays authentication logs so users can follow session setup progress
src/app/profile/page.tsx:
- Wrapped with
<ProtectedRoute>to ensure only authenticated users can access. - Uses
useProfile()to display builder profile details - Renders
<Notes />, which is where CRUD happens
src/components/Notes.tsx:
- Hook integration
- Calls
useNotes()which wires together:useNotesCollection()(ensures/creates collection with encrypted content schema).useNillionClient()(providesnillionClient+ per-node tokens).- All CRUD operations
- Encryption happens in the hook via
ensureAllot()which wraps content as{ "%allot": string }.
- Calls
7. Running Your App
After you run your app with pnpm run dev, open http://localhost:3000 and:
- Click "Connect with MetaMask"
- Approve the MetaMask connection
- Wait for session initialization
- Open the profile page
- Create a note - the content is automatically encrypted
- Refresh the page - your session persists
During session initialization, MetaMask may prompt you to sign 3 or more times. This is expected: nilDB uses per-node invocation authorization (typically one signature per node) in addition to the root session flow.
What Just Happened?
🎉 Congratulations! You built a privacy-preserving notes app where:
- Users authenticate with MetaMask - no passwords needed
- A builder profile is created (or reused) automatically during session setup
- A standard notes collection is created (or reused) per wallet
- Note titles and wallet addresses stay plaintext for filtering/display
- Note content is encrypted and split into secret shares across nilDB nodes
- Sessions persist via root token + per-node invocation tokens in localStorage
Were you able to complete the quickstart?
Advanced Features
Query Operations
// Create and run queries on encrypted data
const query = {
_id: randomUUID(),
name: 'Find Users by Name',
collection: collectionId,
variables: {
searchName: {
description: 'Name to search for',
path: '$.pipeline[0].$match.name',
},
},
pipeline: [{ $match: { name: '' } }, { $count: 'total' }],
};
await builder.createQuery(query);
OpenAPI
You can access the OpenAPI specifications for any node by visiting the following URL pattern: https://{endpoint}/openapi.json, where {endpoint} is replaced with your specific node address.
For instance, to view the API specs for the staging node, you would use: https://nildb-stg-n1.nillion.network/openapi.json.
Next Steps
Now that you understand the basics of Nillion private storage, you can:
- Explore more complex collection schemas
- Implement query operations on encrypted data
- Build applications that respect user privacy by default