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

@xshieldai/aegis-guard

v0.6.2

Published

AEGIS Guard SDK — Five Locks primitives (approval-token, nonce, idempotency, SENSE, quality-evidence) + opt-in Agentic Control Center event bus for AEGIS-governed services

Readme

@xshieldai/aegis-guard

🔍 Verification status (2026-10-14 IST — v0.6.2)

  • New in 0.6.2: plain Node can import it. Until 0.6.1 the entry was the TypeScript source, so it loaded under Bun only. It now ships compiled JavaScript with type declarations in dist/. Nothing else changed.
  • Tests: ✅ 184/184 passing in the repository; the npm package ships src/ only, so run them from a clone (bun test): 70 in tests/aegis-guard.test.ts and 114 in tests/hardening.test.ts for key trust, malformed tokens, nonces, the duplicate check, the audit event, the quality bar, the envelope helpers, the stated limits and the action digest. No test contacts a network or touches ~/.aegis.
  • Examples: ✅ runnable quickstart, in the repository (not in the npm package): examples/quickstart.ts — bun run examples/quickstart.ts shows all 5 Locks in action with receipts
  • Live demo: ⚠️ planned (Tier 3, see PROOF-STACK.md)
  • Limits: see "What this does not do" below, and the v0.2.0 ACC section
  • Upgrading from 0.3.x: some calls that passed are now refused, and minting no longer makes a key. See "Upgrading from 0.3.x".
  • Upgrading from 0.4.x, 0.5.0 or 0.6.0: minting needs AEGIS_MINT_AUTHORITY=1, an approval must name the concrete action, and since 0.6.1 an action must be plain data. See "Upgrading to 0.6.x".

AEGIS Guard SDK — reusable approval-token, nonce, idempotency, SENSE, and quality-evidence primitives for AEGIS-governed services.

Carbonx proved the locks. Batch 93 makes the locks reusable.

The Five Locks were proven across 13 batches (62–74) of carbonx-backend. This package extracts them into a service-agnostic SDK so any AEGIS-governed service can adopt them without copy-pasting bespoke logic.

Five Locks

| Lock | Primitive | Rule | |---|---|---| | LOCK_1 — decision | verifyApprovalToken | AEG-E-016 | | LOCK_2 — identity | verifyScopedApprovalToken | AEG-E-016 | | LOCK_3 — observability | emitAegisSenseEvent | CA-003, AEG-HG-2B-003/005 | | LOCK_4 — rollback | checkIdempotency | AEG-HG-2B-006 | | LOCK_5 — idempotency | verifyAndConsumeNonce | AEG-HG-2B-006 |

Install

bun add @xshieldai/aegis-guard
# or
npm install @xshieldai/aegis-guard

Keys — who signs, who verifies

An approval token is an EdDSA-signed JWT. One box signs; every other box only verifies.

| Box | Holds | Set up by | |---|---|---| | The approving authority (AEGIS) | ~/.aegis/approval-signing.key and .pub | ensureSigningKeypair(), once, at boot | | Every service that checks approvals | the authority's public key | the file ~/.aegis/approval-signing.pub, or the PEM in AEGIS_APPROVAL_PUBKEY_PEM |

AEGIS_DIR replaces ~/.aegis. The rules the package enforces (AEG-HG-2B-007):

  • Minting never makes a key. mintApprovalToken() throws on a box with no private key.
  • A box that was given a public key never makes its own. ensureSigningKeypair() throws there and writes nothing.
  • Keys that disagree are not trusted. If the private key, the public file and the environment PEM are not all the same Ed25519 key, every verification is refused and the reason names the conflict.
  • No key, no approval. With no key material at all, verification is refused.

Usage

LOCK_1 + LOCK_2 — decision + identity

verifyApprovalToken() checks, in order: the signature; that service, capability and operation match exactly; that expires_at is a number and not past; that issued_at, when present, is a number, not more than 60 seconds ahead of the clock and not after expires_at; that status, when present, is exactly approved. Anything else throws IrrNoApprovalError (code: 'IRR-NOAPPROVAL') — including a token that is not text.

Since 0.6.0 two things are refused by default, and the two calls below are therefore not enough on their own:

  • Minting is an authority action. mintApprovalToken() and ensureSigningKeypair() throw unless the process declares itself the approving authority with AEGIS_MINT_AUTHORITY=1. An agent process verifies; it does not mint. On one user account an agent can set that variable itself: the real boundary is a private key under a different account that the agent cannot read.
  • An approval must name the concrete action. A token without an action_digest is refused by every verify call, because an approval scoped only to service, capability and operation fits every instance of that operation. Use the action-bound pair described in the next section. AEGIS_ALLOW_LABEL_ONLY_APPROVAL=1 brings back the old label-only behaviour for a caller that knowingly accepts it.

Mint with issued_at and expires_at in milliseconds: mintActionApprovalToken({ service_id, capability, operation, nonce, issued_at: Date.now(), expires_at: Date.now() + 60_000 }, action).

import { verifyApprovalToken, verifyScopedApprovalToken } from '@xshieldai/aegis-guard';

// LOCK_1 — base token verification (service_id + capability + operation)
const payload = verifyApprovalToken(token, 'my-service', 'settle', 'record_settle');

// LOCK_2 — scoped verification (add service-specific field bindings)
const payload = verifyScopedApprovalToken(
  token, 'my-service', 'settle', 'record_settle',
  { vessel_id: args.vesselId, amount: args.amount },
);

Each scope field must be the token's own field and strictly equal (100 is not "100"). A field the caller has no value for (undefined or null) is refused: it would bind the token to nothing.

One approval, one action

import { mintActionApprovalToken, verifyActionApprovalToken } from '@xshieldai/aegis-guard';

// On the authority (AEGIS_MINT_AUTHORITY=1): the approval names what was approved.
const action = { table: 'audit_log_2023', run_at: '2026-10-11T06:40:00.000Z' };
const token = mintActionApprovalToken({ service_id, capability, operation, nonce, issued_at, expires_at }, action);

// In the service, with the action it is about to perform:
const payload = verifyActionApprovalToken(token, 'my-service', 'schema', 'drop_table', actionInHand);

The token carries a SHA-256 digest of the action; the service recomputes it from the action in hand and a mismatch is refused. Keys are sorted at every depth, so the order they were written in does not matter.

An action must be plain data: text, finite numbers, booleans, null, arrays and plain objects of these. Anything else is refused with IrrNoApprovalError, at mint and at verify, and the message says where in the action it is (action.pay.to[0].when is a Date, not a plain object). That covers Date, URL, Map, Set, RegExp, Buffer, any class instance, NaN, ±Infinity, undefined (also as a field's value, and a hole in an array), bigint, functions, symbols, and an action that contains itself. Write such a value as text yourself: a date as its ISO text, a URL as its href, a set as a sorted array, a large integer as digits.

Through 0.6.0 the digest gave those values a guessed spelling and the guesses collided: every Date, URL, Map, Set, RegExp and class instance was written {}, and NaN, Infinity and undefined were written null. An approval minted for one such action verified for a different one. If a service on 0.5.0 or 0.6.0 passes any of those as part of an action, upgrade. For an action that was plain data already, the digest is unchanged, so tokens minted before the upgrade still verify.

LOCK_3 — observability (SENSE)

import { emitAegisSenseEvent, digestApprovalToken, configureSenseTransport } from '@xshieldai/aegis-guard';

// Wire your logger (default: process.stdout JSON)
configureSenseTransport((event) => logger.info(event, `SENSE:${event.event_type}`));

emitAegisSenseEvent({
  event_type: 'allowance.settle',
  service_id: 'my-service',
  capability: 'settle',
  operation: 'record_settle',
  before_snapshot: { status: 'pending' },
  after_snapshot:  { status: 'settled' },
  delta:           { status: 'pending→settled' },
  emitted_at: new Date().toISOString(),
  irreversible: true,
  correlation_id: req.headers['x-correlation-id'],
  approval_token_ref: digestApprovalToken(token), // 24-hex digest, never raw token
});

If approval_token_ref is not already a 24-hex digest, the SDK digests it before the event reaches the transport or the bus, so a raw token passed by mistake is not written to the log.

LOCK_4 — rollback guard (idempotency check)

import { checkIdempotency, buildIdempotencyFingerprint } from '@xshieldai/aegis-guard';

const existing = await db.findByExternalRef(args.externalRef);
const fp = buildIdempotencyFingerprint({ amount: args.amount, vessel_id: args.vesselId });
const { isDuplicate, safeNoOp } = checkIdempotency(args.externalRef, existing, fp, existing?.fingerprint);

if (isDuplicate && safeNoOp) return existing; // safe no-op
if (isDuplicate && !safeNoOp) throw new Error('duplicate externalRef: payload differs or cannot be compared');

A duplicate is a safe no-op only when both fingerprints are present and equal (AEG-HG-2B-009). If the existing record has no stored fingerprint, the result is safeNoOp: false, comparable: false: store the fingerprint with the record.

buildIdempotencyFingerprint() sorts keys at every depth and keeps NaN, ±Infinity, bigint, Map and Set distinct. The fingerprint is the payload, base64-encoded — not a hash. Anyone who can read a stored fingerprint can read the payload; keep secrets out of it or hash it before storing.

LOCK_5 — nonce replay prevention

import { verifyAndConsumeNonce } from '@xshieldai/aegis-guard';

// Requires nonce in payload; throws IrrNoApprovalError on missing or replayed nonce
await verifyAndConsumeNonce(payload, redisNonceStore);

The nonce must be non-empty text and the payload must be in date. Only a store that answers exactly true has consumed the nonce; a store that throws propagates its error (fail closed). This call takes a payload and checks no signature: pass it what verifyApprovalToken() returned.

Quality evidence

import { buildQualityMaskAtPromotion, meetsHgQualityRequirement } from '@xshieldai/aegis-guard';

const mask = buildQualityMaskAtPromotion({
  tests_passed: true,
  rollback_tested: true,
  audit_artifact_produced: true,
});

const ready = meetsHgQualityRequirement('HG-2B-financial', mask);
// false here: the financial bar needs all twelve promotion bits

meetsHgQualityRequirement() returns false for a group that is not in the table and for a mask that is not a whole number made of bits 0–11 (-1, "4095", 4095.9, or a mask with a drift bit set). HG_REQUIRED_MASKS is frozen.

Session envelope

import { issueEnvelope, verifyEnvelope } from '@xshieldai/aegis-guard';

const env = await issueEnvelope({ service_key: 'my-service', declared_caps: ['read'] });
const audit = await verifyEnvelope(env.session_id);
if (!audit.verified || audit.drift_detected) { /* quarantine the session */ }

Both call the Aegis dashboard (aegis_url, or AEGIS_URL, or http://localhost:4850; http and https only) and read its answer strictly (ASE-016): verified is true only when the answer says exactly true for the session asked about; an answer with no session id or seal throws. They trust that endpoint and the connection to it — they do not recompute the seal.

NonceStore — production wiring

The default defaultNonceStore is in-memory (single-process only). Multi-instance deployments must provide a Redis-backed store:

import { type NonceStore } from '@xshieldai/aegis-guard';

const redisNonceStore: NonceStore = {
  async consumeNonce(nonce, ttlMs) {
    const key = `aegis:nonce:${nonce}`;
    const result = await redis.set(key, '1', 'NX', 'PX', ttlMs);
    if (result === null) return false; // already consumed
    return true;
    // throws propagate to callers → fail CLOSED (AEG-HG-2B-006)
  },
};

Schema

  • quality_mask_at_promotion: bits 0–11, aegis-quality-16bit-v1
  • quality_drift_score: bits 12–15, aegis-quality-16bit-v1
  • AEG-Q-003 invariant: bits 12–15 must never be set in quality_mask_at_promotion

Upgrading to 0.6.x

  • 0.5.0 added actionDigest(), mintActionApprovalToken() and verifyActionApprovalToken().
  • 0.6.0: set AEGIS_MINT_AUTHORITY=1 for the one process that mints, and for no other. Without it minting and key generation throw. AEGIS_ALLOW_INSECURE_LOCAL_MINT=1 is the stated opt-out for a single-process development box.
  • 0.6.0: mint and verify with the action-bound pair. A token with no action_digest is refused by verifyApprovalToken() and verifyScopedApprovalToken() too, unless AEGIS_ALLOW_LABEL_ONLY_APPROVAL=1.
  • 0.6.1: an action must be plain data. A Date, URL, Map, Set, RegExp, Buffer, class instance, NaN, ±Infinity, undefined or bigint anywhere in an action is now refused, where 0.6.0 digested it (wrongly, see "One approval, one action"). Convert it to text on both sides, the minting side and the verifying side. A Buffer was digested correctly before and is refused now as well: write it as hex or base64 text.

Upgrading from 0.3.x

  • Call ensureSigningKeypair() once on the authority box before minting. Minting no longer makes a key. On a verifier box, do not call it at all.
  • Mint with a numeric expires_at. A token without one is refused (it used to never expire).
  • verifyApprovalToken() refuses a status other than approved. Before, only the scoped call looked at it.
  • checkIdempotency() without a stored fingerprint is no longer a safe no-op. Read the new comparable field.
  • Fingerprints of nested payloads change (inner keys are now sorted). A flat payload of text, finite numbers, booleans and null fingerprints as before. A retry that straddles the upgrade with a nested payload is reported as a mismatch.
  • A stale approval-signing.pub next to the private key is now a conflict, and verification is refused until the two agree.
  • verifyEnvelope() throws when the audit is for another session; issueEnvelope() throws on an answer with no session id or seal.

What this does not do

Each of these is pinned by a test in tests/hardening.test.ts, so it cannot change unnoticed — except the two about what is absent (a shared nonce store, a revocation list), which no test can show.

  • No upper limit on a token's life. The signer chooses expires_at.
  • The package does not decide what "the action" is. The caller builds it. A field the caller leaves out of the action is not bound, and two services that describe the same act differently do not match.
  • Verifying an action-bound token does not use it up. The same token verifies again until it expires; a second use is stopped only by verifyAndConsumeNonce().
  • An empty scope checks nothing more than verifyApprovalToken(). A scope value that is an object never matches.
  • verifyAndConsumeNonce() checks no signature and no status. It trusts the payload it is given.
  • A key file swapped under a running process is not noticed until restart. A change of AEGIS_APPROVAL_PUBKEY_PEM is.
  • The default nonce store is one process's memory. Two instances each accept the same nonce once.
  • The fingerprint is not a hash, a field set to undefined is the same as the field absent, and a Date is the same as its ISO text.
  • Only the approval_token_ref field is digested. A raw token placed anywhere else in a SENSE event reaches the transport.
  • A SENSE transport that throws reaches the caller, and the three snapshots are not checked for presence.
  • Revocation is a field in the token. There is no revocation list: a token signed as approved stays valid until it expires.

License

AGPL-3.0 — Capt. Anil Sharma, powerpbox.org


v0.2.0 — Opt-in Agentic Control Center (ACC) event bus

Added 2026-05-17. Each Five Locks primitive now emits an AccReceipt on success or failure, but only when you wire a bus. Without setEventBus, v0.2.0 behaves identically to v0.1.0 — no emission, no state, no side effect.

Wire it in 3 lines

import { setEventBus, type EventBus, type AccReceipt } from '@xshieldai/aegis-guard';

const myBus: EventBus = {
  emit: (r: AccReceipt) => console.log(`[ACC] ${r.event_type} verdict=${r.verdict} ${r.summary}`),
};
setEventBus(myBus);

Now every primitive call emits a receipt. Pass null to setEventBus to detach.

Receipt events emitted

| Primitive | event_type on success | event_type on failure | |---|---|---| | verifyApprovalToken | lock.approval.verified (PASS) | lock.approval.rejected (FAIL) | | verifyAndConsumeNonce | lock.nonce.consumed (PASS) | lock.nonce.rejected (FAIL) | | checkIdempotency | lock.idempotency.duplicate (PASS), lock.idempotency.mismatch (WARN) or lock.idempotency.unverifiable (WARN, no fingerprint to compare) | (no event for non-duplicate path) | | emitAegisSenseEvent | lock.sense.emitted (PASS or WARN if irreversible) | — |

Receipt shape

interface AccReceipt {
  receipt_id: string;       // primitive-prefixed identifier
  primitive: string;        // always 'aegis-guard' for this package
  event_type: string;       // lock.*
  emitted_at: string;       // ISO 8601
  agent_id?: string;        // reserved — not yet populated by aegis-guard
  verdict?: string;         // PASS | FAIL | WARN
  rules_fired?: string[];   // e.g. ['AEG-E-016']
  summary?: string;         // ≤200 chars
  payload?: Record<string, unknown>;
}

The shape is a strict subset of the EE PRAMANA receipt format. EE consumers ingest these events without translation.

Phase-1 limits (v0.2.0)

  • agent_id is not yet populated — primitives don't receive an agent context as parameter. Future versions may add an optional agent_id argument to each primitive; today you can post-process receipts in the bus to add agent context from your own tracking.
  • buildIdempotencyFingerprint, digestApprovalToken, mintApprovalToken do NOT emit — they're pure helpers called many times per operation. Emitting from them would flood the bus.
  • buildQualityMaskAtPromotion, buildQualityDriftScore, meetsHgQualityRequirement do NOT emit — quality computation is scoring, not a governance decision. They're called during promotion decisions; the calling code emits the governance event.
  • Default bus is in-process only. Multi-process buses (Redis-backed, etc.) are a consumer choice — implement the EventBus interface and call setEventBus(yourBus).

Use with @xshieldai/aegis-suite

If you installed the meta-package, you can wire all 6 primitives in one call:

import { wireAllToBus } from '@xshieldai/aegis-suite';  // available in suite v0.2.0+
wireAllToBus();  // default: in-memory bus + SQLite writer to ~/.aegis/acc-events.db

This sets up the bus on aegis-guard + chitta-detect + lakshmanrekha + hanumang-mandate all at once, and persists events for the Agentic Control Center page.

Discipline

  • Stateless contract preserved. Primitives hold no state beyond a module-private bus reference. Pass null to setEventBus to detach.
  • Emission must never throw. If your bus implementation throws, the primitive's caller is unaffected — the receipt is silently dropped. This is intentional; observability must not break the governed path.