Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.

Tracehound

Deterministic runtime security buffer for high-velocity APIs

External systems signal threats. Tracehound preserves evidence.

Advanced CodeQL AnalysisSemgrep Security AnalysisCodecovOpenSSF ScorecardFull ValidationLicense: Apache2.0npm

Website · Documentation Index · API Reference · Configuration · RFCs · Security · Changelog

Tracehound is a decision-free forensic runtime that sits between upstream detection and downstream response. It does not classify traffic, apply heuristics, or replace a WAF. It receives explicit threat signals, quarantines evidence, and preserves a tamper-evident operational record without becoming a new denial-of-service vector for the host application.

WAFs are one possible upstream authority, not the product boundary. Tracehound’s evidence path can be driven by any explicit external threat signal source that can be mapped into scent.threat, including reverse proxies, bot-management systems, abuse detectors, and internal risk services, while native guardrails such as rate limiting and payload-size controls continue to operate independently.

This repository contains the open-source Tracehound substrate: @tracehound/core, @tracehound/express, @tracehound/fastify, and @tracehound/cli. Current work is centered on real-world OSS validation, deployment confidence, and operator usability.

What Tracehound Guarantees

  • Decision-free runtime: Tracehound never decides whether traffic is malicious. Upstream systems provide the threat signal.
  • Deterministic behavior: No ML, heuristics, or probabilistic branching in the hot path.
  • Fail-open host safety: Runtime faults do not turn Tracehound into an application-layer DoS vector.
  • Payload-less runtime membrane: Raw payload bytes stay inside the quarantine boundary; runtime code receives metadata-only handles.
  • Tamper-evident custody: Evidence lifecycle events are recorded through AuditChain.
  • Bounded resource use: Quarantine, rate limiting, notification delivery, hound queues, and decay workflows have explicit caps.

What Tracehound Is Not

  • Not a WAF, IDS, SIEM, RASP, or threat classifier
  • Not an APM or request analytics platform
  • Not a policy engine or access-control layer
  • Not a payload-inspection service for runtime application code

Runtime Model

Request/Event
-> Adapter extracts Scent
-> agent.intercept(scent)
-> rate_limited (429)
-> clean (pass through)
-> payload_too_large (413)
-> ignored (duplicate signature, pass through)
-> quarantined (403, metadata-only runtime handle)
-> error (fail-open)

Intercept Statuses

StatusMeaningDefault adapter behavior
cleanNo threat signal on the ScentPass through
rate_limitedSource exceeded bounded sliding-window limitsHTTP 429 + Retry-After
payload_too_largePayload exceeded maxPayloadSizeHTTP 413
ignoredDuplicate signature or deterministic drop under pressurePass through
quarantinedEvidence stored successfullyHTTP 403
errorInternal Tracehound failureFail-open pass through by default

Repository Scope

PackageRole
@tracehound/coreSecurity engine, evidence lifecycle, quarantine, AuditChain, watcher, hound pool, notifications
@tracehound/expressThin Express middleware adapter
@tracehound/fastifyThin Fastify plugin adapter
@tracehound/cliCLI and terminal inspection tooling

Installation

pnpm add @tracehound/core
pnpm add @tracehound/core @tracehound/express
pnpm add @tracehound/core @tracehound/fastify
pnpm add -g @tracehound/cli

Requirements:

  • Node.js >=20
  • pnpm workspace tooling for repository development

Quick Start

import{createTracehound,generateSecureId,typeScent}from'@tracehound/core'constth=createTracehound({maxPayloadSize: 1_000_000,quarantine: {maxCount: 10_000,maxBytes: 100_000_000,},rateLimit: {windowMs: 60_000,maxRequests: 100,},})constscent: Scent={id: generateSecureId(),timestamp: Date.now(),source: {ip: '203.0.113.10',userAgent: 'curl/8.7.1',},payload: {method: 'POST',path: '/api/login',body: {username: 'alice'},},threat: {category: 'injection',severity: 'high',},}constresult=th.agent.intercept(scent)if(result.status==='quarantined'){console.log(result.handle.signature)console.log(result.handle.membrane)// metadata_only}th.shutdown()

Notes:

  • Scent.source is a structured object: { ip, userAgent?, tls? }
  • Scent.payload must be JSON-serializable
  • If ingressBytes is present, Tracehound hashes raw ingress bytes instead of canonicalized payload bytes

Framework Adapters

Express

import{Buffer}from'node:buffer'importexpressfrom'express'import{createTracehound}from'@tracehound/core'import{tracehound}from'@tracehound/express'constapp=express()constth=createTracehound()app.use(express.json({verify: (req,_res,buf)=>{Reflect.set(req,'rawBody',Buffer.from(buf))},}),)app.use(tracehound({agent: th.agent,emitTraceIdHeader: true,}),)

Fastify

importfastifyfrom'fastify'import{createTracehound}from'@tracehound/core'import{tracehoundPlugin}from'@tracehound/fastify'constapp=fastify()constth=createTracehound()app.register(tracehoundPlugin,{agent: th.agent,emitTraceIdHeader: true,})

Adapter notes:

  • Express and Fastify adapters are intentionally thin and carry no security decision logic
  • For deterministic ingress-byte signatures, set rawBody before Tracehound runs
  • emitTraceIdHeader is optional and enables x-tracehound-trace-id for local inspection workflows
  • @tracehound/fastify uses the named export tracehoundPlugin

CLI and Operational Snapshots

Tracehound CLI provides runtime status, stats, live watch output, and local trace inspection workflows.

tracehound status
tracehound stats
tracehound inspect --trace-id <trace-id>
tracehound watch
tracehound history clear
tracehound disk clear

status, stats, and watch require a signed runtime snapshot. Configure snapshot export in the application runtime:

import{createTracehound}from'@tracehound/core'constth=createTracehound({snapshot: {path: '/var/run/tracehound/system-snapshot.json',secret: process.env.TRACEHOUND_SNAPSHOT_SECRET,intervalMs: 1000,},})
export TRACEHOUND_SYSTEM_SNAPSHOT_PATH=/var/run/tracehound/system-snapshot.json
export TRACEHOUND_SNAPSHOT_SECRET=replace-me
export TRACEHOUND_SNAPSHOT_MAX_AGE_MS=5000
export TRACEHOUND_SNAPSHOT_MAX_FUTURE_SKEW_MS=5000

If snapshot input is missing, stale, tampered, or unverifiable, CLI commands fail explicitly instead of fabricating healthy runtime state.

Documentation Map

AreaDocument
OnboardingGetting Started
API surfaceAPI Reference
Runtime optionsConfiguration Reference
Upgrade pathBreaking Changes
Evidence custodyEvidence Lifecycle Policy
Fail-open behaviorFail-Open Spec
Performance envelopePerformance SLA
Security validationSecurity Assurance
Architecture governanceRFC Index
Security review corpusSecurity Readme

Development

pnpm build
pnpm test
pnpm test:coverage
pnpm lint
pnpm validate:paranoid

Per-package examples:

pnpm --filter @tracehound/core test
pnpm --filter @tracehound/express test
pnpm --filter @tracehound/fastify test
pnpm --filter @tracehound/cli test

Contributing and Security

License

Tracehound is licensed under the Apache-2.0 License.

Releases

Sponsor this project

Used by

Contributors

Languages