Vallum
Docs/System guide
VALLUM DOCUMENTATION

Deploy and operate the response-resistance proxy

Vallum gives an application-authenticated browser a truthful response path while making protected raw API automation unreliable. This guide reflects the repository’s implemented CLI, SDKs, protocol, storage, and limitations.

Give this to your coding agent

One file with the full integration surface: admission broker, every framework adapter, route policy reference, and the constraints agents get wrong.

View raw

Quickstart

The repeatable local demonstration starts the proxy, demo origin, legitimate SDK frontend, PostgreSQL, Redis, and Prometheus. It requires Docker Compose, a currently patched Go 1.25+ toolchain, Node 22+, curl, jq, and npm.

SHELL
git clone https://github.com/LiteEagle262/vallum.git
cd vallum
docker compose -f deploy/compose.yaml up --build -d
./scripts/demo.sh

Open http://127.0.0.1:8088 and sign in with demo / vallum-local-demo. The demo proves three views: direct origin truth on :9090, a raw Vallum response containing no protected truth, and exact reconstruction through the authenticated browser SDK.

Production is not the base demo.

Use the isolated topology, unique secrets, TLS, secure cookies, private management/storage networks, and an authenticated origin hop before evaluating a shared deployment. ./scripts/evaluate-isolated.sh runs that harder configuration and writes a JSON summary.

SDK packages

One framework-neutral browser core plus first-party adapters. The adapters own lifecycle and dependency injection only — every one speaks the identical wire protocol, keeps initialization in the browser, exposes explicit loading/error/retry state, and leaves global fetch untouched.

PackageApplicationEntry point
@liteeagle226/clientAny JS or TypeScript frameworkcreateVallumClient()
@liteeagle226/browserHTML with no build stepCDN ESM or Vallum global
@liteeagle226/reactReact 18+VallumProvider, useVallum()
@liteeagle226/nextjsNext.js App or Pages Router/client and /server subpaths
@liteeagle226/vueVue 3.3+ and NuxtcreateVallum(), useVallum()
@liteeagle226/svelteSvelte 5 and SvelteKitprovideVallum(), getVallumContext()
@liteeagle226/angularAngular 22provideVallum(), VallumService
@liteeagle226/admissionNode application backendcreateVallumAdmissionHandler()
@liteeagle226/renderOptional display frictionexperimental, reversible

Choose one frontend recipe. Every adapter installs the core client automatically; the explicit client name below makes the complete runtime pair auditable. Add the admission package in the trusted backend, except when the Next.js server entry already provides that wrapper.

SHELL
npm install @liteeagle226/client @liteeagle226/admission
npm install @liteeagle226/browser @liteeagle226/admission
npm install @liteeagle226/client @liteeagle226/react react @liteeagle226/admission
npm install @liteeagle226/client @liteeagle226/vue vue @liteeagle226/admission
npm install @liteeagle226/client @liteeagle226/svelte svelte @liteeagle226/admission
npm install @liteeagle226/client @liteeagle226/angular @angular/core @liteeagle226/admission
npm install @liteeagle226/client @liteeagle226/react @liteeagle226/admission @liteeagle226/nextjs next react
npm install @liteeagle226/render # optional; add to any frontend recipe

All nine packages are Apache-2.0, typed, SSR import-safe, independently publishable, and checked as real npm tarballs by npm run sdk:verify. Astro, Solid, Qwik, Lit, Alpine, Preact, and custom elements use @liteeagle226/client directly.

Build the admission broker first

The page load grants nothing, and the browser bundle cannot hold the Ed25519 signing key. An authenticated application backend must expose POST /.well-known/vallum/admission on the same public origin the page is served from. It authenticates its ordinary session, enforces same-origin policy and an issuance budget, derives subject and scopes from server-side authorization state, and signs a short-lived, one-time grant.

TYPESCRIPT
import { admissionConfiguration, createVallumRouteHandler } from "@liteeagle226/nextjs/server";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

export const POST = createVallumRouteHandler({
  configuration: () => admissionConfiguration(),
  async authenticate(request) {
    const session = await applicationSessions.read(request);
    // Never read the subject or scopes from the request body.
    return session ? { subject: session.userId, scopes: session.scopes } : null;
  },
  async rateLimit(_request, principal) {
    return admissionBudgets.consume(principal.subject, 20, "1m");
  },
});

@liteeagle226/admission exposes the same handler for any Node framework that can hand it a standard Request. Both callbacks are required: there is no allow-all default. Generate the keypair with npm run keys:admission; the backend holds VALLUM_ADMISSION_PRIVATE_KEY and Vallum receives only VALLUM_ADMISSION_PUBLIC_KEY.

Issuer, audience, key ID, application ID, and environment must match.

The backend’s environment variables and the proxy’s session.admission block are verified against each other. A mismatch, an expired grant, or a reused grant is rejected — grants are atomically consumed once.

Browser client and request proof

The SDK creates non-extractable Web Crypto keys, exchanges the application grant for wrapped AES-256-GCM material, and signs every protected request over method, canonical URI, body hash, timestamp, JTI, and server nonce. Redis atomically rejects proof replay before authorization, quota, or any origin call.

TYPESCRIPT
import { createVallumClient } from "@liteeagle226/client";

// The user must already hold the application's ordinary authenticated session.
const client = await createVallumClient({ endpoint: location.origin });

const response = await client.fetch("/api/protected");
const data = await response.json(); // the exact origin object

client.destroy(); // release session and key references with the owning UI scope

Application code never selects a wire layout, identifies metadata aliases, or decodes a field. The SDK strips every false-view and prompt-carrier field, passes unprotected responses through unchanged, renews expired material and retries a safe request once, and fails closed on malformed, expired, replayed, or tampered protected data. Pass client.fetch or client.wrapFetch(existingFetch) into your API layer rather than patching global fetch.

Deployment topology

Keep the data plane independent from the product web app. Public protected paths terminate at an edge or load balancer and route directly to the Go proxy. The Next.js application provides the product UI and same-origin admission broker; it must not proxy all application traffic.

Browser or raw client→Edge routing→Vallum proxy→Fixed origin
  • Expose only the public proxy listener.
  • Keep the management listener, Redis, PostgreSQL, and origin private.
  • Authenticate the Vallum-to-origin hop with the configured HMAC header. The bundled verifier’s replay cache is process-local, so a replicated origin needs one shared atomic replay store.
  • Route /.well-known/vallum/admission to the authenticated application backend on the same public origin used by the SDK.

Validate configuration before startup

The CLI implements run, validate, print-config, benchmark, and version. Configuration is YAML and every secret is an environment reference. Route, carrier, and synthetic-world policy is fully checked before the proxy starts — an invalid policy refuses to load rather than degrading silently, so run validate in CI.

SHELL
go run ./cmd/vallum validate --config config/vallum.yaml
go run ./cmd/vallum print-config --config config/vallum.yaml
go run ./cmd/vallum run --config config/vallum.yaml
VALLUM.YAML
version: 1
application:
  id: app_production
  name: Protected API
  environment: production
  tenant_id_env: VALLUM_TENANT_ID
proxy:
  listen_address: ":8080"          # the only public listener
  origin_url: "https://origin.internal"
  origin_auth_header: "X-Vallum-Origin-Authorization"
  origin_auth_secret_env: VALLUM_ORIGIN_AUTH_SECRET
  max_transform_body_bytes: 1048576
management:
  enabled: true
  listen_address: ":9091"          # private network only
session:
  cookie_secure: true
  cookie_secret_env: VALLUM_COOKIE_SECRET
  quarantine_on_honeytoken: true
  admission:
    required: true
    issuer: customer-frontend
    audience: payments:production
    max_grant_ttl: "30s"
    public_keys:
      - key_id: frontend-2026-08
        ed25519_public_key_env: VALLUM_ADMISSION_PUBLIC_KEY

Route policies

Routes are evaluated in order, in two phases. Before the origin call Vallum selects the first path/method match and enforces authorization and quota from it, because the response content type is not known yet. After the origin responds it may select a later content-type variant — but a later variant can never weaken a fail_closed or fail_static route, nor add authorization the request-time route did not already impose.

encode

Encrypts the canonical JSON for reconstruction by the authorized SDK.

scramble

Adds a session-stable false representation while preserving configured types and shapes.

inject

Adds advisory prompt-injection carriers outside the canonical application object.

hybrid

Composes encoding, scrambling, and optional injection under one route policy.

Authorization evaluates only grant-bound, proof-verified state — require_proof, require_subject, and required_scopes. Raw identity, role, and scope headers are never evidence. Quotas are atomic fixed-window Redis budgets across session, proof-bound identity, and mutation dimensions; an unavailable quota store fails into containment rather than into an origin call.

ROUTE POLICY
routes:
  - name: internal-api
    match: {path: "/api/internal/**", methods: [GET, POST]}
    mode: hybrid
    authorization:
      require_proof: true
      require_subject: true
      required_scopes: ["route:internal-api"]
    quota:
      session:  {limit: 120, window: 1m}
      identity: {limit: 240, window: 1m}
      mutation: {limit: 20,  window: 1m}
    data_minimization:
      remove_json_pointers: ["/diagnostics/private_note"]
    stealth: true
    failure:
      behavior: fail_closed

Failure behavior is explicit: fail_open returns an untouched supported origin response when transformation cannot complete and therefore carries a deliberate disclosure consequence; fail_closed discards it; fail_static returns configured static content. V1 transforms bounded, UTF-8, non-streaming JSON over HTTP(S) only — route SSE, gRPC, and protocol upgrades around Vallum.

Containment, disclosure, and deception

Authorization failure, exhausted quotas, quarantine, and honeytoken events resolve locally without an origin call. The synthetic result uses a bounded, coherent session world instead of leaking whether an origin object or privileged route exists. The service_control_v1 profile generates a workspace, services, permissions, an audit record, one job, and one false credential that stay coherent for a session across a fixed transition vocabulary.

  • Data minimization removes configured RFC 6901 pointers before encryption and scrambling, so removed values are absent from both representations.
  • Deferred disclosure omits selected values from the response entirely — the wire body, the false view, and the decrypted truth alike — and redeems them later through a proof-bound, budgeted endpoint. It is the one layer whose effectiveness does not depend on an attacker failing to decode something.
  • Render-only disclosure delivers selected values as pixels rather than DOM text. It is a one-shot reference you mount, an accessibility tradeoff, and targeted friction — not cryptographic secrecy.
  • False affordances present fabricated routes, parameters, and privileged fields as ordinary API data with no imperative, so no normalization step removes them. Every actionable trigger is registered as a canary.
  • Injection carriers are advisory and reversible. Never attach an authorization decision to a model obeying injected text.

Detection and response normalization

Session-scoped behavioral detection scores automated probing — enumeration, sequential-ID walking, method fuzzing, and high error rates — into the same risk pipeline as rule signals such as honeytoken use and canary-route access. A hostile session is force-contained; a flagged session pays an added per-response delay. No single request has to be individually blocked.

On routes that do reach the origin, response normalization strips fingerprint headers, cloaks origin error bodies, collapses selected statuses, and adds a timing floor with jitter. That removes the differential oracles — errors, headers, status, latency — an autonomous agent reads to confirm a hypothesis. It complements an inbound WAF, which inspects requests.

Storage, telemetry, and the console

Redis holds short-lived sessions, proof replay state, quotas, hashed honeytokens, synthetic worlds, and sealed deferred values until redemption or expiry. PostgreSQL stores sanitized application metadata, configuration snapshots, sessions, events, domains, and audit records.

The operations console uses a tenant-scoped API on the private management listener. Telemetry reads and validated policy writes are authenticated with a server-held console token; customer browsers never receive that token or connect to the data plane database. The web service has separate, least-privilege storage for product workflows such as support and legal documents. The management API requires both console_api.enabled and a resolved token; without either it is not mounted at all, and it is never exposed on the public listener.

MANAGEMENT
management:
  console_api:
    enabled: true
    token_env: VALLUM_CONSOLE_TOKEN
    max_window: "168h"
    # Unset disables console-driven domain registration entirely.
    domain_cname_suffix: "edge.vallum.dev"
Zero-retention boundary

Vallum does not persist plaintext request or response bodies, cookies, credentials, decoded payloads, or real scrambling source values. Deferred disclosure is the explicit origin-data exception: authenticated ciphertext exists briefly in Redis.

Security boundaries

Vallum is a response-resistance and origin-admission layer. It does not classify a caller as human or AI, and it is not a WAF, secure enclave, endpoint agent, sandbox, bot detector, or replacement for origin isolation.

  • An agent controlling an authenticated browser can instrument the SDK and inspect reconstructed data. Non-extractable keys are not a hardware boundary.
  • Blind and out-of-band attacks may not depend on response understanding.
  • Prompt-injection carriers are advisory and reversible; @liteeagle226/render is reversible visual obfuscation, not a security control.
  • Security properties come from admission, proof, authorization, minimization, containment, deferred disclosure, isolation, and storage boundaries.
  • Production readiness requires conventional controls around identity, secrets, network segmentation, patching, monitoring, and incident response.