

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# Bitwarden Seeder Library - Claude Code Configuration23## Quick Reference45**For detailed pattern descriptions (Factories, Recipes, Models, Scenes, Queries, Data), read `README.md`.**67**For detailed usages of the Seeder library, read `util/SeederUtility/README.md` and `util/SeederApi/README.md`**89## Commands1011```bash12# Build13dotnet build util/Seeder/Seeder.csproj1415# Run tests16dotnet test test/SeederApi.IntegrationTest/1718# Run single test19dotnet test test/SeederApi.IntegrationTest/ --filter "FullyQualifiedName~TestMethodName"20```2122## Pattern Decision Tree2324```25Need to create test data?26├─ ONE entity with encryption? → Factory27├─ ONE cipher from a SeedVaultItem? → CipherSeed.FromSeedItem() + {Type}CipherSeeder.Create()28├─ MANY entities as cohesive operation? → Recipe or Pipeline29├─ Flexible preset-based seeding? → Pipeline (RecipeBuilder + Steps)30├─ Complete test scenario with ID mangling? → Scene31├─ READ existing seeded data? → Query32└─ Data transformation plaintext ↔ encrypted? → Model33```3435## Pipeline Architecture3637**Modern pattern for composable fixture-based and generated seeding.**3839**Flow**: Preset JSON or Options → RecipeOrchestrator → RecipeBuilder → IStep/IAsyncStep[] → RecipeExecutor → SeederContext → BulkCommitter → IPostCommitStep[]4041**Key actors**:4243- **RecipeBuilder**: Fluent API with dependency validation44- **IStep / IAsyncStep**: Isolated units of work (CreateOrganizationStep, CreateUsersStep, etc.). Use `IAsyncStep` for steps that do real I/O. A step additionally marked `IPostCommitStep` is deferred until after the bulk commit, so it observes committed rows — but sees cleared entity lists, since only the `EntityRegistry` and the context's scalar properties survive the commit.45- **SeederContext**: Shared mutable state bag (NOT thread-safe)46- **RecipeExecutor**: Awaits steps sequentially, captures statistics, commits via BulkCommitter, then runs any post-commit steps47- **RecipeOrchestrator**: Orchestrates recipe building and execution (from presets or options)48- **SeederDependencies** (`Options/`): Bundles infrastructure services (`DatabaseContext`, `IMapper`, `IPasswordHasher<User>`, `IManglerService`, `ILicensingService`, `IAttachmentStorageService`) into a single record. Recipes and the Orchestrator accept this instead of loose parameters. The CLI utility builds it via `SeederServiceFactory.Create().ToDependencies()`.4950**Why two step interfaces, not one async contract?** Deliberate — don't unify. Collapsing to one `Task ExecuteAsync(SeederContext)` costs: rewrite 22 step classes (18 in `Steps/`, 4 test doubles); force 20 `.Execute(context)` sites in `test/SeederApi.IntegrationTest/Steps/` to `await`, their test methods to `async`; and `TreatWarningsAsErrors` is on repo-wide (`Directory.Build.props`), so CS1998 makes `async` without `await` a build error — every sync step needs `return Task.CompletedTask`. Permanent trap. The split costs less: two-arm union in `OrderedStep`, `object`-typed `Inner`, one duplicated `RecipeBuilder` registration. Diverges from `IScene`/`IQuery` — single `Task`-returning, no sync twin.5152**Fixture/preset separation**: Fixtures (organizations, rosters, ciphers) are independent and never reference each other. The preset is the only layer that composes fixtures and defines cross-cutting relationships (folder assignments, favorites). See `Seeds/docs/architecture.md`.5354**Phase order (org presets)**: Org → OrgApiKey → Roster → Owner (conditional) → Generator (conditional) → Users → Groups → Collections → Folders → Ciphers → CipherAttachments → CipherCollections → CipherFolders → CipherFavorites → PersonalCiphers55**Phase order (individual presets)**: IndividualUser → NamedFolders → Generator → Folders → Ciphers → CipherAttachments → FolderAssignments → FavoriteAssignments5657**Individual user presets** use the Pipeline with `CreateIndividualUserStep` (no org, no groups, no collections). These presets live in `Seeds/fixtures/presets/individual/` and are identified by having a `"user"` key instead of `"organization"`. They support `folderNames`, `folderAssignments`, and `favoriteAssignments` for fixture-driven personal vault organization. See `Seeds/docs/presets.md` for the catalog.5859See `Pipeline/` folder for implementation.6061## Parallelism6263Steps execute sequentially (phase order preserved by RecipeExecutor). Async steps are awaited one at a time and MUST NOT be batched with `Task.WhenAll` — `SeederContext` is not thread-safe and each step reads state written by the ones before it. Within a step, `CreateUsersStep` and `GeneratePersonalCiphersStep` use `Parallel.For` internally for CPU-bound Rust FFI work (key generation, encryption).6465**Thread-safety requirements:**6667- `GeneratorContext` lazy properties (`??=`) must be force-initialized before any `Parallel.For` loop to prevent a data race68- Generators use `ThreadLocal<Faker>` for thread-safe deterministic data generation69- `ManglerService` and `SeederContext` are NOT thread-safe -- pre-compute their outputs before entering parallel loops7071## Performance A/B Testing7273When measuring step-level performance changes, use paired worktrees:7475- Create `server-PM-XXXXX/perf-baseline` and `server-PM-XXXXX/perf-optimized` worktrees76- Both worktrees get `Stopwatch` timing in `RecipeExecutor.ExecuteAsync()` (the baseline measurement)77- Only the optimized worktree gets actual code changes78- Run presets with `--mangle` flag to avoid DB collisions between runs79- Compare per-step timings across 3+ runs each, discard the first run (JIT warmup)80- `.worktrees/` is already in `.gitignore`8182## Density Profiles8384Steps accept an optional `DensityProfile` that controls relationship patterns between users, groups, collections, and ciphers. When null, steps use the original round-robin behavior. When present, steps branch into density-aware algorithms.8586**Key files**:8788- `Options/DensityProfile.cs` — strongly-typed options (public class)89- `Models/SeedPresetDensity.cs` — JSON preset deserialization targets (internal records)90- `Data/Enums/MembershipDistributionShape.cs` — Uniform, PowerLaw, MegaGroup91- `Data/Enums/CollectionFanOutShape.cs` — Uniform, PowerLaw, FrontLoaded92- `Data/Enums/CipherCollectionSkew.cs` — Uniform, HeavyRight93- `Data/Distributions/PermissionDistributions.cs` — 11 named distributions by org tier9495**Backward compatibility contract**: `DensityProfile? == null` MUST produce identical output to the original code. Every step guards this with `if (_density == null) { /* original path */ }`.9697**Preset JSON**: Add an optional `"density": { ... }` block. See `Seeds/schemas/preset.schema.json` for the full schema.9899**Presets**: Organized into `features/`, `qa/`, `scale/`, `validation/` folders under `Seeds/fixtures/presets/`. See `Seeds/docs/presets.md` for the full catalog.100101**Verification**: SQL queries for validating density algorithms are in `Seeds/docs/verification.md`.102103## Regression Testing104105Changes to `Factories/`, `Steps/`, `Scenes/`, or `Recipes/` need more than the unit suite — it covers none of the CLI, the SeederApi, or a real database. `Seeds/docs/regression.md` maps each changed path to the preset that reaches it and the assertion that proves it, and records the known non-regressions worth not chasing. Claude drives the CLI, API, and SQL; the developer smoke-tests the web vault.106107## Data/ File Organization108109New files under `Data/` belong in the matching subfolder (`Distributions/`, `Enums/`, `Generators/`, `Static/`) — never loose at the top level. See `Data/README.md` for what each subfolder holds. If a new file's concern doesn't fit an existing subfolder, that's a signal to create one, not to drop it loose.110111**Two Enums homes, by concern:** `Data/Enums/` (namespace `Bit.Seeder.Data.Enums`) holds the generation-config surface (`CompanyType`, `PasswordStrength`, distribution shapes, etc. — "Enums are the API"). Crypto-taxonomy enums that describe how seeded vault data is encrypted (`CipherEncryptionType`, `AttachmentSchemeType`) live in the top-level `Enums/` folder (namespace `Bit.Seeder.Enums`), one enum per file.112113## The Recipe Contract114115Recipes follow strict rules:1161171. A Recipe SHALL accept `SeederDependencies` as its single constructor parameter1182. A Recipe SHALL have exactly one public entry point — `Seed()` when synchronous, `SeedAsync()` when it returns `Task`/`Task<T>`. Pipeline-backed Recipes (`OrganizationRecipe`, `IndividualUserRecipe`) are async; the direct-to-database Recipes (`CollectionsRecipe`, `GroupsRecipe`, `OrganizationDomainRecipe`, `OrganizationWithUsersRecipe`) remain synchronous.1193. A Recipe MUST produce one cohesive result1204. A Recipe MAY overload that entry point with different parameters1215. A Recipe SHALL use private helper methods for internal steps1226. A Recipe SHALL use BulkCopy for performance when creating multiple entities1237. A Recipe SHALL compose Factories for individual entity creation1248. A Recipe SHALL NOT expose implementation details as public methods125126## Zero-Knowledge Architecture127128**Critical:** Unencrypted vault data never leaves the client. The server never sees plaintext.129130The Seeder uses the Rust SDK via FFI because it must behave like a real Bitwarden client:1311321. Generate encryption keys (like client account setup)1332. Encrypt vault data client-side (same SDK as real clients)1343. Store only encrypted result135136## Data Flow137138### Pipeline path (fixture → entity)139140```141SeedVaultItem → CipherSeed.FromSeedItem() → CipherSeed → {Type}CipherSeeder.Create(options) → CipherViewDto → encrypt_fields (Rust FFI) → EncryptedCipherDto → EncryptedCipherDtoExtensions → Server Cipher Entity142```143144### Core encryption (shared by all paths)145146```147CipherViewDto → JSON + [EncryptProperty] field paths → encrypt_fields (Rust FFI, bitwarden_crypto) → EncryptedCipherDto → EncryptedCipherDtoExtensions → Server Cipher Entity148```149150Shared logic: `Factories/CipherEncryption.cs`, `Models/EncryptedCipherDtoExtensions.cs`151152## Rust Crypto Dependency153154The Rust shim (`util/RustSdk/rust/`) depends only on `bitwarden_crypto`. It does **not** depend on `bitwarden_vault` — the seeder drives field selection via `[EncryptProperty]` attributes, not SDK cipher types.155156Before modifying encryption integration, run `RustSdkCipherTests` to validate roundtrip encryption.157158## Encryption Schemes (crypto taxonomy)159160Seeded data spans two orthogonal encryption axes, named with Bitwarden's canonical vocabulary (defined in `Enums/CipherEncryptionType.cs` and `Enums/AttachmentSchemeType.cs`):161162- **Cipher encryption** (`cipherEncryption`): `userKey` (no cipher key; `Cipher.Key` null) or `cipherKey` (per-cipher key wrapped by the vault key).163- **Attachment scheme version** (`attachmentVersion`): `v0` (no attachment key), `v1` (attachment key wrapped by the vault key), `v2` (attachment key wrapped by the cipher key).164165**Invariant:** a cipher and its attachments use the same strategy — `v2` requires a `cipherKey` host. `Steps/CreateCipherAttachmentsStep.cs` and `Seeds/schemas/cipher.schema.json` both enforce this; keep them in sync.166167**Wire mapping:** `AttachmentSchemeType.{V0,V1,V2}` casts to `u32 {0,1,2}` and is matched verbatim in `util/RustSdk/rust/src/attachment.rs`. The value *is* the version number — do not reintroduce an offset.168169**Do not conflate with account Encryption V1/V2.** Attachment `v0/v1/v2` is key-wrapping only. Everything the seeder emits is Encryption-V1 type-2 `EncString` (AES-256-CBC-HMAC); no COSE/type-7 path exists. A future V2/COSE capability is a **separate** axis (a new enum), never a new attachment version.170171## Deterministic Data Generation172173Same domain = same seed = reproducible data:174175```csharp176var seed = options.Seed ?? DeriveStableSeed(options.Domain);177```178179## Scenarios180181Developer-facing documentation in `Seeds/docs/scenarios/`. Each file maps an engineering problem to a Seeder command.182183**Maintenance rules:**184185- When adding a new preset, check if an existing scenario should reference it as a variation186- When adding a new command or flag, check if it enables a new scenario or changes an existing one187- When CLI flags, commands, or preset names change, scan all `*.md` files under `Seeds/` and `SeederUtility/` for stale references188- Scenario files follow the template in `Seeds/docs/scenarios/README.md`189- Never duplicate CLI flag documentation — link to `SeederUtility/README.md`190- Never duplicate preset catalog details — link to `Seeds/docs/presets.md`191- Scenarios describe _why_ (the problem). READMEs describe _how_ (the tool). Keep the split clean.192193**File relationships:**194195- `SeederUtility/README.md` → CLI reference (commands, flags, examples) → links to scenarios196- `Seeds/docs/presets.md` → what exists (the catalog) → scenarios link back to it197- `Seeds/docs/scenarios/` → why you'd use it (problem → command)198199## Collection Management Settings200201**Collection management settings are not plan-gated.** `AllowAdminAccessToAllCollectionItems`, `LimitCollectionCreation`, `LimitCollectionDeletion`, and `LimitItemDeletion` apply identically across all plan types. They are org-level admin settings, not billing-plan features.202203**These settings alter access control behavior.** When seeding scenarios that test member vs. admin permissions, collection creation/deletion policies, or item-level access, set them explicitly in the preset rather than relying on defaults.204205**Configurable in presets and CLI.** Use the JSON preset `organization` block (e.g. `"limitCollectionCreation": true`) or the CLI flags: `--limit-collection-creation`, `--limit-collection-deletion`, `--limit-item-deletion`, `--allow-admin-collection-access`.206207## Security Reminders208209- Default test password: `asdfasdfasdf` (overridable via `--password` CLI flag or `SeederSettings`)210- Never commit database dumps with seeded data211- Seeded keys are for testing only212
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| bitwarden/server.claude/CLAUDE.md · 20k | CLAUDE.md | archsecuritydo-notagent-behaviour | 78/100 | 14 days ago |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| stacklok/toolhiveCLAUDE.md · 2.0k | CLAUDE.md | buildteststylearch+4 | 100/100 | 14 days ago | |
| Adit-Jain-srm/NightmareNetCLAUDE.md · 46 | CLAUDE.md | buildtestlint-formatstyle+6 | 100/100 | 14 days ago | |
| microsoft/playwrightCLAUDE.md · 95k | CLAUDE.md | buildtestlint-formatstyle+7 | 100/100 | 7 days ago | |
| nimbalyst/nimbalystpackages/android/CLAUDE.md · 1.5k | CLAUDE.md | setupbuildstylearch+2 | 100/100 | 14 days ago | |
| tphakala/birdnet-goCLAUDE.md · 1.6k | CLAUDE.md | buildtestlint-formatstyle+8 | 100/100 | today | |
| dotCMS/corecore-web/CLAUDE.md · 949 | CLAUDE.md | teststylearchtesting-strategy+3 | 100/100 | 14 days ago | |
| tyrchen/geektime-bootcamp-aiw7/genslides/backend/CLAUDE.md · 230 | CLAUDE.md | testlint-formatstylearch+6 | 100/100 | 9 days ago | |
| bagisto/bagistoCLAUDE.md · 28k | CLAUDE.md | setupbuildteststyle+5 | 100/100 | 7 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/bitwarden-server-util-seeder-claude)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.