Docs
  • 0.0.1latest

Running it

Start the server and a TessariDB node with one compose file, enrol the first person, create a company, and run the binary without Docker.

The server needs a TessariDB node, 0.39.3 or later, reachable over the wire protocol. 0.39.6 is current and 0.39.4 is withdrawn. A client older than the node's protocol is refused before it sends anything, so the minimum is not a suggestion: an older node answers the server's first connection with a refusal and the server does not start.

Status. Release 0.0.1. The compose file runs the published image tessaridb/vault:0.0.1.

With Docker

git clone https://github.com/tessaridb/tessaridb-vault && cd tessaridb-vault
cp .env.example .env    # set TESSARIDB_PASSWORD, VAULT_STORE_PASSWORD and the platform admin
docker compose up -d --wait

The first start pulls the images. The compose file starts four services:

ServiceWhat it does
tessaridbthe node, tessaridb/tessaridb:0.39.6; its first start declares the store's owner from .env. Its wire port is not published
vault-accountsigns in as that owner once, declares the vault namespace, its database main and the account vault, an editor of that namespace alone, and exits. Every statement carries IF NOT EXISTS, so running it again changes nothing
service-keywrites 32 random bytes, as 64 hexadecimal characters, into the vault-keys volume, readable by the server alone, and leaves an existing key untouched
vaultthe server on port 8080, published on 127.0.0.1 only. It runs in the node's network namespace and reaches it at 127.0.0.1:9080

The server speaks to a store without TLS only over loopback, which is why it shares the node's network namespace. To reach a node elsewhere, give the node a certificate and set TESSARIDB_VAULT_STORE_AUTHORITY to the PEM it leads to.

Before anyone else reaches the server, put a TLS-terminating proxy in front of it and set VAULT_PUBLIC_URL in .env to the https address people use. Without TLS a sign-in password crosses the network in the clear, and passkeys need the address to be right.

The image is distroless and runs as the user 65532. It carries no credential, and the server refuses to start, naming the variable, when a value it needs is missing. Its health check runs tessaridb-vault health, which asks the server in the container whether it answers and exits 0 when it does, so docker compose up --wait returns once the server serves.

The first person

While the platform admin has no credential, enrolment-link prints a single-use enrolment token, valid for 24 hours, to the terminal that ran it and nowhere else:

docker compose run --rm vault enrolment-link

Open http://localhost:8080/#/join, paste the token, and set a sign-in password and an authenticator. The platform admin creates companies; it is not a member of any of them and reads none of their records.

The first company

The platform admin creates a company and names its owner. The answer carries the owner's enrolment token, which you send to them. A session comes from the two sign-in steps the pages use: the password, then a code from the authenticator.

VAULT=http://localhost:8080; JSON="content-type: application/json"
CHALLENGE=$(curl -s $VAULT/api/sign-in -H "$JSON" \
  -d '{"email":"ops@example.com","password":"…"}' | jq -r .challenge)
SESSION=$(curl -s $VAULT/api/sign-in/code -H "$JSON" \
  -d "{\"challenge\":\"$CHALLENGE\",\"code\":\"123456\"}" | jq -r .session)
curl -s $VAULT/api/companies -H "authorization: Bearer $SESSION" -H "$JSON" \
  -d '{"name":"Northwind","owner_email":"dana@example.com","owner_name":"Dana Owner"}'

The owner joins with that token the same way, sets an unlock password the first time they open the company, and from then on adds members, departments and teams in the browser. See Companies and roles.

Backups

Everything the server keeps is in the tessaridb volume; the service key is in vault-keys. Back them up apart. The service key derives every company's vault key, so a store restored without it cannot be opened by the server, and a key kept beside the store's backups leaves whoever holds them one lock fewer to pick. Take the store's backup the way Backup and restore describes; the server holds nothing outside the store and the key.

Without Docker

Give the server a node, an account on it that may edit one namespace, and the service key file, which its group and others may not read. docker/vault-account.sh in the repository holds the statements the store's owner runs once to declare that account.

openssl rand -hex 32 > service-key && chmod 600 service-key
export TESSARIDB_VAULT_STORE=127.0.0.1:9080 TESSARIDB_VAULT_STORE_USER=vault
export TESSARIDB_VAULT_STORE_PASSWORD="$VAULT_STORE_PASSWORD"
export TESSARIDB_VAULT_SERVICE_KEY_FILE="$PWD/service-key"
export TESSARIDB_VAULT_PLATFORM_ADMIN_EMAIL=ops@example.com TESSARIDB_VAULT_PLATFORM_ADMIN_NAME="Platform Ops"
tessaridb-vault
VariableDefaultWhat it is
TESSARIDB_VAULT_STORErequiredthe node's wire address, host:port
TESSARIDB_VAULT_STORE_USERrequiredthe server's account on the node
TESSARIDB_VAULT_STORE_PASSWORDrequiredthat account's password
TESSARIDB_VAULT_STORE_AUTHORITYnonethe PEM a node's certificate must lead to; without it only a loopback store is reached
TESSARIDB_VAULT_SERVICE_KEY_FILErequiredthe service key file
TESSARIDB_VAULT_NAMESPACEvaultthe namespace it keeps everything in
TESSARIDB_VAULT_DATABASEmainthe database inside it
TESSARIDB_VAULT_LISTEN127.0.0.1:8080where it serves the pages and the API
TESSARIDB_VAULT_PUBLIC_URLnonethe address people reach it at; passkeys need it
TESSARIDB_VAULT_PLATFORM_ADMIN_EMAIL, _NAMEnonethe platform admin to ensure at start, both or neither
TESSARIDB_VAULT_OIDC_ISSUER, _CLIENT_ID, _REDIRECTnonean OpenID Connect issuer to sign in through, all three or none

The server never signs in as the store's owner. It declares its own database inside the namespace at start and nothing wider, so an account that may edit only that namespace is enough.

From source

A Cargo workspace on Rust 1.98. The web application's built output is committed, so building the server never needs Node.

cargo build --release
./target/release/tessaridb-vault