Zum Hauptinhalt springen
LEITFADEN

Wie man einer Node.js-App den Akteurskontext hinzufügt

Eine Schritt-für-Schritt-Anleitung zum Aufbau von Middleware, die festhält, wer oder was hinter jedem Request steht, damit du echten Traffic untersuchen kannst, bevor du etwas durchsetzt.

Toshendra Sharma

Von Toshendra SharmaGründer & CEO, AgentCredia · Updated November 13, 2026

Anfänger · 9 Min. Lesezeit

Warum mit Akteurskontext statt mit Regeln beginnen?

Die meisten Teams, die AI-Agenten im Traffic bemerken, greifen zuerst zu einer Regel. Einen User-Agent blockieren, eine Challenge hinzufügen, ein Rate Limit verschärfen. Das Problem ist, dass eine Regel, die geschrieben wird, bevor man den Traffic versteht, dazu neigt, die falschen Leute zu treffen. Legitime Assistenten werden blockiert, echte Nutzer werden herausgefordert, und die Automatisierung, die dich eigentlich beunruhigt hat, kommt weiter durch, weil sie sich von Anfang an nie zu erkennen gegeben hat.

Akteurskontext ist der Schritt vor den Regeln. Für jeden Request zeichnest du auf, was deine Anwendung darüber beobachten kann, wer oder was handelt: eine interaktive Person, eine Person, die mit einem AI-Assistenten arbeitet, ein autonomer Agent oder etwas, das du noch nicht unterscheiden kannst. Du zeichnest die Belege hinter dieser Einschätzung auf und hältst sie neben dem Ergebnis des Requests fest. Nach einer oder zwei Wochen davon hast du etwas, das dir keine Regelvorlage geben kann: eine Karte deines eigenen Traffics. Wenn du noch entscheidest, ob überhaupt Agenten deine Website erreichen, beginne mit wie man erkennt, ob Website-Traffic von AI-Agenten kommt.

Diese Anleitung baut diese Schicht in einer Node.js-Anwendung mit einfacher Middleware auf. Sie funktioniert mit Express, und dieselben Ideen lassen sich auf Fastify, Koa, Hono oder einen Next.js-Route-Handler übertragen.

Was du bauen wirst

Am Ende wirst du haben:

  1. Eine Middleware, die Akteursbelege aus jedem Request extrahiert.
  2. Eine kleine, explizite Klassifizierung mit einer angegebenen Grundlage für jedes Ergebnis.
  3. Strukturierte Beobachtungs-Logs, die nie verändern, wie der Request behandelt wird.
  4. Eine Möglichkeit, das Ergebnis an den Request zu hängen, damit spätere Handler es lesen können.

Nichts davon blockiert einen Request. Das ist Absicht.

Schritt 1: Welche Belege kann dein Server tatsächlich sehen?

Ein Server sieht weniger, als viele annehmen. Bevor du Code schreibst, liste die Signale auf, die an deinem Rand verfügbar sind:

  • Request-Signaturen. Manche automatisierten Clients signieren Requests mit HTTP Message Signatures (RFC 9421), senden Signature-Input- und Signature-Header und oft zusätzlich einen Signature-Agent-Header, der auf die veröffentlichten Schlüssel des Betreibers verweist, wie im IETF-Entwurf des Web Bot Auth-Protokolls beschrieben.
  • Deklarierte Identität. Clients können sich im User-Agent-Header, in einem From-Header oder über Protokollmetadaten wie einen MCP-Clientnamen benennen. MCP-Clients melden einen selbst gewählten clientInfo-Namen im initialize request der Revision 2025-11-25, und die Revision 2026-07-28 führt die Client-Identität als per-request metadata mit. Das sind Deklarationen, und jeder kann sie kopieren.
  • Authentifizierungskontext. Ob der Request ein Session-Cookie, ein Bearer-Token oder einen API-Schlüssel trägt, und um welche Art von Anmeldeinformation es sich handelt.
  • Protokollform. Accept-Header, ob der Client HTML oder JSON anfordert, ob er Seitenressourcen lädt und wie er Weiterleitungen und Cookies behandelt.
  • Anwendungsdeklarationen. Dein eigenes Produkt kann Dinge kennen, die das Netzwerk nicht sieht, etwa wenn ein Nutzer eine Option wie "AI assisted" auswählt oder ein Request aus deiner eingebauten Assistentenfunktion kommt.

Jedes Signal hat eine andere Stärke. Eine validierte Signatur ist überprüfbar. Ein User-Agent-String ist eine Behauptung. Ein fehlendes Cookie ist einfach fehlend. Diese Trennung beizubehalten ist der eigentliche Punkt der Übung.

Schritt 2: Das Ergebnis explizit modellieren

Definiere die Form eines Akteursresultats, bevor du die Erkennungslogik schreibst. Ein kleines, typisiertes Modell sorgt dafür, dass allen klar bleibt, was eine Klassifizierung bedeutet. Die vier hier verwendeten Klassen werden in Human, AI-assisted oder Agent: eine praktische Taxonomie für Application-Traffic erklärt.

TS
export type ActorClass = "human" | "human_ai" | "agent" | "unknown";
export type EvidenceBasis = "verified" | "declared" | "inferred" | "unknown";

export interface ActorEvidence {
  signaturePresent: boolean;
  signatureAgent?: string;
  declaredClient?: string;
  credentialType: "session" | "api_key" | "bearer" | "none";
  acceptsHtml: boolean;
  appDeclaredAssist: boolean;
}

export interface ActorResult {
  actor: ActorClass;
  basis: EvidenceBasis;
  reasons: string[];
  evidence: ActorEvidence;
  observedAt: string;
}

Zwei Felder sind am wichtigsten. actor ist die Klasse. basis sagt, warum du sie für plausibel hältst. Ein Agentenergebnis, das auf einer validierten Signatur beruht, und ein Agentenergebnis, das auf einem User-Agent-String beruht, sind beide "agent", verdienen später aber sehr unterschiedliche Behandlung. Deshalb darfst du sie nie zusammenwerfen.

Schritt 3: Belege in Middleware extrahieren

Der Extraktionsschritt liest Header und Request-Zustand. Er entscheidet noch nichts.

TS
import type { Request, Response, NextFunction } from "express";

const KNOWN_AGENT_TOKENS = ["bot", "crawler", "spider", "agent", "gpt", "claude"];

export function extractEvidence(req: Request): ActorEvidence {
  const ua = (req.get("user-agent") || "").toLowerCase();
  const auth = req.get("authorization") || "";
  const hasSession = Boolean(req.cookies?.session);

  let credentialType: ActorEvidence["credentialType"] = "none";
  if (hasSession) credentialType = "session";
  else if (auth.toLowerCase().startsWith("bearer ")) credentialType = "bearer";
  else if (req.get("x-api-key")) credentialType = "api_key";

  const declaredToken = KNOWN_AGENT_TOKENS.find((t) => ua.includes(t));

  return {
    signaturePresent: Boolean(req.get("signature") && req.get("signature-input")),
    signatureAgent: req.get("signature-agent") || undefined,
    declaredClient: declaredToken ? ua.slice(0, 200) : undefined,
    credentialType,
    acceptsHtml: (req.get("accept") || "").includes("text/html"),
    appDeclaredAssist: req.get("x-app-assist") === "1",
  };
}

Ein paar Hinweise zu diesem Code:

  • signaturePresent erfasst nur, dass Signatur-Header vorhanden sind. Die Validierung ist ein separater Schritt, der in der Anleitung zum Prüfen signierter Agent-Requests behandelt wird.
  • Die User-Agent-Tokenliste ist absichtlich einfach gehalten. Sie erzeugt eine Deklaration, niemals eine Verifikation.
  • x-app-assist steht für das, was dein eigenes Frontend sendet, wenn ein Nutzer über deine eingebaute AI-Funktion arbeitet. Ersetze es durch deinen echten Mechanismus.

Schritt 4: Mit angegebener Grundlage klassifizieren

Wandle Belege jetzt in ein Ergebnis um. Halte die Regeln kurz, ordne sie von den stärksten Belegen zu den schwächsten und falle immer auf Unknown zurück.

TS
export async function classify(
  evidence: ActorEvidence,
  verifySignature: () => Promise<boolean>,
): Promise<ActorResult> {
  const reasons: string[] = [];
  const observedAt = new Date().toISOString();

  if (evidence.signaturePresent && (await verifySignature())) {
    reasons.push("request signature validated");
    return { actor: "agent", basis: "verified", reasons, evidence, observedAt };
  }

  if (evidence.appDeclaredAssist) {
    reasons.push("application reported its built-in assistant");
    return { actor: "human_ai", basis: "declared", reasons, evidence, observedAt };
  }

  if (evidence.declaredClient) {
    reasons.push("client declared itself as automated");
    return { actor: "agent", basis: "declared", reasons, evidence, observedAt };
  }

  if (evidence.credentialType === "session" && evidence.acceptsHtml) {
    reasons.push("interactive browser session");
    return { actor: "human", basis: "inferred", reasons, evidence, observedAt };
  }

  reasons.push("insufficient evidence");
  return { actor: "unknown", basis: "unknown", reasons, evidence, observedAt };
}

Beachte, was das nicht tut. Es stuft eine Deklaration nie zu einer Verifikation hoch. Es weist einem Request ohne interaktives Signal nie Human zu. Wenn es nichts sicher sagen kann, sagt es Unknown. Unknown ist eine gültige, nützliche Antwort und zeigt dir genau, wo deine Sichtbarkeit endet. Den vollständigen Fall machen wir in warum Unknown ein gültiges Ergebnis für AI-Traffic ist auf.

Schritt 5: Im Beobachtungsmodus einbinden

Binde die Middleware früh ein, hänge das Ergebnis an den Request und protokolliere es. Verzweige nicht danach.

TS
import express from "express";
import cookieParser from "cookie-parser";

const app = express();
app.use(cookieParser());

app.use(async (req, res, next) => {
  try {
    const evidence = extractEvidence(req);
    const result = await classify(evidence, async () => false);
    res.locals.actor = result;

    res.on("finish", () => {
      logActor({
        route: req.route?.path ?? req.path,
        method: req.method,
        status: res.statusCode,
        actor: result.actor,
        basis: result.basis,
        reasons: result.reasons,
      });
    });
  } catch {
    res.locals.actor = undefined;
  }
  next();
});

Drei Details machen das sicher:

  1. Der try-Block stellt sicher, dass ein Fehler in der Erkennung nie die Antwort beeinflusst. Beobachtung muss offen fehlschlagen.
  2. Das Logging passiert auf finish, sodass du den echten Statuscode erfasst und Klassifizierungen später mit Ergebnissen vergleichen kannst.
  3. verifySignature ist bis zur Implementierung echter Validierung auf false gesetzt. So bleiben signierte Requests in den Buckets declared oder unknown, statt sie zu hoch einzustufen.

Schritt 6: Für Analyse loggen, nicht nur zum Debuggen

Schreibe Logs, die du aggregieren kannst. Eine strukturierte Zeile pro Request reicht aus.

TS
function logActor(entry: Record<string, unknown>) {
  process.stdout.write(JSON.stringify({ type: "actor_observation", ...entry }) + "\n");
}

Halte personenbezogene Daten aus diesen Logs heraus. Du brauchst die Route, die Klasse, die Grundlage, die Gründe und das Ergebnis. Du brauchst keine vollständigen IP-Adressen, keine rohen Cookies und keine Request-Bodies, um dein Traffic-Muster zu verstehen. Wenn du für eine Zuordnung irgendeine Kennung behältst, hashe sie mit einem rotierenden Salt und setze eine kurze Aufbewahrungsfrist.

Schritt 7: Lies die Daten, bevor du handelst

Laufe lange genug im Beobachtungsmodus, um normale Wochenmuster zu sehen. Beantworte dann ein paar Fragen:

  • Welche Routen ziehen Agenten an? Dokumentations-, Katalog- und Suchendpunkte tun das meist. Account- und Checkout-Flows sollten es meist nicht.
  • Welcher Anteil der Agentenergebnisse ist verifiziert gegenüber deklariert? Dieses Verhältnis zeigt dir, wie stark du dich beim Schreiben von Richtlinien auf Identität stützen kannst.
  • Wo konzentriert sich Unknown? Eine hohe Unknown-Rate auf einer Route bedeutet oft, dass dir dort ein Signal fehlt, zum Beispiel eine Seite, auf der nie dein Frontend-Code läuft.
  • Unterscheiden sich die Ergebnisse nach Klasse? Vergleiche Fehlerraten, Conversion-Schritte oder Abuse-Meldungen zwischen den Klassen. Von hier kommen echte Richtlinienideen.

Die Antworten werden dich oft überraschen. Teams stellen regelmäßig fest, dass ein großer Teil ihres "Bot-Problems" in Wirklichkeit legitime Automatisierung ist, die einen strukturierten Endpunkt gern nutzen würde, falls es einen gäbe.

Schritt 8: Das Ergebnis dem Rest deiner App zugänglich machen

Sobald die Daten fließen, lass Handler res.locals.actor für nicht durchsetzende Zwecke lesen: Analytics-Tags, ein Antwortformat auswählen oder festhalten, dass ein Support-Ticket aus einer AI-gestützten Sitzung kam. Diese Verwendungen verbessern die Erfahrung, ohne dass jemand ausgesperrt wird.

TS
app.get("/docs/:page", (req, res) => {
  const actor = res.locals.actor as ActorResult | undefined;
  if (actor?.actor === "agent" && req.accepts("text/markdown")) {
    return res.type("text/markdown").send(renderMarkdown(req.params.page));
  }
  return res.render("doc", { page: req.params.page });
});

Durchsetzung, etwa Challenges, Rate Limits oder Step-up-Prüfungen, kommt später, nachdem du False Positives gemessen hast. Die Anleitung zum schrittweisen Einführen akteursbewusster Richtlinien beschreibt diesen Prozess.

Welche Fehler solltest du vermeiden?

  • Den User-Agent mit Identität verwechseln. Er ist der am leichtesten zu ändernde Header. Erfasse ihn als Deklaration. Sogar AI-Betreiber sagen das: In Anthropics Hinweisen für den gehosteten Browser-Agenten steht, dass jeder denselben User-Agent-Header senden kann, und es wird empfohlen, die veröffentlichten IP-Adressen zu prüfen, bevor man den Traffic als echt behandelt.
  • Standardmäßig Human annehmen. Wenn dein Code immer dann Human sagt, wenn er keine Bot-Signale findet, werden deine Human-Zahlen aufgebläht und deine Richtlinien vertrauen dem falschen Traffic.
  • Klasse und Grundlage vermischen. Speichere immer beides. Ein Dashboard, das nur "Agent" zeigt, verschleiert den Unterschied zwischen einem verifizierten Betreiber und einem kopierten String.
  • Während der Beobachtung blockieren. Selbst eine vorübergehende Sperre verfälscht deine Messungen, weil geblockte Clients ihr Verhalten ändern.

Wo Proof of Actor hineinpasst

Die obige Middleware ist ein solider Ausgangspunkt, und sie zeigt das Kernmodell: zuerst Belege, für jedes Ergebnis eine explizite Grundlage, Unknown sichtbar halten und Entscheidungen deiner Anwendung überlassen. Proof of Actor baut auf demselben Modell auf, erweitert die Signalabdeckung über Server-, Edge-, Browser- und MCP-Kontexte hinweg und liefert Analysen, mit denen du Detect, Understand und Route mit Vertrauen anwenden kannst.

Wenn du dabei Hilfe haben willst, das auf einer produktiven Anwendung weiterzuführen, beantrage frühen Zugang. Um den vollständigen Entscheidungsfluss zu sehen, lies How Proof of Actor works.

Quellen

Mit uns aufbauen.

Werden Sie Teil des Founding-Developer-Programms von Proof of Actor.

Möchten Sie zuerst sprechen? Mit den Gründern sprechen