JWT, Crypto & HTTP Security
The engine uses a custom C++ HTTP parser (not llhttp) and a native addon. Read Trust before production. Use the latest release on the public internet (npm install velociradix).
JWT and AES-256-GCM helpers ship in this package. The C++ layer rejects request smuggling, oversize headers, TRACE/CONNECT, and Slowloris-style incomplete requests before JavaScript runs.
Secrets for jwtAuth and session must come from the environment (process.env.JWT_SECRET), never from source.
1. Zero-Dependency JWT (jwtSign / jwtVerify)
Verification is constant-time (crypto.timingSafeEqual), blocks alg: none, and honors exp, nbf, iss, and aud.
import { createApp, jwtSign, jwtAuth } from "velociradix";
const app = createApp();
const JWT_SECRET = process.env.JWT_SECRET;
app.post("/login", (ctx) => {
const token = ctx.jwtSign({ userId: 42, role: "admin" }, JWT_SECRET, {
expiresIn: 3600,
issuer: "velociradix",
});
return { token };
});
app.get(
"/dashboard",
(ctx) => {
return { welcome: ctx.state.user };
},
{
middlewares: [jwtAuth({ secret: JWT_SECRET, issuer: "velociradix" })],
},
);2. AES-256-GCM Encrypted Cookies & Sessions
IV is 12 bytes and the auth tag is 16 bytes. Malformed ciphertext returns undefined without leaking error details.
app.get("/login-session", (ctx) => {
ctx.setEncryptedCookie(
"user_session",
{ userId: 101, token: "abc" },
process.env.COOKIE_SECRET,
);
return { status: "logged_in" };
});
app.get("/profile", (ctx) => {
const session = ctx.getEncryptedCookie(
"user_session",
process.env.COOKIE_SECRET,
);
return { session };
});setCookie() defaults to Path=/ and SameSite=Lax. Session cookies also set HttpOnly.
3. HTTP Parser Guarantees (C++ engine)
The parser is a custom C++ implementation, not llhttp. It closes the smuggling and DoS cases listed below; that is not the same as the operational maturity of a widely fuzzed library parser.
Applied to every connection before JavaScript runs:
| Check | Result |
|---|---|
HTTP/1.1 without Host | 400 |
Conflicting Content-Length | 400 |
Transfer-Encoding + Content-Length | 400 |
| Header folding (obs-fold) / LF-only framing | 400 |
TRACE / CONNECT | 405 |
| Header block > 32 KiB or > 100 headers | 431 |
| URI > 8 KiB | 414 |
| Incomplete headers idle > 10s | 408 |
| CR/LF/NUL in response header names or values | stripped |
ctx.ip uses the TCP peer from accept() unless app.setTrustProxy(true) is set.
4. helmet() and cors()
app.use(helmet());
app.use(cors({ origin: "https://example.com", credentials: true }));helmet() sends CSP, COOP, CORP, HSTS, X-Content-Type-Options, and Permissions-Policy. X-XSS-Protection is 0 (the legacy XSS auditor is harmful).
For a public API consumed from other origins:
app.use(
helmet({
contentSecurityPolicy: false,
crossOriginResourcePolicy: "cross-origin",
}),
);cors({ credentials: true }) never pairs with Access-Control-Allow-Origin: * — the request Origin is reflected instead.
Native app.enableCors() / enable_cors() also never sends ACAO: *. It reflects Origin when present and omits ACAO otherwise. Prefer the JS cors({ origin: 'https://your.app' }) middleware in production so the allow-list is explicit.
5. CSRF
Double-submit cookie (httpOnly: false so JS can read it), SameSite=Strict, constant-time header compare, and Origin match after default-port normalization (example.com ≡ example.com:443 on HTTPS). The token is also sent as X-CSRF-Token. Tokens are not accepted from the query string.
app.use(csrf());6. What 8.2.0 did — and what it did not
8.1.x closed previously identified HTTP, JWT, path, and header issues. 8.2.0 covers the remaining known 8.1.1 findings:
- Any
Transfer-Encodingis rejected (chunked is not implemented). - Keep-alive idle connections close after 30s; incomplete headers still 408 after 10s.
- JS↔C++ handles are
napi_externaltokens (slot + generation), not a heap address. app.static()usesrealpath+ a prefix check.app.ws()was removed (it was never a WebSocket).app.graphql()is POST-only with an 8 KiB query cap.cache(),sizeLimit(), native CORS, CSRF,Set-Cookielines,cookieParse, swagger/metrics/postman exposure, session flags,download/sendFileroot,Content-Disposition, SSE framing,apiKeyheader-default, CLI JWT secret, and IPv6 accept are hardened as documented in this guide.
Still true: the HTTP parser is a custom C++ implementation, not llhttp. ctx.graphql() is a toy resolver, not a GraphQL server. Say the known 8.1.1 issues are covered — do not say security was “fully solved.”
Production vs local demo
- Local demo: the library is usable as-is.
- Internet production: use 8.2.0 or newer. Prefer JS
cors({ origin }). Do not putcache()on authenticated routes withoutvary. Do not exposeswagger/metricsUI/postmanDocon the public internet unless{ expose: true }on a private network. UsehostGuard({ hosts })whensetTrustProxy(true). Keep the custom parser in mind when threat-modeling.