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

arcaveli-client

v1.0.0

Published

Official client for the Arcaveli zero-knowledge AI proxy. Encrypted chat, agents, files, and canvas with end-to-end RSA-OAEP + AES-256-GCM. Works in Node 18+ and modern browsers.

Readme

arcaveli-client

Official client for the Arcaveli zero-knowledge AI proxy.

Every response is encrypted server-side with your RSA-2048 public key (RSA-OAEP-SHA256 wrapping AES-256-GCM). The matching private key — held only on your machine — is required to decrypt. This client handles the unwrap automatically.

A breach of Arcaveli's database produces ciphertext only. We cannot read your conversations, file uploads, or canvas documents. By design.

  • Zero dependencies. Just Node 18+ (built-in fetch + crypto) or any modern browser (Web Crypto).
  • Single file. ~18 KB unminified, no build step.
  • TypeScript declarations included.
  • Drop-in OpenAI-compatible /v1/chat/completions shape — but the response body is encrypted (use this client to auto-decrypt).
npm install arcaveli-client

Quick start

Three steps: generate an API key from your dashboard, run onboarding once, then call chat().

1. Generate an API key

Open arcaveli.com/dashboardGenerate API key (in the API Access card). Copy the key — it's shown once. Looks like arc_4a402a2adc7a3724a486d4802d10aa3c80d4cb2f1c42b89c0dce1c6311bd3e66.

2. Onboard (one-time per key)

const { ArcaveliClient } = require('arcaveli-client');
const fs = require('fs');

const { privateKey } = await ArcaveliClient.onboard({
  apiKey: 'arc_xxxxx',
});

// Save the private key NOW — the server does not store it.
// If you lose it, every encrypted message tied to this key becomes
// permanently unreadable.
fs.writeFileSync('arcaveli.pem', privateKey);

3. Use forever

const { ArcaveliClient } = require('arcaveli-client');
const fs = require('fs');

const client = new ArcaveliClient({
  apiKey:     process.env.ARCAVELI_API_KEY,
  privateKey: fs.readFileSync('arcaveli.pem', 'utf8'),
});

const r = await client.chat([
  { role: 'user', content: 'In one sentence, what is zero-knowledge encryption?' },
]);

console.log(r.content);
//   → "Zero-knowledge encryption is a system where one party can prove..."
console.log(r.usage);
//   → { prompt_tokens: 14, completion_tokens: 33, total_tokens: 47, ... }

That's it. No keypair generation, no decrypt boilerplate — r.content is plaintext.

Recipes

Multi-turn conversation

Pass conversation_id from the first call to give Claude memory across turns.

const r1 = await client.chat([{ role: 'user', content: 'My client is Acme Corp.' }]);

const r2 = await client.chat(
  [
    { role: 'user',      content: 'My client is Acme Corp.' },
    { role: 'assistant', content: r1.content },
    { role: 'user',      content: 'Draft an NDA for them.' },
  ],
  { conversation_id: r1.conversationId }
);
// r2.content references "Acme Corp" without you repeating it.

PDF Q&A

Upload a PDF — the server extracts text once, encrypts it, returns the plaintext one time so you can inject it as a system prompt.

const file = await client.uploadFile(
  fs.readFileSync('./contract.pdf'),
  'contract.pdf',
  'application/pdf',
);

const r = await client.chat([
  { role: 'system', content: `Document content:\n\n${file.extracted_text}` },
  { role: 'user',   content: 'List every payment obligation, with section numbers.' },
]);

console.log(r.content);

Agent research with cited sources

The case-research and medical-literature agents run a server-side tool loop (CourtListener / PubMed), then return the synthesized answer plus the sources cited.

const r = await client.chat(
  [{ role: 'user', content: '2019 9th Circuit cases on FTC consent decrees.' }],
  { agent: 'case-research' }
);

console.log(r.content);
for (const tool of r.toolsUsed || []) {
  for (const src of tool.sources || []) {
    console.log(`- ${src.label} (${src.meta})`);
    console.log(`  ${src.url}`);
  }
}

Streaming with progress events

Streaming returns a single encrypted envelope at the end (RSA-OAEP wraps an AES key per response — there's no incremental ciphertext). The value is the progress stream during agent loops.

await client.chatStream(
  [{ role: 'user', content: 'Find recent appellate rulings on...' }],
  { agent: 'case-research' },
  (e) => {
    if (e.progress?.phase === 'tool_use') {
      console.log('Calling tools:', e.progress.tools.join(', '));
    }
    if (e.text) console.log('Final answer:', e.text);
  },
);

API reference

class ArcaveliClient {
  constructor(opts: { apiKey: string; privateKey: string; baseUrl?: string });

  static onboard(opts: { apiKey: string; baseUrl?: string }): Promise<{
    onboarded: true;
    publicKey: string;
    privateKey: string;     // store immediately — not retrievable later
    warning: string;
    nextSteps: { storeKey: string; docs: string };
  }>;

  // Chat
  chat(messages, options?): Promise<ChatResult>;
  chatStream(messages, options, onChunk): Promise<ChatResult>;

  // Models / agents
  listModels(): Promise<ListResponse<ModelInfo>>;
  listAgents(): Promise<ListResponse<AgentInfo>>;

  // Conversations (encrypted history persisted server-side)
  listConversations(): Promise<ListResponse<ConversationSummary>>;
  getConversation(id): Promise<ConversationDetail>;     // messages decrypted inline
  createConversation(): Promise<{ id, created_at, updated_at }>;
  deleteConversation(id): Promise<{ ok: true }>;

  // Files (PDF / DOCX / TXT, ≤10 MB)
  uploadFile(buffer, filename, mimeType?): Promise<FileUploadResponse>;
  listFiles(): Promise<ListResponse<FileSummary>>;       // filenames decrypted
  deleteFile(id): Promise<{ ok: true }>;

  // Canvas (encrypted Quill Delta documents)
  createDocument(title, delta): Promise<CanvasDocSummary>;
  listDocuments(): Promise<ListResponse<CanvasDocSummary>>;     // titles decrypted
  getDocument(id): Promise<CanvasDocDetail>;                    // title + delta decrypted
  updateDocument(id, body): Promise<CanvasDocSummary>;
  deleteDocument(id): Promise<{ ok: true }>;
  exportDocument(id, delta, title?): Promise<Buffer | Blob>;    // DOCX binary
}

Full TypeScript declarations ship with the package (index.d.ts).

Errors

Every error response is JSON of shape { error: string, code?: string, ... }. The client throws an Error with status (HTTP code) and body (the JSON payload) attached, so:

try {
  await client.chat([{ role: 'user', content: 'hi' }]);
} catch (err) {
  console.error(err.status);    // e.g. 429
  console.error(err.body.code); // e.g. 'credits_exhausted'
  console.error(err.message);   // e.g. 'Monthly AI credits exhausted'
}

Common codes:

| Status | code | Meaning | |---|---|---| | 401 | — | Invalid or missing API key | | 402 | — | Active subscription required | | 402 | byok_key_missing | Solo plan, but no provider key on file | | 403 | — | Onboarding required (call ArcaveliClient.onboard()) | | 403 | managed_anthropic_only | Starter/Business — must pick a claude-* model | | 404 | — | Resource not visible to your API surface | | 409 | — | Already onboarded with this key | | 413 | — | Payload too large (10 MB / 4 MB) | | 429 | — | Rate limit hit (60 req/min/user) | | 429 | credits_exhausted | Monthly credits used up + no top-up balance | | 502 | — | Upstream provider error — safe to retry |

Every successful response includes X-Rate-Limit-Limit + X-Rate-Limit-Remaining headers so well-behaved clients can pace themselves.

OpenAI compatibility

The chat shape mirrors OpenAI so existing OpenAI client code can point at the Arcaveli base URL — but the response body is encrypted, so the official openai SDK will display ciphertext. Use this client to auto-decrypt:

// Drop-in compatibility (but you'll see ciphertext)
const openai = new OpenAI({
  apiKey:  'arc_xxxxx',
  baseURL: 'https://arcaveli.com/api/v1',
});

Security model

  • Server holds the public key. The matching private key is generated server-side at onboarding, returned once, and never persisted.
  • Encryption at rest. Every message body, file upload extraction, and canvas document is encrypted with a fresh AES-256-GCM key, then wrapped with your RSA public key (RSA-OAEP-SHA256). The opaque envelope is what touches the database.
  • API keys are SHA-256 hashed. The plaintext is never stored.
  • No silent recovery. Lose your private key → encrypted history is permanently unreadable. By design.

Read the full security overview at arcaveli.com/security.

Plans + rate limits

  • Solo ($49/mo) — bring your own provider keys (Anthropic / OpenAI / Google / Groq / OpenRouter)
  • Starter ($149/mo) — 5,000,000 AI credits/month on managed Anthropic
  • Business ($499/mo) — 25,000,000 credits/month, shared across team workspace
  • Top-up packs — never expire, charged at $50/M (Solo), $30/M (Starter), $20/M (Business)

API calls count against the same monthly allotment. 60 requests/min/user rate limit.

Links

License

MIT — see LICENSE.