Patterns, without recordings.
Optional movement and timing summaries meet server-owned API patterns. Add optional local-font and runtime evidence. No key values, form content or coordinate trails.
What is collected →One account can have a whole cast of characters. Doorman connects verified people, browsers and agents, and adds private activity estimates to the analytics you already use.
Developer preview. Inferred labels are experimental.
A login tells you which account is open. Doorman adds context about the activity behind it: direct human use, an AI assistant, or a conventional script.
Use verified credentials where available. Use private, experimental scores where you need an estimate. Keep unknown activity visible in your reports.
Explore agent classificationOptional movement and timing summaries meet server-owned API patterns. Add optional local-font and runtime evidence. No key values, form content or coordinate trails.
What is collected →Jev scores human, assistant and scripted activity separately from abuse. Connect suspicious actions across likely related sessions, even when IPs rotate. Tune thresholds on confirmed outcomes.
Follow related activity →Send assessed windows and inferred account profiles to PostHog, Mixpanel and your warehouse. Verified identities stay distinct from estimates.
Build an account report →Developer preview. Scores are uncalibrated; agent-brand recognition and reliable headcounts are not established. Read the public-data results →
Doorman gives a browser a random visitor ID and saves a small history of its signals, such as screen size and browser family.
A first-party cookie remembers that ID. If it disappears, Doorman compares the next visit with past observations and restores the ID when there’s enough evidence.
Understand browser matchingChoose a change below and watch the visitor ID. This demo uses made-up browser data; it does not collect yours.
Synthetic signals only.
No browser data is collected.
Starting the local matching engine.
// Run a scenario to inspect the response.Each option starts from the same example history. All except “Return with cookie” remove the cookie first. The scores shown here normally stay on your server; they demonstrate the matching rules, not real-world accuracy. How matching works ↗
Add the client to your app and mount Doorman on your server. The client sends a small observation; the server looks up history and returns a visitor ID.
import { createVisitorClient } from "@aarondovturkel/doorman-browser";
const visitor = createVisitorClient({
endpoint: "/api/visitor",
});
const identity = await visitor.identify();
// { visitorId, isReturning }
// Scores stay on your server.
// Destroy the client when collection should stop.
visitor.destroy();import { createCloudflareVisitor }
from "@aarondovturkel/doorman-adapters/cloudflare";
export default {
async fetch(request, env) {
const visitor = createCloudflareVisitor({
db: env.VISITORS,
ai: env.AI,
});
return visitor.handle(request);
},
};import { createVercelVisitor }
from "@aarondovturkel/doorman-adapters/vercel";
const visitor = createVercelVisitor({
db, // Your Postgres pool.
evaluator: { apiKey: process.env.JEV_API_KEY! },
});
export async function POST(request: Request) {
return visitor.handle(request);
}import { createNodeVisitor }
from "@aarondovturkel/doorman-adapters/node";
const visitor = createNodeVisitor({
db, // Any compatible Postgres pool.
evaluator: { apiKey: process.env.JEV_API_KEY! },
});
// Standard Web Request / Response.
const response = await visitor.handle(request);
// Maintenance runs on your schedule.
await visitor.cleanup();# Native Elixir · your existing Ecto repo
doorman = Doorman.new(
repo: MyApp.Repo,
evaluator: [api_key: System.fetch_env!("JEV_API_KEY")]
)
# Your Phoenix controller
Doorman.handle(conn, doorman)
# Optional server-side profile updates
# Doorman.Identity.identify_user(config, user.id)import { createDoormanClient } from "@aarondovturkel/doorman-browser";
const doorman = createDoormanClient({
endpoint: "/api/visitor",
analytics: { posthog, mixpanel }, // Initialized SDKs.
});
await doorman.identify();
// After your app verifies login:
await doorman.identify(user.id, { email: user.email });
await doorman.update({ plan: "team" });
await doorman.track("Project created");
// On logout:
await doorman.reset();Jev is an AI model from TypeSafe. It helps Doorman choose useful lookup signals, compare a shortlist of browser histories, and assess automation and suspicious signals. The optional operator service asks separate questions about human, assistant and scripted activity.
Use those estimates to inform your own checks, such as when to request a CAPTCHA. Doorman returns information; your app decides what happens next.
Meet Jev and the scoresEnable learning to ask Jev about anonymous visits across devices using earlier login-confirmed sessions. Suggestions stay private until a login confirms the person. Set up learning →
sameVisitorautomationsuspiciousScores are experimental estimates. Missing signals alone do not mean abuse.
Two people can share a laptop. One person can use a laptop and a phone. An AI assistant can act with that person’s permission.
After your app verifies a login or agent credential, Doorman can connect those visits to a known identity. It records who is making a request and whose account they are using.
Learn the few terms you’ll needAn “actor” is the person or agent making a request. Try the examples to see how Doorman checks their permission to use this account.
Example people and credentials.Real permission checks.Runs in your browser.
Enable JavaScript to run the local scenarios.
In an application, your login system verifies these identities. Doorman checks the permission you recorded; it does not guess who is at the keyboard.Build this into your app ↗
Keep your analytics. Let Doorman handle identification. As agents become more of your users, see who is behind an account and tailor the experience to them. Doorman handles identification across anonymous visits, login, profile updates and logout, so your reports get consistent identity context.
Record verified people and agents separately from inferred activity labels. Reports can show which actions have a verified agent credential, which windows look assistant-operated, and where evidence is still missing.
Connect your analyticsAgents can also use your API directly. Add optionalAPI activity middleware to bring those requests into the picture, with private Jev risk assessments.
Illustrative data. Your app supplies verified identities.
Browser data stays in your database. If you enable Jev, a compact set of signals goes to your chosen AI provider. Doorman respects browser privacy restrictions and never captures form values, actual keystrokes or browsing history.
Risk scores stay on your server by default. Doorman does not replace login, prove who is at the keyboard, or automatically block anyone.
See exactly what is collectedRun a local example, inspect browser continuity, then add private activity scores and analytics context as your application needs them.
Get startedExplore the source on GitHub ↗