Skip to content

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

Terminal window
npm install @pyhall/core

Quick 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); // false
console.log(decision.selected_worker_species_id); // "wrk.hello.greeter"
console.log(decision.telemetry_envelopes.length); // 3

Types

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:

ConcernPythonTypeScript
Data modelsPydantic BaseModelTypeScript interface
makeDecision() signaturePositional argsSingle options object
UUIDuuid.uuid4()crypto.randomUUID() with fallback
SHA-256hashlib.sha256Node crypto.createHash / SubtleCrypto
Runtime validationPydanticOptional Zod (peer dep)

Registry Class

import { Registry } from "@pyhall/core";
const registry = new Registry();
// Enrollment
registry.enroll(record: WorkerRegistryRecord): void
// Controls
registry.controlsPresent(): Set<string>
registry.setControlsPresent(controls: string[]): void
registry.addControlsPresent(controls: string[]): void
// Worker availability
registry.workerAvailable(speciesId: string): boolean
registry.workersForCapability(capabilityId: string): string[]
registry.setWorkersAvailable(speciesIds: string[]): void
registry.addWorkersAvailable(speciesIds: string[]): void
// Privilege envelopes
registry.getPrivilegeEnvelope(speciesId: string): PrivilegeEnvelope | undefined
registry.setPrivilegeEnvelopes(envelopes: Record<string, PrivilegeEnvelope>): void
registry.policyAllowsPrivilege(
env: string,
dataLabel: string,
envelope: PrivilegeEnvelope | undefined
): [boolean, string]
// Introspection
registry.enrolledCount(): number
registry.enrolledWorkers(): WorkerRegistryRecord[]
registry.summary(): object

Rules Engine

import { loadRulesFromDoc, loadRulesFromJson, routeFirstMatch, ruleMatches } from "@pyhall/core";
import type { Rule, RulesDocument } from "@pyhall/core";
// Load from a parsed object
const rules = loadRulesFromDoc(doc: RulesDocument): Rule[]
// Load from a JSON string
const 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 rule
const matched = ruleMatches(rule: Rule, inp: Partial<RouteInput>): boolean

Rule 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 | null
r.banned; // boolean
r.ai_generated; // boolean
// Check if a hash is on the ban-list
const banned: boolean = await client.isHashBanned(sha256);
// Fetch all confirmed ban-list entries
const list: BanEntry[] = await client.getBanList(500);
// Health check
const 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.

Terminal window
npm install -g @pyhall/cli

Available commands:

Terminal window
# Build a new WCP worker package
pyhall build
# Fuzzy search across all 245 catalog entities
pyhall search "document summarization"
# Detailed lookup for a specific catalog entity
pyhall explain cap.doc.summarize
# Browse catalog interactively
pyhall browse
pyhall browse --type cap
pyhall browse --type wrk
pyhall browse --type ctrl
pyhall browse --type prof
# Show version
pyhall version

Browser 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.