Deterministic runtime security buffer for high-velocity APIs
External systems signal threats. Tracehound preserves evidence.
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.
- 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.
- 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
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)
| Status | Meaning | Default adapter behavior |
|---|---|---|
clean |
No threat signal on the Scent |
Pass through |
rate_limited |
Source exceeded bounded sliding-window limits | HTTP 429 + Retry-After |
payload_too_large |
Payload exceeded maxPayloadSize |
HTTP 413 |
ignored |
Duplicate signature or deterministic drop under pressure | Pass through |
quarantined |
Evidence stored successfully | HTTP 403 |
error |
Internal Tracehound failure | Fail-open pass through by default |
| Package | Role |
|---|---|
@tracehound/core |
Security engine, evidence lifecycle, quarantine, AuditChain, watcher, hound pool, notifications |
@tracehound/express |
Thin Express middleware adapter |
@tracehound/fastify |
Thin Fastify plugin adapter |
@tracehound/cli |
CLI and terminal inspection tooling |
pnpm add @tracehound/core
pnpm add @tracehound/core @tracehound/express
pnpm add @tracehound/core @tracehound/fastify
pnpm add -g @tracehound/cliRequirements:
- Node.js
>=20 pnpmworkspace tooling for repository development
import { createTracehound, generateSecureId, type Scent } from '@tracehound/core'
const th = createTracehound({
maxPayloadSize: 1_000_000,
quarantine: {
maxCount: 10_000,
maxBytes: 100_000_000,
},
rateLimit: {
windowMs: 60_000,
maxRequests: 100,
},
})
const scent: 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',
},
}
const result = th.agent.intercept(scent)
if (result.status === 'quarantined') {
console.log(result.handle.signature)
console.log(result.handle.membrane) // metadata_only
}
th.shutdown()Notes:
Scent.sourceis a structured object:{ ip, userAgent?, tls? }Scent.payloadmust be JSON-serializable- If
ingressBytesis present, Tracehound hashes raw ingress bytes instead of canonicalized payload bytes
import { Buffer } from 'node:buffer'
import express from 'express'
import { createTracehound } from '@tracehound/core'
import { tracehound } from '@tracehound/express'
const app = express()
const th = createTracehound()
app.use(
express.json({
verify: (req, _res, buf) => {
Reflect.set(req, 'rawBody', Buffer.from(buf))
},
}),
)
app.use(
tracehound({
agent: th.agent,
emitTraceIdHeader: true,
}),
)import fastify from 'fastify'
import { createTracehound } from '@tracehound/core'
import { tracehoundPlugin } from '@tracehound/fastify'
const app = fastify()
const th = 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
rawBodybefore Tracehound runs emitTraceIdHeaderis optional and enablesx-tracehound-trace-idfor local inspection workflows@tracehound/fastifyuses the named exporttracehoundPlugin
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 clearstatus, stats, and watch require a signed runtime snapshot. Configure snapshot export in the application runtime:
import { createTracehound } from '@tracehound/core'
const th = 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=5000If snapshot input is missing, stale, tampered, or unverifiable, CLI commands fail explicitly instead of fabricating healthy runtime state.
| Area | Document |
|---|---|
| Onboarding | Getting Started |
| API surface | API Reference |
| Runtime options | Configuration Reference |
| Upgrade path | Breaking Changes |
| Evidence custody | Evidence Lifecycle Policy |
| Fail-open behavior | Fail-Open Spec |
| Performance envelope | Performance SLA |
| Security validation | Security Assurance |
| Architecture governance | RFC Index |
| Security review corpus | Security Readme |
pnpm build
pnpm test
pnpm test:coverage
pnpm lint
pnpm validate:paranoidPer-package examples:
pnpm --filter @tracehound/core test
pnpm --filter @tracehound/express test
pnpm --filter @tracehound/fastify test
pnpm --filter @tracehound/cli testTracehound is licensed under the Apache-2.0 License.
