Install
Package-manager and framework matrix — one page that covers every way to bring `@pleach/core` into a project.
The soil layer before the trellis goes up. Install paths organized by what you already use: pick a package manager, pick a framework, pick a provider. The wizard wraps all three; the manual snippets are if you want to see what gets generated.
The wizard (recommended)
npx pleach init detects your framework, asks four questions
(template, provider, plugin stub, schema scaffold), and writes a
starter project. Per-framework template matrix lives at
CLI → pleach init.
npx pleach initpnpm dlx pleach inityarn dlx pleach initbunx pleach initRun npm run dev with a provider key set — there is no keyless
mode (with no key the scaffolded route refuses every turn with an
actionable "Set <PROVIDER>_API_KEY" message). The bundled
/audit view shows real rows from your first message. ~60 seconds
wall-clock.
Manual: install @pleach/core
npm install @pleach/corepnpm add @pleach/coreyarn add @pleach/corebun add @pleach/coreManual: set a provider key
export ANTHROPIC_API_KEY="..."export OPENAI_API_KEY="..."export GOOGLE_GENERATIVE_AI_API_KEY="..."export OPENROUTER_API_KEY="..."export DEEPSEEK_API_KEY="..."export MISTRAL_API_KEY="..."export MOONSHOT_API_KEY="..."The runtime auto-detects whichever key is set. Detection priority
matches the tab order above. To pin a provider explicitly (skipping
detection), pass provider: "anthropic" to createPleachRoute().
Manual: wire a route handler
The same createPleachRoute() factory works in any fetch-based
Node.js server — App Router, Pages Router, Astro, SvelteKit, Remix, Hono,
Bun. Pick the framework you're on; the body is identical.
// app/api/chat/route.ts
import { createPleachRoute } from "@pleach/core/quickstart";
export const POST = createPleachRoute();// pages/api/chat.ts
import { createPleachRoute } from "@pleach/core/quickstart";
const handler = createPleachRoute();
export const config = { api: { bodyParser: false } };
export default async function POST(req, res) {
const response = await handler(new Request(`http://x${req.url}`, {
method: "POST",
body: req as unknown as BodyInit,
}));
res.status(response.status);
response.headers.forEach((v, k) => res.setHeader(k, v));
if (response.body) {
const reader = response.body.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
res.write(value);
}
}
res.end();
}// src/pages/api/chat.ts
import type { APIRoute } from "astro";
import { createPleachRoute } from "@pleach/core/quickstart";
const handler = createPleachRoute();
export const POST: APIRoute = ({ request }) => handler(request);// src/routes/api/chat/+server.ts
import { createPleachRoute } from "@pleach/core/quickstart";
const handler = createPleachRoute();
export const POST = ({ request }) => handler(request);// app/routes/api.chat.ts
import { createPleachRoute } from "@pleach/core/quickstart";
const handler = createPleachRoute();
export const action = ({ request }) => handler(request);// src/index.ts — run Hono on Node.js
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { createPleachRoute } from "@pleach/core/quickstart";
const app = new Hono();
const handler = createPleachRoute();
app.post("/api/chat", (c) => handler(c.req.raw));
serve({ fetch: app.fetch, port: 3000 });// server.ts
import { createPleachRoute } from "@pleach/core/quickstart";
const handler = createPleachRoute();
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/api/chat" && req.method === "POST") {
return handler(req);
}
return new Response("Not found", { status: 404 });
},
});What this gives you
A streaming chat handler at POST /api/chat that accepts
{ sessionId?, message } JSON and streams StreamEvent NDJSON
back. No persistence, no plugins, no domain customization — the
runtime returns a substrate placeholder until you adopt a
storage adapter and (optionally) a
plugin.
There is no keyless mode: with no provider key the route refuses
every turn with the actionable "Set <PROVIDER>_API_KEY" message —
a canned answer is never served. The wizard scaffolds the same
key-required route, with an /audit view to read the rows.
For the React side — useChat(), <ChatBox /> — see
Getting started. For a production-shape
handler with custom storage, plugins, and provider pinning, see
Getting started → Custom storage, plugins, or provider pinning.
Optional peer dependencies
@pleach/core installs with no optional peers. Four surfaces load
theirs by dynamic import and fail with a named error naming the
package, rather than a module-resolution stack — so a consumer who
never reaches for the surface never installs the dependency.
| Install when you use | Packages |
|---|---|
CMS attestation envelopes (@pleach/core/attestation/cms) | @peculiar/asn1-schema, @peculiar/asn1-cms, @peculiar/asn1-x509 |
Role-based approval flows (@pleach/core/rbac) | @pleach/rbac-contract |
| An MCP server or client from core | @modelcontextprotocol/sdk |
RFC 3161 receipt verification (@pleach/compliance/timestamp) | @sigstore/core |
The failure is designed to be readable. Missing ASN.1 peers surface as
CmsAttestationError("asn1-peer-missing"); a missing @sigstore/core
makes verifyRfc3161Receipt return { verified: false, reason: "verifier-unavailable" } rather than throwing — an unverifiable
receipt and an unavailable verifier are different facts, and the
result says which one you have.
Where to go next
Getting started
The end-to-end 60-second walkthrough — install, env, route handler, page.
Which SKU do I need?
Most projects need three or four packages. The shortest install list for what you're building.
Runtime construction
When you need to drop below the route handler — SessionRuntime constructor + createPleachRuntime factory.
Storage
Memory, IndexedDB, Supabase, Postgres — when to use which adapter.