Eine fehlgeleitete Agent-Sitzung diagnostizieren

CloudflareBeginner
Jetzt üben

Einführung

Eine zustandsbehaftete Anwendung kann fehlerhaft wirken, obwohl ihre Daten intakt sind. Der Browser fordert möglicherweise den falschen benannten Agent an: Statt sich wieder mit SupportRoutingAgent:planning zu verbinden, öffnet er versehentlich SupportRoutingAgent:triage. Diese Namen wählen unterschiedliche SQLite-basierte Durable-Object-Instanzen aus. Daher ist das Ändern oder Löschen des Zustands nicht die richtige erste Reaktion.

In diesem Lab enthält ein bereitgestellter Client für Support-Notizen genau diesen Routing-Fehler. Ein signiertes Token besagt, dass der Benutzer planning verwenden darf, während der Client triage auswählt. Der Server vergleicht die signierte Sitzung mit der tatsächlichen Route und lehnt die Abweichung ab, bevor Zustand übertragen wird. Sie lesen die Hinweise aus drei Ebenen:

  1. Der Browser zeigt die beabsichtigten und ausgewählten Namen an.
  2. Begrenzte Worker-Logs zeigen, welche Route zugelassen oder abgelehnt wurde.
  3. Unabhängige Prüfungen zeigen, dass planning seinen Verlauf weiterhin besitzt und ein anderer benannter Agent leer bleibt.

Anschließend korrigieren Sie den Routenauflöser, verbinden sich erneut mit dem vorgesehenen Agent, fügen eine normale Aktualisierung hinzu und laden die Seite neu. Der ursprüngliche Verlauf muss während des gesamten Vorgangs erhalten bleiben. Das ist eine wichtige Diagnosegewohnheit: Ermitteln Sie die Route, bevor Sie dauerhafte Daten verändern.

Die Anwendung verwendet synthetische Notizen und kein Sprachmodell. Ein Sitzungstoken ist eine kurzlebige, HMAC-signierte Aussage, die die zulässige Sitzung benennt. Es eignet sich zur Demonstration der Routenautorisierung. Eine Produktionsanwendung sollte solche Token jedoch erst nach der Authentifizierung eines echten Benutzers ausstellen und strengere Richtlinien für Schlüsselrotation und Auditing verwenden.

Bevor Sie diesen Kurs direkt beginnen, absolvieren Sie Connect LabEx to Your Cloudflare Account. Jede neue LabEx-VM benötigt eine eigene Wrangler-Autorisierung. Frühere Labs des Kurses behandeln die Agent-Identität und synchronisierten Zustand. Dieses Lab erklärt die relevanten Konzepte jedoch an der jeweiligen Verwendungsstelle erneut.

Autorisieren Sie die VM und benennen Sie einen temporären Worker

In diesem Schritt autorisieren Sie die neue VM, bestätigen das vorgesehene Lernkonto und legen den Namen eines eindeutig benannten temporären Workers fest.

Öffnen Sie ein Terminal und wechseln Sie in das vorbereitete Projekt:

cd /home/labex/project/agent-routing-diagnostics
npx wrangler login --device --browser=false

Wrangler gibt eine URL aus und öffnet eine Autorisierungsseite. Vergewissern Sie sich, dass dort das von Ihnen vorgesehene dedizierte Cloudflare-Lernkonto genannt wird, und genehmigen Sie anschließend die angeforderten Workers-Berechtigungen. Fügen Sie niemals ein Passwort, einen Autorisierungscode oder ein Token in Kursinhalte ein.

Prüfen Sie das strukturierte Ergebnis zur Identität:

npx wrangler whoami --json

Bestätigen Sie, dass loggedIn auf true steht, und identifizieren Sie das dedizierte Lernkonto anhand seines Anzeigenamens. Wählen Sie seine ID aus, ohne sie auszugeben. Erzeugen Sie anschließend einen eindeutigen Namen für den temporären Worker und einen lokalen Signaturschlüssel:

WHOAMI="$(npx wrangler whoami --json)"
printf '%s\n' "$WHOAMI" | jq '{loggedIn, authType, accounts: [.accounts[] | {name}]}'
export LAB_ACCOUNT_ID="$(printf '%s\n' "$WHOAMI" | jq -r '.accounts[] | select(.name == "LabEx Learning") | .id')"
test -n "$LAB_ACCOUNT_ID"
export LAB_WORKER="labex-c11-s08-$(openssl rand -hex 6)"
export SESSION_SIGNING_KEY="$(openssl rand -hex 32)"
printf 'SESSION_SIGNING_KEY=%s\n' "$SESSION_SIGNING_KEY" > .dev.vars

Erstellen Sie die Worker-Konfiguration:

cat > wrangler.jsonc <<JSON
{
  "\$schema": "node_modules/wrangler/config-schema.json",
  "name": "$LAB_WORKER",
  "account_id": "$LAB_ACCOUNT_ID",
  "main": "src/server.ts",
  "compatibility_date": "2026-09-18",
  "compatibility_flags": ["nodejs_compat"],
  "workers_dev": true,
  "preview_urls": false,
  "observability": { "enabled": true },
  "durable_objects": {
    "bindings": [
      { "name": "SupportRoutingAgent", "class_name": "SupportRoutingAgent" }
    ]
  },
  "migrations": [
    { "tag": "v1", "new_sqlite_classes": ["SupportRoutingAgent"] }
  ]
}
JSON

SupportRoutingAgent ist sowohl die Worker-Bindung als auch der exportierte Klassenname. Das SDK ordnet jeden kleingeschriebenen Instanznamen – etwa planning oder triage – einer anderen SQLite-basierten Durable-Object-Instanz zu. Die Migration erstellt den Klassen-Namensraum. Sie erstellt nicht jede benannte Instanz im Voraus.

Falls Ihr dediziertes Lernkonto einen anderen Anzeigenamen verwendet, ersetzen Sie LabEx Learning erst nach der Bestätigung des richtigen Kontos. Lassen Sie den Signaturschlüssel vorerst lokal. Sie laden ihn erst hoch, nachdem der reparierte Worker existiert:

unset SESSION_SIGNING_KEY

Führen Sie die unabhängige Prüfung von Identität und Konfiguration aus:

python3 .labex/verify.py authorization

Erwartetes Ergebnis:

PASS: authorization

Implementieren Sie einen sitzungsgebundenen zustandsbehafteten Agent

In diesem Schritt implementieren Sie den dauerhaften Zustand der Notizen und setzen die signierte Sitzungsgrenze für jede Agent-Route durch.

Erstellen Sie den Token-Prüfer:

cat > src/session-auth.ts <<'TS'
type SessionClaims = { session: string; exp: number };

function decodeBase64Url(value: string): Uint8Array<ArrayBuffer> {
  const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
  const binary = atob(normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "="));
  const bytes = new Uint8Array(new ArrayBuffer(binary.length));
  for (let index = 0; index < binary.length; index++) {
    bytes[index] = binary.charCodeAt(index);
  }
  return bytes;
}

function encodeText(value: string): Uint8Array<ArrayBuffer> {
  const encoded = new TextEncoder().encode(value);
  const bytes = new Uint8Array(new ArrayBuffer(encoded.byteLength));
  bytes.set(encoded);
  return bytes;
}

export async function verifySessionRequest(
  request: Request,
  expectedSession: string,
  secret: string
): Promise<Response | undefined> {
  const rawToken = new URL(request.url).searchParams.get("token");
  if (!rawToken) return new Response("Missing session token", { status: 401 });

  const [payload, signature, extra] = rawToken.split(".");
  if (!payload || !signature || extra) return new Response("Invalid session token", { status: 401 });

  try {
    const key = await crypto.subtle.importKey(
      "raw",
      encodeText(secret),
      { name: "HMAC", hash: "SHA-256" },
      false,
      ["verify"]
    );
    const valid = await crypto.subtle.verify(
      "HMAC",
      key,
      decodeBase64Url(signature),
      encodeText(payload)
    );
    if (!valid) return new Response("Invalid session token", { status: 401 });

    const claims = JSON.parse(new TextDecoder().decode(decodeBase64Url(payload))) as SessionClaims;
    if (claims.session !== expectedSession || claims.exp <= Math.floor(Date.now() / 1000)) {
      return new Response("Session token does not match this Agent", { status: 401 });
    }
    return undefined;
  } catch {
    return new Response("Invalid session token", { status: 401 });
  }
}
TS

Die Signatur beweist, dass der Sitzungsanspruch nicht verändert wurde. Die zweite Prüfung ist ebenso wichtig: claims.session muss dem Namen entsprechen, den die tatsächliche Agent-Route ausgewählt hat. Ein gültiges Token für planning ist daher für triage ungültig.

Erstellen Sie den zustandsbehafteten Server:

cat > src/server.ts <<'TS'
import { Agent, callable, routeAgentRequest } from "agents";
import { verifySessionRequest } from "./session-auth";

type SessionState = {
  notes: string[];
  revision: number;
  lastEvent: "initialized" | "note-added";
};

type Env = {
  SupportRoutingAgent: DurableObjectNamespace<SupportRoutingAgent>;
  SESSION_SIGNING_KEY: string;
};

export class SupportRoutingAgent extends Agent<Env, SessionState> {
  initialState: SessionState = { notes: [], revision: 0, lastEvent: "initialized" };

  @callable()
  addNote(noteInput: string): SessionState {
    const note = noteInput.trim();
    if (note.length < 3 || note.length > 80) {
      throw new Error("A note must contain 3-80 characters.");
    }
    const next: SessionState = {
      notes: [...this.state.notes, note].slice(-6),
      revision: this.state.revision + 1,
      lastEvent: "note-added"
    };
    this.setState(next);
    console.log(JSON.stringify({
      event: "agent_state_changed",
      instance: this.name,
      revision: next.revision,
      noteCount: next.notes.length
    }));
    return next;
  }
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const authorize = async (candidate: Request, route: { name: string }) => {
      const rejection = await verifySessionRequest(candidate, route.name, env.SESSION_SIGNING_KEY);
      console.log(JSON.stringify({
        event: "agent_route_checked",
        requestedSession: route.name,
        outcome: rejection ? "rejected" : "allowed"
      }));
      return rejection;
    };

    return (await routeAgentRequest(request, env, {
      onBeforeConnect: authorize,
      onBeforeRequest: authorize
    })) ?? new Response("Not found", { status: 404 });
  }
} satisfies ExportedHandler<Env>;
TS

cat > tsconfig.json <<'JSON'
{
  "extends": "agents/tsconfig",
  "compilerOptions": {
    "types": ["@cloudflare/workers-types", "node"]
  },
  "include": ["src/**/*.ts", "vite.config.ts", "worker-configuration.d.ts"]
}
JSON

cat > vite.config.ts <<'TS'
import { cloudflare } from "@cloudflare/vite-plugin";
import agents from "agents/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [agents(), cloudflare()]
});
TS

npx wrangler types
python3 .labex/verify.py server

Die Logs enthalten absichtlich nur den Routennamen, die Entscheidung, die Instanz, die Revision und die Anzahl. Das Token und der Text der Notiz werden niemals protokolliert. So bleibt die Diagnosekette nützlich, ohne dass die Beobachtbarkeit zu einem zweiten Datenleck wird.

Reproduzieren Sie das Symptom des falschen Namens sicher

In diesem Schritt führen Sie den bereitgestellten fehlerhaften Client aus und beobachten eine sichere Autorisierungsablehnung, bevor irgendein Zustand übertragen wird.

Erstellen Sie den bereitgestellten Browser-Client:

cat > src/client.ts <<'TS'
import { AgentClient } from "agents/client";
import { resolveAgentName } from "./route";

type SessionState = {
  notes: string[];
  revision: number;
  lastEvent: "initialized" | "note-added";
};

const parameters = new URLSearchParams(location.search);
const session = parameters.get("session") ?? "planning";
const token = parameters.get("token") ?? "";
const selectedName = resolveAgentName(session);

const intended = document.querySelector<HTMLElement>("#intended")!;
const selected = document.querySelector<HTMLElement>("#selected")!;
const status = document.querySelector<HTMLElement>("#status")!;
const revision = document.querySelector<HTMLElement>("#revision")!;
const notes = document.querySelector<HTMLUListElement>("#notes")!;
const form = document.querySelector<HTMLFormElement>("#note-form")!;
const input = document.querySelector<HTMLInputElement>("#note")!;
const button = form.querySelector<HTMLButtonElement>("button")!;
const error = document.querySelector<HTMLElement>("#error")!;

intended.textContent = session;
selected.textContent = selectedName;
button.disabled = true;
let receivedState = false;

function escapeHtml(value: string): string {
  return value.replace(/[&<>]/g, (character) =>
    character === "&" ? "&amp;" : character === "<" ? "&lt;" : "&gt;"
  );
}

function render(state: SessionState) {
  revision.textContent = `Revision ${state.revision}`;
  notes.innerHTML = state.notes.length
    ? state.notes.map((note) => `<li>${escapeHtml(note)}</li>`).join("")
    : '<li class="empty">This named Agent has no notes.</li>';
}

const client = new AgentClient<SessionState>({
  agent: "SupportRoutingAgent",
  name: selectedName,
  host: location.host,
  query: { token },
  onStateUpdate(state) {
    receivedState = true;
    render(state);
    button.disabled = false;
    status.textContent = `Connected to SupportRoutingAgent:${selectedName}`;
    status.className = "status connected";
  }
});

client.ready.catch(() => undefined);
setTimeout(() => {
  if (!receivedState) {
    status.textContent = `Blocked before state delivery: token for ${session} cannot open ${selectedName}`;
    status.className = "status blocked";
  }
}, 1800);

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  error.textContent = "";
  try {
    await client.call("addNote", [input.value]);
    input.value = "";
  } catch (caught) {
    error.textContent = caught instanceof Error ? caught.message : String(caught);
  }
});
TS

Starten Sie die lokale Laufzeit als abgekoppelten Prozess:

CI=true npm run dev > .labex/vite.log 2>&1 < /dev/null &
echo $! > .labex/vite.pid
sleep 8
curl -fsS http://127.0.0.1:5173/ > /dev/null

Erzeugen Sie ein Token für die vorgesehene Sitzung planning und geben Sie eine Browser-URL aus:

TOKEN="$(node scripts/create-session-token.mjs planning)"
printf 'http://localhost:5173/?session=planning&token=%s\n' "$TOKEN"
unset TOKEN

Öffnen Sie die ausgegebene URL in der LabEx-Browservorschau. Die beiden Routenkarten sollten Folgendes anzeigen:

Intended session       planning
Selected Agent name    triage

Nach kurzer Zeit wechselt der Status zu Blocked before state delivery. Der Verlauf bleibt nicht verfügbar. Das ist ein erfolgreiches, sicheres Fehlschlagen: Der Client hat den falschen Agent angefordert, und der Server hat die Anfrage abgelehnt, bevor er den Zustand zurückgab.

Führen Sie die deterministische Symptomprüfung aus:

python3 .labex/verify.py client
python3 .labex/verify.py symptom

Erwartete Ergebnisse:

PASS: client
PASS: symptom

Verfolgen Sie die Route, bevor Sie den Zustand ändern

In diesem Schritt kombinieren Sie Browser- und Serverhinweise, um den Routing-Fehler zu lokalisieren. Anschließend korrigieren Sie ausschließlich den Namensauflöser.

Prüfen Sie den Auflöser, der den ausgewählten Namen bestimmt hat:

sed -n '1,120p' src/route.ts

Die Eingabe wird normalisiert und validiert, aber die letzte Zeile ignoriert sie:

return "triage";

Prüfen Sie jetzt nur die begrenzten lokalen Routing-Ereignisse:

grep 'agent_route_checked' .labex/vite.log | tail -5

Sie sollten ein ähnliches Ereignis sehen:

{"event":"agent_route_checked","requestedSession":"triage","outcome":"rejected"}

Der Browser liefert die erste Hälfte der Diagnose: beabsichtigt war planning, ausgewählt wurde triage. Der Server liefert die zweite Hälfte: triage wurde abgelehnt. Keine der beiden Quellen ist allein so eindeutig wie beide zusammen.

Löschen Sie keine Durable Objects, leeren Sie den Browserspeicher nicht und erzeugen Sie kein Token für triage. Diese Aktionen würden den Fehler verdecken oder die Autorisierungsregel schwächen. Korrigieren Sie die Namensauswahl:

python3 - <<'PY'
from pathlib import Path
path = Path('src/route.ts')
text = path.read_text()
old = '  // Intentional lab defect: every browser is sent to the triage Agent.\n  return "triage";'
new = '  // Route to the validated session requested by this page.\n  return normalized;'
if old not in text:
    raise SystemExit('The expected supplied defect was not found.')
path.write_text(text.replace(old, new))
PY

Vite lädt den Client automatisch neu. Öffnen Sie bei Bedarf dieselbe planning-URL erneut. Beide Routenkarten sollten nun planning anzeigen, der Status sollte grün sein und der Agent sollte seinen aktuellen Zustand übertragen.

Beweisen Sie Wiederherstellung, Wiederverbindung und Isolation

In diesem Schritt weisen Sie nach, dass der Verlauf eine Wiederverbindung übersteht, normale Aktualisierungen weiterhin funktionieren und ein anderer benannter Agent isoliert bleibt.

Die erste erfolgreiche Verbindung mit einer neuen planning-Instanz zeigt die Revision 0. Fügen Sie auf der Seite diese synthetische Notiz hinzu:

Preserve planning history during route repair

Die Revision steigt auf 1. Aktualisieren Sie die Browserseite. Dieselbe Notiz und dieselbe Revision müssen erneut angezeigt werden, weil der reparierte Client denselben benannten Agent auswählt und sein Zustand in SQLite und nicht in der Seite gespeichert wird.

Die reparierte planning-Sitzung bei Revision eins

Der akzeptierte Testlauf oben verwendet einen synthetischen Notiztext und den temporären Namen planning. Ihre Notiz kann anders lauten. Entscheidend ist, dass beide Routenkarten übereinstimmen und Revision 1 sichtbar ist.

Der planning-Verlauf nach der Aktualisierung

Nach der Aktualisierung zeigen die unveränderte Notiz und Revision, dass der Zustand vom benannten Agent und nicht aus dem Browserspeicher zurückgekehrt ist.

Fügen Sie nach der Aktualisierung eine weitere Notiz hinzu:

Confirm normal updates after reconnect

Die Revision steigt auf 2. Damit lassen sich zwei Fragen unterscheiden, die leicht verwechselt werden:

Eine normale Aktualisierung nach der Wiederverbindung erhöht die Revision auf zwei

  • Wiederherstellung: Ist der alte Verlauf nach der Wiederverbindung zurückgekehrt?
  • Funktionsfähigkeit: Kann die reparierte Sitzung weiterhin eine neue normale Aktualisierung annehmen?

Erzeugen Sie eine separat autorisierte URL für einen anderen benannten Agent:

PRIVATE_TOKEN="$(node scripts/create-session-token.mjs private)"
printf 'http://localhost:5173/?session=private&token=%s\n' "$PRIVATE_TOKEN"
unset PRIVATE_TOKEN

Öffnen Sie die URL in einem zweiten Vorschau-Tab. In beiden Routenkarten sollte private stehen, außerdem Revision 0 und keine Notizen. Ein anderer benannter Agent darf den planning-Verlauf nicht erhalten, auch wenn beide Instanzen dieselbe Klasse verwenden.

Ein separat autorisierter privater Agent bleibt leer

Die leere private-Sitzung dient der visuellen Orientierung. Die unabhängige Prüfung unten bleibt maßgeblich, weil sie zusätzlich das Wiederverbindungsverhalten und eine HTTP-401-Ablehnung bei sitzungsübergreifendem Zugriff testet.

Führen Sie die unabhängige Prüfung aus. Sie verwendet neue zufällige Namen, schreibt eine Notiz, schließt die Verbindung und verbindet sich erneut, schreibt eine weitere Notiz, bestätigt, dass eine separate Sitzung leer bleibt, und bestätigt, dass ein sitzungsfremdes Token HTTP 401 erhält:

npm run check
python3 .labex/verify.py repaired

Erwartetes Ergebnis:

PASS: repaired

Stellen Sie die reparierte Route bereit

In diesem Schritt stellen Sie die reparierte Anwendung bereit und wiederholen die Prüfung von Wiederherstellung und Isolation gegenüber Cloudflare.

Erstellen Sie die Anwendung erneut, stellen Sie exakt die reparierte Anwendung bereit und laden Sie anschließend den lokalen Signaturschlüssel als verschlüsseltes Worker-Secret hoch:

npm run check
npm run deploy
npx wrangler secret bulk .dev.vars

Wrangler gibt eine URL aus, die auf .workers.dev endet. Erzeugen Sie ein neues planning-Token und hängen Sie es an diese URL an:

TOKEN="$(node scripts/create-session-token.mjs planning)"
printf 'https://%s.YOUR_WORKERS_SUBDOMAIN.workers.dev/?session=planning&token=%s\n' "$LAB_WORKER" "$TOKEN"
unset TOKEN

Ersetzen Sie YOUR_WORKERS_SUBDOMAIN durch die Subdomain aus der Bereitstellungsausgabe von Wrangler und öffnen Sie die URL. Bestätigen Sie, dass die beabsichtigten und ausgewählten Namen beide planning anzeigen. Fügen Sie anschließend eine synthetische Notiz hinzu und aktualisieren Sie die Seite. Der entfernte Verlauf sollte genau wie der lokale Verlauf zurückkehren.

Die lokalen und entfernten Instanzen verwenden keine gemeinsamen Daten: Der lokale Zustand gehört zur Entwicklungsumgebung, während der bereitgestellte Worker einen Cloudflare-Durable-Object-Namensraum besitzt. Das Verhalten – nicht die genaue Anzahl der Notizen – sollte übereinstimmen.

Führen Sie die unabhängige Prüfung der entfernten Anwendung aus:

python3 .labex/verify.py deployed

Erwartetes Ergebnis:

PASS: deployed

Lesen Sie die Cloudflare-Hinweise und entfernen Sie eigene Ressourcen

In diesem Schritt prüfen Sie begrenzte Routing-Hinweise und entfernen anschließend ausschließlich den Worker und den Agent-Namensraum, die in diesem Durchlauf erstellt wurden.

Öffnen Sie Workers & Pages, wählen Sie den Worker aus, dessen Name mit labex-c11-s08- beginnt, und öffnen Sie Settings → Bindings. Bestätigen Sie, dass SupportRoutingAgent auf die Klasse SupportRoutingAgent verweist. Die Bindung identifiziert den Klassen-Namensraum. Jeder Routenname wählt darin weiterhin eine eigene Instanz aus.

Der bereitgestellte Worker und seine SupportRoutingAgent-Bindung

Der Name des temporären Workers in diesem akzeptierten Durchlauf ist nur ein Beispiel. Verwenden Sie den exakten eindeutigen Namen, der in Ihrer eigenen VM erzeugt wurde.

Öffnen Sie den Bereich Durable Objects des Kontos und suchen Sie den SQLite-Namensraum, der diesem exakten Worker und dieser Klasse gehört. Verwenden Sie nicht die Namespace-ID aus einem Beispiel oder einem anderen Durchlauf.

Der für die Agent-Klasse erstellte SQLite-Durable-Object-Namensraum

Kehren Sie zum Worker zurück und öffnen Sie Observability → Logs. Filtern Sie nach agent_route_checked. Ein hilfreicher Lauf enthält abgelehnte und zugelassene Entscheidungen für verschiedene Diagnoseanfragen. Die Ereignisse sollten Routennamen und Ergebnisse enthalten, niemals Token oder Notiztexte. Leere aktuelle Logs sind nicht eindeutig, da die Verarbeitung verzögert eintreffen kann. Die unabhängigen Live-Prüfungen bleiben maßgeblich.

Ein begrenztes agent_route_checked-Ereignis in den Cloudflare-Logs

Das erweiterte Ereignis eines akzeptierten Laufs zeigt die angeforderte Sitzung und ein zugelassenes Ergebnis, während Cloudflare das Token ausblendet. Logs helfen bei der Erklärung einer Entscheidung. Ob Routing und Isolation funktionieren, entscheidet jedoch weiterhin die Live-Prüfung.

Überprüfen Sie das zugehörige Cloud-Inventar, bevor Sie etwas löschen:

python3 .labex/verify.py observed

Erwartetes Ergebnis:

PASS: observed

Löschen Sie den Agent-Klassen-Namensraum mit einer nur erweiterbaren Migration. Behalten Sie die ursprüngliche v1-Migration bei und fügen Sie v2 hinzu:

python3 - <<'PY'
import json
from pathlib import Path
source = json.loads(Path('wrangler.jsonc').read_text())
source.pop('durable_objects', None)
source['migrations'].append({'tag': 'v2', 'deleted_classes': ['SupportRoutingAgent']})
Path('wrangler.cleanup.jsonc').write_text(json.dumps(source, indent=2) + '\n')
PY
npx wrangler deploy --config wrangler.cleanup.jsonc
npx wrangler delete --config wrangler.cleanup.jsonc --force

Das Löschen ausschließlich des Worker-Skripts beendet die Durable-Object-Klasse nicht ausdrücklich. Die Migration entfernt zunächst den Klassen-Namensraum dieses Labs. Der zweite Befehl entfernt anschließend genau diesen Worker.

Weisen Sie nach, dass beide Ressourcen nicht mehr vorhanden sind, während die Autorisierung weiterhin gültig ist:

python3 .labex/verify.py deleted

Erwartetes Ergebnis:

PASS: deleted

Aktualisieren Sie im Dashboard die Listen der Worker und Durable Objects. Die exakten temporären Namen sollten nicht mehr angezeigt werden. Löschen Sie niemals eine ähnlich benannte Ressource, die Sie in diesem Lab nicht erstellt haben.

Der exakte temporäre Worker wird nicht mehr angezeigt

Aus dem akzeptierten Durchlauf sind keine Durable-Object-Namensräume mehr vorhanden

Diese Screenshots zeigen den akzeptierten temporären Durchlauf nach der Bereinigung. Ihr Konto kann nicht zugehörige Ressourcen enthalten. Die Abwesenheit muss anhand der exakten Namen Ihres Workers und Namensraums geprüft werden. Die schreibgeschützte Prüfung oben ist maßgeblich.

Melden Sie die temporäre VM ab

In diesem Schritt entfernen Sie die gespeicherte Wrangler-Autorisierung der neuen VM, nachdem die Ressourcenbereinigung bestätigt wurde.

Entfernen Sie die gespeicherte Cloudflare-Autorisierung der VM:

npx wrangler logout
npx wrangler whoami --json

Das strukturierte Ergebnis sollte Folgendes enthalten:

{"loggedIn":false}

Führen Sie die abschließende unabhängige Prüfung aus:

python3 .labex/verify.py logout

Erwartetes Ergebnis:

PASS: logout

Das Abmelden der VM löscht keine Cloud-Ressourcen. Deshalb wurde die Löschung zuerst überprüft. Außerdem wird dadurch Ihr gewöhnlicher Browser nicht vom Cloudflare-Dashboard abgemeldet.

Zusammenfassung

Sie haben einen Fehler beim Routing einer zustandsbehafteten Anwendung diagnostiziert, ohne intakte Daten zu löschen. Der Browser zeigte, dass er planning öffnen sollte, aber triage auswählte. Der Server lehnte die Abweichung der signierten Sitzung sicher ab, bevor er den Zustand übertrug. Begrenzte Logs bestätigten die tatsächliche Routenentscheidung. Sie korrigierten den Auflöser so, dass er den validierten vorgesehenen Namen zurückgibt, und wiesen anschließend die Wiederherstellung des dauerhaften Verlaufs, normale Aktualisierungen nach der Wiederverbindung, die Isolation verschiedener Namen und die sitzungsübergreifende Ablehnung lokal und auf Cloudflare nach.

Die zentrale Debugging-Regel lässt sich wiederverwenden: Wenn ein Agent leer oder nicht verfügbar zu sein scheint, vergleichen Sie die vorgesehene Sitzung, den ausgewählten Agent-Namen und die Autorisierungsentscheidung des Servers, bevor Sie den Zustand ändern. Die Identität eines benannten Agents ist Teil der Datengrenze und nicht nur eine Anzeige.