Skip to content

Development setup

In progress

The repository holds the plans, the docs, the website, the Rust crypto core and sync server, and the web vault. This page grows as the apps arrive.

Tool Version
Node.js 22.13 or newer (24 recommended; the e2e suite uses node:sqlite)
pnpm 11
Rust The version in rust-toolchain.toml, installed by rustup, with the wasm32-unknown-unknown target
wasm-pack cargo install wasm-pack --locked
Google Chrome For the end-to-end tests only
Git Any recent version
Terminal window
git clone https://github.com/octap-app/octapass.git
cd octapass
pnpm install
pnpm dev # http://localhost:4321

Other root scripts:

Terminal window
pnpm build # static build into apps/website/dist
pnpm preview # serve the build locally
pnpm check # type-check the website

The web vault runs on the real stack: the Rust crypto core compiled to WebAssembly, in a Web Worker, talking to octapass-server. One command starts both:

Terminal window
pnpm install
pnpm --filter @octapass/web dev:stack # server on :8080, web vault on http://localhost:5174

It builds the server (cargo build -p octapass-server) and, whenever its Rust sources changed since the last build, the WASM core (pnpm --filter @octapass/core build:if-stale). pnpm build:web, pnpm start and the browser end-to-end tests check the core the same way, so a pull that changes the Rust crates never leaves them on an old build. The server runs in dev mode with a throwaway data directory that’s deleted when you press Ctrl-C; set OCTAPASS_DATA_DIR=./somewhere to keep it. In dev mode the sign-up screen shows the email verification code, so no mail server is needed.

To run the two halves yourself:

Terminal window
OCTAPASS_DEV_MODE=true OCTAPASS_WEB_ORIGINS=http://localhost:5174 cargo run -p octapass-server
pnpm --filter @octapass/web dev # talks to http://localhost:8080

VITE_SERVER_URL points the web vault at another server. VITE_VAULT_BACKEND=mock (or pnpm --filter @octapass/web dev:mock) runs it on the in-browser mock instead, with example items and no server, which is handy for UI work and demos.

Terminal window
cargo test --workspace # crypto core, test vectors, server
pnpm --filter @octapass/core test # the WASM flows in Node (after its build)
pnpm --filter @octapass/vault-client test # item model, tools, mock, HTTP client, worker bridge
pnpm --filter @octapass/vault-client test:server # the real service against a real octapass-server
pnpm --filter @octapass/web test # UI tests, on the mock
pnpm --filter @octapass/web e2e # Chrome against a real server: see below
pnpm --filter @octapass/web build # type-check and production build

Each Rust crate’s integration tests build as one binary, tests/main.rs, which declares every file in tests/ as a module. That way cargo links the crate once rather than once per file. To run one file’s tests, use cargo test -p octapass-server --test integration sharing::. A new test file needs a mod line in tests/main.rs; a test there fails if you forget it. The test-vector files in octapass-core stay as their own binaries, so --test vectors regenerates only its own files.

The e2e suite (apps/web/e2e) builds the server and the web vault, starts both on free ports with a fresh data directory, and drives the system Chrome with playwright-core. It signs up, checks that the server’s database files hold no plaintext from the vault, locks and unlocks, signs in again with the Secret Key, unlocks with a virtual passkey (WebAuthn PRF), syncs an edit from a second browser, and deletes the account, checking that no table still mentions it. Set HEADED=1 to watch, and CHROME_PATH if Chrome isn’t in /Applications.

pnpm start builds the web vault and the server and serves both on one port, 127.0.0.1:8380, for a tunnel or a proxy to publish. Dev mode stays off. The first run in a terminal asks two things:

  • Where people open it: through a tunnel or proxy at an address you choose (for example https://pass.example.com), or only on this computer at http://localhost:8380.
  • Who can create an account: only the emails you invite, anyone who can open the address, or no one new. Your git email is the suggested invite, and you can change it or add more.

It offers to save the answers in the gitignored .env.local at the repository root, and pnpm start --setup asks again. Without a terminal, or with OCTAPASS_NO_PROMPT=1, it uses the defaults and prints them. pnpm serve does the same without building.

The native apps’ scripts work out the device and the server, and ask only when they can’t tell:

Command Device Server
pnpm dev:ios The booted Simulator, else a choice pnpm dev:api or pnpm start on this Mac, whichever is running
pnpm dev:ios:device The connected iPhone or iPad, else a choice pnpm start’s public address, or one you type, since a phone can’t reach the Mac’s localhost
pnpm dev:android The only emulator or phone, else a choice pnpm dev:api or pnpm start on this computer, through adb reverse

pnpm dev:ios:device also asks which team signs the app. The choices come from the accounts signed in to Xcode and the Apple Development certificates in the keychain, and the answers can be saved in the gitignored apps/ios/device.env. Each command takes --setup to ask every question again. Environment variables skip the questions: OCTAPASS_SERVER_URL, DEVICE, APPLE_DEVELOPMENT_TEAM and ANDROID_SERIAL.

The iPhone and Android commands rebuild the app’s Rust library whenever its sources changed since the last build, because the committed Swift and Kotlin bindings change with it. When an Xcode build fails, they print its error lines and the path of the full log in apps/ios/build/.

Each app has a build command at the repository root:

Command What it builds Where it runs
pnpm build:extension Chrome and Firefox, unpacked in apps/extension/dist Anywhere
pnpm build:safari The Safari apps for macOS and the iOS Simulator A Mac
pnpm build:ios The iPhone and iPad app, signed for App Store Connect (--upload uploads it) A Mac
pnpm build:android The Android app, as an unsigned release APK Anywhere with the Android SDK
pnpm build:desktop The desktop app for the system you’re on: build:macos, build:windows or build:linux Anywhere
pnpm build:macos The Mac app and disk image, in apps/macos/build A Mac
pnpm build:windows The Windows installer Windows, or cross-compiled on a Mac
pnpm build:linux .deb, .rpm and AppImage packages Linux
pnpm build:cli The octapass command and its Git and Docker helpers Anywhere
pnpm build:vscode The VS Code extension, as a .vsix Anywhere
pnpm build:jetbrains The JetBrains plugin, as a .zip Anywhere with JDK 17

The extensions and the iPhone, Android and desktop apps offer one server as “Octa Pass” on their sign-in screen, and anyone can still type another under “Self-hosted”. The first build in a terminal asks which server that is. It offers pnpm start’s public address and the hosted service, and can save the answer in .env.local as OCTAPASS_BUILD_SERVER_URL. Without a terminal the builds use the hosted service. VITE_SERVER_URL, which the release workflow sets, wins over both, and --setup asks again. The apps accept only https:// addresses, or http://localhost on the same computer.

The builds are unsigned unless the app’s own signing settings are set, as each app’s README describes. The Windows and Linux builds need the Tauri CLI (cargo install tauri-cli --locked --version 2.12.4).

pnpm build:macos builds the Mac app (apps/macos), the native SwiftUI app that is Octa Pass on a Mac. The Tauri app (apps/desktop) is for Windows and Linux only and no longer builds for macOS. The Mac app needs XcodeGen and an Apple developer team, from APPLE_DEVELOPMENT_TEAM or the one pnpm dev:ios:device signs the iPhone app with: the app keeps its keys in the Mac’s Keychain, which needs a signed app, so there’s no unsigned build. It’s signed for development, so it opens on the Macs registered with that team. Developer ID signing and notarisation, for downloads anyone can open, aren’t set up yet.

Path Contents
plans/ Internal plans and decisions, numbered and read in order
docs/ These docs, served at /docs/
changelog/ One Markdown file per notable change, served at /changelog/
skills/octapass-docs/ The agent skill for querying these docs
apps/website/ Astro + Starlight website
apps/web/ The web vault (Vite + React)
packages/ui/ Design tokens and React UI primitives
packages/vault-client/ Item model, VaultService interface, local tools, the real service (src/remote) and the mock
packages/core/ The WASM build of the crypto core (@octapass/core)
crates/ Rust: octapass-core (crypto, items, tools), octapass-wasm (bindings), octapass-api (API types)
apps/server/ The sync server (axum + SQLite)
AGENTS.md Rules for AI coding agents

The planned layout for the apps and crates is in architecture.

The site URL and repository URL come from apps/website/src/site.config.ts. Override them at build time:

Terminal window
PUBLIC_SITE_URL=https://octap.app PUBLIC_REPO_URL=https://github.com/octap-app/octapass pnpm build