Skip to content

Where workspaces are stored

A workspace is a folder on disk, but you never choose where — the path is derived server-side from the data root, the authenticated tenant, and the slug. Plus the three directories a deployment keeps and the two records behind every workspace.

Updated View as Markdown

Each workspace is backed by its own directory on the service’s filesystem — that is how the underlying engine persists a password-locked container. What the API deliberately does not expose is where that directory goes: there is no filesystem field in tenants.json and no path parameter on POST /v1/workspaces. The location is derived, not chosen.

One data root, set by the operator

Every workspace directory lives under a single root the operator sets once, in the environment — WATIVE_DATA_ROOT. Nothing else in the service builds a path from configuration. In the shipped image it defaults to /var/lib/tee-docker/data, which the Compose file bind-mounts from a fixed host path (/opt/tee-docker/data).

One folder per workspace, derived

Within that root, a workspace’s folder is computed from two trusted values — the authenticated tenant (from X-Api-Key, never a body field) and the validated slug:

$WATIVE_DATA_ROOT/
└── <tenantId>/
    └── <workspaceSlug>/
        ├── config          workspace metadata
        ├── network         the network registry
        ├── asset           asset catalogue
        ├── log             append-only operation log
        └── accounts/       one entry per account (the encrypted key material)

No path component ever comes from raw client input: the slug grammar excludes . and /, so traversal is impossible by construction, and a containment check rejects any resolved path that escapes the tenant’s own subtree. That is why POST /v1/workspaces takes only { slug, password } — placement is a uniform operator concern, identical for every tenant, not a per-request or per-tenant decision.

Creating the workspace is the only operation allowed to initialize storage; every later unlock opens an existing container and refuses to create an empty one in its place.

Two records behind every workspace

A workspace exists as two things that must stay in step:

Record Where What it is
The container $WATIVE_DATA_ROOT/<tenant>/<slug>/ The engine’s encrypted workspace — accounts, wallets, key material
The ledger row state.json, under TEE_STATE_DIR The service’s index of which workspaces a tenant has, and its wallet counts

Create writes both; delete removes both. This split is why backups must capture state/ and data/ together, with the service stopped — a backup that catches them at different moments, or restores one without the other, can strand a workspace whose ledger row and on-disk container disagree. See the deploy backup notes.

The three directories a deployment keeps

Storage is one of three separate directories, each with its own environment variable, so their concerns never mix:

Directory Env var Container default Holds
Config TEE_CONFIG_DIR /app/config tenants.json (bind-mounted read-only) and errors.json (baked into the image)
State TEE_STATE_DIR /var/lib/tee-docker/state state.json — the workspace/quota ledger
Data WATIVE_DATA_ROOT /var/lib/tee-docker/data the per-workspace container folders above

TEE_SECRET_HMAC_KEY — which authenticates tenants and signs session tokens — lives only in the environment, never on disk. It does not encrypt the containers themselves: each workspace’s data/ is locked under its own password, which the service never stores. A restored data/ still cannot be reached without the key, because no tenant can authenticate to unlock it.

  • Deploy — the persistent directories, mounts, and backups in full
  • Workspaces and tokens — what a workspace is and how a token opens one
  • Networks — the network registry stored under network
Navigation

Type to search…

↑↓ navigate↵ selectEsc close