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

@action-state-group/capsule-emit

v0.3.0

Published

TypeScript producer and verifier for AAC format 4

Readme

capsule-emit-ts

TypeScript-native AAC format-4 producer and verifier. The package is ESM-first, uses strict TypeScript, preserves signer-independent Capsule IDs, and emits the same attached-payload COSE Producer Envelopes as capsule-emit-go.

The root entry point builds records only. It does not execute actions, generate business IDs or timestamps, persist Capsules, retry effects, contact witnesses, or authorize signers. Optional verified persistence is available behind the ./artifact subpath.

Install

Node.js 22 or newer is required.

npm install @action-state-group/capsule-emit

Build, sign, and verify

seal is the recommended application-facing API. It digests caller-owned JSON, builds and verifies the format-4 Capsule, then signs its raw 32-byte Capsule ID with an independent Producer Envelope.

import { randomBytes } from "node:crypto";
import {
  createEd25519Identity,
  seal,
  verifyCapsule,
  verifyEnvelope,
} from "@action-state-group/capsule-emit";

const identity = createEd25519Identity(randomBytes(32));
const result = seal({
  capsule: {
    actionId: "example/1",
    actionType: "decide",
    operator: "example-org",
    developer: "example-agent@v1",
    timestamp: new Date("2026-09-02T12:00:00Z"),
    disposition: {
      decision: "accept",
      approver: "policy",
      humanDisposed: false,
      verdictClass: "executed",
    },
  },
  payload: { task: "publish", issue: 123 },
  agentOutput: { accepted: true },
  model: { provider: "example", modelId: "model-v1" },
  runtime: "example-runtime@1",
  identity,
});

const capsule = verifyCapsule(result.payload);
if (capsule.capsuleId !== result.capsuleId) {
  throw new Error("Capsule ID mismatch");
}
const envelope = verifyEnvelope(result.capsuleId, result.envelope);
if (!envelope.ok) {
  throw new Error(
    `Producer Envelope failed: ${JSON.stringify(envelope.findings)}`,
  );
}
if (
  !envelope.publicKey ||
  !Buffer.from(envelope.publicKey).equals(Buffer.from(identity.publicKey))
) {
  throw new Error("Producer Envelope signer is not authorized");
}

verifyCapsule validates Capsule identity and Class 1 structure. It does not authenticate local-only signature or key_id fields. verifyEnvelope authenticates the public key carried by the Producer Envelope. Whether that key is authorized for an operator, developer, or action remains caller policy.

Typed construction

Use build when the application already owns all typed Capsule fields and wants construction separate from signing. received binds exact opaque bytes under a caller-declared CPB type. carry is the same operation with the generic foreign-artifact type. who, can, did, and audit reference already-built Capsules in a typed composition without minting or persisting those members.

import { randomBytes } from "node:crypto";
import {
  build,
  buildComposition,
  can,
  createEd25519Identity,
  did,
  received,
  seal,
  sign,
  who,
} from "@action-state-group/capsule-emit";

const identity = createEd25519Identity(randomBytes(32));
const common = {
  actionType: "fyi" as const,
  operator: "example-org",
  developer: "example-agent@v1",
  timestamp: "2026-09-02T12:00:00Z",
};

const identityCapsule = build({
  ...common,
  actionId: "identity/1",
  domain: "identity",
});
const providerAck = received(
  { ...common, actionId: "provider-ack/1" },
  new TextEncoder().encode("opaque provider acknowledgement"),
  "provider-ack",
);
const actionCapsule = build({
  ...common,
  actionId: "action/1",
  effect: {
    type: "example.publish",
    status: "planned",
    irreversibilityClass: "two_way",
  },
});
const composition = buildComposition({ ...common, actionId: "composition/1" }, [
  who(identityCapsule),
  can(providerAck),
  did(actionCapsule),
]);
const envelope = sign(composition, identity);

console.log(providerAck.capsuleId, composition.capsuleId, envelope.length);

The same composition can use the high-level signing path:

const signedComposition = seal({
  capsule: { ...common, actionId: "composition/2" },
  members: [who(identityCapsule), can(providerAck), did(actionCapsule)],
  identity,
});

Composition members must occupy distinct slots and refer to distinct verified format-4 Capsules. Carried and composed construction rejects explicit agent input/output digests because those records already own their construction commitments.

Compose with CLL

This package constructs and verifies AAC records. It does not persist them. Applications that also need checkpointed inclusion install the independent CLL package, store the full Capsule and Producer Envelope in application storage, and append only the verified 32-byte Capsule ID to CLL.

import { build, verifyCapsule } from "@action-state-group/capsule-emit";
import { MysqlStore } from "@action-state-group/cll/mysql";

const built = build({
  actionId: "deploy-42",
  actionType: "fyi",
  operator: "example-org",
  developer: "example-agent@v1",
  timestamp: new Date(),
});
verifyCapsule(built.json); // returns verified metadata or throws

// Persist built.json and any Producer Envelope in application storage.
const mysqlUrl = process.env.MYSQL_URL;
if (!mysqlUrl) throw new Error("MYSQL_URL is required");
const cll = await MysqlStore.open(mysqlUrl, "application-log");
try {
  await cll.append({
    value: Buffer.from(built.capsuleId, "hex"),
    appendedAt: new Date(),
  });
} finally {
  await cll.close();
}

Neither package depends on the other. An application that uses both declares both dependencies explicitly.

capsule-emit creates no database tables. MysqlStore.open() and SqliteStore.open() create only CLL's internal tables, documented in the @action-state-group/cll backend guide. Full Capsules and Producer Envelopes remain in application-owned storage.

JSON digests

digestJSON(value) returns the lowercase SHA-256 of RFC 8785 JCS bytes. It rejects duplicate object names, excessive depth, floats, unsafe integers, invalid UTF-8, and trailing JSON data on strict decoding paths.

import { digestJSON } from "@action-state-group/capsule-emit";

const requestDigest = digestJSON({ issue: 123, operation: "publish" });
const responseDigest = digestJSON({ accepted: true });

Callers own the JSON shape and assign these values to effect request/response fields where appropriate. Raw payload values never enter the Capsule.

Verification and compatibility

The @action-state-group/capsule-emit/aac subpath exposes strict JSON decoding, current and vintage Capsule-ID computation, Class 1 verification, and store verification for persistence adapters. Top-level construction and verification remain format-4-only.

isV4IrreversibilityClass(value) tests membership in the four irreversibility-class values seeded by AAC draft-04. It deliberately returns false for future registry extensions without claiming that an extension is invalid.

Tests replay the complete upstream AAC corpus, all Producer Envelope vectors, and Go/Python authored, received, WHO, DID, and composition fixtures.

Cross-record references

Input.references accepts { type, digestAlg, digest, citationPurpose?, logCoordinates? }. Use the registered type agent-action-capsule with SHA-256 and its Capsule ID for an AAC citation. acted_on and responds_to are seeded citation purposes; unknown purposes remain informational. References cannot duplicate the same Capsule's chain parent. Foreign digest representations belong to the referenced CPB type and are not restricted to AAC's hex encoding.

logCoordinates carries the wire members log_id, leaf_index and inclusion_proof together as opaque claims. Class 1 does not authenticate the proof or resolve external targets. Undefined references are omitted; [] is preserved, including its effect on the format-4 Capsule ID.

import { build, verifyCapsule } from "@action-state-group/capsule-emit";

const common = {
  actionType: "fyi" as const,
  operator: "example-org",
  developer: "example-agent@v1",
  timestamp: "2026-09-02T12:00:00Z",
};
const request = build({ ...common, actionId: "request/1" });
const response = build({
  ...common,
  actionId: "response/1",
  references: [
    {
      type: "agent-action-capsule",
      digestAlg: "SHA-256",
      digest: request.capsuleId,
      citationPurpose: "responds_to",
    },
  ],
});
verifyCapsule(response.json);
console.log(request.capsuleId, response.capsuleId);

References enter through Input, including seal({ capsule: { ... } }). There is no separate reference builder or closed purpose enum.

Artifact storage

The optional ./artifact subpath persists exact sealed Capsules, Producer Envelopes, and business originals. The root entry point stays storage-free: better-sqlite3 and mysql2 are optional peer dependencies, and an application installs only the backend it imports. TypeScript users of the SQLite backend also install @types/better-sqlite3 (an optional peer dependency), because better-sqlite3 ships no bundled type declarations; mysql2 bundles its own. Reads verify Capsule identity, the Producer Envelope against caller-owned trusted keys, the storage inventory, and every retained bound original before returning. Records are immutable, byte-identical retries are idempotent, and a divergent write for the same Capsule ID throws an ArtifactError with code: "conflict".

import Database from "better-sqlite3";
import { PAYLOAD_DIGEST } from "@action-state-group/capsule-emit/artifact";
import { SqliteArtifactStore } from "@action-state-group/capsule-emit/artifact/sqlite";

const db = new Database("artifacts.db");
db.pragma("foreign_keys = ON");
const store = new SqliteArtifactStore(db, "my-namespace", [trustedPublicKey]);
await store.init(); // provision v1 tables once during deployment

await store.put({
  capsuleId: sealed.capsuleId,
  capsule: sealed.payload,
  producerEnvelope: sealed.envelope,
  artifacts: [
    {
      name: "payload",
      binding: PAYLOAD_DIGEST,
      content: payloadBytes,
      state: "present",
    },
  ],
});
const record = await store.get(sealed.capsuleId);

./artifact/mysql exposes the same API over a mysql2 pool. The inventory checksum is byte-compatible with capsule-emit-go, so a Go writer and a TypeScript reader interoperate over a shared database. See DESIGN.md for the full contract.

Development

The embedded provisional registry snapshot mirrors agent-action-capsule/python/agent_action_capsule/data/cpb_provisional.json. Known provisional values change informational diagnostics without increasing assurance. Refresh the snapshot from that source; the test suite checks its provenance and content. Raw JCS still normalizes -0 to 0; Python's optional strict input verification tier is a separate acceptance policy.

Shared reference and vocabulary vectors live in the AAC source checkout under go/verify/testdata/. The producer-to-CLL check is maintained in capsule-emit-go/scripts/check-producer-cll-interop.sh and runs in both emitters' CI. It covers in-memory append/checkpoint interoperability without witness I/O.

npm install
npm run check
npm run build

Interop tests expect agent-action-capsule and capsule-emit-go as sibling checkouts. Override those paths with AAC_ROOT and CAPSULE_EMIT_GO_ROOT.

Release

Releases are published from main with the manual Publish npm package GitHub Action:

  1. Update version in package.json and package-lock.json, commit the change, and wait for main CI to pass.
  2. In GitHub, open the workflow, choose Run workflow, and select main.
  3. Verify the workflow published @action-state-group/capsule-emit and created the v<version> GitHub release and tag on the published commit.

The npm package must have a GitHub Actions trusted publisher configured for the action-state-group/capsule-emit-ts repository and .github/workflows/publish.yml. No long-lived npm token is required. Re-running the workflow is safe: it skips an existing npm version and verifies that its Git tag points to the gitHead recorded by npm.

If npm contains the version but its tag is missing after this workflow has changed, GitHub may reject recovery with the workflow's GITHUB_TOKEN. A maintainer with workflow scope must create the tag at the npm gitHead, then rerun the workflow to verify the tag and create any missing GitHub release:

version=0.1.2
git_head=$(npm view "@action-state-group/capsule-emit@$version" gitHead)
git fetch origin --tags
git tag -a "v$version" "$git_head" -m "Release v$version"
git push origin "refs/tags/v$version"

License

Apache-2.0. The upstream Agent Action Capsule dependency is BSD-3-Clause.