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

@graphorin/security

v0.8.0

Published

Security primitives for the Graphorin framework: SecretValue runtime-safe wrapper, SecretRef URI parser, pluggable SecretsStore implementations, per-tool ACL, AsyncLocalStorage-based audit scope tracking, server token authentication (HMAC-SHA256 + pepper

Readme

@graphorin/security

Security primitives for the Graphorin framework.

@graphorin/security ships the runtime building blocks every other @graphorin/* package uses to handle credentials safely:

  • SecretValue - a runtime-safe wrapper class with full leakage barriers (toString, toJSON, Symbol.toPrimitive, [Symbol.for('nodejs.util.inspect.custom')]) and a cross-realm brand (Symbol.for('graphorin.SecretValue')).
  • SecretRef - strict RFC 3986 URI parser for the env: / keyring: / file: / encrypted-file: / literal: / ref: / vault:// schemes that appear in *Ref config fields.
  • A pluggable SecretResolver registry that turns a parsed SecretRef into a live SecretValue.
  • Four built-in SecretsStore implementations (KeyringSecretsStore, EncryptedFileSecretsStore, EnvSecretsStore, MemorySecretsStore), a composeChain(...) helper, and the createSecretsStore({ kind: 'auto' }) factory with headless detection and downgrade audit.
  • Per-tool secretsAllowed ACL plumbing built on AsyncLocalStorage, withSecret(...) scope tracking, and an in-process audit emitter the audit-log subsystem subscribes to.
  • Server token auth - generator + parser for the canonical <prefix>_<env>_v1_<entropy>_<crc32> token format, scope grammar (<resource>:<action>[:<id-or-glob>]), TokenVerifier with HMAC-SHA256-against-pepper verification, an LRU warm cache, per-IP and per-token brute-force lockouts, a concurrent-verify cap, and a set of CRUD helpers (createToken, revokeToken, rotateToken, rekeyTokens, generatePepper).
  • Tamper-evident audit log - appendAudit, verifyAuditChain, pruneAudit, exportAudit, a canonical-JSON serialiser, an AuditDb interface, a binding registry that fail-fasts when the encrypted-SQLite peer is missing, and the bridgeSecretsToAudit / bridgeMemoryGuardToAudit subscribers that forward events from the secrets and memory-guard layers. On top of the hash chain, an RFC-6962-style Merkle layer (computeAuditTreeHead, signAuditCheckpoint / verifyAuditAgainstCheckpoint with Ed25519 keys, inclusion + consistency proofs) lets an external verifier hold a signed tree head and prove the log was not rewritten.
  • Tool-argument policies (@graphorin/security/policy) - Progent-style declarative constraints over tool arguments (evaluateToolArgumentPolicy) plus a Rule-of-Two profile builder (buildRuleOfTwoPolicy), wired into the agent via AgentConfig.toolPolicy / ruleOfTwo.
  • Sandbox - createNoneSandbox, createWorkerThreadsSandbox (default for user-defined tools, with optional noNetwork / noFilesystem shields), createIsolatedVMSandbox (opt-in peer dependency, auto-fallback to worker-threads with WARN-once when the peer is unavailable), createDockerSandbox (opt-in dockerode peer), and the resolveSandbox(...) tier resolver that mandates the worker-threads + no-network + no-filesystem policy on untrusted skills.
  • Memory-modification guard - classifyTool(...)'pure' | 'side-effecting-no-memory' | 'memory-aware' | 'unknown' | 'untrusted', the four guard implementations (NO_GUARD, API_BOUNDARY_GUARD, AUDIT_ONLY_GUARD, STRICT_FULL_GUARD), an xxhash integrity helper, and a typed memoryGuardAuditEmitter the audit log subscribes to.
  • Guardrails - defineInputGuardrail / defineOutputGuardrail builders, composeGuardrails(...) runner with documented block / warn / rewrite short-circuit semantics, and seven built-ins under guardrails.*: maxLength, promptInjectionHeuristics, piiDetection, languageWhitelist, llmModeration, outputModeration, toolUsageValidator.
  • Process hardening - applyProcessHardening({ refuseRoot, umask }) startup helper, ensureFileMode / ensureDirMode / verifyFileMode POSIX-mode utilities, the graphorin doctor library functions (checkPerms, checkSecrets, checkEncryption, checkSystemd), and the generateBootstrapToken / generateAesSalt helpers.
  • Outbound OAuth - createOAuthClient(...) wires the OAuth 2.1 surface required by the MCP authorization spec: discovery (RFC 8414 + RFC 9728), Dynamic Client Registration (RFC 7591), Authorization Code + PKCE-S256 (RFC 7636) with a built-in localhost callback server + cross-platform browser launcher, Device Authorization Grant (RFC 8628), refresh-token rotation with per-server-id debounce, and revocation (RFC 7009). The high-level helpers (loginInteractive, listOAuthSessions, refreshOAuthSession, revokeOAuthSession, getOAuthStatus) are the surface the CLI wraps. A typed lifecycle emitter (onOAuthLifecycle) surfaces oauth.granted / oauth.refreshed / oauth.revoked / oauth.registered / mcp.auth.expired events; a sibling audit emitter feeds the tamper-evident chain via bridgeOAuthToAudit({ db }).
  • Skills supply chain - verifySkillSignature(...) parses the graphorin-signature: block from a SKILL.md frontmatter and verifies the ed25519-SHA-256 signature via Node's built-in crypto.verify(...) against a publisher key resolved from a well-known URL (with optional pinned fingerprint), an inline PEM, or an operator-installed Sigstore verifier. An operator trustRoot (allowed publishers / pinned key fingerprints / allowSigstore) caps which keys may verify at all - an unknown key fails closed with 'untrusted-key'. installSkillFromNpm / installSkillFromGit enforce --ignore-scripts for every untrusted install (no override; pnpm preferred, falling back to npm / yarn only when pnpm is missing) and reject unsigned skills outright. The trust-policy resolver (resolveTrustPolicy, evaluateSupplyChainPolicy) honours operator-managed allow / deny lists with npm-style globbing. Every install fires a skill:installed audit event with the trust level, signature outcome, publisher, algorithm, and key source.

Status

  • Version: v0.8.0 - secrets foundations + server token auth + tamper-evident audit log + sandbox / memory-guard / guardrails / process-hardening runtime safety + outbound OAuth flows + skills supply-chain helpers.
  • License: MIT - © 2026 Oleksiy Stepurenko.
  • Engines: Node.js 22+ (ESM only).

Installation

pnpm add @graphorin/security @graphorin/core
# Optional native peers (pulled in only when you opt into the matching backend)
pnpm add -O @napi-rs/keyring @node-rs/argon2 openid-client

@napi-rs/keyring@^1.2.0 is required at runtime if you use the KeyringSecretsStore; @node-rs/argon2@^2.0.2 is required if you use the EncryptedFileSecretsStore; better-sqlite3-multiple-ciphers@^12.9.0 is required if you open the audit log directly (most consumers go through @graphorin/store-sqlite, which depends on it transitively); isolated-vm@^5.0.0 is required if you opt into IsolatedVMSandbox; dockerode@^4.0.0 is required if you opt into DockerSandbox; openid-client@^6.8.0 is the recommended reference implementation for the OAuth subsystem (the built-in flows already cover every MVP use case). All six are declared as optional peer dependencies so deployments that do not exercise the corresponding code paths never pull the native binaries. yaml@^2.8.0 is a regular dependency required to parse SKILL.md frontmatter for the supply-chain signature verifier.

Quick reference

import {
  SecretValue,
  parseSecretRef,
  validateSecretRefs,
  resolveSecret,
  registerResolver,
  createSecretsStore,
  composeChain,
  detectHeadless,
  getSecretsStoreStatus,
  withSecret,
  MemorySecretsStore,
  EnvSecretsStore,
  EncryptedFileSecretsStore,
  KeyringSecretsStore,
  // Server token auth
  TokenVerifier,
  authorize,
  createToken,
  generatePepper,
  parseScope,
  scopeMatches,
  // Audit log
  appendAudit,
  verifyAuditChain,
  pruneAudit,
  exportAudit,
  bridgeSecretsToAudit,
  bridgeMemoryGuardToAudit,
  openAuditDb,
  registerAuditDbBinding,
  // Sandbox
  createNoneSandbox,
  createWorkerThreadsSandbox,
  createIsolatedVMSandbox,
  createDockerSandbox,
  resolveSandbox,
  // Memory-modification guard
  classifyTool,
  createGuard,
  // Guardrails
  defineInputGuardrail,
  defineOutputGuardrail,
  composeGuardrails,
  guardrails,
  // Process hardening
  applyProcessHardening,
  ensureFileMode,
  ensureDirMode,
  verifyFileMode,
  generateBootstrapToken,
  checkPerms,
  checkSecrets,
  checkEncryption,
  checkSystemd,
  // Outbound OAuth
  createOAuthClient,
  createInMemoryOAuthServerStore,
  loginInteractive,
  listOAuthSessions,
  refreshOAuthSession,
  revokeOAuthSession,
  getOAuthStatus,
  registerOAuthStrategy,
  onOAuthAudit,
  onOAuthLifecycle,
  bridgeOAuthToAudit,
  // Skills supply chain
  verifySkillSignature,
  installSkillFromNpm,
  installSkillFromGit,
  evaluateSupplyChainPolicy,
  resolveTrustPolicy,
  auditInstalledSkills,
  onSupplyChainAudit,
  bridgeSupplyChainToAudit,
} from '@graphorin/security';
// Wrap a raw string at the I/O boundary (e.g. directly after env read).
const apiKey = SecretValue.fromString(process.env.OPENAI_API_KEY ?? '');

// Scoped access - the preferred read pattern.
await apiKey.use((raw) => fetch(url, { headers: { Authorization: `Bearer ${raw}` } }));

// Auto-pick the best store for the current environment.
const store = await createSecretsStore({ kind: 'auto' });
const value = await store.require('openai_api_key');

// Issue a fresh server token and authorise it.
const pepper = generatePepper();
const created = await createToken({
  tokenStore,
  pepper,
  env: 'live',
  scopes: ['agents:invoke', 'memory:read'],
  label: 'web-ui',
});
const verifier = new TokenVerifier({ tokenStore, pepper });
const result = await verifier.verify(created.raw.reveal(), { ip: '203.0.113.7' });
const auth = authorize(result, 'agents:invoke');
// Open the audit log (the SQLite-backed binding ships from
// @graphorin/store-sqlite; deployments with a custom store register
// their own binding via registerAuditDbBinding(...)).
const db = await openAuditDb({ path: 'audit.db', passphrase });
bridgeSecretsToAudit({ db });
bridgeMemoryGuardToAudit({ db });
await appendAudit(db, {
  actor: { kind: 'system', id: 'graphorin/security' },
  action: 'audit:db-opened',
  target: 'audit.db',
  decision: 'success',
});
const verify = await verifyAuditChain(db);
// Pick the sandbox tier from operator + trust-level inputs and
// run a user-defined tool through it.
const policy = resolveSandbox({
  trustLevel: 'user-defined',
  override: { kind: 'worker-threads', noNetwork: true, noFilesystem: true },
});
const sandbox = createWorkerThreadsSandbox({
  noNetwork: policy.noNetwork,
  noFilesystem: policy.noFilesystem,
});
const result = await sandbox.run<{ greeting: string }, string>(
  { kind: 'handler', module: '/abs/path/handler.mjs', export: 'main' },
  { input: { greeting: 'hi' }, timeoutMs: policy.timeoutMs },
);
// Compose a small input pipeline: heuristic injection + PII redaction.
const pipeline = [
  guardrails.promptInjectionHeuristics<string>(),
  guardrails.piiDetection<string>({ action: 'rewrite' }),
  guardrails.maxLength<string>({ chars: 4_000 }),
];
const decision = await composeGuardrails(pipeline, userInput, { stage: 'input' });
if (!decision.ok && decision.action === 'block') {
  throw new Error(`input rejected by ${decision.name}: ${decision.message}`);
}
const sanitized = decision.ok ? decision.value : decision.value;
// Apply process hardening on startup; refuse to run as root.
applyProcessHardening({ refuseRoot: true, umask: 0o077 });
await ensureDirMode(`${process.env.HOME}/.graphorin`, 0o700);
await ensureFileMode(`${process.env.HOME}/.graphorin/data.db`, 0o600);
const initialToken = generateBootstrapToken();
// Drive an interactive OAuth login against an MCP server. The
// helper opens the system browser, waits on the localhost
// callback, and persists the resulting tokens via the supplied
// OAuthServerStore.
const storage = createInMemoryOAuthServerStore();
const { client, status } = await loginInteractive({
  serverId: 'linear-mcp',
  serverUrl: 'https://mcp.linear.app',
  storage,
  scope: 'read write',
});
console.log('Granted scope:', status.scope, 'expires at', status.expiresAt);
// Refresh + revoke share the same client surface.
await client.refresh();
await client.revoke({ reason: 'user-initiated' });
// Verify a SKILL.md signature before loading the skill.
const result = await verifySkillSignature({ skillMd });
if (!result.valid) throw new Error(`Skill rejected: ${result.reason}`);

// Install a third-party skill from the npm registry. The runner
// always passes --ignore-scripts and rejects unsigned skills at
// trust level 'untrusted' (the default for npm + git sources).
await installSkillFromNpm({
  packageName: '@vendor/pdf-processing',
  version: '1.2.3',
  skillMd,
});

Stability

Every exported symbol carries one of two TSDoc tags:

  • @stable - covered by semver guarantees for the v0.x line.
  • @experimental - may change between minor versions; release notes call out every breaking change.

Versioning

@graphorin/security follows the lockstep release cadence of the rest of the @graphorin/* packages while the framework is on the 0.x line.


Project Graphorin · v0.8.0 · MIT License · © 2026 Oleksiy Stepurenko · https://github.com/o-stepper/graphorin