Open-Source Wikis

/

Bitwarden Server

/

How to contribute

/

Development workflow

bitwarden/server

Development workflow

Branches

  • main is the trunk. Every PR targets main.
  • Release-candidate branches are named rc-YYYY-MM and cut from main shortly before a release. The publish workflow (.github/workflows/publish.yml) tags from rc branches.
  • The metadata branch holds the JSON consumed by the README image-hash badges; it is written back to by the publish workflow.

Local cycle

  1. git checkout -b feature/PM-12345-short-name
  2. Code + tests.
  3. Run the relevant tests: dotnet test test/Core.Test/Core.Test.csproj. For controller-level changes, also run test/Api.IntegrationTest. Integration tests require the Docker cloud profile to be up.
  4. Format: dotnet format (or set up the pre-commit hook with git config --local core.hooksPath .git-hooks).
  5. git push origin HEAD and open a PR against main.

Database migrations

Because the repo supports both Dapper-on-SQL-Server and EF on three other engines, every schema change is dual-tracked:

  1. Canonical T-SQL — drop / alter / create the object inside src/Sql/dbo/. Tables under src/Sql/dbo/Tables/, sprocs under src/Sql/dbo/Stored Procedures/, etc. The Sql.sqlproj SQL Server Database Project asserts the schema is buildable end-to-end.
  2. Forward migration scriptutil/Migrator/DbScripts/YYYY-MM-DD_xx_<slug>.sql. Idempotent T-SQL applied in alphabetical order; the migrator writes a row to dbo.Migration per script.
  3. EF Core migrationsdotnet ef migrations add <Name> --project util/MySqlMigrations, ditto util/PostgresMigrations and util/SqliteMigrations. Each migration class lives in the appropriate naming-convention subfolder (Snapshot, Migrations).
  4. Run./dev/migrate.ps1 for SQL Server, ./dev/ef_migrate.ps1 for 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/:

  1. Add a request / response model under src/Api/<Domain>/Models/Request|Response/.
  2. Add or extend a controller under src/Api/<Domain>/Controllers/. Decorate with [Authorize(Policy = Policies.Application)] (or whatever policy applies) and an explicit route.
  3. If the operation can be expressed as a command / query, add it under src/Core/<Domain>/Commands or Queries and inject the interface — the domain layer encapsulates business rules.
  4. If the call needs authorization beyond the JWT scope (for example "the user must own this cipher"), add an IAuthorizationRequirement + AuthorizationHandler in the domain (e.g. src/Core/Vault/AuthorizationHandlers/) and call IAuthorizationService.AuthorizeAsync.
  5. Update the OpenAPI generation (./dev/generate_openapi_files.ps1) if you ship a public-API change. The Public Swagger group is enforced by PublicApiControllersModelConvention.

Adding a feature flag

  1. Add a public const string My_Flag = "my-flag"; constant under FeatureFlagKeys in src/Core/Constants.cs.
  2. Read it via IFeatureService.IsEnabled(FeatureFlagKeys.My_Flag, currentContext).
  3. 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 Failure status 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 from rc-* 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 the metadata branch.
  • 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.

Development workflow – Bitwarden Server wiki | Factory