TypeScript / JS SDK Integration
PyHall TypeScript / JavaScript SDK
Add WCP governance to any TypeScript or JavaScript application with @pyhall/core. One hall.decisions.make() call wraps any worker capability in the full governance chain: registration, attestation, policy evaluation, ban list check, and audit record.
Installation
npm install @pyhall/core# orpnpm add @pyhall/coreWhat you can do
- Register workers — give AI agents verified identities with capability declarations
- Make routing decisions — check authorization before every capability execution
- Verify attestation — confirm a worker’s signed capability card is valid and unrevoked
- Enforce policy tiers — apply WCP risk controls to every invocation
- Query audit history — inspect every ALLOW/DENY decision
- Manage namespaces — claim
x.*community ororg.<name>.*org namespaces
Quick start
import { Hall } from '@pyhall/core';
const hall = new Hall({ apiKey: process.env.PYHALL_API_KEY! });
// Register a workerconst worker = await hall.workers.register({ name: 'my-agent', namespace: 'x.myagent', capabilities: ['cap.data.read.v1', 'cap.report.generate.v1'], policyTier: 2,});console.log(worker.id); // wrk_abc123console.log(worker.namespace); // x.myagent
// Make a routing decision before executing a capabilityconst decision = await hall.decisions.make({ workerId: worker.id, capability: 'cap.data.read.v1', env: 'dev', dataLabel: 'PUBLIC', tenantId: 'org.acme',});
if (decision.denied) { throw new Error(`pyhall denied: ${decision.denyReason}`);}
// Safe to proceedconst result = await runMyCapability();console.log(decision.proof); // attestation proof hashWrapping any function with governance
import { Hall } from '@pyhall/core';
const hall = new Hall({ apiKey: process.env.PYHALL_API_KEY! });
function governed(capability: string, workerId: string) { return function <T extends (...args: any[]) => Promise<any>>(fn: T): T { return (async (...args: any[]) => { const decision = await hall.decisions.make({ workerId, capability, env: process.env.PYHALL_ENV ?? 'dev', dataLabel: 'PUBLIC', });
if (decision.denied) { throw new Error(`pyhall denied ${capability}: ${decision.denyReason}`); }
return fn(...args); }) as T; };}
const fetchSensitiveData = governed( 'cap.data.read.v1', 'wrk_abc123')(async (query: string) => { // Only runs when pyhall says ALLOW return { rows: [] };});Next.js API route example
import { NextRequest, NextResponse } from 'next/server';import { Hall } from '@pyhall/core';
const hall = new Hall({ apiKey: process.env.PYHALL_API_KEY! });
export async function POST(req: NextRequest) { const workerId = req.headers.get('x-worker-id'); const capability = req.headers.get('x-capability');
if (!workerId || !capability) { return NextResponse.json({ error: 'Missing governance headers' }, { status: 400 }); }
const decision = await hall.decisions.make({ workerId, capability, env: 'prod', dataLabel: 'INTERNAL', });
if (decision.denied) { return NextResponse.json({ error: decision.denyReason }, { status: 403 }); }
// Proceed with the actual request return NextResponse.json({ ok: true, proof: decision.proof });}Cloudflare Worker example
import { Hall } from '@pyhall/core';
export default { async fetch(request: Request, env: Env): Promise<Response> { const hall = new Hall({ apiKey: env.PYHALL_API_KEY });
const decision = await hall.decisions.make({ workerId: env.WORKER_ID, capability: 'cap.data.read.v1', env: 'prod', dataLabel: 'PUBLIC', });
if (decision.denied) { return new Response(JSON.stringify({ error: decision.denyReason }), { status: 403, headers: { 'Content-Type': 'application/json' }, }); }
return new Response('Authorized', { status: 200 }); },};
interface Env { PYHALL_API_KEY: string; WORKER_ID: string;}Local Hall Server (Hall Monitor)
When Hall Monitor is running locally on port 8765:
const token = process.env.HALL_SESSION_TOKEN!;const serverUrl = process.env.HALL_SERVER_URL ?? 'http://localhost:8765';
const resp = await fetch(`${serverUrl}/api/route`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ capability_id: 'cap.data.read.v1', worker_id: 'wrk_abc123', env: 'dev', data_label: 'PUBLIC', tenant_id: 'org.acme', }),});
const decision = await resp.json();
if (decision.denied) { throw new Error(decision.deny_reason ?? 'Denied by pyhall');}Checking attestation
const attest = await hall.workers.attest('wrk_abc123');
console.log(attest.status); // "attested" | "pending" | "denied"console.log(attest.artifactHash); // SHA-256 of attested worker packageconsole.log(attest.attestedAt); // ISO timestamp
if (attest.status !== 'attested') { throw new Error('Worker is not attested — refusing to proceed');}Environment variables
PYHALL_API_KEY=your-api-key # registry authentication (required)PYHALL_REGISTRY=https://api.pyhall.dev # default registry URLPYHALL_ENV=dev # environment tag (dev | staging | prod)HALL_SESSION_TOKEN=your-token # local Hall Server auth (Hall Monitor)HALL_SERVER_URL=http://localhost:8765 # local Hall Server URLGetting started
# 1. Installnpm install @pyhall/core
# 2. Authenticate (CLI)npm install -g @pyhall/clipyhall auth login
# 3. Claim a namespacepyhall namespace check x.yourname
# 4. Register a workerpyhall worker register
# 5. Make a governed decisionpyhall decision make --worker <id> --cap cap.data.read.v1Full documentation: https://pyhall.dev/docs/reference/typescript/ WCP specification: https://workerclassprotocol.dev/spec/