TessariDB Vault
Get started

Get started

Run it with Docker

TessariDB Vault 0.0.1 runs as two containers: the server, with the web application inside it, and the TessariDB node that keeps everything it holds. One compose file starts both, declares the server's own database account and writes its key.

Docker

Two containers and one command

Set the two passwords and the platform admin in .env, then start it. The first start builds the server's image from the repository and takes a few minutes; later starts take seconds.

shellsh
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

What runs

What the compose file starts

  • tessaridb: the TessariDB node, tessaridb/tessaridb:0.39.6, reachable only from the server over the compose network. Its first start declares the store's owner from .env.
  • vault-account: signs in as that owner once, declares the vault namespace and an account that may edit that namespace and nothing else, then exits. The server never signs in as the owner.
  • service-key: writes 32 random bytes as the service key into a volume of its own, readable by the server alone, and leaves an existing key untouched.
  • vault: the server, on http://localhost:8080, published on 127.0.0.1 only. Put a TLS-terminating proxy in front and set VAULT_PUBLIC_URL to the https address before anyone else reaches it.

The first person

A single-use link for the platform admin

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 to nowhere else. Open http://localhost:8080/#/join, paste the token, and set a sign-in password and an authenticator.

shellsh
docker compose run --rm vault enrolment-link
# enrolment token for ops@example.com, valid for one use within 24 hours:
# 3f9c...

The first company

A company and its owner

The platform admin creates a company and names its owner; the answer carries the owner's enrolment token, which you send to them. The platform admin's session comes from the two sign-in steps the pages use: the password, then a code from the authenticator. The owner joins 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.

shellsh
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"}'

Keeping it

Two volumes, backed up apart

Everything the server keeps is in the tessaridb volume; the service key is in vault-keys. Back them up separately: the key derives every company's vault key, so a database restored without it cannot be opened, and a key kept with the database's backups gives whoever holds them one fewer lock to pick.

Without Docker

The binary beside a node

The server needs a TessariDB node, 0.39.3 or later (0.39.6 is current; 0.39.4 is withdrawn), an account on it that may edit one namespace, and the key file, which its group and others may not read. It refuses to start, naming the variable, when anything is missing.

docker/vault-account.sh in the repository holds the statements the store's owner runs once to declare that account.

What the server reads
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_AUTHORITYnonea PEM file, for a node reached over TLS
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; needed for passkeys
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
shellsh
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" 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

From source

Building the binary

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

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