Install
npm install @akhara/pep
Construct the PEP
import { PolicyEnforcementPoint } from "@akhara/pep";
const pep = new PolicyEnforcementPoint({
baseUrl: process.env.AKHARA_URL ?? "https://api.akhara.dev",
agentId: "health-ai",
apiKey: process.env.AKHARA_API_KEY,
session: sessionId, // correlates decisions in the evidence feed
});
Methods
Each method targets one enforcement stage and resolves to aPolicyDecision.
pep.checkInput(content: string): Promise<PolicyDecision>
pep.checkContextEgress(content: string): Promise<PolicyDecision>
pep.checkOutput(content: string): Promise<PolicyDecision>
pep.checkDelivery(content: string): Promise<PolicyDecision>
pep.authorizeAction(tool: string, args: Record<string, unknown>): Promise<PolicyDecision>
authorizeAction sends simulated: true by default in demo builds; pass real
tool args (e.g. { medication: "atorvastatin" }): the PDP normalizes the tool
name before matching latches.
End-to-end example
export async function handleTurn(userText: string, patientCtx: string) {
// 1. input gate
const input = await pep.checkInput(userText);
if (!input.mayContinue) return refusal(input);
// 2. context egress: PHI minimization
const egress = await pep.checkContextEgress(patientCtx);
const modelInput = egress.transformedContent ?? patientCtx;
const draft = await llm.complete(modelInput, userText);
// 3. output gate
const output = await pep.checkOutput(draft);
if (!output.mayContinue) return escalateOrDrop(output);
// 4. delivery gate
const finalText = output.transformedContent ?? draft;
const delivery = await pep.checkDelivery(finalText);
if (!delivery.mayContinue) return escalateOrDrop(delivery);
return { text: finalText };
}
Permit-gated action
async function renew(medication: string) {
const permit = await pep.authorizeAction("renew_meds", { medication });
if (permit.verdict !== "ALLOW") {
return permit.verdict === "ESCALATE"
? queueForClinician(permit)
: refusal(permit);
}
// permitId is required by the service; a blocked action can't leak through
return renewalService.submit(medication, permit.permitId!);
}
Implement it yourself
The client is thin enough to reproduce overfetch. Preserve the fail-closed
default:
async function authorize(stage: string, body: object): Promise<PolicyDecision> {
try {
const res = await fetch(`${baseUrl}/api/policy/authorize`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ agentId, session, stage, ...body }),
signal: AbortSignal.timeout(8000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const d = await res.json();
if (!["ALLOW", "WARN", "BLOCK", "ESCALATE"].includes(d.verdict)) {
throw new Error(`unknown verdict ${d.verdict}`);
}
return { ...d, mayContinue: d.verdict === "ALLOW" || d.verdict === "WARN" };
} catch (err) {
// fail closed
return {
verdict: "BLOCK", stage,
policyId: "reliability-4", rule: "Fail-Closed Defaults",
reason: `Akhara policy authorization is unavailable: ${String(err)}`,
mayContinue: false,
};
}
}
Never map an unrecognized verdict to “continue”. Default to
BLOCK on any
ambiguity. See Fail-closed.