|
1 | 1 | # Bitwarden Internal SDK |
2 | 2 |
|
3 | | -Cross-platform Rust SDK implementing Bitwarden's core business logic. |
4 | | - |
5 | | -**Rust Edition:** The SDK targets the |
6 | | -[2024](https://doc.rust-lang.org/nightly/edition-guide/rust-2024/index.html) edition of Rust. |
7 | | - |
8 | | -**Crate documentation**: Before working in any crate, read available documentation: `CLAUDE.md` for |
9 | | -critical rules, `README.md` for architecture, `examples/` for usage patterns, and `tests/` for |
10 | | -integration tests. **Before making changes or reviewing code, review relevant examples and tests for |
11 | | -the specific functionality you're modifying.** |
12 | | - |
13 | | -## Architecture Overview |
14 | | - |
15 | | -Monorepo crates organized in **four architectural layers**: |
16 | | - |
17 | | -### 1. Foundation Layer |
18 | | - |
19 | | -- **bitwarden-crypto**: Cryptographic primitives and protocols, key store for securely working with |
20 | | - keys held in memory. |
21 | | -- **bitwarden-organization-crypto**: Cryptographic primitives and protocols related to organizations |
22 | | - and sharing. |
23 | | -- **bitwarden-state**: Type-safe Repository pattern for SDK state (client-managed vs SDK-managed) |
24 | | -- **bitwarden-threading**: ThreadBoundRunner for !Send types in WASM/GUI contexts (uses PhantomData |
25 | | - marker) |
26 | | -- **bitwarden-ipc**: Type-safe IPC framework with pluggable encryption/transport |
27 | | -- **bitwarden-error**: Error handling across platforms (basic/flat/full modes via proc macro) |
28 | | -- **bitwarden-random**: Single CRNG source for the SDK (`rng()` / `SdkRngImpl`); Use this instead of |
29 | | - `rand::rng()` directly. |
30 | | -- **bitwarden-encoding**, **bitwarden-uuid**: Encoding and UUID utilities |
31 | | - |
32 | | -### 2. Core Infrastructure |
33 | | - |
34 | | -- **bitwarden-core**: Base Client struct extended by feature crates via extension traits. **DO NOT |
35 | | - add functionality here - use feature crates instead.** |
36 | | - - **bitwarden-api-api**, **bitwarden-api-identity**: Auto-generated API clients (**DO NOT edit - |
37 | | - regenerate from OpenAPI specs**) |
38 | | - |
39 | | -### 3. Feature Implementations |
40 | | - |
41 | | -- **bitwarden-pm**: PasswordManagerClient wrapping core Client, exposes sub-clients: `auth()`, |
42 | | - `vault()`, `crypto()`, `sends()`, `generators()`, `exporters()` |
43 | | - - Lifecycle: Init → Lock/Unlock → Logout (drops instance) |
44 | | - - Storage: Apps use `HashMap<UserId, PasswordManagerClient>` |
45 | | -- **bitwarden-vault**: Vault item models, encryption/decryption and management |
46 | | -- **bitwarden-collections**: Collection models, encryption/decryption and management |
47 | | -- **bitwarden-auth**: Authentication (send access tokens) |
48 | | -- **bitwarden-send**: Encrypted temporary secret sharing |
49 | | -- **bitwarden-generators**: Password/passphrase generators |
50 | | -- **bitwarden-ssh**: SSH key generation/import |
51 | | -- **bitwarden-exporters**: Vault export/import with multiple formats |
52 | | -- **bitwarden-fido**: FIDO2 two-factor authentication |
53 | | - |
54 | | -### 4. Cross-Platform Bindings |
55 | | - |
56 | | -- **bitwarden-uniffi**: Mobile bindings (Swift/Kotlin) via UniFFI |
57 | | - - Structs: `#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]` |
58 | | - - Enums: `#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]` |
59 | | - - Include `uniffi::setup_scaffolding!()` in lib.rs |
60 | | -- **bitwarden-wasm-internal**: WebAssembly bindings (**thin bindings only - no business logic**) |
61 | | - - Structs: `#[derive(Serialize, Deserialize)]` with |
62 | | - `#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]` |
63 | | - |
64 | | -## Critical Patterns & Rules |
65 | | - |
66 | | -### Cryptography (bitwarden-crypto, bitwarden-organization-crypto) |
67 | | - |
68 | | -- **DO NOT modify** without careful consideration - backward compatibility is critical |
69 | | -- **KeyStoreContext**: Never hold across await points |
70 | | -- Naming: `derive_` for deterministic key derivation, `make_` for non-deterministic generation |
71 | | -- Use `bitwarden_crypto::safe` module first (password-protected key envelope, data envelope) instead |
72 | | - of more low-level primitives |
73 | | -- IMPORTANT: Use constant time equality checks |
74 | | -- Do not expose low-level / hazmat functions from the crypto crate. |
75 | | -- Do not expose key material from the crypto crate, use key references in the key store instead |
76 | | -- These are foundational crates, **not** client crates, and _shall not_ depend on `bitwarden-core` |
77 | | - or any `bitwarden-core` dependendents. |
78 | | - |
79 | | -### State Management (bitwarden-state) |
80 | | - |
81 | | -- **Client-managed**: App and SDK share data pool (requires manual setup) |
82 | | -- **SDK-managed**: SDK exclusively handles storage (migration-based, ordering is critical) |
83 | | -- Register types with `register_repository_item!` macro |
84 | | -- Type safety via TypeId-based type erasure with runtime downcast checks |
85 | | - |
86 | | -### Threading (bitwarden-threading) |
87 | | - |
88 | | -- Use ThreadBoundRunner for !Send types (WASM contexts, GUI handles, Rc<T>) |
89 | | -- Pins state to thread via spawn_local, tasks via mpsc channel |
90 | | -- PhantomData<\*const ()> for !Send marker (zero-cost) |
91 | | -- **Do not use `#[async_trait(?Send)]` in hand-written code.** Wrap `!Send` types (e.g. JS |
92 | | - `extern "C"` bindings holding `JsValue`) in `ThreadBoundRunner` instead, which is `Send + Sync`. |
93 | | - This keeps the SDK compatible with external crates (e.g. `passkey-rs`) that require `Send` |
94 | | - upstream and avoids future refactors. Exceptions: the `bitwarden-api-*` crates (auto-generated |
95 | | - from OpenAPI specs) and `impl`s of `reqwest_middleware::Middleware`, whose trait declaration |
96 | | - applies `?Send` on `wasm32` upstream so impls must mirror it. |
97 | | - |
98 | | -### Error Handling (bitwarden-error-macro) |
99 | | - |
100 | | -- Three modes: **basic** (string), **flat** (variant), **full** (structure) |
101 | | -- Generates FlatError trait, WASM bindings, TypeScript interfaces, UniFFI errors |
102 | | -- Conditional code generation via cfg! for WASM |
103 | | - |
104 | | -### Security Requirements |
105 | | - |
106 | | -- **Never log** keys, passwords, or vault data in logs or error paths |
107 | | -- **Redact sensitive data** in all error messages |
108 | | -- **Unsafe blocks** require comments explaining safety and invariants |
109 | | -- **Encryption/decryption changes** must maintain backward compatibility (existing encrypted data |
110 | | - must remain decryptable) |
111 | | -- **Breaking serialization** strongly discouraged - users must decrypt vaults from older versions |
112 | | - |
113 | | -### API Changes |
114 | | - |
115 | | -- **Breaking changes**: Automated detection via cross-repo workflow (see commit 9574dcc1) |
116 | | -- TypeScript compilation tested against `clients` repo on PR |
117 | | -- Document migration path for clients |
118 | | - |
119 | | -### Data Model Conventions |
120 | | - |
121 | | -The SDK uses distinct model layers; use the correct type for the context. See the |
122 | | -[data models docs](https://contributing.bitwarden.com/architecture/sdk/data-models) for the full |
123 | | -picture. |
124 | | - |
125 | | -| Suffix | Role | Example | |
126 | | -| ---------- | --------------------------------------------------------------------------------- | ------------------------ | |
127 | | -| _(none)_ | Server/storage-layer model — prefer `View`/`Request`/`Response` at API boundaries | `Cipher`, `Send` | |
128 | | -| `View` | Decrypted DTO returned to clients | `CipherView`, `SendView` | |
129 | | -| `Request` | Public input DTO from client into SDK | `CipherCreateRequest` | |
130 | | -| `Response` | Public output DTO from SDK to client | `LoginResponse` | |
131 | | - |
132 | | -**Create vs. Edit requests**: When the fields for creating and editing an item differ (e.g. edit |
133 | | -requires an `id`, `revision_date`, or fields that are immutable after creation), use **separate** |
134 | | -`*CreateRequest` and `*EditRequest` structs. |
135 | | - |
136 | | -**Variant data**: When a model has a type discriminant with per-variant associated data (e.g. a send |
137 | | -is either a file or text, never both), use an enum with associated data rather than a bare |
138 | | -discriminant field alongside multiple `Option` fields. The mapping from server wire format (numeric |
139 | | -discriminant + optional fields) belongs at the API→domain boundary. |
140 | | - |
141 | | -## Development Workflow |
142 | | - |
143 | | -**Build & Test:** |
144 | | - |
145 | | -- `cargo check --all-features --all-targets` - Quick validation |
146 | | -- `cargo test --workspace --all-features` - Full test suite |
147 | | - |
148 | | -**Format & Lint:** |
149 | | - |
150 | | -- `npm run lint` - Run every formatting/linting check CI runs (fmt, clippy, sort, udeps, dylint, |
151 | | - doc, prettier, dep-ownership, cargo-lock). Matches `.github/workflows/lint.yml`. |
152 | | -- `npm run lint:fix` - Same set, auto-fixing where the tool supports it. |
153 | | -- `npm run lint -- --only <check>` - Run a single check (e.g. `--only clippy`). |
154 | | -- Underlying script: `scripts/lint.sh`. |
155 | | - |
156 | | -**WASM Testing:** |
157 | | - |
158 | | -- `cargo test --target wasm32-unknown-unknown --features wasm -p bitwarden-error -p bitwarden-threading -p bitwarden-uuid` - |
159 | | - WASM-specific tests |
| 3 | +Bitwarden's internal cross-platform SDK: core business logic in Rust (edition 2024), consumed by web |
| 4 | +and desktop clients through WASM bindings and by mobile through UniFFI. Not for public use — the API |
| 5 | +is unstable and changes without warning. Two license zones: `crates/` (OSS) and `bitwarden_license/` |
| 6 | +(commercial: `bitwarden-sm`, `bitwarden-commercial-vault`). |
| 7 | + |
| 8 | +Path-scoped rules live in `.claude/rules/` and load automatically when you touch matching files |
| 9 | +(crypto crates, generated API crates, binding crates, repo-wide Rust conventions). Several crates |
| 10 | +also carry their own `CLAUDE.md` and substantive `README.md` (e.g. `bitwarden-state`, |
| 11 | +`bitwarden-exporters`, `bitwarden-importers`, `bitwarden-ipc`, `bitwarden-threading`) — read the |
| 12 | +crate's docs, `examples/`, and `tests/` before changing it. |
| 13 | + |
| 14 | +## Commands |
| 15 | + |
| 16 | +Toolchain: stable is pinned in `rust-toolchain.toml`; fmt, udeps, and dylint use the nightly pinned |
| 17 | +there as `nightly-channel`. CLI tools (cargo-sort, cargo-dylint, cargo-udeps, …) are version-pinned |
| 18 | +in `[workspace.metadata.bin]` and invoked via `cargo bin <tool>` (cargo-run-bin; source installs |
| 19 | +only, no binstall). |
| 20 | + |
| 21 | +Build & test: |
| 22 | + |
| 23 | +- `cargo check --all-features --all-targets` — quick validation |
| 24 | +- `cargo test --workspace --all-features` — full suite (what CI runs) |
| 25 | +- `cargo test -p <crate> --all-features <filter>` — single crate / single test |
| 26 | +- `cargo test --target wasm32-unknown-unknown -p bitwarden-wasm-internal -p bitwarden-threading -p bitwarden-error -p bitwarden-uuid --all-features` |
| 27 | + — WASM suite (matches CI; the test runner is wired up in `.cargo/config.toml`). Browser-dependent |
| 28 | + tests: |
| 29 | + `cargo test --target wasm32-unknown-unknown -p bitwarden-state --features wasm,browser-tests` |
| 30 | + (needs chromedriver). |
| 31 | + |
| 32 | +Lint & format: |
| 33 | + |
| 34 | +- `npm run lint` — every check CI runs (fmt, clippy, sort, udeps, dylint, doc, prettier, |
| 35 | + dep-ownership, cargo-lock); `npm run lint:fix` auto-fixes; `npm run lint -- --only <check>` runs |
| 36 | + one. Backed by `scripts/lint.sh`. |
| 37 | +- **Never run bare `cargo fmt`** — `rustfmt.toml` uses nightly-only options that stable rustfmt |
| 38 | + silently ignores. Use `npm run lint:fix -- --only fmt`. |
| 39 | +- Custom dylint lints live in `support/lints/` (not a workspace member). |
| 40 | +- Husky pre-commit runs prettier, clippy, and dylint on staged files (plus udeps and sort when |
| 41 | + `Cargo.toml` changes) — commits are slow and can fail on lint. |
| 42 | + |
| 43 | +Generated code: |
| 44 | + |
| 45 | +- `bitwarden-api-api` / `bitwarden-api-identity` are generated from the server's OpenAPI specs — |
| 46 | + never edit by hand. Regenerate with `./support/build-api.sh` (expects a sibling `server` checkout) |
| 47 | + or the "Update API Bindings" GitHub workflow. |
| 48 | +- WASM npm packages (`@bitwarden/sdk-internal`, `@bitwarden/commercial-sdk-internal`) build via |
| 49 | + `crates/bitwarden-wasm-internal/build.sh` (`-r` release, `-b` commercial). |
| 50 | + |
| 51 | +## Architecture |
| 52 | + |
| 53 | +Four layers; dependencies point strictly downward: |
| 54 | + |
| 55 | +1. **Foundation** — `bitwarden-crypto`, `bitwarden-organization-crypto`, `bitwarden-state`, |
| 56 | + `bitwarden-threading`, `bitwarden-ipc`, `bitwarden-error`, `bitwarden-random` (the SDK's single |
| 57 | + CRNG source — use it instead of calling `rand::rng()` directly), plus small utilities. These must |
| 58 | + not depend on `bitwarden-core` or anything that does. |
| 59 | +2. **Core** — `bitwarden-core` defines `Client`, a dependency-injection container (user identity, |
| 60 | + key store, API configuration, state). **Do not add features here** — feature crates extend |
| 61 | + `Client` via extension traits. |
| 62 | +3. **Features** — one crate per domain (`bitwarden-vault`, `bitwarden-auth`, `bitwarden-send`, |
| 63 | + `bitwarden-generators`, `bitwarden-exporters`, `bitwarden-importers`, `bitwarden-sync`, |
| 64 | + `bitwarden-policies`, …). `bitwarden-pm` assembles them into `PasswordManagerClient`, the facade |
| 65 | + apps consume — one instance per user (`HashMap<UserId, PasswordManagerClient>`), lifecycle init → |
| 66 | + lock/unlock → logout (drop). The current sub-client list is in `crates/bitwarden-pm/src/lib.rs`. |
| 67 | +4. **Bindings** — `bitwarden-uniffi` (Swift/Kotlin) and `bitwarden-wasm-internal` |
| 68 | + (TypeScript/JavaScript). Thin bindings only — no business logic. |
160 | 69 |
|
161 | 70 | ## References |
162 | 71 |
|
163 | | -- [SDK Architecture](https://contributing.bitwarden.com/architecture/sdk/) |
164 | | -- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/) |
165 | | -- [Contributing Guidelines](https://contributing.bitwarden.com/contributing/) |
166 | | -- [Setup Guide](https://contributing.bitwarden.com/getting-started/sdk/internal/) |
167 | | -- [Code Style](https://contributing.bitwarden.com/contributing/code-style/) |
168 | | -- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/) |
169 | | -- [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions) |
170 | | -- [Rust 2024 Edition Guide](https://doc.rust-lang.org/edition-guide/rust-2024/) |
| 72 | +- [SDK architecture](https://contributing.bitwarden.com/architecture/sdk/) · |
| 73 | + [data models](https://contributing.bitwarden.com/architecture/sdk/data-models) · |
| 74 | + [ADRs](https://contributing.bitwarden.com/architecture/adr/) · |
| 75 | + [code style](https://contributing.bitwarden.com/contributing/code-style/) · |
| 76 | + [security definitions](https://contributing.bitwarden.com/architecture/security/definitions) |
0 commit comments