|
1 | 1 | --- |
2 | 2 | name: bump-rust-sdk |
3 | | -description: This skill should be used when the user asks to "bump the Rust SDK", "update sdk-internal", "bump bitwarden-crypto", "update RustSdk dependencies", "align server SDK with clients", or needs to update the bitwarden/sdk-internal git rev pins in util/RustSdk/rust/Cargo.toml. Provides the methodology for mapping client NPM versions to git commit SHAs, analyzing breaking changes, auditing the API surface, and verifying the bump end-to-end. |
| 3 | +description: Bump the server's util/RustSdk `bitwarden-crypto` git-rev pin to a bitwarden/clients release — map the client's sdk-internal version to a commit, fix breaking changes, then verify. |
| 4 | +when_to_use: When asked to bump the Rust SDK, sdk-internal, or bitwarden-crypto, update RustSdk dependencies, or align the server SDK with clients. Not client-side @bitwarden/sdk-internal npm/package.json bumps. |
4 | 5 | --- |
5 | 6 |
|
6 | | -# Bump sdk-internal Rust Crate Dependencies |
| 7 | +# Bump sdk-internal Rust crate dependencies |
7 | 8 |
|
8 | 9 | ## Overview |
9 | 10 |
|
10 | | -The server's `util/RustSdk/rust/Cargo.toml` pins `bitwarden-crypto` from the |
11 | | -`bitwarden/sdk-internal` repository by git rev. This must be periodically bumped to stay |
12 | | -aligned with the Bitwarden client applications. |
| 11 | +`util/RustSdk/rust/Cargo.toml` pins `bitwarden-crypto` from `bitwarden/sdk-internal` by git rev; bump |
| 12 | +it periodically to track a `bitwarden/clients` release. RustSdk is Seeder **test** infrastructure |
| 13 | +(`util/`, not `src/` production): it gives the C# Seeder field-level encryption |
| 14 | +(`encrypt_string`/`decrypt_string`/`encrypt_fields`) to produce cryptographically correct Protected |
| 15 | +Data for integration tests. `EncryptPropertyAttribute` drives which fields get encrypted. |
13 | 16 |
|
14 | | -The RustSdk is used by the Seeder to produce cryptographically correct Protected Data for |
15 | | -integration testing. It is NOT part of the production server runtime. The Rust layer provides |
16 | | -generic field-level encryption (`encrypt_string`, `decrypt_string`, `encrypt_fields`) and |
17 | | -key generation — the C# Seeder drives which fields to encrypt via `EncryptPropertyAttribute`. |
| 17 | +## NPM → git-rev mapping |
18 | 18 |
|
19 | | -## Key Challenge: NPM-to-Git-Rev Mapping |
20 | | - |
21 | | -The clients consume sdk-internal via **NPM packages** (`@bitwarden/sdk-internal`), while the |
22 | | -server consumes it via **Rust git rev pins**. The NPM version (e.g., `0.2.0-main.522`) does not |
23 | | -directly correspond to a git tag — it encodes a GitHub Actions **workflow run number**. |
24 | | - |
25 | | -### Version Format |
26 | | - |
27 | | -``` |
28 | | -0.2.0-main.522 |
29 | | -│ │ │ |
30 | | -│ │ └── GitHub Actions run number for publish-wasm-internal workflow |
31 | | -│ └── Branch name (/ replaced with -) |
32 | | -└── Base version from sdk-internal |
33 | | -``` |
34 | | - |
35 | | -### How to Find the Git Rev |
36 | | - |
37 | | -1. Determine the target NPM version from the clients repo (see Step 1 below) |
38 | | -2. Find the `Publish @bitwarden/sdk-internal` workflow ID in the sdk-internal repo |
39 | | -3. Query the GitHub Actions API for the specific run number |
40 | | -4. Extract the `head_sha` — that is the git rev to pin in Cargo.toml |
41 | | - |
42 | | -The specific API queries are documented in `references/methodology.md`. |
43 | | - |
44 | | -## Process Overview |
45 | | - |
46 | | -### Step 1: Identify Target Version |
47 | | - |
48 | | -Determine which sdk-internal version to target. Check the latest production release tag from |
49 | | -`bitwarden/clients` (e.g., `web-v2026.2.0`): |
| 19 | +Clients consume sdk-internal as npm (`@bitwarden/sdk-internal`, e.g. `0.2.0-main.841`); the server |
| 20 | +pins a git rev. The `.NNN` suffix is an opaque counter from a private Azure publish task — **not** a |
| 21 | +GitHub Actions `run_number`, and it does not track `main` commits 1:1. Do not infer the SHA from a run |
| 22 | +number or timestamp; both pick the wrong commit (verified: `841→c5d5bba`, `842→c9f9dba`, `840→1e45444` |
| 23 | +— no time correlation). Read the commit the build bakes into the published WASM instead: |
50 | 24 |
|
51 | 25 | ```bash |
52 | | -cd /path/to/clients |
53 | | -git show web-v2026.2.0:package.json | grep sdk-internal |
| 26 | +# <VERSION> is the npm version from the clients release tag (Step 1), e.g. 0.2.0-main.841 |
| 27 | +cd "$(mktemp -d)" |
| 28 | +curl -sSL "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-<VERSION>.tgz" -o pkg.tgz |
| 29 | +tar xzf pkg.tgz |
| 30 | +grep -ao "main ([0-9a-f]\{7\})" package/bitwarden_wasm_internal_bg.wasm | sort -u # -> main (c5d5bba) |
| 31 | +cd /path/to/sdk-internal && git rev-parse c5d5bba # -> full rev to pin in Cargo.toml |
54 | 32 | ``` |
55 | 33 |
|
56 | | -This gives the NPM version (e.g., `0.2.0-main.522`). Extract the run number (522). |
57 | | - |
58 | | -### Step 2: Map NPM Version to Git SHA |
59 | | - |
60 | | -Query the GitHub Actions API to find the commit that produced that NPM build. See |
61 | | -`references/methodology.md` for the exact commands. |
62 | | - |
63 | | -### Step 3: Analyze Breaking Changes |
64 | | - |
65 | | -Compare the current pinned rev against the target rev, focusing on `bitwarden-crypto`: |
66 | | - |
67 | | -```bash |
68 | | -cd /path/to/sdk-internal |
69 | | -git log --oneline <old-rev>..<new-rev> -- crates/bitwarden-crypto |
70 | | -``` |
71 | | - |
72 | | -Cross-reference each commit against the API surface documented in `references/api-surface.md`. |
73 | | - |
74 | | -### Step 4: Apply Changes |
75 | | - |
76 | | -1. Update `Cargo.toml` — bump the `bitwarden-crypto` rev pin to the new SHA |
77 | | -2. Fix any compilation errors from breaking changes (type renames, new parameters, etc.) |
78 | | -3. Add `#[allow(deprecated)]` for any newly-deprecated APIs (with a comment explaining why) |
79 | | - |
80 | | -### Step 5: Build and Verify (Claude) |
81 | | - |
82 | | -```bash |
83 | | -cd util/RustSdk/rust |
84 | | -cargo build # Must compile cleanly |
85 | | -cargo test # All tests must pass (roundtrip test is critical) |
86 | | -cargo fmt --check # Formatting must be clean |
87 | | -git diff ../NativeMethods.g.cs # FFI signatures should be unchanged |
88 | | -``` |
89 | | - |
90 | | -Also run the C# integration tests: |
91 | | - |
92 | | -```bash |
93 | | -dotnet test test/SeederApi.IntegrationTest/ |
94 | | -``` |
95 | | - |
96 | | -### Step 6: Human Verification (HUMAN ONLY) |
97 | | - |
98 | | -**Claude does NOT perform this step.** Present these commands to the human engineer and wait |
99 | | -for confirmation before proceeding. |
100 | | - |
101 | | -The human runs SeederUtility and SeederApi to verify Protected Data is correctly produced and |
102 | | -decryptable by the web client. See `references/methodology.md` for the specific test commands |
103 | | -and validation criteria. |
104 | | - |
105 | | -## Security Notes |
106 | | - |
107 | | -- The RustSdk lives in `util/` (test infrastructure), not `src/` (production) |
108 | | -- The server never decrypts Vault Data — zero-knowledge invariant is unaffected |
109 | | -- Aligning with the production client release ensures the Seeder produces Protected Data |
110 | | - using the same cryptographic primitives as real clients |
111 | | -- Review `Cargo.lock` diff for unexpected transitive crypto crate changes (rsa, aes, sha2, etc.) |
112 | | - |
113 | | -## Keeping References Current |
114 | | - |
115 | | -The API surface reference (`references/api-surface.md`) must always reflect the actual code. |
116 | | -Two mechanisms enforce this: |
117 | | - |
118 | | -1. **Post-bump step** — The `/bump-rust-sdk` command includes a mandatory final step to |
119 | | - regenerate `api-surface.md` by reading the actual `*.rs` source files. |
120 | | -2. **Stop hook** — `.claude/hooks/rust-sdk-surface-check.sh` blocks if `Cargo.toml` was |
121 | | - modified but `api-surface.md` was not updated in the same session. |
122 | | - |
123 | | -To regenerate: read all `.rs` files in `util/RustSdk/rust/src/`, extract every `use` statement |
124 | | -from `bitwarden_crypto`, and rewrite `references/api-surface.md` to match. |
125 | | - |
126 | | -## Additional Resources |
127 | | - |
128 | | -### Reference Files |
129 | | - |
130 | | -- **`references/methodology.md`** — Detailed step-by-step commands including GitHub Actions API |
131 | | - queries, breaking change analysis checklist, human verification commands, and a worked example |
132 | | - from the Feb 2026 bump |
133 | | -- **`references/api-surface.md`** — Complete inventory of types, traits, and functions the RustSdk |
134 | | - imports from `bitwarden-crypto`, used to assess breaking change impact |
135 | | - |
136 | | -## Files Modified in a Typical Bump |
137 | | - |
138 | | -| File | Change | |
139 | | -| --------------------------------- | ----------------------------------------------- | |
140 | | -| `util/RustSdk/rust/Cargo.toml` | `bitwarden-crypto` rev pin update | |
141 | | -| `util/RustSdk/rust/src/*.rs` | Type renames, new parameters, deprecation fixes | |
142 | | -| `util/RustSdk/rust/Cargo.lock` | Auto-regenerated (commit alongside) | |
143 | | -| `util/RustSdk/NativeMethods.g.cs` | Should NOT change (verify) | |
| 34 | +That full SHA is the exact source the production client runs. `@bitwarden/commercial-sdk-internal` |
| 35 | +ships the same commit, so the open-source tarball's embedded SHA is authoritative for both. |
| 36 | + |
| 37 | +## Process |
| 38 | + |
| 39 | +1. **Identify target** — latest `web-v*` release tag from clients, then its npm version: |
| 40 | + ```bash |
| 41 | + gh release list --repo bitwarden/clients --limit 5 | grep web-v |
| 42 | + git -C /path/to/clients show <tag>:package.json | grep sdk-internal # -> 0.2.0-main.841 |
| 43 | + ``` |
| 44 | +2. **Map npm → git SHA** — the mapping section above. |
| 45 | +3. **Analyze breaking changes** — cross-ref each commit against `references/api-surface.md`; watch for |
| 46 | + type renames, removed/deprecated functions, changed signatures, and trait changes: |
| 47 | + ```bash |
| 48 | + cd /path/to/sdk-internal |
| 49 | + git log --oneline <old-rev>..<new-rev> -- crates/bitwarden-crypto |
| 50 | + git diff <old-rev>..<new-rev> -- crates/bitwarden-crypto/src/keys/mod.rs crates/bitwarden-crypto/src/lib.rs |
| 51 | + ``` |
| 52 | +4. **Apply** — |
| 53 | + - Update the `bitwarden-crypto` `rev` in `Cargo.toml`. |
| 54 | + - **MSRV check:** compare the target's workspace `rust-version` against `util/RustSdk/rust-toolchain.toml`; |
| 55 | + if the MSRV rose above our channel, bump the channel to the **MSRV** (not sdk-internal's dev |
| 56 | + toolchain), else the build fails "requires rustc X or newer". |
| 57 | + - `cargo update -p bitwarden-crypto` (targeted — re-resolves only what the new rev needs; a bare |
| 58 | + `cargo update` churns the lockfile). |
| 59 | + - Fix compile errors; add `#[allow(deprecated)]` + a why-comment for newly-deprecated APIs. |
| 60 | +5. **Build & verify (Claude)** — |
| 61 | + ```bash |
| 62 | + cd util/RustSdk/rust |
| 63 | + cargo build && cargo test # roundtrip test is the gate: encrypt_string_decrypt_string_roundtrip |
| 64 | + cargo fmt --check |
| 65 | + git diff ../NativeMethods.g.cs # must be unchanged |
| 66 | + dotnet test test/SeederApi.IntegrationTest/ |
| 67 | + ``` |
| 68 | + Review the `Cargo.lock` diff for unexpected transitive crypto crates (`rsa`, `aes`, `sha2`). |
| 69 | +6. **Human verification (human only — present these, do not run them)** — the human seeds and confirms |
| 70 | + Protected Data is decryptable in the web client: |
| 71 | + ```bash |
| 72 | + cd util/SeederUtility |
| 73 | + dotnet run -- organization -n SdkBumpTest -d sdk-bump-test.example -u 3 -c 10 -g 5 -o Traditional -m |
| 74 | + # or a preset: dotnet run -- seed --preset dunder-mifflin-enterprise-full --mangle |
| 75 | + |
| 76 | + # SeederApi HTTP alternative — start `dotnet run` in util/SeederApi, then: |
| 77 | + curl -X POST localhost:5000/seed -H 'X-Play-Id: sdk-bump-test' -H 'Content-Type: application/json' \ |
| 78 | + -d '{"template":"SingleUserScene","arguments":{"email":"test@example.com","password":"<8+ char pwd>"}}' |
| 79 | + curl -X DELETE localhost:5000/seed/sdk-bump-test # cleanup |
| 80 | + ``` |
| 81 | + Pass = seeded users log in with the fake master password (see `util/SeederUtility/README.md`), |
| 82 | + ciphers are decryptable in the web vault, no Seeder errors, and cleanup deletes all tracked entities. |
| 83 | + |
| 84 | +## Security notes |
| 85 | + |
| 86 | +- RustSdk is `util/` test infrastructure, not `src/` production; the server never decrypts Vault Data, |
| 87 | + so the zero-knowledge invariant is unaffected. |
| 88 | +- Matching the production client release makes the Seeder produce Protected Data with the same |
| 89 | + cryptographic primitives real clients use. |
| 90 | + |
| 91 | +## Keeping api-surface.md current |
| 92 | + |
| 93 | +`references/api-surface.md` must mirror the actual code. After a bump, regenerate it: read every `.rs` |
| 94 | +in `util/RustSdk/rust/src/`, extract the `bitwarden_crypto` `use` statements, and rewrite the file. |
| 95 | +A Stop hook (`.claude/hooks/rust-sdk-surface-check.sh`) blocks if `Cargo.toml` changed but |
| 96 | +`api-surface.md` did not. |
| 97 | + |
| 98 | +## Files typically modified |
| 99 | + |
| 100 | +| File | Change | |
| 101 | +| --- | --- | |
| 102 | +| `util/RustSdk/rust/Cargo.toml` | `bitwarden-crypto` rev pin | |
| 103 | +| `util/RustSdk/rust-toolchain.toml` | bump `channel` only if the MSRV rose | |
| 104 | +| `util/RustSdk/rust/src/*.rs` | breaking-change fixes | |
| 105 | +| `util/RustSdk/rust/Cargo.lock` | re-resolved (`cargo update -p bitwarden-crypto`) | |
| 106 | +| `util/RustSdk/NativeMethods.g.cs` | should NOT change — verify | |
| 107 | + |
| 108 | +## References |
| 109 | + |
| 110 | +- `references/api-surface.md` — types, traits, and functions imported from `bitwarden-crypto`. |
| 111 | +- `references/examples/2026-06-bump.md` — the June 2026 bump, worked end-to-end. |
0 commit comments