Skip to main content

Vaultwarden

The Gateway can publish the database credentials it holds into one collection of a Vaultwarden organization. Each credential becomes one Database item. The Vault app and Conn read those items, so people get current credentials without asking Orbit. Orbit stays the source of truth: it creates, rewrites, and permanently deletes the items. vault lists the commands.

Set up

Do these steps in Vaultwarden first:
  1. Create an account for the Gateway. Keep the default KDF, PBKDF2-SHA256. The Gateway refuses an account that uses Argon2id.
  2. Create an organization, for example Orbit, and a collection in it, for example Databases.
  3. Invite the Gateway account to the organization and give it Can manage on the collection. Confirm the membership.
  4. Sign in as the Gateway account and copy its personal API key: client_id and client_secret.
Vaultwarden must run behind an Orbit Route, such as https://vault.orbit. Setup accepts only an HTTPS origin on a .orbit host, with no user info, path, query, or fragment. Every request trusts only the Orbit root CA, so the Gateway sends credentials only to a server with a certificate that Orbit issued. Then run vault:setup with the Vaultwarden origin, the API key, the master password, and the organization and collection, each by name or ID. The Gateway logs in, unlocks the account, and checks that it can write to the collection. Only then does it store the values, encrypted. Setup can run again, for example with a new API key or master password. It refuses another organization or collection with vault.items_published while items are published, because they would stay behind in the old one. Run vault:teardown first. No response or Activity entry returns the API key or the master password. The MCP vault-setup tool takes them as arguments, so they reach the agent’s transcript. Run setup with the CLI, which reads them from files, when that matters.

Who can redirect the credentials

Setup, sync, and teardown need access to the Gateway. A caller with that access chooses where the Gateway publishes every MySQL and PostgreSQL password, within the hosts that Orbit routes. Grant Gateway access only to Nodes you trust with those passwords.

Items

The Gateway publishes one item for each of these: Redis and SQLite connections get no item. A mysql or pgsql connection that lacks one of the fields, and a Database user whose password Orbit does not store, are listed as skipped in vault:status. Each item is a Secure Note in the configured collection. Every write names the Gateway account as encryptedFor, which Vaultwarden 1.37 requires, and sends a Bitwarden client version header. Its custom fields follow schema version 1 of Vault’s Database item: The note says that Orbit manages the item. Orbit overwrites changes made in Vaultwarden.

Database user passwords

database:user:create stores the user’s password encrypted on the Database user record. No response returns it. A user created before Orbit stored passwords has no item until database:user:create sets the password again.

Sync

The scheduler runs orbit:vault-sync every minute while Vaultwarden is set up, as a job on the Gateway queue. The command builds the wanted items from the database records and compares them with the published items it recorded: A run that finds nothing to do does not contact Vaultwarden, except once an hour. A run that contacts Vaultwarden also reads the collection and recognizes Orbit’s items by their orbit.source field: The collection belongs to Orbit. Do not add items with an orbit.source field by hand. One failed change does not stop the others. The run records its error, keeps that item’s state, and the next run retries it. Database operations never wait for Vaultwarden, so an outage delays only the copy. The scheduled command exits with a failure while a run records an error. vault:sync runs the same reconcile at once, including the hourly check. It returns the status when every change succeeded. When a change failed, it returns vault.sync_failed (502) with failed, the number of failed changes, and first_error, the code of the first failure. Only one run happens at a time.

Status

vault:status returns:

Tear down

vault:teardown permanently deletes every Orbit item in the collection, recorded or not, then deletes the stored values and the record of published items. It needs Vaultwarden to answer. With --keep-items, it leaves the items in Vaultwarden and does not contact it. A later setup on the same collection adopts or deletes those items. If Vaultwarden cannot delete an item, teardown fails and keeps the setup and the items it did not delete, so it can run again.

API

All operations need access to the Gateway.

Why it works this way

The Gateway already holds every database password, and the API never returns one. Vaultwarden gives people a reader they already trust, with sharing and history, without a new way to read passwords from Orbit.

Orbit keeps its own copy

Orbit stays the source of truth. If the secrets lived only in Vaultwarden, the Gateway would still need the organization key to use them, so they would not be hidden from it. Deploys and recovery would also depend on Vaultwarden, which runs on an Orbit Node.

The scheduler runs the sync

The sync runs from the scheduler instead of inside each database write. When Vaultwarden is down, database operations still succeed, and the Gateway needs no general job queue.

The Gateway encrypts items itself

The Gateway writes the standard Bitwarden format itself instead of driving the Bitwarden CLI. That avoids Node.js on the Gateway and an unlocked session that outlives a run.

TLS trusts only Orbit

The Gateway pins the Orbit root CA and accepts only .orbit hosts. With the system trust store and any URL, a caller with Gateway access could point setup at a server it controls and receive every password within a minute. The Gateway reads the CA from root.pem in its state directory, next to the root key, and checks it before each request. It never copies the CA to a shared temporary directory, where another local account could plant its own CA under the expected name. That CA file is the only trust anchor. The system trust store, such as /etc/ssl/certs, does not apply, so a certificate from a public CA or any other CA fails the TLS check. The Laravel HTTP client uses cURL with its default CA directory disabled. Each request has a 15-second overall deadline, including connecting, headers, and the body. A peer that sends headers slowly cannot extend that deadline. PHP’s HTTP stream wrapper cannot enforce this deadline while it reads headers.

Removed credentials are deleted

When a credential is removed, its item is deleted permanently instead of moved to the trash, so a removed password does not stay readable.