bitwarden/server
Development workflow
Branches
mainis the trunk. Every PR targetsmain.- Release-candidate branches are named
rc-YYYY-MMand cut frommainshortly before a release. The publish workflow (.github/workflows/publish.yml) tags fromrcbranches. - The
metadatabranch holds the JSON consumed by the README image-hash badges; it is written back to by the publish workflow.
Local cycle
git checkout -b feature/PM-12345-short-name- Code + tests.
- Run the relevant tests:
dotnet test test/Core.Test/Core.Test.csproj. For controller-level changes, also runtest/Api.IntegrationTest. Integration tests require the Dockercloudprofile to be up. - Format:
dotnet format(or set up the pre-commit hook withgit config --local core.hooksPath .git-hooks). git push origin HEADand open a PR againstmain.
Database migrations
Because the repo supports both Dapper-on-SQL-Server and EF on three other engines, every schema change is dual-tracked:
- Canonical T-SQL — drop / alter / create the object inside
src/Sql/dbo/. Tables undersrc/Sql/dbo/Tables/, sprocs undersrc/Sql/dbo/Stored Procedures/, etc. TheSql.sqlprojSQL Server Database Project asserts the schema is buildable end-to-end. - Forward migration script —
util/Migrator/DbScripts/YYYY-MM-DD_xx_<slug>.sql. Idempotent T-SQL applied in alphabetical order; the migrator writes a row todbo.Migrationper script. - EF Core migrations —
dotnet ef migrations add <Name> --project util/MySqlMigrations, dittoutil/PostgresMigrationsandutil/SqliteMigrations. Each migration class lives in the appropriate naming-convention subfolder (Snapshot,Migrations). - Run —
./dev/migrate.ps1for SQL Server,./dev/ef_migrate.ps1for the EF providers, then run the test suite.
The CI workflow .github/workflows/test-database.yml boots SQL Server, MySQL, Postgres, and SQLite; runs the migrator against each; and asserts that verify_migrations.ps1 returns no diff.
If your change moves a stored procedure or column, double-check the corresponding repository implementations in src/Infrastructure.Dapper/ and src/Infrastructure.EntityFramework/. The two paths are not generated; they are hand-written.
Adding a new endpoint
The general flow inside src/Api/:
- Add a request / response model under
src/Api/<Domain>/Models/Request|Response/. - Add or extend a controller under
src/Api/<Domain>/Controllers/. Decorate with[Authorize(Policy = Policies.Application)](or whatever policy applies) and an explicit route. - If the operation can be expressed as a command / query, add it under
src/Core/<Domain>/CommandsorQueriesand inject the interface — the domain layer encapsulates business rules. - If the call needs authorization beyond the JWT scope (for example "the user must own this cipher"), add an
IAuthorizationRequirement+AuthorizationHandlerin the domain (e.g.src/Core/Vault/AuthorizationHandlers/) and callIAuthorizationService.AuthorizeAsync. - Update the OpenAPI generation (
./dev/generate_openapi_files.ps1) if you ship a public-API change. ThePublicSwagger group is enforced byPublicApiControllersModelConvention.
Adding a feature flag
- Add a
public const string My_Flag = "my-flag";constant underFeatureFlagKeysinsrc/Core/Constants.cs. - Read it via
IFeatureService.IsEnabled(FeatureFlagKeys.My_Flag, currentContext). - The flag resolves through LaunchDarkly in the cloud and returns the static default in self-host. Configure cloud LaunchDarkly through the platform-team-managed dashboard.
Localization
User-facing strings sit under src/Core/Resources/ (SharedResources.resx) and src/Admin/Sass/ for admin views. Mail bodies live in src/Core/MailTemplates/Mjml/<Team>/ and are rendered at runtime by HandlebarsMailService.
Definition of done
A PR is "done" when:
- Builds locally (
dotnet build) and on CI. - All affected unit tests pass; new behaviour has a test.
- Database migrations exist for all four providers if the schema changed.
- CODEOWNERS-required reviewers have approved.
- No
Failurestatus from the security scan workflow (.github/workflows/scan.yml). - Swagger doc regenerated if the public API surface changed.
Release engineering (for context)
- Tags follow
YYYY.MM.x— semver-ish year/month numbers — created fromrc-*branches by the BRE team via.github/workflows/release.yml. - The publish workflow builds Docker images for every deployable service and pushes them to
ghcr.io/bitwarden/<service>. Production-image hashes are then committed to themetadatabranch. - Self-hosted users pin to release tags via
bitwarden.sh updateself.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.