This project uses the following technologies:
- Turborepo for monorepo management
- pnpm for package management
- Next.js for the application framework
- Clerk for auth
- Vercel for hosting
- Supabase for database
- Drizzle ORM for database access
- CipherStash for data encryption
- jseql for interacting with CipherStash Encrypt
First, install dependencies:
pnpm installSecond, create a .env.local file in the root directory with the following content:
# Clerk auth
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
# Supabase postgres connection string
POSTGRES_URL=
# CipherStash encryption and access keys
CS_CLIENT_ID=
CS_CLIENT_KEY=
CS_CLIENT_ACCESS_KEY=
CS_WORKSPACE_ID=Finally, run the development server:
pnpm run devOpen http://localhost:3000 with your browser to see the result.
The database is hosted on Supabase and has the following schema which is defined using the Drizzle ORM:
// Data that is encrypted using jseql is stored as jsonb in postgres
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: varchar("name").notNull(),
email: jsonb("email").notNull(),
role: varchar("role").notNull(),
});Note
This example does not include any searchable encrypted fields. If you want to search on encrypted fields, you will need to install EQL. The EQL library ships with custom types that are used to define encrypted fields. See the EQL documentation for more information.
All the email data is encrypted using jseql and CipherStash.
The cipherstext is stored in the email column of the users table.
The application is configured to only decrypt the data when the user is signed in, otherwise it will display the encrypted data.
@cipherstash/jseql uses custom Rust bindings in order to perform encryptions and decryptions.
We leverage the Neon project to provide a JavaScript API for these bindings.
There is a helper script which will insert records into the database:
pnpm tsx packages/core/helpers/insert.ts --name 'user_name' --email 'user_email' This will insert a record into the database with an encrypted email field.
To view the decrpytion implementation, see the getUsers function in src/app/page.tsx.
Since @cipherstash/jseql is a native Node.js module, you need to opt-out from the Server Components bundling and use native Node.js require instead.
next.config.ts configuration:
const nextConfig = {
...
serverExternalPackages: ['@cipherstash/jseql'],
}next.config.mjs configuration:
const nextConfig = {
...
experimental: {
serverComponentsExternalPackages: ['@cipherstash/jseql'],
},
}Note
At the time of this writing, some Node-API functions are not implemented so this environment may not work with Bun.
See the .npmrc in the root of the project which contains the following contents. This is requires when @cipherstash/jseql is a nested dependency of your application:
public-hoist-pattern[]=*@cipherstash/jseql*