Case Study: Zyght SaaS Breach — Tenant Isolation
In April 2026, Zyght — a Chilean HSE (Health, Safety, Environment) SaaS platform — was breached. 6.1TB of data from 90+ organizationswas stolen, including worker PII, medical records, incident reports, compliance audits, and operational security information. Many affected companies are classified as Critical Infrastructure Operators (Operadores de Importancia Vital) by Chile's national cybersecurity agency (ANCI).
The Root Problem
The breach was catastrophic not because of a sophisticated exploit, but because of shared-everything multi-tenancy. When the attacker gained access to one entry point, they reached data from all 90+ tenants through a single application and database layer.
Internet
└─ Load Balancer
└─ 1 Application (shared)
└─ 1 Database (shared)
├─ Codelco data
├─ SQM data
├─ Falabella data
├─ Nestlé data
├─ Banco de Chile data
└─ ... 85 more organizations
Compromise the app = access ALL tenant data
Total exfiltration: 6.1TB, 19 million filesMentat: Sandbox-per-Tenant
In a Mentat deployment, each tenant runs in its own isolated sandbox with its own database connection. The kernel enforces the boundary — not application logic.
Internet
└─ Caddy (ingress)
├─ Sandbox: zyght-codelco
│ ├─ PID namespace (isolated)
│ ├─ Mount namespace + pivot_root
│ ├─ Own DB credentials (secrets isolated)
│ ├─ cgroups v2 limits
│ └─ DB: codelco_hse
│
├─ Sandbox: zyght-sqm
│ ├─ PID namespace (isolated)
│ ├─ Mount namespace + pivot_root
│ ├─ Own DB credentials
│ ├─ cgroups v2 limits
│ └─ DB: sqm_hse
│
└─ ... 88 more sandboxes
Compromise one sandbox = access ONE tenant
Blast radius: ~68GB instead of 6.1TBImpact Comparison
| Metric | Shared Tenancy (Zyght) | Sandbox-per-Tenant (Mentat) |
| Data exposed | 6.1TB — all 90+ tenants | ~68GB — one tenant only |
| Files exposed | 19 million | ~210K (proportional) |
| Organizations affected | 90+ | 1 |
| Lateral movement | Trivial — shared process space | Blocked — PID + mount namespace isolation |
| Credential scope | Master DB credentials serve all tenants | Each sandbox has only its own DB credentials |
| Exfiltration detection | 6.1TB over network — hard to miss but wasn't caught | cgroups v2 memory + CPU anomaly detection per sandbox |
| Recovery | Rotate credentials for 90+ orgs, notify all, assume total compromise | Rotate credentials for 1 org, contained incident |
Defense Layers
| Layer 1 | Sandbox per tenant | Each organization runs in its own namespace. No shared process space |
| Layer 2 | Isolated credentials | Each sandbox receives only its own DB connection string via Mentat secrets. No master credentials |
| Layer 3 | pivot_root | Attacker inside sandbox cannot read host filesystem, other sandboxes, or Mentat state |
| Layer 4 | cgroups v2 monitoring | Anomalous data transfer (6.1TB) triggers ScaleWatcher alerts. Per-sandbox memory and CPU tracked |
| Layer 5 | Network namespace | CLONE_NEWNET + veth restricts egress to only the tenant's DB endpoint. No arbitrary outbound connections |
| Layer 6 | Immutable base | Application code in read-only overlayfs layer. Attacker cannot modify the app to persist a backdoor |
Hull: Per-Tenant seccomp, Landlock, and Bridge Egress
Sandbox-per-tenant already limits the blast radius to one tenant. The Hull driver hardens each tenant further by adding policy that is trivial to configure once and applies to every new tenant automatically:
| seccomp profile per tenant | Each tenant runs under the Hull node profile (or beam / dotnet for non-Node stacks). An attacker who gets code exec via SQL injection cannot ptrace into sibling processes, cannot call bpf to read kernel memory, cannot keyctl to steal credentials from other sandboxes. Even <100 syscalls out of ~400 are allowed. |
| Landlock per tenant | Filesystem allowlist locks the workload to its own tenant directory. Even if an attacker runs as uid 0 inside the sandbox, open()on another tenant’s files returns EACCES — the LSM enforces the path boundary below the VFS, below capabilities. |
| Bridge egress per tenant | With network: bridge, each tenant gets its own IP on hull0(10.88.0.X). An nftables rule can restrict egress so tenant N can only reach its own database endpoint — not other tenants’ DBs, not arbitrary C2 servers, not the Mentat control plane. The tenant exfiltration paths literally do not route. |
| Zero shared daemon | Hull has no dockerd, no containerd, no shim process tree. A CVE in container tooling cannot be used to jump between tenants through a shared root-owned daemon — there is no shared daemon. Each hull run exits as soon as it has forked the tenant workload. |
| --rootless NEWUSER | Each tenant can run --rootless so even the in-container uid 0 is mapped to a distinct unprivileged host uid per tenant. Even if an attacker fully compromises one tenant sandbox, the hostidentity they land on cannot touch any other tenant’s data directory. |
The sandbox driver reduces blast radius from 90 tenants to 1. Hull further reduces the depth of what that one compromised tenant can do — a SQL injection victim cannot trivially become a full container escape, and a full container escape cannot trivially become lateral movement.
Example Configuration
# Each tenant gets its own service definition
services:
- name: zyght-codelco
replicas: 1
driver: sandbox
config:
base: node22
rootfs: /opt/mentat/sandboxes/zyght-codelco/rootfs
command: /usr/local/bin/node
args: ["server.js"]
env:
- TENANT_ID=codelco
# DB credentials injected via mt secret set
memory_mb: 512
cpu_percent: 25
max_pids: 64
port: 3001
health_check:
path: /health
interval_secs: 10
ingress:
domain: codelco.zyght.com
path: /
scale:
min: 1
max: 3
metric: cpu
scale_up_at: 70
scale_down_at: 20
cooldown_secs: 30
# Secrets set separately (never in YAML)
# mt secret set ZYGHT_CODELCO_DB_URL "postgres://..."
# mt secret set ZYGHT_SQM_DB_URL "postgres://..."
# Each sandbox only receives its own secretHonest Limitations
Mentat is an infrastructure orchestrator, not a WAF or application security tool. It provides the second line of defense:
- First line(not Mentat's scope): input validation, authentication, authorization, encryption at rest, WAF rules. These prevent the initial compromise.
- Second line (Mentat): tenant isolation, blast radius reduction, credential scoping, anomaly detection via cgroups. These limit damage when the first line fails.
If the application itself has a SQL injection vulnerability, Mentat cannot prevent the attacker from using the application's legitimate database connection. But the attacker only reaches one tenant's data instead of all 90.
The Lesson
The Zyght breach is not a story about a missing firewall or an unpatched CVE. It is a story about architectural trust boundaries. When 90 critical infrastructure operators share one application and one database, the blast radius of any single vulnerability is total. Sandbox-per-tenant isolation makes that architecture physically impossible — the kernel enforces what application logic failed to protect.