Start in Observe on one route. Use the deployment URL and installed package version supplied for your workspace. Historical host examples and source capabilities do not establish a live service or published release.
MANDATE's first production wedge is high-value action protection.
Use this guide for browser-originated routes such as:
- login
- signup
- checkout or payment submit
- invite redemption
- account recovery
- queue admission
- API key creation
- admin or security changes
Keep server-to-server APIs, webhooks, cron jobs, and CLI routes outside this flow unless you have a separate integration path.
Rollout model
- Serve the collector through a first-party origin so MANDATE can issue the HttpOnly
Mndt-Sesscredential. - Put ordinary protected browser routes behind MANDATE gate middleware.
- Pick one high-value route.
- Add hosted validation for that route.
- Put it on login or checkout. Log the decision. Leave the request alone.
- Review Activity and false positives.
- Move that route to enforcement with a rollback path.
Local versus hosted
Production browser routes use the server-issued HttpOnly Mndt-Sess credential.
The browser should not read, store, or forward MANDATE token material. Protected
routes should sit behind the MANDATE gate or a first-party proxy path that
enforces the cookie credential.
Hosted validation is the normal authority for protected browser actions,
including ordinary actions. Local crypto is limited to the explicit DEADMAN
incident tier and bounded legacy compatibility. Use the real hosted adapters
below; never infer authenticated identity from a browser header.
The current deployed writer uses Mndt-Sess; the candidate reader prefers
_mndt and accepts unambiguous legacy input. Select the actual deployed name
explicitly for manual hosted integration and coordinate any writer migration.
These examples use the unpublished kit candidate; see SDK overview.
Route table
| Route | Default posture | Stronger proof |
|---|---|---|
| Login | observe -> enforce | hosted validation |
| Signup | observe | route pressure |
| Checkout submit | enforce candidate | route proof + request proof |
| Queue admission | enforce candidate | one-use queue token |
| Account recovery | enforce candidate | fresh proof |
Framework examples
Next.js App Router
// app/api/checkout/route.ts
import { withMandateHosted } from "@mandate-security/node/next";
export const POST = withMandateHosted(
async (_request, { mandate }) => {
return Response.json({ ok: true, sessionId: mandate.sessionId });
},
{
siteSecret: process.env.MANDATE_SITE_SECRET,
siteKey: process.env.MANDATE_SITE_KEY,
apiKey: process.env.MANDATE_API_KEY,
gateUrl: process.env.MANDATE_GATE_URL,
credentialMode: "cookie",
credentialCookieName: "Mndt-Sess",
highValue: true,
origin: "https://app.example.com",
resolveHostedOptions: (request) => ({
// Resolve accountKey from your authenticated server session.
queueToken: request.headers.get("X-Mandate-Queue-Token") ?? undefined,
}),
},
);Express
import { mandateHosted } from "@mandate-security/node/express";
app.post(
"/api/checkout",
mandateHosted({
siteSecret: process.env.MANDATE_SITE_SECRET,
siteKey: process.env.MANDATE_SITE_KEY,
apiKey: process.env.MANDATE_API_KEY,
gateUrl: process.env.MANDATE_GATE_URL,
credentialMode: "cookie",
credentialCookieName: "Mndt-Sess",
highValue: true,
origin: "https://app.example.com",
resolveHostedOptions: (req) => ({
accountKey: req.user?.id,
queueToken: req.get("X-Mandate-Queue-Token"),
}),
}),
checkoutHandler,
);FastAPI
from mandate.fastapi import hosted_response, validate_mandate_hosted
from mandate.verify import HostedValidateOptions
hosted_options = HostedValidateOptions(
site_secret=os.environ["MANDATE_SITE_SECRET"],
site_key=os.environ.get("MANDATE_SITE_KEY"),
api_key=os.environ["MANDATE_API_KEY"],
gate_url=os.environ["MANDATE_GATE_URL"],
credential_mode="cookie", credential_cookie_name="Mndt-Sess",
high_value=True,
origin="https://app.example.com",
)
@app.post("/api/checkout")
async def checkout(request: Request):
result = await validate_mandate_hosted(request, hosted_options)
if not result.success:
return hosted_response(result)
return {"ok": True, "sessionId": result.session_id}Go net/http
highValue := true
middleware := mandate.HostedMiddleware{
Options: mandate.HostedValidateOptions{
SiteSecret: os.Getenv("MANDATE_SITE_SECRET"),
SiteKey: os.Getenv("MANDATE_SITE_KEY"),
APIKey: os.Getenv("MANDATE_API_KEY"),
GateURL: os.Getenv("MANDATE_GATE_URL"),
CredentialMode: "cookie", CredentialCookieName: "Mndt-Sess",
HighValue: &highValue,
Origin: "https://app.example.com",
},
}
http.Handle("/api/checkout", middleware.Handler(checkoutHandler))Escalation outcomes
Hosted validation can return route-level outcomes when abuse pressure is
configured:
| Outcome | Customer response |
|---|---|
throttle | Return 429 and honor Retry-After. |
fresh_proof | Return 401 and ask the browser to obtain fresh proof. |
queue | Return 202 with opaque queueToken; retry with X-Mandate-Queue-Token. |
step_up | Return the configured customer-owned step-up path. |
block | Return 403 and do not run the protected action. |
The browser receives only the coarse public code. Keep the full hosted decision
server-side for logging and support; do not echo its diagnostic reason.
See examples/queue-wait-room/ for customer-owned wait room
examples for Next.js, Express, FastAPI, Flask, and Go.
Rollback
Every enforcement change needs a rollback path:
- flip that path back to logging-only (
mode: "observe"), - disable hosted validation enforcement for that route,
- keep the gate cookie credential boundary active for ordinary protection,
- review Activity for false-positive impact,
- document the reason for rollback.
Do not publish claims about broad bot-management replacement, no false positives,
or emulator defeat from a single route rollout.
Require established trust on sensitive actions
The first browser request may carry a provisional session while behavior warms
up. Keep browsing routes available, and set requireTrust: "established" on
checkout, login, and account actions. In enforce mode the adapter returns
fresh_proof_required; in observe mode it records trust_below_required.
This follows Cloudflare's guidance not to require JavaScript detection on the
first request.