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

@agenttool/adds

v0.2.3

Published

ADDS 0.1 — offline-first, agent-native encrypted distribution and storage

Readme

@agenttool/adds

Experimental reference implementation of ADDS 0.1 — Agent Data Distribution & Storage: a small, offline-first encrypted object substrate for agents and humans.

@agenttool/adds does:

  • address exact bytes as CIDv1 raw + SHA-256 + base32lower;
  • encrypt fixed-size chunks with a fresh per-object AES-256-GCM key;
  • sign canonical Manifests and direct read Grants with domain-separated, strict-profile Ed25519;
  • wrap the object key directly to one X25519 audience with a mandatory expiry;
  • keep provider selection outside the signed Manifest;
  • export and import complete keyless encrypted objects as strict portable bundles;
  • work with memory, filesystem, or caller-provided stores without an API key or network.

It does not provide discovery, query/index APIs, global revocation, secure deletion, proof of storage, identity resolution, or a durability guarantee. @agenttool/data is the higher collection/query layer; @agenttool/sdk is the hosted AgentTool client. ADDS is the lower encrypted-object data plane.

Install

Portable bundle import/export shipped in 0.2.0 and remains in the licensed 0.2.3 package:

bun add https://docs.agenttool.dev/packages/v1/@agenttool/adds/0.2.3/agenttool-adds-0.2.3.tgz

This versioned tarball is published through love-package/v1; its manifest lists the SHA-256 digest and interchangeable mirrors. No npm account or npm publication is required. Package managers still resolve declared upstream dependencies through their configured registries or cache.

Version 0.2.3 raises the declared @noble/ed25519 minimum to 2.3.0, matching the strict verifier's Point API. A broken dependency/runtime API is surfaced as an environment fault rather than being reported as an invalid signature; malformed or forged signatures still fail closed.

Offline roundtrip

import {
  AgentData,
  MemoryBlockStore,
  generateIdentity,
} from "@agenttool/adds";

const blocks = new MemoryBlockStore();
const alice = generateIdentity("did:example:alice");
const bob = generateIdentity("did:example:bob");

const publisher = new AgentData({
  identity: alice,
  store: blocks,
});

const published = await publisher.put("private agent context", {
  mediaType: "text/plain; charset=utf-8",
  schema: "https://example.org/agent-context/v1",
});

// `published` contains a ref, signed Manifest, and write acknowledgements.
// It deliberately does not contain the plaintext object key.
const grant = await publisher.share(published.ref, {
  audience: bob.id,
  audienceBoxPublicKey: bob.boxPublicKey,
  expiresAt: Math.floor(Date.now() / 1000) + 3600,
});

const reader = new AgentData({ identity: bob, store: blocks });
const plaintext = await reader.get(published.ref, { grant });

No constructor reads AT_API_KEY, AGENTTOOL_API_KEY, or any other environment variable.

Stores and key custody

BlockStore stores immutable ciphertext/control-document bytes by CID. MemoryBlockStore is ephemeral. MultiBlockStore attempts writes against every configured provider, requires an explicit minimum number of acknowledgements, and reads in ordered local-first fallback order:

const data = new AgentData({
  identity: alice,
  stores: [localStore, peerStore, hostedStore],
  minimumWrites: 2,
  storeTimeoutMs: 5_000,
});

The returned acknowledgement counts are per stored object. They are not proof of complete replicas, physical independence, retention, or future availability.

The Node/Bun filesystem adapter is a separate subpath so the root package remains browser-compatible:

import { FileSystemBlockStore } from "@agenttool/adds/fs";

const blocks = new FileSystemBlockStore("./adds-blocks");

The filesystem adapter stores only addressed ciphertext and signed documents. It never persists a DEK. Object keys go through the injected KeyStore; the default MemoryKeyStore is process-local. importKey() is an explicit custody operation. forgetKey() is best-effort local forgetting, not secure deletion, and cannot erase Grants, recipient copies, backups, or plaintext already disclosed.

S3-compatible store (included since 0.2.2)

The licensed 0.2.3 package includes a Node/Bun network adapter at the isolated @agenttool/adds/s3 subpath. It is not re-exported from the browser-compatible package root:

import { AgentData, type AgentDataIdentity } from "@agenttool/adds";
import { S3CompatibleBlockStore } from "@agenttool/adds/s3";

declare const credentials: {
  accessKeyId: string;
  secretAccessKey: string;
  sessionToken?: string;
};
declare const publisherIdentity: AgentDataIdentity;

const blocks = new S3CompatibleBlockStore({
  // The bucket is the endpoint's one path segment, not a separate option.
  endpoint: "https://objects.example.test/evidence-bucket",
  region: "auto",
  accessKeyId: credentials.accessKeyId,
  secretAccessKey: credentials.secretAccessKey,
  sessionToken: credentials.sessionToken,
  prefix: "whitehack/capsules",
});

// AgentData's direct `store` form supplies no deadline. Compose a network
// provider through `stores` so MultiBlockStore applies a finite timeout.
const data = new AgentData({
  identity: publisherIdentity,
  stores: [blocks],
  minimumWrites: 1,
  storeTimeoutMs: 5_000,
});

The caller supplies credentials explicitly; the adapter reads no environment, config file, Keychain, or AgentTool bearer. It accepts only a canonical HTTPS path-style bucket endpoint without userinfo, query, fragment, encoded path components, or a trailing slash. The endpoint is trusted configuration, not an SSRF sandbox: arbitrary HTTPS private, loopback, and DNS-rebound destinations remain reachable. An API-facing caller must apply its own hostname/address allowlist and egress policy before constructing the store. An explicitly named allowInsecureLoopbackHttpForTests option permits HTTP only for a loopback test endpoint; it is not a production transport mode.

The path-style, Signature Version 4 contract targets existing Cloudflare R2, Backblaze B2, Filebase, and caller-operated MinIO buckets when they implement the bounded GET/PUT subset used here. Endpoint, signing-region, credential-scope, billing, lifecycle, and retention configuration remains provider-specific. Exact signing fixtures and a real loopback HTTP composition test do not certify every provider deployment.

Each CID maps deterministically to <endpoint>/<optional-prefix>/<cid>. The optional prefix is bounded to 964 ASCII bytes so the prefix, separator, and 59-byte ADDS CID stay within S3's 1,024-byte object-key ceiling. Requests use AWS Signature Version 4 with service s3, manual redirect handling, credentials: "omit", no retry, no referrer, and caller-provided abort and byte limits. Response-body cancellation is best-effort and never delays the caller. The adapter has no intrinsic wall-clock timeout. Direct callers must supply an AbortSignal; AgentData callers should use its stores composition plus a finite storeTimeoutMs, as above. GET streams into a byte-bounded buffer, rejects a response after more than 16,384 delivered chunks including empty chunks, and verifies the exact ADDS CID. The chunk ceiling bounds fragmented and zero-byte delivery work; it is not a wall-clock timeout. Only a bounded, strictly parsed S3 NoSuchKey 404 maps to null; every other, malformed, or oversized 404 is a static provider failure. PUT validates the size and caller's CID before sending; a 2xx result is only one historical provider acknowledgement. It is not proof of future retention, independent replication, geographic placement, or durability.

The adapter does not create or list buckets, discover endpoints, delete objects, perform multipart upload, manage versions or lifecycle policy, issue ADDS ProviderRecords/Receipts, or persist an ADDS object key. Used through AgentData, the provider receives encrypted Blocks and signed Manifests but still observes the access-key identity, CIDs, sizes, timing, and access patterns. Direct S3CompatibleBlockStore.put() calls upload the supplied bytes verbatim; encryption is provided only by composing the store through AgentData. JavaScript credential strings necessarily remain in the caller and store instance fields for that instance's lifetime, until garbage collection; recreate the store to rotate them. Callers still need a scoped credential source and an independent KeyStore/Grant recovery route.

Core API

  • put(source, options) encrypts, stores, signs, and returns { ref, manifest, replication } without a DEK.
  • inspect(ref) verifies the Manifest CID, canonical JSON, schema invariants, signer binding, and Ed25519 signature.
  • share(ref, options) issues a publisher-signed, finite, direct read Grant for one principal ID and X25519 key.
  • verify(ref) checks Manifest and ciphertext Block CIDs, framing, sizes, ordering, and nonce descriptors. It does not possess a key and therefore does not authenticate GCM tags or plaintext.
  • get(ref, { grant }) validates the Grant first, verifies its root issuer against the Manifest publisher, unwraps the key, verifies/decrypts every Block, and enforces maxBytes.
  • importKey(ref, key) installs a caller-supplied key explicitly for local/offline custody.

Portable encrypted bundles

exportBundle(ref) snapshots one complete encrypted object into a transport-neutral PortableBundle. Its first PortableBlock is the signed Manifest named by root; the remaining Blocks follow the Manifest's signed chunk order. Each Block carries exact Uint8Array bytes, so an HTTP, archive, or message transport chooses its own byte encoding instead of changing ADDS.

importBundle(bundle) strictly validates the bundle shape, aggregate byte and block limits, root CID, canonical Manifest, signature, exact block set/order, Block CIDs, lengths, and nonce descriptors before making any store write. It writes ciphertext Blocks before attempting the root Manifest. A provider failure can leave immutable partial writes; a partially successful final provider call can include the root. Retrying the same bundle is safe with an immutable deduplicating store, but the write order is not a cross-provider transaction or durability guarantee.

Portable bundles contain no DEK and no Grant. Importing one proves possession of a verified encrypted copy, not read authority. A recipient still supplies a separate direct Grant to get(). maxBundleBytes bounds the combined Manifest and framed ciphertext held by export/import, while maxBytes continues to bound the declared plaintext size.

Inputs accept Uint8Array, ArrayBuffer, strings, Blob, and sync/async byte iterables. Local maxBytes, maxBlocks, manifest-size, provider-count, provider-timeout, 64-level canonical nesting, and 100,000-value canonical-document limits are enforced. Direct Grants default to a 30-day maximum lifetime; callers may configure a stricter policy or raise it only up to the package's 10-year safety ceiling.

Cryptographic profile

  • Control documents use restricted I-JSON/JCS bytes and canonical unpadded base64url.
  • Manifests sign UTF8("adds-manifest/v1:") || JCS(unsigned_manifest).
  • Grants sign UTF8("adds-grant/v1:") || JCS(unsigned_grant).
  • Stored Blocks are nonce_12 || AES-GCM ciphertext || tag_16; their CID covers the whole frame.
  • Block AAD uses adds-block/v1 and binds object/key IDs, random AAD context, index, block count, chunk size, per-chunk size, and total size.
  • Direct key wrap uses X25519, HKDF-SHA256 info adds-grant-kek/v1, and AES-256-GCM under adds-grant-wrap/v1 AAD.
  • Ed25519 verification requires canonical, non-small-order, torsion-free public-key and signature points; permissive ZIP 215/cofactored-only acceptance is rejected for cross-library agreement.

The direct wrap construction is the ADDS 0.1 wire profile; it is not HPKE and is not a libsodium sealed box. Ed25519 and X25519 are separate key roles, and exact private-key reuse is rejected.

Security boundaries

  • A CID/signature proves byte integrity and control by a key, not that data or provenance is true.
  • A signed principal ID/key claim does not prove an external DID or account binding.
  • Grant validity is exactly effective_not_before <= now < expires_at; expiry cannot make a recipient forget a disclosed key.
  • ADDS 0.1's reference profile accepts publisher-rooted direct rights: ["read"] Grants only. Delegation, task scope, and broader rights fail closed.
  • Manifests expose sizes, timing, publisher, schema hints, and lineage. Stores cannot read plaintext without a key, but decentralisation alone does not hide metadata or access patterns.
  • Decrypted content remains untrusted input even when its ciphertext, signature, and claimed schema verify.

Development

bun install
bun run ci

Tests consume the shared pinned JCS/CID/signature/AES/Grant-wrap/tamper vectors and cover offline and cross-recipient roundtrips, expiry boundaries, fallback and timeout behavior, wrong recipients/keys, publisher-only root grants, resource limits, and filesystem mutation races.

License

Apache-2.0. See LICENSE and NOTICE.