npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hexmon_tech/audit-core

v2.0.0

Published

Runtime-agnostic audit logger, schema, and reliability controls.

Downloads

94

Readme

@hexmon_tech/audit-core

Runtime-agnostic audit event schema, validation helpers, and policy knobs. This package defines the canonical event shape and developer-facing logger API.

Install

pnpm add @hexmon_tech/audit-core
npm install @hexmon_tech/audit-core

Compatibility

  • Node.js >= 18
  • Dual ESM/CJS entrypoints (dist/index.{mjs,cjs}) with tsup-generated types

Usage

import { createAuditLogger } from '@hexmon_tech/audit-core';

const audit = createAuditLogger({
  service: 'billing-api',
  environment: 'production',
  strictValidation: false,
  sinks: [
    /* sink instances */
  ],
});

const result = await audit.log({
  action: 'billing.invoice.create',
  outcome: 'SUCCESS',
  actor: { type: 'user', id: 'user-123' },
  target: { type: 'invoice', id: 'inv-456' },
  context: { requestId: 'req-789' },
});

if (!result.ok) {
  console.error(result.error);
}

const child = audit.child({ defaultContext: { tenantId: 'tenant-1' } });
await child.log({
  action: 'user.login',
  outcome: 'SUCCESS',
  actor: { type: 'user', id: 'user-456' },
});

const stats = audit.getStats();
console.log(stats.counters.audit_events_total);

await audit.flush();
await audit.shutdown();

Result Shape

audit.log returns a structured result:

  • { ok: true, event, warnings }
  • { ok: false, error, warnings }

Metrics

Use the in-memory metrics registry for counters/gauges and Prometheus output:

const metrics = audit.getMetrics();
const promText = audit.getMetricsPrometheus();

See docs/METRICS.md for metric names and labels.

Schema

AuditEvent (normalized)

Required fields are fully populated in the normalized event used by sinks and storage.

  • schemaVersion (number): version of the event schema.
  • eventId (string): stable identifier for dedupe across sinks (default ULID).
  • action (string): low-cardinality action name (e.g., user.login, billing.invoice.create).
  • outcome (SUCCESS | FAILURE | DENIED | ERROR)
  • actor (AuditActor): who performed the action.
  • target (AuditTarget, optional): entity acted upon.
  • context (AuditContext): request/session/tenant/source metadata.
  • metadata (object, optional): extra structured fields.
  • diff (object, optional): structured before/after data for updates.
  • metadataTruncated / diffTruncated (boolean, optional): size limit flags.
  • integrity (AuditIntegrity, optional): hash chain/signature metadata.
  • retentionTag (string, optional): storage retention hint.

AuditEventInput (developer input)

Input allows partial fields so the core can apply defaults later (e.g., eventId, context.occurredAt).

Policy Knobs

  • Mode: QUEUE | BLOCK | DROP
  • FanoutMode: BEST_EFFORT | ALL_OR_NOTHING
  • StrictValidation: boolean
  • retentionTag: optional string
  • strictActionNaming: when true, UUID-like action names are rejected
  • integrityMode: none | hash-chain | signed
  • integrity: integrity hashing configuration (hash algorithm, signer hook)
  • sinks: array of AuditSink implementations
  • sinkConcurrency: max concurrent sink writes (default 4)
  • batchSize: batch size for writes (default 100)
  • maxBatchBytes: max bytes per batch (default 1MB)
  • queue: queue sizing and flush interval settings
  • retry: transient retry policy
  • circuitBreaker: per-sink circuit breaker thresholds
  • shutdownTimeoutMs: drain timeout for shutdown
  • metrics: custom metrics registry (defaults to in-memory)

Presets

Use presets as a baseline and override per deployment:

import { createAuditLogger, saasMultiTenantStrict } from '@hexmon_tech/audit-core';

const audit = createAuditLogger({
  ...saasMultiTenantStrict(),
  sinks: [/* sinks */],
});

Available presets:

  • saasMultiTenantStrict(): strict validation, all-or-nothing fanout, hash-chain integrity.
  • onPremSelfHosted(): queue mode with conservative backpressure defaults.
  • highThroughput(): larger batches, higher concurrency, aggressive queueing.

Privacy by Default

Redaction is enabled by default and runs before any write. Sensitive keys are masked (***) and can be configured with paths or regex patterns.

Default redaction keys: password, otp, token, accessToken, refreshToken, authorization, cookie, set-cookie, secret, apiKey, privateKey

const audit = createAuditLogger({
  redaction: {
    enabled: true,
    mask: '***',
    paths: ['metadata.headers.authorization'],
    keyPatterns: ['^secret_', 'token$'],
  },
  payloadLimits: {
    maxEventBytes: 64 * 1024,
    maxMetadataBytes: 32 * 1024,
    maxDiffBytes: 32 * 1024,
    oversizeEventBehavior: 'REJECT',
  },
});

Oversize metadata and diff payloads are replaced with a truncation marker and flagged on the event.

See docs/PRIVACY.md and docs/STORAGE_SECURITY.md for broader guidance.

Integrity (Hash Chain)

Enable tamper-evident hash chaining:

const audit = createAuditLogger({
  integrityMode: 'hash-chain',
  integrity: {
    hashAlgorithm: 'SHA-256',
  },
});

Hash chains are scoped per tenant when context.tenantId is present; otherwise they are scoped per service. For distributed systems, see docs/INTEGRITY.md for coordination limits.

Signed mode requires a signer hook (integrity.signer).

Reliability

Queueing, retries, and circuit breakers are enabled by default:

const audit = createAuditLogger({
  queue: {
    maxQueueSize: 5000,
    maxQueueBytes: 2 * 1024 * 1024,
    flushIntervalMs: 250,
    overflowPolicy: 'DROP',
  },
  maxBatchBytes: 1024 * 1024,
  retry: {
    maxAttempts: 8,
    baseBackoffMs: 250,
    maxBackoffMs: 30_000,
  },
  circuitBreaker: {
    failureThreshold: 5,
    cooldownMs: 10_000,
    halfOpenMaxInFlight: 1,
  },
  shutdownTimeoutMs: 5000,
});

See docs/RELIABILITY.md for mode behavior, retry semantics, and circuit breaker guidance. See docs/PERFORMANCE.md for batching and throughput tuning.

Multi-Sink Usage

import { createAuditLogger } from '@hexmon_tech/audit-core';
import { createHttpAuditSink } from '@hexmon_tech/audit-sink-http';
import { createPostgresAuditSink } from '@hexmon_tech/audit-sink-postgres';

const httpSink = createHttpAuditSink({ endpoint: 'https://audit.example.com/batch' });
const pgSink = createPostgresAuditSink({ connectionString: process.env.DATABASE_URL });

const audit = createAuditLogger({
  fanout: 'BEST_EFFORT',
  sinks: [httpSink, pgSink],
});

Sink Contract

Sinks implement a batch writer with explicit error classification:

type AuditSink = {
  writeBatch(events: AuditEvent[], signal?: AbortSignal): Promise<WriteResult>;
  flush?(): Promise<void>;
  shutdown?(): Promise<void>;
};

Validation

validateRequiredFields and validateEventInput provide lightweight runtime checks. Action names are expected to be low-cardinality; validateEventInput warns by default if an action includes a UUID-like identifier, and can reject when strictActionNaming is enabled.

What to Audit

See docs/WHAT_TO_AUDIT.md for guidance on what to log.