Skip to main content

Noter Example

The noter example (apps/noter) is a note-taking app with zkLogin sign-in, so users authenticate with OAuth instead of managing a wallet. It shows the note-to-memory pattern: free-form writing becomes structured facts that recall can find later.

How it uses Walrus memory​

Noter keeps a shared server-side Walrus Memory client and uses analyze to turn note content into discrete facts, which the relayer stores as separate memories, and returns the text of each extracted fact:

Source: examples/noter.md
export const extractMemories = async (text: string): Promise<string[]> => {
const memwal = getMemWalClient();
const result = await memwal.analyze(text);
return (result.facts ?? []).map((f) => f.text);
};

Because analyze stores each extracted fact as its own memory, later recall returns the specific fact that matches a query instead of a whole note. The user configures the delegate key and account at runtime.

Run it locally​

Noter ships without an .env.example, so create apps/noter/.env yourself with the variables the noter README lists:

  • DATABASE_URL for PostgreSQL
  • Google OAuth credentials: GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET
  • The zkLogin endpoints
  • OPENROUTER_API_KEY
  • NEXT_PUBLIC_APP_URL
  • The Walrus Memory values from the dashboard: MEMWAL_PRIVATE_KEY, MEMWAL_ACCOUNT_ID, and MEMWAL_SERVER_URL

With the environment configured, create the database tables and start the app from the repo root:

Source: examples/noter.md
$ pnpm install
$ pnpm --filter noter db:push
$ pnpm dev:noter

The app runs on localhost:3002.

How does zkLogin sign-in work in Noter?​

Noter signs users in through Enoki zkLogin, which runs client-side in app/components/enoki-login-card.tsx and sui-providers.tsx. The Enoki wallet only gives the app a Sui address. To prove control of that address, the client signs a server-issued, single-use challenge with its zkLogin key, and the server verifies the signature against the address. Noter stores each challenge in Redis and removes it at verification, so nobody can replay a captured signature. On success, the app registers or looks up the user by Sui address and creates a session in the wallet_sessions table with walletType = "enoki".

The auth tRPC router (package/feature/auth/api/route.ts) also supports sign-in with a Sui wallet (connectWallet) and manual login with a delegate private key and account ID (connectDelegateKey).