🔓

Tier 3 Zero-Knowledge Decryption — SDK Reference

Overview

Tier 3 ships two decryption SDKs — both open-source under Apache 2.0, both share the same envelope wire format. Pick based on where you decrypt:

SDKWhere it runsUse case
Browser SDK (TypeScript)EchelonGraph dashboard at app.echelongraph.ioDashboard rendering, ad-hoc analyst queries
sdk/zkdecrypt (Go)Anywhere Go runsSOC pipelines, SIEM forwarders, server-side decryption proxies, analytics notebooks

Both authenticate against your KMS (AWS / GCP / Vault) using your own credentials — EchelonGraph never sees the plaintext.

> Distribution note. The browser SDK lives at > frontend/src/lib/zkdecrypt/ and is bundled with the EchelonGraph > dashboard — there's nothing for you to npm install. If you want > to host your own decryption UI, copy the source (Apache 2.0) into > your project; the code samples below show the public surface.

Browser SDK (TypeScript)

Quick start with React

import { useZkConfig, useZkDecrypt } from "@echelongraph/zkdecrypt";

function FindingViewer({ finding, jwt, vaultToken }) {
  const { config } = useZkConfig(jwt);
  const { decrypt, busy, error } = useZkDecrypt(config, { vaultToken });
  const [plaintext, setPlaintext] = useState<string | null>(null);

  async function reveal() {
    const out = await decrypt({
      envelope: finding.encryptedPayload,
      tenantId: finding.tenantId,
      agentId: finding.agentId,
    });
    setPlaintext(new TextDecoder().decode(out.plaintext));
  }
  if (error) return <div>Decrypt failed: {error.code}</div>;
  if (busy) return <div>Decrypting…</div>;
  return plaintext ? <pre>{plaintext}</pre> : <button onClick={reveal}>Reveal</button>;
}

Auth flow per provider

ProviderCustomer identityToken sent to KMS
Vault Transit (T3.11 — shipped)OIDC sign-in via your IdP → Vault returns a tokenX-Vault-Token
AWS KMS (T3.12 — shipped)Your IdP → Cognito Identity Pool / AssumeRoleWithWebIdentity → STS temp credsSigV4-signed KMS Decrypt
GCP Cloud KMS (T3.13 — shipped)Google Identity Services / Workload Identity Federation → OAuth access tokenAuthorization: Bearer

The SDK never holds long-lived credentials. Tokens are passed via the React hook's options argument; rotation is the customer's auth context's responsibility.

AWS quick start (T3.12)

The SDK signs KMS Decrypt requests with hand-rolled SigV4 — no aws-sdk-js dependency. The dashboard's auth layer is responsible for fetching STS credentials (Cognito Identity Pool federation, AssumeRoleWithWebIdentity, IAM Roles Anywhere — your choice). Pass them to the hook as a static object or an async getter:

import { useZkDecrypt } from "@echelongraph/zkdecrypt";

// Async getter — SDK refetches when within 60s of expiresAt.
async function getAwsCreds() {
  const stsResp = await myCognitoRefresh();  // your IdP / federation
  return {
    accessKeyId:     stsResp.AccessKeyId,
    secretAccessKey: stsResp.SecretAccessKey,
    sessionToken:    stsResp.SessionToken,
    expiresAt:       stsResp.Expiration, // ISO-8601
  };
}

const { decrypt } = useZkDecrypt(config, { awsCredentials: getAwsCreds });

The provider auto-refreshes credentials within 60s of expiresAt and surfaces kms_auth_failed when STS / Cognito returns 401/403 so you can re-prompt the user for sign-in. Custom KMS endpoints (FIPS, VPC endpoints) are supported via config.aws.endpoint.

GCP quick start (T3.13)

The SDK calls Cloud KMS Decrypt over REST with a customer-fetched OAuth access token — no @google-cloud/kms (gRPC + protobuf) dependency, ~3 KB gzipped. The dashboard's auth layer fetches the token via Google Identity Services (google.accounts.oauth2), Workload Identity Federation, or any token broker. Required scope: https://www.googleapis.com/auth/cloudkms.

import { useZkDecrypt } from "@echelongraph/zkdecrypt";

// Async getter — SDK refetches when within 60s of expiresAt.
async function getGcpCreds() {
  const tokenResponse = await myGisRefresh();  // your GIS / federation
  return {
    accessToken: tokenResponse.access_token,
    expiresAt:   tokenResponse.expiry, // ISO-8601
  };
}

const { decrypt } = useZkDecrypt(config, { gcpCredentials: getGcpCreds });

The customer's IAM grant must include cloudkms.cryptoKeyVersions.useToDecrypt on the configured key for the federated identity. Cloud KMS returns 401/403 → kms_auth_failed (re-prompt for sign-in); 429 RESOURCE_EXHAUSTED + 5xx are retried with backoff. Regional endpoints (e.g., https://us-east1-cloudkms.googleapis.com) and VPC SC perimeter endpoints are supported via config.gcp.endpoint.

Error codes

ZkSdkError.code is stable — pattern-match on it instead of parsing messages: envelope_too_small, envelope_version_unsupported, envelope_malformed, wrapped_dek_invalid, provider_not_implemented, kms_unwrap_failed, kms_auth_failed, aes_gcm_decrypt_failed, config_disabled, config_missing, no_subtle_crypto.

Bundle size

Tree-shakable: provider implementations load lazily via dynamic import(). A Vault-only customer ships ~5 KB gzipped. An AWS-only customer ships ~7 KB gzipped (provider + hand-rolled SigV4 signer). A GCP-only customer ships ~3 KB gzipped (REST + bearer token; no gRPC or protobuf). Unselected providers are not pulled into the bundle.

Go SDK

Quick start

import "github.com/AkshayDubey29/EchelonGraph/tier3-continuous-agent/sdk/zkdecrypt"

plaintext, err := zkdecrypt.Open(zkdecrypt.OpenInput{
    EnvelopePayload: rowFromAPI,
    BYOK:            customerKey32Bytes, // fetch from your KMS first
    TenantID:        "acme",
    AgentID:         "tentacle-node-1",
})

Fetching the KEK

# AWS
aws kms decrypt --ciphertext-blob fileb://wrapped-kek.bin \
  --query Plaintext --output text | base64 -d > kek.bin

# GCP
gcloud kms decrypt --location=us-east1 --keyring=echelongraph --key=runtime \
  --ciphertext-file=wrapped-kek.bin --plaintext-file=kek.bin

# Vault
vault write -field=plaintext transit/decrypt/echelongraph \
  ciphertext=vault:v1:... | base64 -d > kek.bin

Wire format

byte 0:                version (uint8, currently 1)
bytes 1..5:            wrappedLen (uint32 LE)
bytes 5..5+wrappedLen: wrapped DEK
next 12 bytes:         AES-GCM nonce
remainder:             ciphertext + 16-byte AEAD tag

AAD: ${tenantID}|${agentID} UTF-8 bytes; both empty → no AAD.

Threat model

Protects against: EchelonGraph staff curiosity, EchelonGraph DB compromise, network interception, cross-tenant leak.

Does NOT protect against (by design): customer's KMS compromise, customer's browser session compromise (XSS / malicious extension), EchelonGraph backend code injection that could MITM the customer's KMS calls. Mitigations: HTTPS-only, CSP locked to our origin, SRI on the SDK chunk, customer rotates KEK on suspected compromise.

License gate

zk_browser_decrypt license claim required for the browser SDK (T3.11+). The Go SDK is unrestricted (open-source — no license check).

Source

  • Go SDK: tier3-continuous-agent/sdk/zkdecrypt/ (Apache 2.0)
  • Browser SDK: frontend/src/lib/zkdecrypt/ (Apache 2.0)
  • Tests: 111 unit tests covering envelope edge cases, AES-GCM round-trip,
all four providers (Local + Vault + AWS SigV4 + GCP OAuth REST) —

KMS Decrypt happy paths, auth flows, credential refresh, retry policies, error mapping per HTTP status, edge cases (oversized / empty / wrong-length / malformed responses), HTTPS-only endpoint enforcement, AbortSignal propagation, dispose() lifecycle, single- flight credential refresh under concurrent burst.