@azmr/security
Shared security utilities — validation, RBAC, JWT, audit logging, sanitisation, and env guards.
Installation
pnpm add @azmr/securityValidation
import { validate, z } from "@azmr/security";
const UserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
balance: z.number().nonnegative(),
});
const user = validate(UserSchema, req.body);
// Throws structured error on failure — never leaks raw inputEnvironment guard
Call once at startup to fail fast if required env vars are missing.
import { validateEnv } from "@azmr/security";
validateEnv(["OPENAI_API_KEY", "AZMARA_ENCRYPTION_KEY"]);Access control (RBAC)
createAccessControl is a framework-agnostic role-based access control primitive: {resource, action, params} → {can, reason}. Policy is a static grant map keyed by role, with "*" wildcards for role/resource/action and fail-closed defaults.
import { createAccessControl } from "@azmr/security";
const ac = createAccessControl({
policy: {
admin: [{ resource: "*", action: "*" }],
editor: [{ resource: "reports", action: "read" }],
},
defaultEffect: "deny",
});
const result = await ac.can({ id: "u1", roles: ["editor"] }, {
resource: "reports",
action: "read",
});
// { can: true }An optional resolve callback lets a caller back checks with a database or any other backend — the package never imports a DB client itself.
Prop
Type
JWT
import { createJWT } from "@azmr/security";
const jwt = createJWT(process.env.AZMARA_JWT_SECRET!); // must be >= 32 chars
const token = jwt.sign({ sub: "u1" });
const payload = jwt.verify(token); // throws on invalid/expiredHMAC-SHA256, Node crypto only, timing-safe signature comparison, default 15-minute expiry.
Rate limiting
import { createRateLimiter } from "@azmr/security";
const limiter = createRateLimiter({ maxRequests: 100, windowMs: 60_000 });
const result = limiter.check(userId);
if (!result.allowed) {
// result.remaining === 0, result.resetAt is a Date
}In-memory sliding window, keyed per caller. Not distributed-safe — use a Redis-backed limiter for multi-process deployments.
Audit logger
import { createAuditLogger } from "@azmr/security";
const audit = createAuditLogger("my-service");
audit.log("user:login", { userId: "abc123" });See Audit Logging for full details.
Sanitisation
import { assertSafeIdentifier, assertSafePath, sanitiseForLog } from "@azmr/security";
assertSafeIdentifier("customers"); // passes
assertSafeIdentifier("users; DROP TABLE"); // throws
assertSafePath("/data/user.db", "/data"); // passes
assertSafePath("../../etc/passwd", "/data"); // throws
sanitiseForLog(userInput); // strips null bytes, flattens newlines, truncatesAPI Reference
| Export | Description |
|---|---|
validate(schema, data) | Zod parse with structured error |
validateEnv(keys) | Assert env vars present at startup |
createAccessControl(options) | RBAC: {resource, action} → {can, reason}, fail-closed |
createJWT(secret) | HMAC-SHA256 sign/verify, timing-safe |
createRateLimiter(options) | In-memory sliding-window rate limiter |
createAuditLogger(context) | Hash-chained tamper-evident logger |
assertSafeIdentifier(value) | Validate SQL identifier |
assertSafePath(path, base) | Prevent path traversal |
sanitiseForLog(value) | Safe string for log output |
z | Re-exported Zod instance |
For a higher-level policy engine that composes rate limiting, RBAC, and CORS into a single
per-route check, see @azmr/policycore.