TypeScript SDK
@pyhall/core is the TypeScript/Node.js implementation of WCP. It mirrors the Python SDK, targeting WCP-Full compliance. Works in Node.js and modern browsers (with a Web Crypto API fallback for SHA-256).
Version: 0.3.0 | npm: @pyhall/core | WCP spec: 0.1
Install
npm install @pyhall/coreQuick Start
import { makeDecision, Registry, loadRulesFromDoc } from "@pyhall/core";
const rules = loadRulesFromDoc({ rules: [ { rule_id: "rr_hello_dev_001", match: { capability_id: "cap.hello.greet", env: { in: ["dev", "stage"] } }, decision: { candidate_workers_ranked: [ { worker_species_id: "wrk.hello.greeter", score_hint: 1.0 } ], required_controls_suggested: ["ctrl.obs.audit-log-append-only"], escalation: { policy_gate: false }, preconditions: {}, }, } ]});
const registry = new Registry();registry.enroll({ worker_id: "org.example.hello-greeter", worker_species_id: "wrk.hello.greeter", capabilities: ["cap.hello.greet"], currently_implements: ["ctrl.obs.audit-log-append-only"],});
const decision = makeDecision({ inp: { capability_id: "cap.hello.greet", env: "dev", data_label: "PUBLIC", tenant_risk: "low", qos_class: "P2", tenant_id: "demo", correlation_id: "550e8400-e29b-41d4-a716-446655440000", }, rules, registryControlsPresent: registry.controlsPresent(), registryWorkerAvailable: (id) => registry.workerAvailable(id),});
console.log(decision.denied); // falseconsole.log(decision.selected_worker_species_id); // "wrk.hello.greeter"console.log(decision.telemetry_envelopes.length); // 3Types
All types are TypeScript interfaces. Runtime validation is optional via Zod (peer dependency).
Type Aliases
type Env = "dev" | "stage" | "prod" | "edge";type DataLabel = "PUBLIC" | "INTERNAL" | "RESTRICTED";type QoSClass = "P0" | "P1" | "P2" | "P3";type TenantRisk = "low" | "medium" | "high";RouteInput
interface RouteInput { capability_id: string; env: Env; data_label: DataLabel; tenant_risk: TenantRisk; qos_class: QoSClass; tenant_id: string; correlation_id: string; request?: Record<string, unknown>; blast_radius?: Record<string, unknown> | null; blast_score?: number | null; privilege_context?: Record<string, unknown> | null; dry_run?: boolean;}RouteDecision
interface RouteDecision { decision_id: string; timestamp: string; correlation_id: string; tenant_id: string; capability_id: string; matched_rule_id: string; env: Env; data_label: DataLabel; tenant_risk: TenantRisk; qos_class: QoSClass; denied: boolean; deny_reason_if_denied?: Record<string, unknown> | null; selected_worker_species_id?: string | null; candidate_workers_ranked: CandidateWorker[]; required_controls_effective: string[]; recommended_profiles_effective: Record<string, unknown>[]; escalation_effective: Escalation; preconditions_checked: PreconditionsChecked; dry_run?: boolean; worker_attestation_checked?: boolean; worker_attestation_valid?: boolean | null; telemetry_envelopes: Record<string, unknown>[];}CandidateWorker
interface CandidateWorker { worker_species_id: string; score_hint?: number | null; requires_controls_minimum?: string[] | null; skip_reason?: string | null;}Escalation
interface Escalation { policy_gate?: boolean; msavx_step_up?: boolean; human_required_default?: boolean; human_required_if?: Record<string, unknown>[]; rationale?: string | null;}PreconditionsChecked
interface PreconditionsChecked { must_have_correlation_id?: boolean; must_attach_policy_version?: boolean; must_record_artifact_hash_if_executes?: boolean; deny_if_missing_required_controls?: boolean; deny_if_unsigned_artifact_in_prod?: boolean; deny_if_no_attestation_in_prod?: boolean;}HallConfig
Hall-level governance floors. Individual rules may tighten but never loosen these.
interface HallConfig { requireSignatory?: boolean; // default: false allowedTenants?: string[]; enforceCorrelationId?: boolean; // default: true enforceRequiredControls?: boolean; // default: true enforceBlastScoringInProd?: boolean; // default: true enforcePrivilegeEnvelopes?: boolean; // default: false requireWorkerAttestation?: boolean; // default: false}WorkerRegistryRecord
interface WorkerRegistryRecord { worker_id: string; worker_species_id: string; capabilities: string[]; risk_tier?: string; required_controls?: string[]; currently_implements?: string[]; allowed_environments?: string[]; blast_radius?: Record<string, unknown>; privilege_envelope?: PrivilegeEnvelope; owner?: string; contact?: string; notes?: string; catalog_version_min?: string; [key: string]: unknown;}PrivilegeEnvelope
interface PrivilegeEnvelope { secrets_access?: string[]; network_egress?: string; filesystem_writes?: string[]; tools?: string[]; egress?: { allowlist?: string[]; [key: string]: unknown; }; [key: string]: unknown;}makeDecision()
The Hall’s core routing function. Takes a single options object. Never throws for normal routing — all failures return denied RouteDecision objects.
interface MakeDecisionOptions { inp: RouteInput; rules: Rule[]; registryControlsPresent: Set<string>; registryWorkerAvailable: (speciesId: string) => boolean; registryGetPrivilegeEnvelope?: (speciesId: string) => PrivilegeEnvelope | undefined; registryPolicyAllowsPrivilege?: ( env: string, dataLabel: string, envelope: PrivilegeEnvelope | undefined ) => [boolean, string]; policyGateEval?: PolicyGateEvaluator | null; conformanceSpec?: ConformanceSpec | null; taskId?: string; hallConfig?: HallConfig; registryGetWorkerHash?: ((speciesId: string) => string | null) | null; registryGetCurrentWorkerHash?: ((speciesId: string) => string | null) | null;}
function makeDecision(opts: MakeDecisionOptions): RouteDecision;Comparison with Python SDK:
| Concern | Python | TypeScript |
|---|---|---|
| Data models | Pydantic BaseModel | TypeScript interface |
makeDecision() signature | Positional args | Single options object |
| UUID | uuid.uuid4() | crypto.randomUUID() with fallback |
| SHA-256 | hashlib.sha256 | Node crypto.createHash / SubtleCrypto |
| Runtime validation | Pydantic | Optional Zod (peer dep) |
Registry Class
import { Registry } from "@pyhall/core";
const registry = new Registry();
// Enrollmentregistry.enroll(record: WorkerRegistryRecord): void
// Controlsregistry.controlsPresent(): Set<string>registry.setControlsPresent(controls: string[]): voidregistry.addControlsPresent(controls: string[]): void
// Worker availabilityregistry.workerAvailable(speciesId: string): booleanregistry.workersForCapability(capabilityId: string): string[]registry.setWorkersAvailable(speciesIds: string[]): voidregistry.addWorkersAvailable(speciesIds: string[]): void
// Privilege envelopesregistry.getPrivilegeEnvelope(speciesId: string): PrivilegeEnvelope | undefinedregistry.setPrivilegeEnvelopes(envelopes: Record<string, PrivilegeEnvelope>): voidregistry.policyAllowsPrivilege( env: string, dataLabel: string, envelope: PrivilegeEnvelope | undefined): [boolean, string]
// Introspectionregistry.enrolledCount(): numberregistry.enrolledWorkers(): WorkerRegistryRecord[]registry.summary(): objectRules Engine
import { loadRulesFromDoc, loadRulesFromJson, routeFirstMatch, ruleMatches } from "@pyhall/core";import type { Rule, RulesDocument } from "@pyhall/core";
// Load from a parsed objectconst rules = loadRulesFromDoc(doc: RulesDocument): Rule[]
// Load from a JSON stringconst rules = loadRulesFromJson(json: string): Rule[]
// Find first matching rule (returns null if none match)const match = routeFirstMatch(rules: Rule[], inp: Partial<RouteInput>): Rule | null
// Test a single ruleconst matched = ruleMatches(rule: Rule, inp: Partial<RouteInput>): booleanRule type
interface Rule { rule_id: string; match: Record<string, unknown>; decision: Record<string, unknown>;}Policy Gate
import { PolicyGate } from "@pyhall/core";import type { PolicyGateEvaluator, PolicyGateContext, PolicyGateResult, PolicyDecision } from "@pyhall/core";
type PolicyDecision = "ALLOW" | "DENY" | "REQUIRE_HUMAN";
interface PolicyGateContext { capability_id: string; tenant_id: string; env: string; data_label: string; tenant_risk: string; qos_class: string; policy_version?: string;}
type PolicyGateResult = [PolicyDecision, string, string];// [decision, policy_version, reason]
type PolicyGateEvaluator = (context: PolicyGateContext) => PolicyGateResult;Custom policy gate example:
const policyGateEval: PolicyGateEvaluator = (context) => { if (context.env === "prod" && context.data_label === "RESTRICTED") { return ["REQUIRE_HUMAN", "policy.v1", "restricted_data_in_prod"]; } return ["ALLOW", "policy.v1", "default_allow"];};
const decision = makeDecision({ inp, rules, ..., policyGateEval });Utilities
import { nowUtc, uuidV4, sha256Hex, ok, err, partial, VERSION, WCP_VERSION } from "@pyhall/core";import type { ResultStatus, WorkerResultEnvelope } from "@pyhall/core";
VERSION // "0.3.0"WCP_VERSION // "0.1"RegistryClient
HTTP client for the pyhall.dev registry API. Mirrors the Python RegistryClient — same endpoints, same semantics.
import { RegistryClient, RegistryRateLimitError } from "@pyhall/core";import type { VerifyResponse, BanEntry } from "@pyhall/core";
const client = new RegistryClient({ baseUrl: "https://api.pyhall.dev", // default; override for self-hosted sessionToken: "...", // pyhall_session JWT (authenticated calls) timeout: 10_000, // ms, default 10 000 cacheTtl: 60_000, // verify() cache TTL, ms, default 60 000});Public endpoints (no auth required):
// Verify a worker's attestation status — returns status='unknown' on 404 (IDOR-safe)const r: VerifyResponse = await client.verify("org.example.my-worker");r.status; // 'active' | 'revoked' | 'banned' | 'unknown'r.current_hash; // string | nullr.banned; // booleanr.ai_generated; // boolean
// Check if a hash is on the ban-listconst banned: boolean = await client.isHashBanned(sha256);
// Fetch all confirmed ban-list entriesconst list: BanEntry[] = await client.getBanList(500);
// Health checkconst h = await client.health(); // { ok: boolean, version: string }Authenticated endpoint:
// Report a hash for review (requires sessionToken)await client.reportHash(sha256, "reason text", "https://evidence.url");makeDecision() integration:
import { makeDecision, RegistryClient } from "@pyhall/core";
const client = new RegistryClient();
// Pre-populate cache (async; makeDecision() callback is sync)await client.prefetch(["org.example.my-worker"]);
const hashCallback = client.getWorkerHashCallback();
const decision = makeDecision({ inp, rules, registryControlsPresent: registry.controlsPresent(), registryWorkerAvailable: (id) => registry.workerAvailable(id), registryGetWorkerHash: hashCallback,});@pyhall/cli
The @pyhall/cli package provides a command-line interface for the WCP catalog.
npm install -g @pyhall/cliAvailable commands:
# Build a new WCP worker packagepyhall build
# Fuzzy search across all 245 catalog entitiespyhall search "document summarization"
# Detailed lookup for a specific catalog entitypyhall explain cap.doc.summarize
# Browse catalog interactivelypyhall browsepyhall browse --type cappyhall browse --type wrkpyhall browse --type ctrlpyhall browse --type prof
# Show versionpyhall versionBrowser Compatibility
The @pyhall/core source has no Node.js-only APIs except sha256Hex, which falls back to SubtleCrypto in browser environments. crypto.randomUUID() is used for UUID generation with a fallback for environments that lack it.