Skip to content

TokenVeil: customer install guide

This document describes a full deployment of TokenVeil (Enterprise edition) at a customer site, with Docker. It is written to be followed start to finish with no prior knowledge of the project.

The Enterprise edition is a licensed product: TokenVeil delivers it (image plus license key) once the contract is active. This guide starts from the point where you have received those pieces. To evaluate the product without a license, use the Community edition instead (public repository, docker compose up).


  • A Linux server (bare metal, VM, or cloud) with Docker and Docker Compose v2 installed
  • 4 GB of RAM minimum (the fr/en NLP models loaded in memory at startup use a good part of it), 4 vCPU recommended
  • About 3 GB of disk for the image (spaCy models included)
  • One free HTTP port (8500 by default, configurable)
  • If exposed publicly: a domain name and a reverse proxy in front of the service (see section 7)
  • If LDAP/Active Directory authentication: network access to the domain controller from the server
  • For license activation: either outbound HTTPS access to the license server, or a deliberate fully air-gapped deployment (see section 3, license note)

Nothing to install by hand on the server: Python, Node.js, and the Claude Code CLI all ship inside the Docker image.

2. Get the Enterprise image and the license

Section titled “2. Get the Enterprise image and the license”

The Enterprise edition ships as a prebuilt Docker image (code compiled to bytecode, no Python source in the image), together with a signed license key issued for the customer. Two delivery modes, depending on the customer’s network access:

a. Authenticated private registry (recommended when the server has outbound access)

Fenêtre de terminal
docker login <private-registry> # credentials delivered with the license
docker pull <private-registry>/tokenveil:enterprise

b. Offline archive (air-gapped customer, or no access to the registry)

Fenêtre de terminal
# tokenveil-enterprise-<version>.tar file sent over a secure channel
docker load -i tokenveil-enterprise-<version>.tar

In both cases TokenVeil also sends you:

  • the deployment docker-compose.yml and .env.example,
  • a license key (LICENSE_KEY): an Ed25519-signed token, specific to this customer and contract.

Put docker-compose.yml and .env.example in a working folder (for example tokenveil/), then continue with the configuration below.

Fenêtre de terminal
cp .env.example .env

Open .env and set:

VariablePurposeHow to get it
LICENSE_KEYEnterprise license key (signed token) that unlocks the instanceDelivered by TokenVeil when the contract is activated
LICENSE_SERVER_URLLicense server URL, for periodic validation (phone-home) and revocationDelivered with the license; leave empty only for a fully air-gapped instance
ANON_DB_KEYEncryption key for data at rest (anonymization mapping, OAuth tokens)python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
AUTH_BACKENDlocal or ldapDepending on the customer’s infrastructure
WEBAPP_USERSBootstrap local accounts, format user:password,user2:password2Set if AUTH_BACKEND=local
LDAP_*Directory configSee the commented examples in .env.example if AUTH_BACKEND=ldap
ANON_LANGUAGEDefault detection language (fr/en)Depending on the language of the customer’s logs/data
ANON_PORTListening port exposed on the host8500 by default, change it if already taken

Security note: ANON_DB_KEY must never be lost or regenerated on an instance that already holds data. Without it, the anonymization mapping becomes unreadable and existing conversations can no longer be de-anonymized when displayed. Keep it in a vault (an enterprise password manager), not only in the .env on the server.

License: without a valid LICENSE_KEY, the instance starts in a grace period and then shuts down after 15 days with no license. Once activated and in contact with LICENSE_SERVER_URL, it gets a 14-day network grace if the license server becomes unreachable; past that it suspends itself (so a revocation cannot be dodged by cutting the network after activation). A fully air-gapped instance (never any contact, LICENSE_SERVER_URL empty) is not subject to the network grace and runs on the token validity alone. The key can also be supplied outside the .env: mounted as the data/license.lic file, or pasted later under Administration > License (it is validated before being written, so an invalid token cannot break the license already in place).

The image is prebuilt, so no local build is needed:

Fenêtre de terminal
docker compose up -d

No NLP model download: the models are already in the image. The first start takes about 30 to 60 seconds, the time to load the fr/en models into memory.

Check that the service answers:

Fenêtre de terminal
curl http://localhost:8500/healthz
# {"status": "ok"}

If ANON_PORT was changed in .env, adjust the URL above accordingly. After the first sign-in (section 5), also check the license status under Administration > License.

  • Open http://<server>:<port>/ (or the public domain if already in place, see section 7)
  • Sign in with one of the accounts defined in WEBAPP_USERS (the first account created becomes an administrator automatically)
  • Under Preferences > AI accounts, each user links their own AI:
    • Claude: OAuth, personal Pro/Max subscription, no billed API key
    • Gemini: a personal API key generated at aistudio.google.com, free on the Flash models
  • To add more accounts later: the Administration panel (visible only to admin accounts) > Users tab
Section titled “6. The customer’s business keywords (optional but recommended)”

Under Preferences > Keywords to anonymize, add the codenames, internal project names, or identifiers specific to the customer that generic detection cannot know in advance. An admin can push these rules to the whole team or to a specific user from the Administration panel.

TokenVeil uses SSE streaming (the AI response appears as it is generated). Most reverse proxies buffer responses by default, which breaks that effect (the response arrives all at once at the end instead of progressively). Add these directives on the block pointing to TokenVeil:

Nginx (a dedicated location block, or a custom Nginx Proxy Manager config):

proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_read_timeout 300s;

Without this, the application still works but loses the streaming effect. It is not a software bug, it is a proxy default you turn off explicitly.

Forcing HTTPS (Force SSL) is strongly recommended: otherwise login credentials and the content of the exchanges (even anonymized on the AI side) travel in clear text on the network.

Backup: one folder to back up, ./data (mounted as a Docker volume). It holds the SQLite database (conversations, encrypted mapping), the per-user linked AI accounts, and the license state. Simple backup:

Fenêtre de terminal
tar -czf tokenveil-backup-$(date +%F).tar.gz data/

Update (a new version delivered): pull the new image, then restart.

Fenêtre de terminal
docker pull <private-registry>/tokenveil:enterprise # or: docker load -i tokenveil-enterprise-<version>.tar
docker compose up -d

Replacing the image never touches ./data: linked accounts, history, and the license survive the update.

  • LICENSE_KEY set and the instance activated (status checked under Administration > License)
  • ANON_DB_KEY generated specifically for this customer, backed up off the server
  • HTTPS active if exposed beyond the local network
  • WEBAPP_USERS passwords changed from the .env.example defaults
  • AUTH_BACKEND=ldap configured if the customer already has an enterprise directory (avoids managing duplicate passwords)
  • Network access to the exposed port restricted if you do not want public exposure (firewall/VPN)
  • Outbound access to LICENSE_SERVER_URL allowed (or air-gap accepted)
  • Backups of ./data scheduled (cron, or folded into the customer’s existing backup policy)
SymptomLikely causeFix
docker compose up fails, port already in useAnother service holds the portChange ANON_PORT in .env
Healthcheck stays unhealthyThe server takes time to load the NLP models on first startWait about 30 to 60 seconds, recheck with docker ps
License banner expired or instance suspendedLICENSE_KEY missing/expired, or phone-home unreachable past the grace periodCheck LICENSE_KEY in .env, connectivity to LICENSE_SERVER_URL, or paste the token again under Administration > License
Streaming not smooth behind a reverse proxyProxy buffering on by defaultSee section 7
Nginx “Congratulations” page instead of the appDomain not attached to the right Proxy HostCheck that the domain name is in the “Domain Names” of the target host
Claude linking failsThe claude CLI is unavailableCheck docker exec <container> claude --version (it must return a version)
A user does not see the Administration panelNon-admin rolePromote them from Administration > Users (by an existing admin)

For the full technical detail (architecture, design choices, what is still alpha), see ARCHITECTURE.md.