1
0
mirror of https://github.com/bitwarden/server synced 2026-02-28 10:23:24 +00:00

Craft tooling to aid in bumping RustSdk and Seeder document updating (#7077)

This commit is contained in:
Mick Letofsky
2026-02-27 15:22:26 +01:00
committed by GitHub
parent 9ca1874723
commit 1e7f7acb97
7 changed files with 671 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
---
description: Bump sdk-internal Rust crate dependencies in util/RustSdk to align with a Bitwarden clients release
argument-hint: [clients-release-tag]
allowed-tools: Read, Write, Edit, Glob, Grep, Bash(cargo *), Bash(dotnet *), Bash(git *), Bash(gh *)
---
Bump the sdk-internal Rust crate dependencies in `util/RustSdk/rust/Cargo.toml` to align with
the Bitwarden clients production release specified by `$ARGUMENTS`. If no release tag is given,
determine the latest `web-v*` release tag from `bitwarden/clients`.
Invoke the `bump-rust-sdk` skill using the task tool for the full methodology, API surface reference, and worked examples.
## Required Context
Before starting, read these files to understand the current state:
- `util/RustSdk/rust/Cargo.toml` — current rev pins
- `util/RustSdk/rust/src/*.rs` — current API usage
- `.claude/skills/bump-rust-sdk/references/api-surface.md` — documented API surface
- `.claude/skills/bump-rust-sdk/references/methodology.md` — detailed process
## Execution
Follow the skill's process in order:
1. **Identify target** — Find the `@bitwarden/sdk-internal` NPM version at the release tag
2. **Map to git SHA** — Query the GitHub Actions API for the publish workflow run number
3. **Analyze breaking changes** — Compare old rev to new rev across the three crates, using the API surface reference
4. **Apply changes** — Update Cargo.toml, fix compilation errors, handle deprecations
5. **Build and verify**`cargo build`, `cargo test`, `dotnet test test/SeederApi.IntegrationTest/`, `cargo fmt --check`
6. **Human verification** — Present the SeederUtility and SeederApi test commands to the human. **Do NOT run these yourself.** Wait for the human to confirm.
7. **Regenerate API surface** — Read all `.rs` files in `util/RustSdk/rust/src/` and update `.claude/skills/bump-rust-sdk/references/api-surface.md` to reflect the current imports, types, and traits. This step is mandatory — the reference must always match the actual code.
## Important Rules
- All three crate rev pins MUST be the same SHA
- Do NOT make unrelated formatting or style changes to the Rust source files
- Do NOT run SeederUtility or SeederApi yourself — the human performs all end-to-end testing
- Do NOT skip the API surface regeneration step — a Stop hook will block if it is missed
- `util/RustSdk/NativeMethods.g.cs` should NOT change — verify with `git diff` after build
- `util/RustSdk/rust/Cargo.lock` will change and must be included alongside the other changes

64
.claude/hooks/README.md Normal file
View File

@@ -0,0 +1,64 @@
# Claude Code Hooks
All hooks are Stop hooks — they fire when Claude finishes responding and check
whether documentation or references need updating based on what was changed.
## Configuration
Register hooks in `.claude/settings.local.json` — not `settings.json`. Local settings are gitignored, keeping your personal hook configuration out of source control.
## Requirements
- `jq` must be installed (`brew install jq`)
- Must be run from within a git repository
## Testing & Debugging
- **Verbose mode**: Press `Ctrl+O` in Claude Code to see hook execution details
- **Debug mode**: Run `claude --debug` for full execution logging
## Disabling
- Use the `/hooks` menu in Claude Code to toggle individual hooks
- Or set `"disableAllHooks": true` in `.claude/settings.local.json`
---
## seeder-docs-check.sh
**Event:** Stop
**Purpose:** Reminds developers to update Seeder documentation when code in
`util/Seeder/`, `util/SeederApi/`, or `util/SeederUtility/` was modified but no
`.md` files in those directories were touched.
**How it works:**
1. Runs `git diff` to detect all changed files
2. If non-markdown files were changed under a Seeder project AND no `.md` files
in any of the three Seeder projects were modified, blocks the stop with a
reminder (intentionally cross-project — a doc update anywhere in the Seeder
subsystem satisfies the check)
3. The reminder lists all `.md` files in the affected projects (discovered dynamically)
4. On the next stop, `stop_hook_active` is true, so the hook allows through
5. Result: one reminder per stop, then the developer decides
---
## rust-sdk-surface-check.sh
**Event:** Stop
**Purpose:** Ensures the RustSdk API surface reference stays current when the
sdk-internal dependency rev is bumped. Prevents `.claude/skills/bump-rust-sdk/references/api-surface.md`
from going stale.
**How it works:**
1. Runs `git diff` to detect all changed files
2. If `util/RustSdk/rust/Cargo.toml` was modified BUT
`.claude/skills/bump-rust-sdk/references/api-surface.md` was NOT, blocks the
stop with a reminder to regenerate the API surface inventory
3. On the next stop, `stop_hook_active` is true, so the hook allows through
4. Result: one reminder per stop — Claude reads the `.rs` source files and
regenerates the reference

View File

@@ -0,0 +1,42 @@
#!/bin/bash
# rust-sdk-surface-check.sh
# Stop hook: reminds developers to update the RustSdk API surface reference
# when Cargo.toml rev pins were modified but api-surface.md was not touched.
#
# Behavior: blocks Claude from stopping exactly once with a reminder.
# On the second stop (stop_hook_active=true), allows through.
set -euo pipefail
INPUT=$(cat)
# Guard: if a Stop hook already blocked this turn, allow through.
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
if [[ "$STOP_HOOK_ACTIVE" == "true" ]]; then
exit 0
fi
CWD=$(echo "$INPUT" | jq -r '.cwd')
# Gather all changed files (staged, unstaged, and untracked) relative to repo root.
DIFF_HEAD=$(git -C "$CWD" diff --name-only HEAD 2>/dev/null || true)
UNTRACKED=$(git -C "$CWD" ls-files --others --exclude-standard 2>/dev/null || true)
ALL_CHANGED=$(printf "%s\n%s" "$DIFF_HEAD" "$UNTRACKED" | sort -u | grep -v '^$' || true)
if [[ -z "$ALL_CHANGED" ]]; then
exit 0
fi
# Check if the RustSdk Cargo.toml was modified.
if ! echo "$ALL_CHANGED" | grep -q '^util/RustSdk/rust/Cargo.toml$'; then
exit 0
fi
# Check if the API surface reference was already updated.
if echo "$ALL_CHANGED" | grep -q '^\.claude/skills/bump-rust-sdk/references/api-surface.md$'; then
exit 0
fi
REASON="util/RustSdk/rust/Cargo.toml was modified but the API surface reference was not updated. Read all .rs files in util/RustSdk/rust/src/ and regenerate .claude/skills/bump-rust-sdk/references/api-surface.md to reflect the current imports, types, and traits used from bitwarden-core, bitwarden-crypto, and bitwarden-vault."
jq -n --arg reason "$REASON" '{ "decision": "block", "reason": $reason }'

View File

@@ -0,0 +1,61 @@
#!/bin/bash
# seeder-docs-check.sh
# Stop hook: reminds developers to update Seeder documentation when
# Seeder code was modified but no documentation files were touched.
#
# Behavior: blocks Claude from stopping exactly once with a reminder.
# On the second stop (stop_hook_active=true), allows through.
set -euo pipefail
INPUT=$(cat)
# Guard: if a Stop hook already blocked this turn, allow through.
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
if [[ "$STOP_HOOK_ACTIVE" == "true" ]]; then
exit 0
fi
CWD=$(echo "$INPUT" | jq -r '.cwd')
# Gather all changed files (staged, unstaged, and untracked) relative to repo root.
DIFF_HEAD=$(git -C "$CWD" diff --name-only HEAD 2>/dev/null || true)
UNTRACKED=$(git -C "$CWD" ls-files --others --exclude-standard 2>/dev/null || true)
ALL_CHANGED=$(printf "%s\n%s" "$DIFF_HEAD" "$UNTRACKED" | sort -u | grep -v '^$' || true)
if [[ -z "$ALL_CHANGED" ]]; then
exit 0
fi
# Check which Seeder projects have non-markdown code changes.
SEEDER_CODE_CHANGED=false
SEEDER_PROJECTS_CHANGED=()
for project in "util/Seeder/" "util/SeederApi/" "util/SeederUtility/"; do
if echo "$ALL_CHANGED" | grep -q "^${project}" && \
echo "$ALL_CHANGED" | grep "^${project}" | grep -qv '\.md$'; then
SEEDER_CODE_CHANGED=true
SEEDER_PROJECTS_CHANGED+=("$project")
fi
done
if [[ "$SEEDER_CODE_CHANGED" == "false" ]]; then
exit 0
fi
# Check if any Seeder .md files were already modified.
if echo "$ALL_CHANGED" | grep -qE '^util/(Seeder|SeederApi|SeederUtility)/.*\.md$'; then
exit 0
fi
# Dynamically discover all .md files in each modified project.
DOCS_LIST=""
for project in "${SEEDER_PROJECTS_CHANGED[@]}"; do
while IFS= read -r md_file; do
DOCS_LIST="${DOCS_LIST}\n - ${md_file}"
done < <(find "$CWD/$project" -name "*.md" | sed "s|^$CWD/||" | sort)
done
REASON=$(printf "Seeder code was modified but no Seeder documentation was updated. Please check whether any of these docs need updating:%b\n\nIf the docs are already accurate, let the user know you verified them." "$DOCS_LIST")
jq -n --arg reason "$REASON" '{ "decision": "block", "reason": $reason }'

View File

@@ -0,0 +1,141 @@
---
name: bump-rust-sdk
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.
---
# Bump sdk-internal Rust Crate Dependencies
## Overview
The server's `util/RustSdk/rust/Cargo.toml` pins three crates from the `bitwarden/sdk-internal`
repository by git rev: `bitwarden-core`, `bitwarden-crypto`, and `bitwarden-vault`. These must
be periodically bumped to stay aligned with the Bitwarden client applications.
The RustSdk is used by the Seeder to produce cryptographically correct Protected Data for
integration testing. It is NOT part of the production server runtime.
## Key Challenge: NPM-to-Git-Rev Mapping
The clients consume sdk-internal via **NPM packages** (`@bitwarden/sdk-internal`), while the
server consumes it via **Rust git rev pins**. The NPM version (e.g., `0.2.0-main.522`) does not
directly correspond to a git tag — it encodes a GitHub Actions **workflow run number**.
### Version Format
```
0.2.0-main.522
│ │ │
│ │ └── GitHub Actions run number for publish-wasm-internal workflow
│ └── Branch name (/ replaced with -)
└── Base version from sdk-internal
```
### How to Find the Git Rev
1. Determine the target NPM version from the clients repo (see Step 1 below)
2. Find the `Publish @bitwarden/sdk-internal` workflow ID in the sdk-internal repo
3. Query the GitHub Actions API for the specific run number
4. Extract the `head_sha` — that is the git rev to pin in Cargo.toml
The specific API queries are documented in `references/methodology.md`.
## Process Overview
### Step 1: Identify Target Version
Determine which sdk-internal version to target. Check the latest production release tag from
`bitwarden/clients` (e.g., `web-v2026.2.0`):
```bash
cd /path/to/clients
git show web-v2026.2.0:package.json | grep sdk-internal
```
This gives the NPM version (e.g., `0.2.0-main.522`). Extract the run number (522).
### Step 2: Map NPM Version to Git SHA
Query the GitHub Actions API to find the commit that produced that NPM build. See
`references/methodology.md` for the exact commands.
### Step 3: Analyze Breaking Changes
Compare the current pinned rev against the target rev, focusing on the three crates:
```bash
cd /path/to/sdk-internal
git log --oneline <old-rev>..<new-rev> -- crates/bitwarden-core crates/bitwarden-crypto crates/bitwarden-vault
```
Cross-reference each commit against the API surface documented in `references/api-surface.md`.
### Step 4: Apply Changes
1. Update `Cargo.toml` — bump all three rev pins to the same SHA
2. Fix any compilation errors from breaking changes (type renames, new struct fields, etc.)
3. Add `#[allow(deprecated)]` for any newly-deprecated APIs (with a comment explaining why)
### Step 5: Build and Verify (Claude)
```bash
cd util/RustSdk/rust
cargo build # Must compile cleanly
cargo test # All tests must pass (roundtrip test is critical)
cargo fmt --check # Formatting must be clean
git diff ../NativeMethods.g.cs # FFI signatures should be unchanged
```
Also run the C# integration tests:
```bash
dotnet test test/SeederApi.IntegrationTest/
```
### Step 6: Human Verification (HUMAN ONLY)
**Claude does NOT perform this step.** Present these commands to the human engineer and wait
for confirmation before proceeding.
The human runs SeederUtility and SeederApi to verify Protected Data is correctly produced and
decryptable by the web client. See `references/methodology.md` for the specific test commands
and validation criteria.
## Security Notes
- The RustSdk lives in `util/` (test infrastructure), not `src/` (production)
- The server never decrypts Vault Data — zero-knowledge invariant is unaffected
- Aligning with the production client release ensures the Seeder produces Protected Data
using the same cryptographic primitives as real clients
- Review `Cargo.lock` diff for unexpected transitive crypto crate changes (rsa, aes, sha2, etc.)
## Keeping References Current
The API surface reference (`references/api-surface.md`) must always reflect the actual code.
Two mechanisms enforce this:
1. **Post-bump step** — The `/bump-rust-sdk` command includes a mandatory final step to
regenerate `api-surface.md` by reading the actual `*.rs` source files.
2. **Stop hook**`.claude/hooks/rust-sdk-surface-check.sh` blocks if `Cargo.toml` was
modified but `api-surface.md` was not updated in the same session.
To regenerate: read all `.rs` files in `util/RustSdk/rust/src/`, extract every `use` statement
from the three bitwarden crates, and rewrite `references/api-surface.md` to match.
## Additional Resources
### Reference Files
- **`references/methodology.md`** — Detailed step-by-step commands including GitHub Actions API
queries, breaking change analysis checklist, human verification commands, and a worked example
from the Feb 2026 bump
- **`references/api-surface.md`** — Complete inventory of types, traits, and functions the RustSdk
imports from each crate, used to assess breaking change impact
## Files Modified in a Typical Bump
| File | Change |
| --------------------------------- | -------------------------------------------------- |
| `util/RustSdk/rust/Cargo.toml` | Rev pin update |
| `util/RustSdk/rust/src/*.rs` | Type renames, new struct fields, deprecation fixes |
| `util/RustSdk/rust/Cargo.lock` | Auto-regenerated (commit alongside) |
| `util/RustSdk/NativeMethods.g.cs` | Should NOT change (verify) |

View File

@@ -0,0 +1,116 @@
# RustSdk API Surface Inventory
> **Auto-generated from actual source files.** Last updated: 2026-02-25
> Pinned rev: `abba7fdab687753268b63248ec22639dff35d07c`
This documents every type, trait, and function the server's RustSdk imports from the three
sdk-internal crates. Use this to assess breaking change impact when bumping revs.
**Location:** `util/RustSdk/rust/src/`
## bitwarden-crypto (primary dependency)
### Types Used
| Type | File | Usage |
| ------------------------- | ----------------- | -------------------------------------------------------------------------------------------- |
| `BitwardenLegacyKeyBytes` | lib.rs, cipher.rs | `BitwardenLegacyKeyBytes::from()` — wraps raw key bytes for `SymmetricCryptoKey::try_from()` |
| `HashPurpose` | lib.rs | `HashPurpose::ServerAuthorization` enum variant |
| `Kdf` | lib.rs | `Kdf::PBKDF2 { iterations }` enum variant with `NonZeroU32` |
| `KeyStore` | cipher.rs | `KeyStore::<KeyIds>::default()`, `.context_mut()`, `.add_local_symmetric_key()` |
| `MasterKey` | lib.rs | `MasterKey::derive()`, `.derive_master_key_hash()`, `.make_user_key()` |
| `PrivateKey` | lib.rs | `PrivateKey::from_pem()`, `.to_public_key()`, `.to_der()` |
| `PublicKey` | lib.rs | `PublicKey::from_der()` |
| `RsaKeyPair` | lib.rs | Struct literal: `RsaKeyPair { private, public }` |
| `SpkiPublicKeyBytes` | lib.rs | `SpkiPublicKeyBytes::from()` — wraps public key DER bytes |
| `SymmetricCryptoKey` | lib.rs, cipher.rs | `.make_aes256_cbc_hmac_key()`, `::try_from()`, `.to_base64()` |
| `UnsignedSharedKey` | lib.rs | `::encapsulate_key_unsigned()` (deprecated — wrapped with `#[allow(deprecated)]`) |
| `UserKey` | lib.rs | `UserKey::new()`, `.make_key_pair()`, `.0` field access |
### Traits Used
| Trait | File | Methods Called |
| ---------------------- | ----------------- | -------------------------------------------------------------------------- |
| `KeyEncryptable` | lib.rs, cipher.rs | `.encrypt_with_key(&key)` — encrypts DER bytes and strings |
| `CompositeEncryptable` | cipher.rs | `.encrypt_composite(&mut ctx, key_id)` — encrypts `CipherView` -> `Cipher` |
| `Decryptable` | cipher.rs | `.decrypt(&mut ctx, key_id)` — decrypts `Cipher` -> `CipherView` |
## bitwarden-core (minimal dependency)
| Type | File | Usage |
| ------------------------ | --------- | -------------------------------------------- |
| `key_management::KeyIds` | cipher.rs | Generic type parameter: `KeyStore::<KeyIds>` |
## bitwarden-vault (data model dependency)
### Production Code
| Type | File | Usage |
| ------------ | --------- | ----------------------------------------------------- |
| `Cipher` | cipher.rs | Protected Data container (encrypted), deserialized from JSON via serde |
| `CipherView` | cipher.rs | Vault Data in Use (decrypted view), serialized to/from JSON via serde |
### Test-Only Types
| Type | File | Usage |
| -------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `CipherRepromptType` | cipher.rs tests | `CipherRepromptType::None` enum variant |
| `CipherType` | cipher.rs tests | `CipherType::Login` enum variant |
| `LoginView` | cipher.rs tests | Struct literal with fields: `username`, `password`, `password_revision_date`, `uris`, `totp`, `autofill_on_page_load`, `fido2_credentials` |
### CipherView Fields (used in test struct literal)
The test helper `create_test_cipher_view()` constructs a `CipherView` with these fields:
`id`, `organization_id`, `folder_id`, `collection_ids`, `key`, `name`, `notes`, `type`,
`login`, `identity`, `card`, `secure_note`, `ssh_key`, `favorite`, `reprompt`,
`organization_use_totp`, `edit`, `permissions`, `view_password`, `local_data`, `attachments`,
`attachment_decryption_failures`, `fields`, `password_history`, `creation_date`, `deleted_date`,
`revision_date`, `archived_date`
Any new required field added to `CipherView` upstream will break this struct literal.
## Breaking Change Risk Matrix
When reviewing upstream commits, prioritize checking for changes to:
**Critical (compilation failure):**
- Any type rename or removal listed above
- New required fields on `CipherView`, `Cipher`, or `LoginView`
- Changes to `KeyStore` generic parameters or `context_mut()` method
- Changes to encryption/decryption trait method signatures
**High (runtime failure):**
- Changes to `Kdf::PBKDF2` enum variant
- Changes to `HashPurpose::ServerAuthorization`
- Changes to `MasterKey::derive()` or key derivation behavior
- Changes to `UnsignedSharedKey::encapsulate_key_unsigned()` signature
**Medium (deprecation warnings):**
- Functions annotated with `#[deprecated]` — suppress with `#[allow(deprecated)]`
and add a comment explaining why and what the migration path is
**Low (transparent):**
- Internal implementation changes that don't affect the public API
- New optional fields on structs (serde defaults to `None` for `Option<T>`)
- New methods added to existing types (additive, non-breaking)
## How to Check for Changes
For each crate, check the public API exports:
```bash
cd /path/to/sdk-internal
# bitwarden-crypto public API
git diff <old>..<new> -- crates/bitwarden-crypto/src/lib.rs crates/bitwarden-crypto/src/keys/mod.rs
# bitwarden-vault Cipher/CipherView changes
git diff <old>..<new> -- crates/bitwarden-vault/src/cipher/cipher.rs crates/bitwarden-vault/src/cipher/login.rs
# bitwarden-core KeyIds
git diff <old>..<new> -- crates/bitwarden-core/src/key_management/mod.rs
```

View File

@@ -0,0 +1,206 @@
# Bump sdk-internal: Detailed Methodology
## Step-by-Step Process
### 1. Identify the Current Server Pin
```bash
grep 'rev = ' util/RustSdk/rust/Cargo.toml
```
All three crates (`bitwarden-core`, `bitwarden-crypto`, `bitwarden-vault`) must always pin to
the **same rev**. If they don't, something is wrong.
### 2. Identify the Target Version from Clients
Determine the latest production release tag from the clients repo:
```bash
# Check latest web release
gh release list --repo bitwarden/clients --limit 5 | grep web-v
# Get the sdk-internal NPM version at that tag
cd /path/to/clients
git show <tag>:package.json | grep sdk-internal
```
Example output: `"@bitwarden/sdk-internal": "0.2.0-main.522"`
The run number is **522** — the last segment after the branch name.
### 3. Map NPM Run Number to Git SHA
The NPM version encodes a GitHub Actions workflow run number, not a git tag. Query the API:
```bash
# Find the publish workflow ID
gh api "repos/bitwarden/sdk-internal/actions/workflows" \
--jq '.workflows[] | "\(.id) \(.name)"' | grep -i "Publish.*sdk-internal"
# Query for the specific run number (replace WORKFLOW_ID and RUN_NUMBER)
gh api "repos/bitwarden/sdk-internal/actions/workflows/WORKFLOW_ID/runs?per_page=100" \
--jq '.workflow_runs[] | select(.run_number == RUN_NUMBER) | "\(.run_number) \(.head_sha) \(.created_at) \(.head_branch)"'
```
The `head_sha` in the output is the git commit to pin in Cargo.toml.
**If the run is older than 100 runs ago**, paginate:
```bash
gh api "repos/bitwarden/sdk-internal/actions/workflows/WORKFLOW_ID/runs?per_page=100&page=2" \
--jq '.workflow_runs[] | select(.run_number == RUN_NUMBER) | ...'
```
### 4. Verify the Current Pin (for context)
```bash
cd /path/to/sdk-internal
git log --oneline -1 <current-rev>
```
This shows when the current pin was made and what commit it corresponds to.
### 5. Analyze Breaking Changes
List all commits touching the three crates between the old and new revs:
```bash
cd /path/to/sdk-internal
git log --oneline <old-rev>..<new-rev> -- \
crates/bitwarden-core crates/bitwarden-crypto crates/bitwarden-vault
```
For each commit, check for:
- **Type renames** (e.g., `AsymmetricCryptoKey` -> `PrivateKey`)
- **New required struct fields** (e.g., new `Option<T>` fields on `CipherView`)
- **Removed or deprecated functions** (look for `#[deprecated]` annotations)
- **Changed function signatures** (parameter types, return types)
- **Trait changes** (new required methods, changed generic bounds)
To check the public API diff for a specific crate:
```bash
git diff <old-rev>..<new-rev> -- crates/bitwarden-crypto/src/keys/mod.rs
git diff <old-rev>..<new-rev> -- crates/bitwarden-crypto/src/lib.rs
git diff <old-rev>..<new-rev> -- crates/bitwarden-vault/src/cipher/cipher.rs
```
Cross-reference findings against `references/api-surface.md` to assess impact.
### 6. Apply Code Changes
1. **Cargo.toml** — Update all three `rev = "..."` to the new SHA
2. **Rust source files** — Fix compilation errors from breaking changes
3. **Deprecation warnings** — Add `#[allow(deprecated)]` with a comment explaining why
4. Do NOT make unrelated formatting or style changes
### 7. Build and Test (Claude)
```bash
cd util/RustSdk/rust
# Compile
cargo build
# Review transitive dependency changes (focus on crypto crates)
git diff Cargo.lock | grep "^[+-]name\|^[+-]version" | head -40
# Verify FFI signatures unchanged
git diff ../NativeMethods.g.cs
# Run Rust tests (roundtrip test is critical)
cargo test
# Run C# integration tests
dotnet test test/SeederApi.IntegrationTest/
# Format checks
cargo fmt --check
```
**Key validation:** The `encrypt_decrypt_roundtrip_preserves_plaintext` test proves the new SDK
version correctly encrypts and decrypts Vault Data. If this passes, the crypto is working.
### 8. Human Verification (HUMAN ONLY — Claude does NOT run these)
Present these commands to the human engineer. Wait for confirmation before proceeding.
**SeederUtility — seed an org with vault data:**
```bash
cd util/SeederUtility
dotnet run -- organization -n SdkBumpTest -d sdk-bump-test.example -u 3 -c 10 -g 5 -o Traditional -m
```
**SeederUtility — seed a fixture preset:**
```bash
dotnet run -- seed --preset dunder-mifflin-enterprise-full --mangle
```
**SeederApi — start and seed via HTTP:**
You need to replace the empty password argument with at least an 8-character master password for the fake user account
```bash
cd util/SeederApi
dotnet run
# In another terminal:
curl -X POST http://localhost:5000/seed \
-H "Content-Type: application/json" \
-H "X-Play-Id: sdk-bump-test" \
-d '{"template": "SingleUserScene", "arguments": {"email": "test@example.com", "password": ""}}'
```
**SeederApi — cleanup:**
```bash
curl -X DELETE http://localhost:5000/seed/sdk-bump-test
```
**Validation criteria (human checks):**
- Seeded users can log in to the web vault with the fake master password
- See `util/SeederUtility/README.md` for the default master password used by Seeder
- Vault Data (ciphers) is visible and decryptable in the web client
- No errors in SeederUtility or SeederApi output
- SeederApi cleanup deletes all tracked entities
---
## Worked Example: February 2026 Bump
This section documents the actual bump performed in Feb 2026 as a reference.
### Context
- **Old rev:** `7080159154a42b59028ccb9f5af62bf087e565f9` (2025-11-20)
- **Target:** `web-v2026.2.0` production release
- **NPM version:** `@bitwarden/sdk-internal` `0.2.0-main.522`
- **Workflow ID:** `126086102` (Publish @bitwarden/sdk-internal)
- **New rev:** `abba7fdab687753268b63248ec22639dff35d07c` (2026-02-05)
### Breaking Changes Found
| Change | Impact | Fix |
| --------------------------------------------------------- | ---------------------------------------- | ------------------------------------------ |
| `AsymmetricCryptoKey` renamed to `PrivateKey` | Import + usage in lib.rs | Rename type |
| `AsymmetricPublicCryptoKey` renamed to `PublicKey` | Import + usage in lib.rs | Rename type |
| `CipherView` added `attachment_decryption_failures` field | Test struct literal in cipher.rs | Add `attachment_decryption_failures: None` |
| `PrivateKey::to_der()` returns `Pkcs8PrivateKeyBytes` | Low risk — auto-refs to `KeyEncryptable` | No code change needed |
| `encapsulate_key_unsigned` deprecated | Deprecation warning | `#[allow(deprecated)]` + comment |
### Cargo.lock Review
- All bitwarden-\* crates: `1.0.0` -> `2.0.0` (workspace version bump — expected)
- `coset`: `0.3.8` -> `0.4.1` (COSE library — minor bump)
- New transitive deps: `mockall`, `predicates`, `tracing-attributes` (test/dev deps)
- **No changes to core crypto crates** (rsa, aes, sha2, hmac, pbkdf2)
### Results
- 7/7 Rust unit tests passed
- 65/65 C# integration tests passed
- NativeMethods.g.cs unchanged
- Human verification: login, vault decryption, seeding all confirmed working