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 imagetessaridb/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:
| Service | What it does |
|---|---|
tessaridb | the node, tessaridb/tessaridb:0.39.6; its first start declares the store's owner from .env. Its wire port is not published |
vault-account | signs 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-key | writes 32 random bytes, as 64 hexadecimal characters, into the vault-keys volume, readable by the server alone, and leaves an existing key untouched |
vault | the 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
| Variable | Default | What it is |
|---|---|---|
TESSARIDB_VAULT_STORE | required | the node's wire address, host:port |
TESSARIDB_VAULT_STORE_USER | required | the server's account on the node |
TESSARIDB_VAULT_STORE_PASSWORD | required | that account's password |
TESSARIDB_VAULT_STORE_AUTHORITY | none | the PEM a node's certificate must lead to; without it only a loopback store is reached |
TESSARIDB_VAULT_SERVICE_KEY_FILE | required | the service key file |
TESSARIDB_VAULT_NAMESPACE | vault | the namespace it keeps everything in |
TESSARIDB_VAULT_DATABASE | main | the database inside it |
TESSARIDB_VAULT_LISTEN | 127.0.0.1:8080 | where it serves the pages and the API |
TESSARIDB_VAULT_PUBLIC_URL | none | the address people reach it at; passkeys need it |
TESSARIDB_VAULT_PLATFORM_ADMIN_EMAIL, _NAME | none | the platform admin to ensure at start, both or neither |
TESSARIDB_VAULT_OIDC_ISSUER, _CLIENT_ID, _REDIRECT | none | an 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