Development
Prerequisites
- Bun (the monorepo uses Bun workspaces).
- Go (for the backend).
- A POSIX shell with
make.
First run
Backend
cd backend
make build
./bin/open-secret-server -server-url=http://localhost:5173 &
On first start it creates backend/data/open-secret.db (mode 0700) and binds :8080. The default CORS allow-list already includes the Vite dev ports.
Install workspace deps
cd ..
bun install
Each TypeScript workspace depends on @open-secret/shared via workspace:*.
Web app
cd open-secret-ui
bun run dev # http://localhost:5173
Extension
cd open-secret-extension
bun run dev # WXT live-reload dev shell
# or
bun run build # production bundle under .output/<browser>-mv3/
Per-workspace commands
cd backend
make build # build the server binary
make test # go test ./...
make proto # regenerate Connect-RPC stubs after a .proto change
make sqlc # regenerate DB query code after a queries/*.sql change
Never edit generated code (gen/proto/, internal/db/dbgen/) by hand - regenerate it.
cd open-secret-shared
bunx vitest run # unit tests (crypto parity, payload, transport, …)
bunx buf generate # regenerate proto-derived TS after a backend proto change
cd open-secret-ui
bun run check # svelte-check type check
bunx vitest run # unit tests
bun run build # production build (adapter-static)
cd open-secret-extension
bun run check # svelte-check
bunx vitest run # unit tests
bun run build # WXT production build (also the only check on entrypoint wiring)
bun run test:e2e # Playwright: builds the extension + backend, runs the suite
The end-to-end suite mounts the real extension in a persistent browser context against a seeded backend, it's the canonical way to verify content-script behavior (autofill, save prompt, passkeys).
Changing the wire contract
The proto definitions in backend/proto/opensecret/v1/ are the source of truth. A wire change flows in order:
Edit the proto
Change the .proto in backend.
Regenerate both sides
cd backend && make proto, then cd open-secret-shared && bunx buf generate. Validation tags (validate:"…") are injected post-generation, so re-check the @gotags: comments.
Consume
Wire the new field/RPC through the shared transport, then the web app and extension. Add an apiError mapping and surface errors through the shared logger.
Migrations
Schema changes are Goose migrations under backend/internal/db/migrations/ (SQLite dialect, modernc.org/sqlite driver, no CGO). They run automatically on startup. Provide a working down migration, and assume existing data, backfills must be idempotent.
Security review
Changes are measured against the project's load-bearing invariant, the server never sees plaintext and holds no cross-device secret. The repo ships a preflight checklist (design-time and pre-commit) that walks the crypto, auth, and bridge touchpoints; run it before non-trivial changes and before committing. Never introduce Math.random / math/rand in a security path, and never log secret material.