Cryptly is a zero-knowledge, end-to-end encrypted secrets management platform (see cryptly.dev / github.com/cryptly-dev/cryptly). Teams use it to store API keys, credentials, and other secrets, share them with collaborators, track version history, and sync to GitHub.
The core promise is that the server never sees plaintext secrets or private keys. All encryption happens client-side: user RSA-OAEP key pairs are generated in the browser, the private key is encrypted with a user passphrase before it ever leaves the device, and per-project symmetric keys are wrapped with the user's public key. Sharing (invitation links, direct invites) is built on top of these primitives without ever exposing plaintext to the backend.
Because the whole value proposition is cryptographic, security bugs are product bugs of the highest severity. When working on anything that touches crypto, key storage, invitations, auth, or anything that could leak plaintext / keys / passphrases to the server or to logs:
- Be extra careful and explicit about what is encrypted, where, and with which key.
- Never add logging, telemetry, or error messages that could expose plaintext secrets, passphrases, private keys, or symmetric project keys.
- Never move decryption to the server or send unwrapped keys to it, even "temporarily".
- If something feels off about a crypto flow, stop and flag it rather than papering over it.
This section tells coding agents exactly which commands to run to verify changes. Do not improvise other commands (tsc -b, nest build --watch, ad-hoc jest invocations, etc.) — use only what's listed here.
Two commands are all you need. Run them at the very end, once you're done editing, not iteratively while working.
# From backend/
pnpm test # full jest suite
pnpm build # nest build (compile check)- To narrow the test run, pass a filter flag, e.g.
pnpm test invitationsorpnpm test -- --testPathPattern=device-flow. Jest flags after--are forwarded. pnpm testalready runs with--runInBand --forceExit; don't second-guess those.- If both
testandbuildpass, the backend change is verified. There is no separate typecheck step.
One command. Run it at the very end.
# From frontend/
pnpm buildpnpm buildchainstsc -b && vite build, so it is the typecheck. Do not runtscon its own.- There is no frontend test suite. Do not add one unless explicitly asked.
Two commands. Run them at the very end.
# From apps/web/
pnpm check # svelte-kit sync + svelte-check
pnpm build # vite build using the Cloudflare adapterpnpm checkis the typecheck for the SvelteKit app.pnpm buildverifies the Cloudflare Workers output.- Production (cryptly.dev) is served by Vercel:
frontend/vercel.jsonbuildsapps/webwithadapter-vercel(selected whenVERCEL=1). - Public env is inlined at build time; production values live in
apps/web/.env.production. - Analytics: Logdash, served first-party by
apps/web/src/routes/_ld. Usetrack/identifyfrom$lib/analytics/logdash. Never put emails, names, user IDs or other personal data in event names or props; add new dynamic routes toPATH_TEMPLATESin_ld/events/+server.ts.
apps/web and cli talk to the backend only through this typed client, generated from the backend's OpenAPI document.
# From the repo root, after changing any backend controller or DTO
pnpm generate:backend-sdk- Commit the regenerated
openapi.jsonandsrc/openapi.generated.ts. CI fails when they are stale. - Declare response bodies with
@ApiOkResponse/@ApiCreatedResponse(matching the real status code), not@ApiResponse. Untypeddefaultresponses leave the SDK response type empty.
# From cli/
pnpm test # vitest run
pnpm build # tsc --noEmit && tsuppnpm buildchains the typecheck into the bundle step. Do not runtscon its own.- Both should pass at the end.
After a change you want shipped to npm:
- From
cli/, runpnpm changesetand describe the change (pick patch / minor / major). - Commit the generated
.changeset/*.md(repo root) alongside your code change. - Merge to
main. Thecli-releaseworkflow auto-bumpscli/package.json, commits the bump back tomain, and publishes@cryptly/clito npm in one run.
If you don't add a changeset, nothing publishes — the change just sits on main until someone bundles a changeset with it.
This repo is a single pnpm workspace.
- The root
pnpm-workspace.yamlincludesbackend,cli,frontend,apps/*, andpackages/*. - The root
pnpm-lock.yamlis the only lockfile. Runpnpm installfrom the repo root; never add per-package lockfiles. - The backend Docker image is built from the repo root (
docker build -f backend/Dockerfile .).
cm: commit and push only changes in files you made to the current branch, using theghCLI for the push flow where relevant. Do not include unrelated user changes.