PageCTRLSDK docs · v1.0.0

WebMCP runtime guard

Put a human-controlled boundary around in-page agent tools.

PageCTRL wraps WebMCP tool execution with validation, policies, budgets, approvals, tamper detection, and a redacted flight recorder. It runs entirely inside the page.

01 · Install

Quick start

Today, PageCTRL ships as one dependency-free browser file. Self-host that reviewed file and load it before any script that registers WebMCP tools.

HTML
<script src="/pagecontrol.js"></script>
<script type="module">
  const guard = window.PageControl;

  await guard.init({
    appName: "My Store",
    budget: { limit: 300, currency: "USD" },
    defaultMode: "allow",
    defaultMaxPerMinute: 30,
    tools: {
      add_to_cart: { maxAmount: 200, maxQty: 5 },
      checkout: { mode: "approve", chargesBudget: true },
      delete_account: { mode: "deny" },
    },
  });

  // Register tools through PageControl, then lock the reviewed surface.
  await guard.registerTool(myTool);
  guard.seal();
</script>
Load order matters.

The SDK must see tool registration to wrap it. In Next.js, load the script with beforeInteractive.

No browser key is required.

The script tag installs the in-page guard. Server credentials are needed only when an integration adds signed checkout.

02 · Configure

Keep credentials on the server

The browser SDK needs no secret. Signed checkout adds two server environments: the merchant app and the separate PageCTRL signing service. Create two different random secrets with openssl rand -base64 32.

Merchant app

Add these values to the repository-root .env.local file for local work, or to the merchant app's Vercel environment for deployment.

PAGECONTROL_SERVICE_TOKEN

Used by: the merchant server when requesting a signed grant. Create it: run openssl rand -base64 32, then paste the same output into Vercel and Render.

PAGECONTROL_DASHBOARD_SESSION_SECRET

Used by: the merchant dashboard to sign and encrypt its session cookies. Create it: run the command again and keep this different value only in Vercel.

PAGECONTROL_API_URL

Used by: the merchant server to request grants and download the verification key. Value: https://api.pagecontrol.app.

Merchant app environment
# Merchant app — .env.local or Vercel
PAGECONTROL_API_URL=https://api.pagecontrol.app
PAGECONTROL_SERVICE_TOKEN=PASTE_THE_SHARED_RANDOM_SECRET
PAGECONTROL_DASHBOARD_SESSION_SECRET=PASTE_A_DIFFERENT_RANDOM_SECRET

Signing service

Add these values to the Render service environment. The PAGECONTROL_SERVICE_TOKEN value must match the merchant app exactly. The dashboard session secret does not belong in Render. Generate the Ed25519 key with openssl genpkey -algorithm ED25519 -outform DER | base64 and paste its output directly into Render.

PAGECONTROL_SERVICE_TOKEN

Used by: Render to authenticate the merchant server. Value: exactly the same random token stored in Vercel.

PAGECONTROL_PRIVATE_KEY

Used by: Render to sign each 60-second checkout grant. Storage: Render only—never Vercel, browser code, or Git.

PAGECONTROL_ALLOWED_ORIGINS

Used by: Render to restrict which storefronts may receive grants. Format: comma-separated exact origins with no paths or trailing slashes.

Render service environment
# Signing service — Render
PAGECONTROL_SERVICE_TOKEN=PASTE_THE_SHARED_RANDOM_SECRET
PAGECONTROL_PRIVATE_KEY=PASTE_THE_ED25519_PRIVATE_KEY
PAGECONTROL_ALLOWED_ORIGINS=https://your-store.example
PAGECONTROL_ISSUER=https://api.pagecontrol.app

Public verification key

You do not copy a public key into the script or .env. Render derives it from PAGECONTROL_PRIVATE_KEY and publishes it as a public JWK. The merchant server downloads it automatically from PAGECONTROL_API_URLwhile verifying a grant.

Never expose either secret to browser code.

Do not add NEXT_PUBLIC_ to either name. After changing a deployed variable, redeploy the affected service.

03 · Understand

One guarded execution path

When native WebMCP exists, PageCTRL registers wrapped tools with document.modelContext. It mirrors the active context to navigator.modelContext for older clients.

  1. 1
    Validate

    Reject malformed inputs before merchant code runs.

  2. 2
    Enforce

    Resolve modes, rate limits, quantities, amounts, and reserved budget.

  3. 3
    Ask

    Keep sensitive calls pending until a person allows or blocks them.

  4. 4
    Record

    Redact sensitive strings and append a hash-linked journey entry.

If native WebMCP arrives after page load, PageCTRL watches for 10 seconds and migrates already-guarded tools. Without native support, the same pipeline remains available through its demo shim.

04 · Align

One guard, two beneficiaries

PageCTRL faces both directions. It gives people the confidence to delegate a task, while giving merchants a controlled WebMCP surface instead of unrestricted automation.

For the person

Keep the final say

  • Spending boundary

    The agent cannot exceed the session budget or per-action caps.

  • Approval before impact

    Address changes and checkout can stop for a real decision.

  • Immediate pause

    The kill switch stops new calls and denies pending approvals.

  • Readable evidence

    A redacted journey shows what ran, what was blocked, and why.

For the merchant

Open the store without opening chaos

  • Validated inputs

    Malformed arguments stop before merchant code executes.

  • Operational limits

    Rate, quantity, amount, and budget rules contain runaway agents.

  • Sealed tool surface

    A third-party script cannot silently replace a reviewed tool.

  • Shared accountability

    The flight recorder gives both sides the same call history.

Two tiers prevent a policy tug-of-war.

The merchant sets the minimum protection. The person may make it stricter, but cannot weaken that baseline.

05 · Register

Register a guarded tool

A tool uses the normal WebMCP definition. The optional label and guard fields stay inside PageCTRL and are removed before native registration.

JavaScript
await PageControl.registerTool({
  name: "add_to_cart",
  label: "Add to cart",
  description: "Add one catalog item to the cart.",
  inputSchema: {
    type: "object",
    properties: {
      id: { type: "string", minLength: 1 },
      qty: { type: "integer", minimum: 1 },
    },
    required: ["id", "qty"],
  },
  annotations: { readOnlyHint: false },
  guard: {
    getQty: ({ qty }) => qty,
    getCost: ({ id, qty }) => catalog[id].price * qty,
  },
  execute: async ({ id, qty }, { signal }) => {
    return JSON.stringify(await cart.add(id, qty, { signal }));
  },
});
getQty(inputs)

Returns the quantity checked against maxQty.

getCost(inputs)

Returns the amount checked against caps and session budget.

annotations

Pass through to native WebMCP unchanged.

signal

Registration and execution cancellation propagate safely.

PageCTRL audits the browser's real tool surface.

It calls document.modelContext.getTools() after setup and whenever the native toolchange event fires. A browser-reported tool that PageCTRL did not wrap is shown as unguarded.

06 · Control

Layer merchant and user policy

The merchant sets the minimum protection. A user can make it stricter immediately. Moving back toward the merchant setting requires explicit confirmation.

ModeBehavior
allowRun after validation and limits pass.
approveWait for a human decision.
denyReturn a plain-language refusal.
User policy
// Tightening a rule applies immediately.
PageControl.setUserPolicy("checkout", { mode: "deny" });

// Reducing a user-added restriction requires an explicit human confirmation.
PageControl.setUserPolicy(
  "checkout",
  { mode: "approve" },
  { humanConfirmed: true },
);

07 · Decide

Use the built-in human approval UI

PageCTRL renders its own keyboard-accessible approval dialog with Run once and Block actions. Unanswered requests deny themselves after 60 seconds. Pausing the guard denies every pending request.

The dialog needs no merchant UI code. Public events contain an opaque display handle, never the internal approval id. After seal(), only a browser-trusted click in PageCTRL's controls can resolve the request.

JavaScript
const stop = PageControl.on("approval", ({ pending }) => {
  // Handles are display-only. They cannot settle an approval.
  renderPendingApprovals(pending);
});

// Unsubscribe when your UI unmounts.
stop();

08 · Reference

Client API

init(config)

Set the merchant policy, session budget, default mode, and rate limit.

registerTool(tool, options?)

Register a WebMCP tool through the guarded execution pipeline.

seal()

Lock the reviewed tool surface and flag later replacements or additions.

on(event, callback)

Subscribe to entries, tools, budget, approvals, alerts, state, or environment.

approve(id) / deny(id)

Setup-only helpers. Both refuse after the reviewed surface is sealed.

pause() / resume()

Stop every agent call immediately, then restore guarded execution.

setUserPolicy(name, rule, options?)

Change a user rule without going below the merchant policy.

setBudget(limit, options?)

Setup-only helper. The trusted panel owns budget changes after seal.

getPolicies()

Read merchant, user, and effective policy maps.

getJourney() / exportJourney()

Read or download the redacted, hash-chained flight record.

getEnvironment()

Return native WebMCP or shim mode and the active API surface.

canInterceptNativeRegistration()

Check whether direct modelContext.registerTool calls enter PageCTRL on this host.

getSurface()

Compare browser-reported WebMCP tools with the tools PageCTRL wrapped.

explainLast()

Return the plain-language reason for the most recent blocked call.

09 · Observe

Events

PageControl.on(event, callback) returns an unsubscribe function.

entry

A new guarded call or system event was added to the journey.

approval

The pending human-approval list changed.

alert

Tampering, late registration, migration trouble, or suspicious output was detected.

tools

The registered tool surface or a tool's tamper status changed.

budget

Reserved or spent session budget changed.

state

The kill switch paused or resumed execution.

environment

PageCTRL entered native WebMCP or fallback shim mode.

surface

The guarded and unguarded browser-reported tool lists changed.

10 · Scope

Protect the action layer, not the whole internet

PageCTRL sees structured WebMCP calls inside the page. It does not sit on the network path, so it cannot see ordinary crawlers, DDoS traffic, or direct server attacks.

RiskPageCTRLRight layer
Malformed WebMCP inputsProtectsPageCTRL validation
Runaway calls or oversized ordersProtectsPageCTRL policies
A script replacing a sealed toolProtectsPageCTRL tamper guard
Scraping and crawlingOutside scopeEdge and bot controls
DDoS or server exploitationOutside scopeNetwork and application security
A malicious page ownerOutside scopeBrowser or extension verification
The layers complement each other.

Edge security decides which automated traffic reaches a site. PageCTRL decides what an admitted agent may do through WebMCP.

11 · Verify

Trust and privacy boundary

Zero SDK network calls

The browser SDK calls no PageCTRL backend and sends no telemetry.

Tab-scoped journey

The redacted record survives a reload and disappears with the tab unless the user exports it.

Redacted before logging

Email addresses and long card-number-like strings are masked first.

Honest boundary

An in-page guard cannot protect a user from the page owner itself.

Production distribution

This repository does not claim a live PageCTRL CDN. For production distribution, publish an immutable versioned asset and provide its real Subresource Integrity hash. The browser then rejects any changed file, including one served by a compromised CDN.

Pattern — replace every placeholder
<script
  src="https://your-cdn.example/pagecontrol/v1.0.0/pagecontrol.js"
  integrity="sha384-YOUR_RELEASE_HASH"
  crossorigin="anonymous">
</script>

HTTPS authenticates the named origin. Subresource Integrity verifies the exact release bytes. Neither stops a malicious merchant from omitting the SDK or drawing a lookalike interface.

The approval component today

The SDK creates the approval dialog, styles it, traps keyboard focus, runs the countdown, and wires the decision. The merchant writes no approval component. Today that dialog still lives in the merchant page, so same-page scripts can inspect or imitate it.

The remaining limit

A malicious site can omit the SDK or imitate its interface. Independent verification requires the browser or a companion extension.

12 · Extend

Roadmap: move trust outside the host page

These are deliberate next layers, not claims about the current release. Each one reduces merchant integration work or moves a trust decision into a stronger browser boundary.

  1. 1
    Versioned distribution

    Publish immutable releases from a PageCTRL origin with a real SRI hash and CORS headers.

  2. 2
    Placement Web Component

    Add an <page-control-panel> custom element for layout only. The boot script must still load first so no tool registration escapes the guard.

  3. 3
    Cross-origin approval frame

    Serve the sensitive decision UI from a PageCTRL origin. The browser same-origin policy then prevents the host page from reading its internal DOM.

  4. 4
    Independent verification

    A companion extension can warn when a site claims protection without loading the genuine release.

  5. 5
    Browser-owned prompt

    Long term, the browser should own the unforgeable approval surface while PageCTRL supplies policy decisions.

Roadmap sketch — not implemented
<!-- Roadmap sketch — not available in v1.0.0. -->
<script
  src="https://your-cdn.example/pagecontrol/v1.0.0/pagecontrol.js"
  data-budget="300"
  data-currency="USD">
</script>

<page-control-panel></page-control-panel>

Architecture references