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).
1. Requirements on the customer side
Section titled “1. Requirements on the customer side”- 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)
docker login <private-registry> # credentials delivered with the licensedocker pull <private-registry>/tokenveil:enterpriseb. Offline archive (air-gapped customer, or no access to the registry)
# tokenveil-enterprise-<version>.tar file sent over a secure channeldocker load -i tokenveil-enterprise-<version>.tarIn both cases TokenVeil also sends you:
- the deployment
docker-compose.ymland.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.
3. Configuration (.env)
Section titled “3. Configuration (.env)”cp .env.example .envOpen .env and set:
| Variable | Purpose | How to get it |
|---|---|---|
LICENSE_KEY | Enterprise license key (signed token) that unlocks the instance | Delivered by TokenVeil when the contract is activated |
LICENSE_SERVER_URL | License server URL, for periodic validation (phone-home) and revocation | Delivered with the license; leave empty only for a fully air-gapped instance |
ANON_DB_KEY | Encryption key for data at rest (anonymization mapping, OAuth tokens) | python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" |
AUTH_BACKEND | local or ldap | Depending on the customer’s infrastructure |
WEBAPP_USERS | Bootstrap local accounts, format user:password,user2:password2 | Set if AUTH_BACKEND=local |
LDAP_* | Directory config | See the commented examples in .env.example if AUTH_BACKEND=ldap |
ANON_LANGUAGE | Default detection language (fr/en) | Depending on the language of the customer’s logs/data |
ANON_PORT | Listening port exposed on the host | 8500 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).
4. First start
Section titled “4. First start”The image is prebuilt, so no local build is needed:
docker compose up -dNo 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:
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.
5. First access and account creation
Section titled “5. First access and account creation”- 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
6. The customer’s business keywords (optional but recommended)
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.
7. Public exposure behind a reverse proxy
Section titled “7. Public exposure behind a reverse proxy”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.
8. Backup and updates
Section titled “8. Backup and updates”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:
tar -czf tokenveil-backup-$(date +%F).tar.gz data/Update (a new version delivered): pull the new image, then restart.
docker pull <private-registry>/tokenveil:enterprise # or: docker load -i tokenveil-enterprise-<version>.tardocker compose up -dReplacing the image never touches ./data: linked accounts, history, and the license survive the update.
9. Pre-production security checklist
Section titled “9. Pre-production security checklist”-
LICENSE_KEYset and the instance activated (status checked under Administration > License) -
ANON_DB_KEYgenerated specifically for this customer, backed up off the server - HTTPS active if exposed beyond the local network
-
WEBAPP_USERSpasswords changed from the.env.exampledefaults -
AUTH_BACKEND=ldapconfigured 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_URLallowed (or air-gap accepted) - Backups of
./datascheduled (cron, or folded into the customer’s existing backup policy)
10. Quick troubleshooting
Section titled “10. Quick troubleshooting”| Symptom | Likely cause | Fix |
|---|---|---|
docker compose up fails, port already in use | Another service holds the port | Change ANON_PORT in .env |
Healthcheck stays unhealthy | The server takes time to load the NLP models on first start | Wait about 30 to 60 seconds, recheck with docker ps |
| License banner expired or instance suspended | LICENSE_KEY missing/expired, or phone-home unreachable past the grace period | Check LICENSE_KEY in .env, connectivity to LICENSE_SERVER_URL, or paste the token again under Administration > License |
| Streaming not smooth behind a reverse proxy | Proxy buffering on by default | See section 7 |
| Nginx “Congratulations” page instead of the app | Domain not attached to the right Proxy Host | Check that the domain name is in the “Domain Names” of the target host |
| Claude linking fails | The claude CLI is unavailable | Check docker exec <container> claude --version (it must return a version) |
| A user does not see the Administration panel | Non-admin role | Promote them from Administration > Users (by an existing admin) |
For the full technical detail (architecture, design choices, what is still alpha), see ARCHITECTURE.md.