Bitwarden Server

repository·main·Indexed 12 days ago

https://github.com/bitwarden/server

Core APIs, database infrastructure, and backend services that power Bitwarden client applications. Includes documentation for local development using .NET Aspire, Docker-based deployment, and system administration via the bitwarden-admin portal.

Tokens
96.2K
Snippets
222
Records
342
Agent score
97%

What's inside Bitwarden

  1. Overview of the Bitwarden Database Migrator

    main

    The Bitwarden Database Migrator is a class library used to perform SQL database migrations. It is designed to ensure database schemas are up-to-date before application deployment.

    It is primarily utilized in two ways:

    1. Hosted Applications: Integrated into application startup via services like DatabaseMigrationHostedService.
    2. Utilities: Leveraged by standalone tools such as the MsSqlMigratorUtility.

    While the library is general-purpose, the default implementation provided is a SqlServerDbMigrator for MSSQL.

  2. Overview of the Bitwarden PostgreSQL Database Migrator

    main
    The Bitwarden PostgreSQL Database Migrator is a class library used to perform PostgreSQL database migrations using Entity Framework. It is leveraged by hosted applications, such as the DatabaseMigrationHostedService, to ensure the database schema is kept up to date with the application code.
  3. Overview of the Bitwarden MSSQL Database Migrator Utility

    main
    The Bitwarden MSSQL Database Migrator Utility is a command-line tool designed to perform MSSQL database migrations for both self-hosted and cloud Bitwarden deployments. It utilizes the DbUp library to manage the execution and tracking of SQL scripts. The utility ensures reliability by running scripts in a specific order and tracking which scripts have already been executed to prevent duplicate runs. It supports executing migrations either inside or outside of transactions depending on the specific application requirements.
  4. Use the Bitwarden SQLite Database Migrator for migrations

    main

    The Bitwarden SQLite Database Migrator is a class library used to perform SQLite database migrations using Entity Framework. It is primarily leveraged by hosted applications, such as the DatabaseMigrationHostedService, to automate schema updates.

    If you need to implement or manage database schema changes, refer to the official documentation on creating migrations for specific instructions on how to utilize the migration files within this library.

  5. Use the Bitwarden MySQL Database Migrator for Entity Framework migrations

    main

    The Bitwarden MySQL Database Migrator is a class library used by hosted applications (such as DatabaseMigrationHostedService) to execute MySQL database migrations using Entity Framework.

    To implement or utilize new migrations within the Bitwarden ecosystem, refer to the official Bitwarden contribution guide for Entity Framework migrations.

  6. What is ExtendedCache and how does it work?

    main

    Concept

    ExtendedCache is a wrapper around FusionCache designed to provide named, isolated caches with sensible defaults. It simplifies the registration of subsystem-specific caches that can include optional distributed caching (L2) and backplane support for multi-instance synchronization.

    Mental Model: L1 vs L2

    • L1 (In-process memory cache): The first layer of caching. It is fast but bounded by the application heap.
    • L2 (Distributed cache): The second layer, implemented via IDistributedCache (e.g., Redis, Cosmos DB, SQL Server, or EF).

    Data Flow: FusionCache reads from L1 first. If a miss occurs, it falls back to L2. When data is retrieved or set, it writes through to both layers.

    Key Features

    • Stampede Protection: Prevents multiple concurrent requests for the same key from hitting the backend simultaneously; only one request executes the factory, while others wait for the result.
    • Fail-safe Mode: Allows the system to serve stale data from the cache if the backend (factory) fails.
    • Eager Refresh: Automatically refreshes cache entries in the background before they expire (based on EagerRefreshThreshold) to prevent latency spikes at TTL boundaries.
    • Backplane Support: Uses Redis pub/sub to synchronize cache invalidations across multiple application instances (only available with Redis L2).
  7. What is Push Registration and how to use it

    main

    Push Registration is a feature used to manage devices that are eligible to receive push notifications. The primary entrypoint for interacting with this feature is the IPushRegistrationService.

    Important Integration Note: If you are developing a feature that modifies the following data, you must coordinate with the Platform team to ensure push registration remains functional:

    • Creating or deleting a Device.
    • Adding or removing an organization from a User.

    If a user's organization memberships change, you must call CreateOrUpdateRegistrationAsync with the updated memberships to ensure notifications are correctly targeted via tags.

  8. How the two-tier event integration architecture works

    main

    Bitwarden uses a two-tier messaging architecture to decouple event generation from event consumption. This allows for high scalability (fan-out) and robust error handling through retries.

    1. Event Tier (First Tier)

    Events are broadcast from the IEventWriteService (specifically the EventIntegrationEventWriteService) to an AMQP exchange or topic.

    • EventRepositoryHandler: Responsible for long-term storage of all events into the database or Azure Tables.
    • EventIntegrationHandler: A generic handler that checks for organization-specific configurations. It parses events into template strings and produces an IntegrationMessage<T>, which is then published to the second tier.

    2. Integration Tier (Second Tier)

    This tier handles the actual delivery to external services (e.g., Slack, Webhooks).

    • Integration Handlers: Specific logic for a single integration (e.g., SlackIntegrationHandler). They receive an IntegrationMessage<T> and return an IntegrationHandlerResult.
    • Integration Listeners: Manage the messaging platform (RabbitMQ/ASB) and execute the handler. They handle the outcome of the handler: success acknowledges the message, while failure triggers the retry logic.
  9. How the policy update workflow works

    main

    When an organization policy is created or updated via ISavePolicyCommand, the system executes an ordered sequence of steps. You can hook into these steps by implementing specific IPolicyUpdateEvent interfaces. If no handler is implemented, the policy is simply upserted to the database with an audit log event.

    The workflow sequence is:

    1. Validate organization can use policies
    2. Validate policy dependencies (via IEnforceDependentPoliciesEvent)
    3. Run policy-specific validation (via IPolicyValidationEvent)
    4. Execute pre-save side effects (via IOnPolicyPreUpdateEvent)
    5. Upsert policy + log event
    6. Execute post-save side effects (via IOnPolicyPostUpdateEvent)

    Important Limitation: The workflow is not atomic. If an exception occurs at any step, changes made by prior steps (like pre-save side effects) are not automatically rolled back.

    SaveAsync()
      ├─ 1. Validate organization can use policies
      ├─ 2. Validate policy dependencies     ← IEnforceDependentPoliciesEvent
      ├─ 3. Run policy-specific validation   ← IPolicyValidationEvent
      ├─ 4. Execute pre-save side effects    ← IOnPolicyPreUpdateEvent
      ├─ 5. Upsert policy + log event
      └─ 6. Execute post-save side effects  ← IOnPolicyPostUpdateEvent
  10. Understand Bitwarden Database Seeder Domain Taxonomy

    main

    The Seeder uses specific terminology to distinguish between plaintext and encrypted data states:

    • CipherView: Represents the plaintext/decrypted form (human-readable). This state is never stored in the database. The View suffix always denotes plaintext.
    • Cipher: Represents the encrypted form where all sensitive fields are EncStrings. This is the state stored in the database. No suffix denotes encrypted data.

    When working with the seeder, ensure you are targeting the correct state for your operation (e.g., providing a CipherView to a factory that will produce a Cipher).

  11. Filter Event Integrations with IntegrationFilterGroup

    main

    Admins can prevent certain events from being sent to an integration by defining Filters within the OrganizationIntegrationConfiguration. These filters are evaluated by the IntegrationFilterService.

    An IntegrationFilterGroup allows for complex logical routing using:

    • AndOperator: A boolean determining if the group requires all (true) or any (false) of its Rules and nested Groups to be true.
    • Rules: A list of IntegrationFilterRule objects.
    • Groups: A list of nested IntegrationFilterGroup objects.

    If the filter evaluates to true, the integration proceeds; if false, the event is ignored.

  12. How to combine organization abilities with feature flags

    main

    Organization abilities (which control plan-based access) should work alongside feature flags (which control rollout). For a new feature, you should typically check both to ensure the feature is both enabled for the system and permitted by the user's plan.

    // 1. Check feature flag first (controls rollout)
    if (!_featureService.IsEnabled(FeatureFlagKeys.MyFeature))
    {
        throw new BadRequestException("This feature is not available.");
    }
    
    // 2. Then check organization ability (controls plan-based access)
    var orgAbility = await _applicationCacheService.GetOrganizationAbilityAsync(organizationId);
    if (!orgAbility.UseMyFeature)
    {
        throw new BadRequestException("Your organization's plan does not support this feature.");
    }
    // Check feature flag first (controls rollout)
    if (!_featureService.IsEnabled(FeatureFlagKeys.MyFeature))
    {
        throw new BadRequestException("This feature is not available.");
    }
    
    // Then check organization ability (controls plan-based access)
    if (!orgAbility.UseMyFeature)
    {
        throw new BadRequestException("Your organization's plan does not support this feature.");
    }