') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Activation E2E: trusted CAPPO request preparation by reprewindai-dev · Pull Request #57 · reprewindai-dev/cAPI · GitHub
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions .env.example
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
# Covenant (cAPI) configuration. Copy to .env.local and fill in as needed.
# Every value is optional — unset means the related integration stays disabled
# and the runtime falls back to its in-process behavior.
# Unset integrations stay disabled. Production governed execution must configure
# every value in the CAPPO boundary section below; there is no local-execution
# fallback for the browser Activation path.

# --- PGL ledger (gnomledger) forwarding ---
# When PGL_LEDGER_URL is set, every sealed evidence record (Phase 7) is mirrored
# into gnomledger's append-only, hash-chained ledger. Leave empty to keep the
# local seal only.
PGL_LEDGER_URL=
PGL_LEDGER_API_KEY=
PGL_LEDGER_TIMEOUT_MS=8000

# --- Governed execution boundary (CAPPO only) ---
# cAPI resolves MCP/capability identity but never calls a provider directly.
# CAPPO_EXECUTION_URL must target CAPPO's /v1/exec endpoint.
# cAPI prepares and signs the request but never executes the consequence.
# CAPPO_EXECUTION_URL must be the exact externally observed CAPPO /v1/exec URI;
# RFC 9421 signs this exact target, so aliases or a different scheme/host fail.
CAPPO_EXECUTION_URL=

# Dedicated high-entropy secret for /api/internal/cappo/prepare. Configure the
# identical value in the Veklom frontend/server deployment. Do not reuse the
# general COVENANT_ADMIN_TOKEN or expose this value to NEXT_PUBLIC_* variables.
CAPPO_INTERNAL_EXEC_KEY=
# Base64 PKCS#8 Ed25519 private key and its configured key id. Keep both
# deployment-only; CAPPO receives the corresponding public key.

# Base64 DER PKCS#8 Ed25519 private key used for cAPI security-envelope and
# RFC 9421 request signing, plus its key id. CAPPO must receive the corresponding
# Base64 DER SPKI public key (or raw public-key hex) as CAPI_GATEKEEPER_PUBLIC_KEY.
COVENANT_HTTP_SIGNING_PRIVATE_KEY=
COVENANT_HTTP_SIGNING_KEY_ID=
COVENANT_EXEC_TIMEOUT_MS=10000

# --- Runtime registry and mutation controls ---
# Production should load agent/capability/policy identity from a real registry.
# If unset, /api/state reports needs_proof and request execution requires fully
# signed CovenantRequest payloads for already-registered agents.
COVENANT_REGISTRY_URL=
COVENANT_REGISTRY_API_KEY=
COVENANT_REGISTRY_JSON=
COVENANT_REGISTRY_TTL_MS=30000
COVENANT_ADMIN_TOKEN=

# --- Permanent Registry State (Redis) ---
# Configure this only in the deployment environment. Do not commit concrete
# internal service hostnames, addresses, or credentials to source examples.
REDIS_URL=

# Development-only toggles. Leave false/empty in production.
Expand Down
63 changes: 63 additions & 0 deletions src/app/api/internal/cappo/prepare/route.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
import { timingSafeEqual } from "crypto";
import { NextRequest, NextResponse } from "next/server";

import { prepareCappoExecution } from "@/lib/covenant/cappo-preparer";

function safeEqual(left: string, right: string): boolean {
const a = Buffer.from(left);
const b = Buffer.from(right);
return a.length === b.length && timingSafeEqual(a, b);
}

function internalKey(request: NextRequest): string {
const authorization = request.headers.get("authorization") ?? "";
if (authorization.toLowerCase().startsWith("bearer ")) {
return authorization.slice(7).trim();
}
return request.headers.get("x-cappo-internal-key")?.trim() ?? "";
}

export async function POST(request: NextRequest) {
const expected = process.env.CAPPO_INTERNAL_EXEC_KEY?.trim() ?? "";
if (!expected) {
return NextResponse.json(
{ error: "CAPPO_PREPARER_LOCKED", detail: "Internal preparation key is not configured." },
{ status: 503, headers: { "cache-control": "no-store" } },
);
}
const provided = internalKey(request);
if (!provided || !safeEqual(provided, expected)) {
return NextResponse.json(
{ error: "CAPPO_PREPARER_UNAUTHORIZED" },
{ status: 401, headers: { "cache-control": "no-store" } },
);
}

try {
const input = await request.json();
if (!input || typeof input !== "object" || Array.isArray(input)) {
throw new Error("Preparation request must be a JSON object");
}
const record = input as Record<string, unknown>;
const body = record.body;
if (!body || typeof body !== "object" || Array.isArray(body)) {
throw new Error("body must be an object");
}
const prepared = prepareCappoExecution({
body: body as Record<string, unknown>,
executionId: typeof record.executionId === "string" ? record.executionId : "",
workspaceId: typeof record.workspaceId === "string" ? record.workspaceId : "",
actorId: typeof record.actorId === "string" ? record.actorId : "",
});
return NextResponse.json(prepared, {
status: 200,
headers: { "cache-control": "no-store, private" },
});
} catch (error) {
const detail = error instanceof Error ? error.message : "Invalid preparation request";
return NextResponse.json(
{ error: "CAPPO_PREPARATION_REJECTED", detail },
{ status: 400, headers: { "cache-control": "no-store" } },
);
}
}
217 changes: 217 additions & 0 deletions src/lib/covenant/cappo-preparer.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
import { createHash, randomBytes, randomUUID } from "crypto";

import {
canonicalJson,
signCanonicalCapiEnvelope,
signCappoExecutionRequest,
} from "./http-message-signatures";

export interface PrepareCappoExecutionInput {
body: Record<string, unknown>;
executionId: string;
workspaceId: string;
actorId: string;
}

export interface PreparedCappoExecution {
targetUri: string;
body: string;
headers: Record<string, string>;
}

function sha256Hex(value: string): string {
return createHash("sha256").update(value).digest("hex");
}

/** Match Python json.dumps(..., sort_keys=True) for the ASCII identity records. */
function pythonSortedJson(value: unknown): string {
if (Array.isArray(value)) return `[${value.map(pythonSortedJson).join(", ")}]`;
if (value && typeof value === "object") {
const record = value as Record<string, unknown>;
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}: ${pythonSortedJson(record[key])}`).join(", ")}}`;
}
return JSON.stringify(value);
}

function hashPythonSorted(value: unknown): string {
return sha256Hex(pythonSortedJson(value));
}

function safeWimseSegment(value: string): string {
const normalized = value.replace(/[^a-zA-Z0-9.-]/g, "-").replace(/^-+|-+$/g, "");
if (!normalized) throw new Error("WIMSE identity segment is empty after normalization");
return normalized.slice(0, 96);
}

function base64Json(value: unknown): string {
return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
}

function requireConfig(name: string): string {
const value = process.env[name]?.trim() || "";
if (!value) throw new Error(`${name} is required`);
return value;
}

function normalizedExecModel(
body: Record<string, unknown>,
workspaceId: string,
actorId: string,
): Record<string, unknown> {
return {
prompt: typeof body.prompt === "string" ? body.prompt : "",
agent_id: typeof body.agent_id === "string" ? body.agent_id : null,
pgl_id: actorId,
workspace_id: workspaceId,
tenant_id: typeof body.tenant_id === "string" ? body.tenant_id : "default",
delegation_depth: typeof body.delegation_depth === "number" ? body.delegation_depth : 0,
budget_approved_cents: typeof body.budget_approved_cents === "number" ? body.budget_approved_cents : 0,
action_cost_cents: typeof body.action_cost_cents === "number" ? body.action_cost_cents : 0,
scope: body.scope && typeof body.scope === "object" ? body.scope : null,
genome_hash: typeof body.genome_hash === "string" ? body.genome_hash : null,
constitution_hash: typeof body.constitution_hash === "string" ? body.constitution_hash : null,
plan_hash: typeof body.plan_hash === "string" ? body.plan_hash : null,
action: typeof body.action === "string" ? body.action : null,
directive: null,
risk_tier: typeof body.risk_tier === "string" ? body.risk_tier : null,
execution_mode: typeof body.execution_mode === "string" ? body.execution_mode : "live",
};
}

export function prepareCappoExecution(input: PrepareCappoExecutionInput): PreparedCappoExecution {
const targetUri = requireConfig("CAPPO_EXECUTION_URL");
const privateKey = requireConfig("COVENANT_HTTP_SIGNING_PRIVATE_KEY");
const keyId = requireConfig("COVENANT_HTTP_SIGNING_KEY_ID");
if (!targetUri.endsWith("/v1/exec")) {
throw new Error("CAPPO_EXECUTION_URL must terminate at /v1/exec");
}
if (process.env.NODE_ENV === "production" && !targetUri.startsWith("https://")) {
throw new Error("CAPPO_EXECUTION_URL must use HTTPS in production");
}
if (!input.executionId || !input.workspaceId || !input.actorId) {
throw new Error("executionId, workspaceId and actorId are required");
}

const action = typeof input.body.action === "string" ? input.body.action.trim() : "";
if (!action) throw new Error("CAPPO request action is required");
const lease = input.body.capability_lease;
if (!lease || typeof lease !== "object") throw new Error("capability_lease is required");

const unsignedBody: Record<string, unknown> = {
...input.body,
workspace_id: input.workspaceId,
pgl_id: input.actorId,
capability_lease: {
...(lease as Record<string, unknown>),
execution_id: input.executionId,
},
};
delete unsignedBody.directive;
delete unsignedBody.security;

// CAPPO's current ExecRequest ignores the lease transport extension before
// constructing the semantic cAPI envelope. Mirror that typed model exactly;
// the full raw body (including the lease) is independently bound by RFC 9421.
const semanticData = normalizedExecModel(unsignedBody, input.workspaceId, input.actorId);
const nonce = randomBytes(24).toString("base64url");
const securityPayload = {
actor_id: input.actorId,
action,
data_hash: sha256Hex(canonicalJson(semanticData)),
nonce,
};
const security = {
nonce,
signature: signCanonicalCapiEnvelope(securityPayload, privateKey),
};
const finalObject = { ...unsignedBody, security };
const body = JSON.stringify(finalObject);
const bodyHash = sha256Hex(body);

const now = Math.floor(Date.now() / 1000);
const expires = now + 60;
const workload = `wimse://veklom/control-plane/${safeWimseSegment(input.workspaceId)}/execution/${safeWimseSegment(input.executionId)}`;
const confirmation = { method: "capi-http-signature", key_id: keyId };
const candidateActHash = sha256Hex(canonicalJson({
action,
execution_id: input.executionId,
workspace_id: input.workspaceId,
scope: unsignedBody.scope ?? {},
}));

const wit = {
iss: "https://capi.veklom.com",
sub: workload,
aud: "https://cappo.veklom.com",
exp: expires,
iat: now,
jti: randomUUID(),
cnf: confirmation,
trust_domain: "veklom.com",
profile_id: input.actorId,
};
const ect = {
iss: "https://capi.veklom.com",
sub: workload,
aud: "https://cappo.veklom.com",
exp: expires,
iat: now,
jti: randomUUID(),
ephemeral_execution_id: input.executionId,
candidate_act_hash: candidateActHash,
cnf: confirmation,
intent_hash: sha256Hex(canonicalJson(unsignedBody)),
p5_operation_id: input.executionId,
};
const authority = {
authority_id: `authority:${input.executionId}`,
ephemeral_execution_id: input.executionId,
scope_hash: sha256Hex(canonicalJson((unsignedBody.scope as Record<string, unknown>) ?? {})),
policy_decision_hash: sha256Hex(canonicalJson({ decision: "candidate", source: "capi-gatekeeper" })),
candidate_act_hash: candidateActHash,
destination_hash: "target_hash",
rights: [action],
issued_at: now,
expires_at: expires,
proof_of_possession: sha256Hex(canonicalJson({
execution_id: input.executionId,
workspace_id: input.workspaceId,
nonce,
})),
inbound_truth_state: "ADMISSIBLE",
required_truth_state: "ADMISSIBLE",
};
const wpt = {
htm: "POST",
htu: "/v1/exec",
body_hash: bodyHash,
wit_hash: hashPythonSorted(wit),
ect_hash: hashPythonSorted(ect),
authority_hash: hashPythonSorted(authority),
jti: randomUUID(),
cnf: confirmation,
exp: expires,
};

const identityHeaders: Record<string, string> = {
"workload-identity": base64Json(wit),
"execution-context": base64Json(ect),
"workload-proof": base64Json(wpt),
"veklom-authority": base64Json(authority),
"x-veklom-actor": input.actorId,
"x-veklom-nonce": nonce,
};
const signatureHeaders = signCappoExecutionRequest(targetUri, body, privateKey, keyId, {
coveredHeaders: identityHeaders,
});

return {
targetUri,
body,
headers: {
"content-type": "application/json",
...identityHeaders,
...signatureHeaders,
},
};
}
Loading
Loading