Montara Pass
Clients

Web app

The vault in the browser, where everything can be managed.

apps/app is the full client: a single-page app built with Vite, with TanStack Router, Query, Form and Table, React 19, zod and Tailwind 4. There is no Node server in production; the API serves the built files.

pnpm dev:app        # http://localhost:5173, proxies /api to the API

Pages

RouteWhat it does
/sign-up, /sign-inAccount, with the sign-in password
/vaultItems: add, edit, favorite, move to a folder, share, trash
/foldersCreate, rename and delete folders
/trashRestore or permanently delete trashed items
/categoriesThe category list; writing needs a super user
/profileAccount details and changing the master password
/generatorPassword generator: length and kinds of characters, with a strength estimate
/healthPassword health: logins with a weak password, and logins sharing one

The generator and the health check run in the browser, on the decrypted vault; no password is sent anywhere. A password counts as weak by its length and variety only: there is no dictionary, so a word with a year and a symbol can pass as fair. Both are also in the extension's full-screen view.

An address that matches no route shows a "Page not found" screen. The server answers every unknown path outside /api with the app's index.html, so that screen, not a server error, is what a mistyped URL leads to.

Route guards

Routes are files under src/routes. Two groups carry a guard that runs before the route loads: (app) sends a visitor without a session to /sign-in, and (auth) sends a signed-in user to /vault.

The vault gate

Every page under the app shell sits behind a gate with three states:

  1. No key set (GET /api/v1/keys answers 404): the user chooses a master password of at least 12 characters and the client creates the keys.
  2. Locked: the user enters the master password to unlock.
  3. Unlocked: the pages render.

The unlocked keys live in a zustand store that is not persisted. Reloading the page, or closing the tab, locks the vault.

Sessions

The access and refresh tokens are kept in cookies and sent as a bearer header. When a request answers 401, the client refreshes the token once and repeats the request; if the refresh fails, it signs the user out.

Search and sort

The server cannot search encrypted content, so the app decrypts the page it received and filters in the browser. Filters the server can apply (folder, type and trash) are sent as query parameters.

Theme

The app is dark by default. A light theme is defined, but there is no switch for it in the UI yet.

On this page