Multi-Tenant Private Document AI
ZettaBrain Teams is a self-hosted, multi-tenant RAG server. Each team in your organisation gets its own isolated document library, users authenticate via Active Directory, and every answer is cryptographically signed — all running on your own infrastructure with no cloud, no API keys, and no data egress.
What It Does
ZettaBrain Teams ingests documents per team and lets each team query their own documents through a shared web interface. The admin panel manages users, teams, licensing, and AD integration. Every answer is signed with an Ed25519 key and logged to a tamper-evident audit trail that can be verified at any time.
Key Features
| Feature | Detail |
|---|---|
| Per-team isolation | Separate ChromaDB collection and BM25 index per team — no cross-boundary leakage |
| Model Governance | Manager-request, admin-approve workflow for model stack assignments — central control over keys, cost, and compliance with delegated autonomy per team |
| Hybrid Model Support | Mix local Ollama models with frontier APIs (OpenAI, Claude) per team — keep embeddings local while routing generation to the cloud, or run fully offline |
| Active Directory SSO | LDAP bind-search-rebind with optional group membership enforcement |
| Hybrid retrieval | MMR vector search + per-team BM25 + FlashRank cross-encoder re-ranking |
| ZettaBrain Verified | Ed25519 cryptographic signature over every answer — query hash, chunk hashes, answer hash stored in the audit log |
| Tamper-evident audit log | Every query logged with provenance signature — one-click verification in the admin panel, downloadable verification report |
| Out-of-scope detection | Answers outside a team's document library are flagged with zero confidence and no sources |
| Offline Ed25519 licensing | Starter / Business / Enterprise plans — license file works air-gapped, no license server required |
| 90-day free trial | Full functionality from day one — no credit card, no feature limits during trial |
| Admin panel | Dashboard, users, teams, audit log, license management, system config, AD settings |
| REST API | FastAPI — all admin and chat operations available as JSON endpoints |
Quick Links
Quick Install
One-line installer for ZettaBrain Teams. Downloads and configures everything automatically — Python, pipx, zettabrain-teams, Ollama, and embedding models.
One command installs everything — runs as root on Linux:
curl -fsSL https://zettabrain.app/install-teams.sh | sudo bash
After installation, start the server:
zettabrain-teams # starts at http://0.0.0.0:7861
One command installs everything:
curl -fsSL https://zettabrain.app/install-teams.sh | sudo bash
After installation:
zettabrain-teams
One command installs everything (no sudo on macOS):
curl -fsSL https://zettabrain.app/install-teams.sh | bash
After installation:
zettabrain-teams
Requires Python 3.9+ from python.org. Manual install via pipx:
pipx install zettabrain-teams
zettabrain-teams # starts at http://0.0.0.0:7861
What the installer does
- Detects your OS and installs system dependencies (Python 3.9+, pipx)
- Installs zettabrain-teams via pipx with automatic PATH configuration
- Installs and starts Ollama as a background service
- Downloads the
nomic-embed-textembedding model (~275 MB) - Runs the setup wizard for storage paths and LLM model selection
- Creates
/opt/zettabrain-teams/with config and data directories - All commands available immediately — no manual PATH setup needed
After install
zettabrain-teams # start server at :7861
# Open http://your-server:7861 — log in with admin / admin
First-Time Setup
After installing the package, run the setup wizard as root to configure Ollama and models, then complete admin setup in the web interface.
1. Run the Setup Wizard
sudo zettabrain-teams-setup
# Options:
sudo zettabrain-teams-setup --port 7861 --llm llama3.1:8b --embed nomic-embed-text
sudo zettabrain-teams-setup --no-systemd # skip service registration
2. Start the Server
zettabrain-teams # starts at :7861
zettabrain-teams --port 8080 # custom port
zettabrain-teams --reload # dev mode
Open http://your-server:7861 in a browser.
3. First Login & Admin Setup
- Log in with
admin/P@ssword!— you will be prompted to change the password on first login - Go to Admin → Teams to create your first team and assign a document folder
- Add users to the team, or configure Active Directory under Admin → Settings → LDAP
- Trigger a document ingest from the team page to build the vector store
Teams & Users
Every document query is scoped to the querying user's team. Teams have their own document folders, vector stores, and BM25 indexes — completely isolated from other teams.
Creating a Team
- Go to Admin → Teams → Create Team
- Enter a team name (used as the slug for storage paths)
- Set the document folder path on the server (e.g.
/data/finance) - Add members — local users or imported AD accounts
- Click Ingest Documents to index the folder
User Roles
| Role | Access |
|---|---|
admin | Full admin panel: users, teams, settings, audit log |
user | Chat interface for their own teams only |
Document Isolation
Each team's documents are stored in a separate ChromaDB collection at /opt/zettabrain-teams/chromadb/{team-slug}/. The BM25 index is also per-team — IDF statistics are computed within each team's corpus only, with no cross-team statistical leakage.
Model Governance
Most organizations get two bad options: force one model on everyone, or let it be a free-for-all where nobody tracks the keys, cost, or compliance exposure. ZettaBrain Teams adds an actual workflow.
How It Works
A team manager requests the model stack their team needs, with a written justification. An admin approves it, or rejects with a reason. Both sides get notified in real time.
Request Workflow
- Manager submits a request — From the team dashboard, a manager selects the desired generation model (e.g.,
claude-sonnet-4.6,gpt-4, orllama3.1:8b) and embedding model (nomic-embed-text,text-embedding-3-large, etc.) - Justification required — A text field captures why: workload requirements, performance constraints, compliance needs
- Admin review — Request appears in Admin → Model Requests with full context: team, current config, requested config, justification
- Approve or reject — Admin can approve (model stack becomes available to the team), or reject with a message explaining why
- Real-time notification — Manager sees status update immediately on the team dashboard
Approved Model Stack
Once approved, the manager can assign it to their team and switch between any previously approved configuration whenever the workload changes.
| Component | Scope |
|---|---|
| Generation Model | Local (Ollama) or API (OpenAI, Claude) |
| Embedding Model | Local (Ollama) or API (OpenAI) |
| API Keys | Stored centrally — teams never see them |
| Cost Tracking | Per-team token usage and estimated spend (coming soon) |
Central Control, Delegated Autonomy
Central control where it has to live: keys, cost, compliance. Delegated autonomy where it helps, because managers know their workload better than IT does.
Configuration
Go to Admin → Settings → Models to configure available models and API keys. Only admins can add or remove model options from the pool.
Hybrid Model Support
Local models keep your data private. Frontier models give the sharpest answers. Most RAG tools make you pick one. ZettaBrain Teams doesn't.
Mix Local and Cloud
Every team runs its own hybrid stack: mix Ollama running locally, OpenAI, and Claude however the workload demands.
Independent Model Selection
Your generation model and your embedding model are configured separately. Keep embeddings fully local — meaning your documents never leave the box — while routing generation to Claude or GPT for answer quality. Or run one team entirely offline while another uses a frontier model.
| Model Type | Local Options | Cloud Options |
|---|---|---|
| Generation (LLM) | llama3.1:8b, mistral:7b, qwen2.5:14b (via Ollama) | gpt-4, gpt-4-turbo, claude-sonnet-4.6, claude-opus-4.7 |
| Embedding | nomic-embed-text, all-minilm (via Ollama) | text-embedding-3-small, text-embedding-3-large (OpenAI) |
Privacy Modes
| Mode | Configuration | Data Locality |
|---|---|---|
| Fully Local | Ollama generation + Ollama embedding | Nothing leaves your infrastructure |
| Hybrid (Privacy-First) | Cloud generation + Local embedding | Documents stay local, only generated answers hit the API |
| Hybrid (Performance-First) | Cloud generation + Cloud embedding | Faster embedding, lower latency — documents are embedded via API |
| Fully Cloud | Cloud generation + Cloud embedding | Maximum performance, full API dependency |
Example Configurations
- Finance Team — Fully local (
llama3.1:8b+nomic-embed-text) for compliance and air-gapped operation - Research Team — Hybrid privacy-first (
claude-sonnet-4.6+nomic-embed-text) for best answers while keeping source documents local - Product Team — Fully cloud (
gpt-4+text-embedding-3-large) for maximum speed and answer quality
Installation
pipx install zettabrain-teams
Configure hybrid models via Admin → Settings → Models. Add your OpenAI and Anthropic API keys, then teams can request access through the Model Governance workflow.
Active Directory / LDAP
ZettaBrain Teams supports Microsoft Active Directory via LDAP. Users authenticate with their AD credentials, and AD accounts can be imported directly into teams.
Configuration
Go to Admin → Settings → Active Directory and fill in:
| Field | Example |
|---|---|
| LDAP URL | ldap://dc.acme.com or ldaps://dc.acme.com |
| Bind DN | CN=svc-zettabrain,CN=Users,DC=acme,DC=com |
| Bind Password | Service account password |
| User Search Base | DC=acme,DC=com |
| User Search Filter | (&(objectClass=user)(sAMAccountName={username})) |
| Required AD Group DN | CN=ZettaBrain-Users,OU=Groups,DC=acme,DC=com (optional) |
Quick Fill
Use the Quick Fill from Domain helper — enter your domain (e.g. acme.com) and it pre-fills the LDAP URL, Base DN, and AD-specific defaults automatically.
Test the Connection
Click Test AD Connection before saving. The test probes your settings and tries fallback filters automatically if users aren't found, then suggests the working filter and base DN.
How Authentication Works
- User submits username + password on the login page
- Local admin account is checked first (protects access if AD is down)
- Service account binds to AD and searches for the user by
sAMAccountName - Optional: group membership is verified
- User re-binds with their own credentials to verify the password
- Account is auto-provisioned locally on first successful AD login
Importing AD Users into Teams
When AD is enabled, each team card shows an Import from Active Directory section. Search for an AD user by name or username and import them directly into the team with one click.
Audit Log
Every chat query is logged with the user, team, query text, response preview, confidence score, duration, and chunks used — plus a cryptographic Ed25519 provenance signature so the answer bundle can be independently verified at any time.
Accessing the Log
Go to Admin → Audit Log. Use the date range presets (1 day, 3 days, 1 week, 1 month) or pick a custom range with the date pickers.
CSV Export
Click Export CSV to download the currently filtered log — ready for Excel, Splunk, or your SIEM.
Provenance Verification
Each log row includes a Verify button. Clicking it calls GET /api/admin/audit/{id}/verify and opens a detail panel showing:
- SHA-256 hash of the original query
- SHA-256 hashes of every context chunk used
- SHA-256 hash of the answer
- Ed25519 signature hex and server public key
- Canonical JSON payload that was signed
- Download Verification Report — exports a self-contained JSON file for offline or third-party verification
The server's Ed25519 public key is available at GET /api/admin/keys/public for out-of-band verification without accessing the admin panel.
Log Fields
| Field | Description |
|---|---|
| timestamp | ISO 8601 UTC |
| username | Resolved from user ID |
| team_name | Resolved from team ID |
| query | Full query text |
| response_preview | First 200 chars of answer |
| confidence | 0.0–1.0 reranker score (0.0 for out-of-scope answers) |
| duration_ms | End-to-end latency in ms |
| chunks_used | Number of context chunks passed to LLM |
| model | Ollama model that generated the response |
| query_hash | SHA-256 hex of the query string |
| chunk_hashes | JSON array of SHA-256 hashes — one per context chunk |
| answer_hash | SHA-256 hex of the full answer text |
| provenance_sig | Ed25519 hex signature over the canonical answer bundle |
ZettaBrain Verified
Every answer produced by ZettaBrain Teams is cryptographically signed with an Ed25519 key held by the server. This makes the entire answer bundle — query, context chunks, and response — tamper-evident and independently verifiable.
How It Works
- When the LLM returns an answer, ZettaBrain computes a SHA-256 hash of the query, of each context chunk, and of the answer text
- These hashes are assembled into a canonical JSON payload and signed with the server's Ed25519 private key
- The signature and all hashes are stored alongside the audit log entry in the database
- Any admin can click Verify on any log row to re-derive the canonical payload and confirm the signature still matches — proving the stored answer has not been altered
What Is Signed
| Field | Value |
|---|---|
query_hash | SHA-256 of the user's question |
chunk_hashes | Sorted array of SHA-256 hashes — one per context chunk retrieved |
answer_hash | SHA-256 of the full LLM response |
model | Ollama model identifier |
team_id | Team scope of the query |
Out-of-Band Verification
The server's Ed25519 public key never changes and is exposed at:
GET /api/admin/keys/public
# Returns: { "public_key_hex": "45262b...", "algorithm": "Ed25519" }
A compliance team or external auditor can retrieve this key once, then verify any downloaded report independently using standard Ed25519 libraries — no access to the ZettaBrain server required.
Out-of-Scope Detection
When a query falls outside a team's document library, ZettaBrain returns a fixed message ("This question is outside the scope of this team's document library.") with a confidence of 0.0 and an empty sources list. The provenance signature is still computed and stored over this clean response, so the absence of an answer is itself auditable.
Licensing & Plans
ZettaBrain Teams ships with a built-in 90-day free trial. After the trial, a license file activates the server without any internet connection — suitable for air-gapped environments.
Trial Period
- 90 days of full functionality from the first server start — no credit card, no feature limits
- A warning banner appears in the admin dashboard when fewer than 14 days remain
- The server exits cleanly on expiry with a message pointing to sales@zettabrain.io
License Plans
| Plan | Max Users | Max Teams | Features |
|---|---|---|---|
| Starter | 10 | 3 | Provenance signing |
| Business | 50 | 20 | LDAP, provenance signing, audit export |
| Enterprise | Unlimited | Unlimited | LDAP, provenance signing, audit export, SSO |
Contact sales@zettabrain.io to purchase. You receive a .lic file by email.
Activating a License
Option A — Admin UI
- Go to Admin → License
- Paste the license key string or click Upload .lic file
- Click Activate License — the status panel updates immediately
Option B — File on disk (air-gapped)
cp your_license.lic /opt/zettabrain-teams/data/license.lic
# Restart the server to pick it up
systemctl restart zettabrain-teams
Seat Enforcement
User and team creation are blocked at the plan limit with a clear error message. To add more seats, upload an upgraded license — no restart required.
CLI Commands
| Command | Description |
|---|---|
sudo zettabrain-teams-setup | First-time wizard — Ollama, models, systemd |
sudo zettabrain-teams-setup --port 7861 | Custom port |
sudo zettabrain-teams-setup --llm llama3.1:8b | Specify LLM model |
sudo zettabrain-teams-setup --no-systemd | Skip systemd registration |
zettabrain-teams | Start web server at :7861 |
zettabrain-teams --port 8080 | Override port |
zettabrain-teams --host 127.0.0.1 | Bind to specific interface |
zettabrain-teams --reload | Dev mode with auto-restart |
Document ingestion
Triggered from the admin web panel (Admin → Teams → Ingest), or via the REST API:
curl -X POST http://localhost:7861/api/teams/{team_id}/ingest \
-H "Authorization: Bearer <token>"
Configuration
Settings via environment variables or /opt/zettabrain-teams/teams.env. Most settings are also configurable through Admin → Settings in the web interface.
| Variable | Default | Description |
|---|---|---|
ZBT_PORT | 7861 | Server port |
ZBT_CHROMA_DIR | /opt/zettabrain-teams/chromadb | Root for per-team ChromaDB collections |
ZBT_DB_PATH | /opt/zettabrain-teams/teams.db | SQLite database (users, teams, settings) |
ZETTABRAIN_LLM_MODEL | llama3.1:8b | Ollama LLM model |
ZETTABRAIN_EMBED_MODEL | nomic-embed-text | Ollama embedding model |
OLLAMA_HOST | http://localhost:11434 | Ollama API endpoint |
ZBT_SECRET_KEY | auto-generated | JWT signing secret — set explicitly in production |
ZBT_TLS_CERT | — | Path to TLS certificate (enables HTTPS) |
ZBT_TLS_KEY | — | Path to TLS private key |
System Requirements
| Minimum | Recommended | |
|---|---|---|
| RAM | 8 GB | 16 GB+ |
| CPU | 4 cores / 2.5 GHz | 8 cores / 3.0 GHz |
| Disk | 20 GB free | 50 GB free |
| OS | Ubuntu 22.04 / Red Hat 8 / macOS 13 | Ubuntu 22.04 LTS |
| Python | 3.9 | 3.11+ |
Multi-team note: Allocate an additional ~1–2 GB RAM per active team's vector store on top of the base Ollama model footprint.
Diagnostics
systemctl status zettabrain-teams
journalctl -u zettabrain-teams -f
curl http://localhost:7861/api/health
curl http://localhost:11434
ollama list
Uninstall
Linux / macOS
sudo systemctl disable --now zettabrain-teams 2>/dev/null || true
pipx uninstall zettabrain-teams
sudo rm -rf /opt/zettabrain-teams
Windows
pip uninstall zettabrain-teams