@zedtech/zedkit-server
v1.3.0
Published
Harden any Node server in minutes: boot-time env parsing, rate limiting, secret/PII redaction, HKDF crypto and HTTP helpers. Original work by the same author.
Maintainers
Readme
@zedtech/zedkit-server
Harden any Node server in minutes: in-process rate limiting, secret/PII redaction for logs, HKDF crypto and HTTP helpers. Original work by the same author. Zero runtime dependencies.
Install from npm only (no shadcn registry copy path). See install or copy on the docs site for how that differs from UI bricks.
Install
pnpm add @zedtech/zedkit-serverUsage
import { redactValue } from "@zedtech/zedkit-server";
logger.info({ event: "login", user: redactValue(user) });When to use
Reach for this package when you rate-limit, redact logs, sign webhooks,
or read database error codes on a Node server. For typed errors and IDs
see @zedtech/zedkit-core instead.
API
MemoryRateLimiter— fixed-window, in-process rate limiting:allow(key, limitPerMinute). Empty or whitespace-only keys are denied.new MemoryRateLimiter({ windowMs?, now?, maxKeys? })changes the window (default 60 s), injects a clock for tests, and sets the key count past which expired windows are swept (default 10 000); live windows are never evicted. Correct for single-instance deployments; multi-instance enforcement needs a shared store behind the same shape.redactValue(value, key?, options?)— recursively replace credential-looking material with placeholders before logging. Secret-like keys become"[redacted]", bearer/basic tokens become"<scheme> [redacted]", cyclic references become"[circular]". Scrubbing applies to every field; truncation is volume control only, somsg/message/stackare never truncated and everything else is bounded at 512 code points. Dates pass through; Maps, Sets, prototypes, and symbol keys are preserved.options.valuePatterns({ pattern, replacement }[]) adds your own credential shapes: matches are replaced literally on every string, before the bearer/basic pattern.hkdfKey(masterKey, info, { salt?, length? })— derive a purpose-bound key (default 32 bytes) from one master secret with HKDF-SHA256. Same inputs, same key;infoseparates purposes. An empty master throws.sealSecret(masterKey, info, plaintext)/openSecret(masterKey, info, sealed)— AES-256-GCM envelope (12-byte IV, ciphertext, 16-byte tag) for credentials you must read back. The key comes fromhkdfKey.openSecretthrows on a wrong master orinfo, tampering, or truncation. Rotating the master means re-sealing stored values.checkoutSuffix(cwd)/suffixedDatabaseUrl(baseUrl, cwd)/databaseNameFromUrl(url)— per-checkout test database names so parallel checkouts do not drop each other's database; the last one throws on any name outside[A-Za-z0-9_].clientKeyFromForwardedFor(header, fallback = "local")— rate-limit key fromX-Forwarded-For: the last entry (appended by your proxy), never the spoofable first. Correct behind exactly one trusted proxy.checkEndpointUrl(url, { allowPrivate? })/isPrivateHostname(host)— SSRF guard for URLs your server calls on a customer's behalf (webhook targets, callbacks). http/https only; refuses loopback, private, link-local (cloud metadata), CGNAT, reserved and special-use names, and sees through decimal/octal/hex IPv4 and IPv6 with an embedded IPv4. Returns{ ok: true, value: undefined }or{ ok: false, error }. It does not resolve DNS, so a name that points at a private address at send time (DNS rebinding) is not caught.computeBackoffMs(attemptNo, random?)— exponential retry backoff (2s × 3^(n-1), ±20% jitter, 1s floor, 1h cap).webhookSignatureHeader(secret, body, timestamp)/verifyWebhookSignatureHeader(secret, body, header, options?)— Stripe-stylet=…,v1=…HMAC signatures over exact body bytes, with an optional freshness check. Bring your own secret; an empty secret is rejected.@zedtech/zedkit-server/webhook-web—verifyWebhookSignatureWeb(async, Web Crypto only): same header format, default ±300 s tolerance both ways; for edge and browser runtimes withoutnode:crypto.pgErrorCode(error)— read the Postgres error code (23505unique,23503FK, ...) from a thrown value, ornullwhen it has none. Also reads onecauselevel down, where ORMs keep the driver error. Lets callers decide conflict-vs-bug.pgErrorRetryable(error)—truefor common transient SQLSTATE values (serialization, deadlock, connection loss),falsefor other Postgres codes,nullwhen there is no Postgrescode.
Find helpers by need in CATALOG.md.
Install or copy?
These modules are small and dependency-free: source-copying the files is a sane alternative to installing (MIT allows it). Install the package for versioned updates.
Verify
pnpm --filter @zedtech/zedkit-server typecheck && pnpm --filter @zedtech/zedkit-server lint && pnpm --filter @zedtech/zedkit-server build && pnpm --filter @zedtech/zedkit-server test