Skip to page content
Docs
Make Agent Fast documentation

Security and CSP

Protect embedded agents with origin allowlists, browser policies, and credential-safe installation.

At a glance

Protect embedded agents with origin allowlists, browser policies, and credential-safe installation.

Embed install pathLoader on your site talks to the hosted agent runtime
Your websiteHTML / appLoader scriptembed.jsWidget UIChat / voiceAgent

Origin allowlist#

Embedding fails closed unless embedding is enabled and the browser's exact parent origin is allowed. An origin is scheme://hostname[:port]; paths and query strings are not part of it.

Host pageAllowlist value
https://example.com/pricinghttps://example.com
https://www.example.com/helphttps://www.example.com
https://preview.example.com:8443/demohttps://preview.example.com:8443
http://localhost:3000http://localhost:3000

Add production, preview, and local origins separately. Do not add a wildcard to solve an installation error: anyone who copies a public slug could otherwise embed the agent and consume the site's credits.

Content Security Policy#

The loader needs script permission, the widget needs frame permission, and the loader's appearance request may need connection permission. Merge these sources into the host's existing directives; do not replace a mature policy with this minimal example.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://makeagent.fast;
  frame-src https://makeagent.fast;
  connect-src 'self' https://makeagent.fast;

If the site uses script-src-elem, add https://makeagent.fast there too. The external loader does not require unsafe-inline, but the host's own framework may have separate nonce or hash requirements. A report-only policy is useful while testing:

Content-Security-Policy-Report-Only:
  script-src 'self' https://makeagent.fast;
  frame-src https://makeagent.fast;
  connect-src 'self' https://makeagent.fast;

When a browser console reports a CSP violation, change the named directive—not default-src broadly—and retest the public page.

Microphone and browser permissions#

The loader creates the iframe with allow="microphone; autoplay; clipboard-write". A stricter host-level Permissions Policy can still override that permission. If voice is enabled, permit the Make Agent Fast frame origin in the host response:

Permissions-Policy: microphone=(self "https://makeagent.fast")

The visitor must also grant microphone access in the browser. Test from HTTPS; browsers normally restrict microphone access on insecure public origins. Text chat does not require microphone permission.

Message and frame boundary#

The loader accepts only maf:ready, maf:open, maf:close, and theme lifecycle messages associated with the current site slug. Incoming frame messages are checked against https://makeagent.fast. Do not copy the loader into your own bundle or remove this origin validation.

Host-page code should not attempt to reach into the iframe DOM. The frame is cross-origin by design, and the supported integration surface is the loader, its documented data-* attributes and the identity methods below.

Secrets and visitor identity#

The site slug and loader URL are public. Everything else stays out of the page source:

  • Never add a MAF API key, OAuth token, AI-provider key, connector token, or webhook secret to the snippet.
  • Never build a secret into a JavaScript bundle or expose it through NEXT_PUBLIC_*, VITE_*, or similar browser environment variables.
  • Use a server-side API integration when your application must create sites, update agents, or retrieve private workspace data.
  • Do not place email addresses, account IDs, or other visitor data in data-label, data-locale, or query parameters.

Identify signed-in customers#

In Agent → Actions → Signed-in customers, create a secret and copy it when shown. Store it only on your server as MAF_SECRET_KEY; never put it in browser code, public environment variables or a repository. This agent secret is separate from a workspace API key. Rotation invalidates old tokens and changes the signing key for Lookups; removal disables verified customer identity and lookups until a secret is configured again.

Use the updated loader URL https://makeagent.fast/embed.js?v=20261022-open in your existing snippet. Older versioned URLs can remain cached for a year and do not provide this SDK. Keep your origin allowlist and CSP protections.

Your authenticated server should issue the token using the customer from its own session. Never accept an arbitrary customer ID from the browser as the identity to sign. This helper runs only on the server:

import { createHmac } from "node:crypto";

export function signCustomerIdentity(customer) {
  const secret = process.env.MAF_SECRET_KEY;
  if (!secret) throw new Error("MAF_SECRET_KEY is required");
  const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
  const now = Math.floor(Date.now() / 1000);
  const header = encode({ alg: "HS256", typ: "JWT" });
  const payload = encode({
    sub: customer.id,
    email: customer.email,
    name: customer.name,
    iat: now,
    exp: now + 3600,
  });
  const body = `${header}.${payload}`;
  const signature = createHmac("sha256", secret).update(body).digest("base64url");
  return `${body}.${signature}`;
}

Require header alg: "HS256" and typ: "JWT". sub is a stable nonblank string up to 128 characters; exp is an integer Unix timestamp in seconds, in the future and at most 24 hours ahead. Optional iat is an integer; optional email must be valid and at most 160 characters; optional name is at most 120. The complete token must fit 2,048 characters. Omit optional claims you do not need. JWT payloads are signed, not encrypted.

Return only the token to that authenticated browser session over HTTPS, with Cache-Control: no-store. Do not log it, add it to URLs or store it in localStorage. After embed.js has loaded, pass that response token to the widget:

window.MakeAgentFast.identify(token);

For initialization before the loader runs, set window.MakeAgentFastSettings = { identityToken: token } in your application bootstrap instead. The loader retains the latest token in memory and forwards it when the frame is ready. Refresh the token from your authenticated server before expiry and call identify again. Every chat request includes it; conversation requests use x-maf-identity. A verified host identity never relies on an af_xid cookie.

When your application signs out, call this before showing another account's chat, and clear any bootstrap token your application retained:

window.MakeAgentFast.logout();

This resets the widget identity and local conversation cache; it does not sign the customer out of your own application or revoke a copied token. A different customer's sub also clears the widget cache. Invalid, expired or wrong-agent tokens cannot access that signed-in customer's history and fall back to the ordinary visitor path. Use short expirations and keep each agent's secret separate. Owner rehearsal ignores identity tokens; check isolation on the actual embed using two separate test accounts.

Classify the embed according to your privacy and consent policy. If your consent manager blocks nonessential third-party scripts, load embed.js only after the relevant consent. Until then, show a normal link or a disabled placeholder rather than bypassing the manager.

Tell visitors when the experience uses AI, microphone recording, lead capture, analytics, or an external messaging provider as required by your policy and applicable law. Confirm your site's conversation retention and deletion workflow before launch; disconnecting or removing the embed does not by itself delete existing conversations.

Security launch checklist#

  • The customer website is deployed and its embedded agent is enabled and tested.
  • Only exact, intended parent origins are allowlisted.
  • The host CSP permits the script, frame, and appearance request without broad wildcards.
  • Microphone is permitted only if voice is enabled and has been tested over HTTPS.
  • No credential or private visitor value appears in HTML, bundle output, or browser logs.
  • Consent-manager behavior is tested before and after consent.
  • A copied snippet fails on an origin that is not in the allowlist.