Dokumentation

TrueSift SDK für Svelte 5 und SvelteKit

Dieses Dokument gilt ausschließlich für @truesift/svelte, nicht für @truesift/next. Das Svelte-SDK nutzt native Svelte-5-APIs und dasselbe TrueSift-Verifikationsprotokoll und Widget-Design wie das Next.js SDK. Aktuelle veröffentlichte Version: 0.2.0.

Voraussetzungen und Installation

Svelte 5, für die folgenden Beispiele SvelteKit mit Server-Routen und Node.js >=20.19.0 gemäß den Paketanforderungen.

bash
pnpm add @truesift/svelte

Alternativ: npm install @truesift/svelte.

Import-Pfade

ImportVerwendung
@truesift/svelteKomponente, Controller, Client-Typen
@truesift/svelte/clientTrueSiftCheck, createTrueSiftCheck, UI-Hilfen
@truesift/svelte/serverServer-Routen und verifyTrueSiftProof
@truesift/svelte/sharedGemeinsame Protokolltypen
@truesift/svelte/styles.cssOptional; die Komponente importiert ihre Styles bereits selbst
ts
import { TrueSiftCheck, createTrueSiftCheck } from '@truesift/svelte/client';
import { createTrueSiftChallengeHandler, createTrueSiftVerifyHandler, verifyTrueSiftProof } from '@truesift/svelte/server';

Niemals /server in Browser-Komponenten importieren.

Serverseitige Umgebungsvariablen

dotenv
TRUESIFT_API_BASE_URL=https://api.truesift.de
TRUESIFT_SITE_KEY=your_site_key
TRUESIFT_SECRET_KEY=your_secret_key
TRUESIFT_DEFAULT_ACTION=your_registered_action
TRUESIFT_FAIL_OPEN=false
TRUESIFT_TIMEOUT_MS=2500

Die API-URL enthält nur den Ursprung, kein /api/v1. SITE_KEY und SECRET_KEY sind erforderlich. In SvelteKit bleiben die Zugangsdaten in $env/dynamic/private oder einem anderen serverseitigen Konfigurationskanal, *niemals in `PUBLIC_**. Standardmäßig arbeitet das SDK mit **Fail-Closed (false`)**. Der Timeout beträgt standardmäßig 2500 ms (zulässig: 250–30000 ms).

Verifikationsablauf

  1. Der Browser ruft die same-origin-Route /api/truesift/challenge auf.
  2. Die Komponente verwendet /api/truesift/verify und erhält bei erfolgreicher Verifikation einen kurzlebigen verificationToken.
  3. Die geschützte Business-Route prüft den einmal verwendbaren Token über verifyTrueSiftProof, mit vertrauenswürdigen Werten für Action, Path und Origin.
  4. Nur nach erfolgreicher serverseitiger Proof-Prüfung wird die fachliche Aktion ausgeführt. Ein clientseitiges allow ist keine Freigabe.

SvelteKit Integration

1. Challenge-Route

Datei: src/routes/api/truesift/challenge/+server.ts

ts
import { env } from '$env/dynamic/private';
import type { RequestHandler } from './$types';
import { createTrueSiftChallengeHandler } from '@truesift/svelte/server';

const handleChallenge = createTrueSiftChallengeHandler({
  apiBaseUrl: env.TRUESIFT_API_BASE_URL,
  siteKey: env.TRUESIFT_SITE_KEY,
  secretKey: env.TRUESIFT_SECRET_KEY,
  defaultAction: env.TRUESIFT_DEFAULT_ACTION,
  failOpen: false
});

export const POST: RequestHandler = ({ request }) => handleChallenge(request);

2. Verify-Route

Datei: src/routes/api/truesift/verify/+server.ts

ts
import { env } from '$env/dynamic/private';
import type { RequestHandler } from './$types';
import { createTrueSiftVerifyHandler } from '@truesift/svelte/server';

const handleVerify = createTrueSiftVerifyHandler({
  apiBaseUrl: env.TRUESIFT_API_BASE_URL,
  siteKey: env.TRUESIFT_SITE_KEY,
  secretKey: env.TRUESIFT_SECRET_KEY,
  defaultAction: env.TRUESIFT_DEFAULT_ACTION,
  failOpen: false
});

export const POST: RequestHandler = ({ request }) => handleVerify(request);

Beide Endpunkte erwarten JSON-POST-Anfragen und erzwingen Same-Origin-Prüfungen.

3. Vertrauenswürdige Action an die Seite geben

Datei: src/routes/contact/+page.server.ts

ts
import { env } from '$env/dynamic/private';
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = () => {
  const action = env.TRUESIFT_DEFAULT_ACTION?.trim();
  if (!action) error(500, 'TrueSift action is not configured');
  return { truesiftAction: action };
};

Client und Business-Route müssen dieselbe Action, denselben Page-Path und denselben Origin verwenden.

4. Svelte-5-Komponente einbinden

Datei: src/routes/contact/+page.svelte

svelte
<script lang="ts">
  import { TrueSiftCheck } from '@truesift/svelte/client';
  import type { TrueSiftClientVerificationResult } from '@truesift/svelte';
  import type { PageData } from './$types';

  let { data }: { data: PageData } = $props();
  let verificationToken = $state<string | null>(null);
  let submitting = $state(false);
  let message = $state('');

  function handleVerified(result: TrueSiftClientVerificationResult): void {
    verificationToken =
      result.status === 'verified' &&
      result.allowed &&
      result.decision === 'allow'
        ? result.verificationToken ?? null
        : null;
  }

  async function submitContact(): Promise<void> {
    if (!verificationToken || submitting) return;

    const token = verificationToken;
    verificationToken = null; // A verification token cannot be reused.
    submitting = true;
    message = '';

    try {
      const response = await fetch('/api/contact', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          verificationToken: token
          // Include validated contact fields from your form.
        })
      });

      message = response.ok
        ? 'Verification accepted.'
        : 'Verification could not be completed. Please try again.';
    } catch {
      message = 'The request could not be completed. Please verify again.';
    } finally {
      submitting = false;
    }
  }
</script>

<TrueSiftCheck
  action={data.truesiftAction}
  path="/contact"
  visualMode="checkbox"
  size="normal"
  theme="auto"
  locale="auto"
  onVerified={handleVerified}
  onError={() => (verificationToken = null)}
/>

<button type="button" disabled={!verificationToken || submitting} onclick={submitContact}>
  Submit
</button>
<p role="status">{message}</p>

Der deaktivierte Submit-Button ist lediglich eine UI-Sperre. Die Sicherheitsgrenze ist die Business-Route auf dem Server.

5. Proof vor der Geschäftsaktion prüfen

Datei: src/routes/api/contact/+server.ts

ts
import { json } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
import type { RequestHandler } from './$types';
import { verifyTrueSiftProof } from '@truesift/svelte/server';

export const POST: RequestHandler = async ({ request, url }) => {
  if (request.headers.get('origin') !== url.origin) {
    return json({ error: 'Invalid request origin' }, { status: 403 });
  }

  if (!request.headers.get('content-type')?.includes('application/json')) {
    return json({ error: 'Unsupported content type' }, { status: 415 });
  }

  const body: unknown = await request.json().catch(() => null);
  if (typeof body !== 'object' || body === null || Array.isArray(body)) {
    return json({ error: 'Invalid request' }, { status: 400 });
  }

  const { verificationToken } = body as Record<string, unknown>;
  const expectedAction = env.TRUESIFT_DEFAULT_ACTION?.trim();
  if (!expectedAction) {
    return json({ error: 'Verification is unavailable' }, { status: 503 });
  }

  const proof = await verifyTrueSiftProof(
    {
      verificationToken: typeof verificationToken === 'string' ? verificationToken : '',
      expectedAction,
      expectedPath: '/contact',
      expectedOrigin: url.origin
    },
    {
      apiBaseUrl: env.TRUESIFT_API_BASE_URL,
      siteKey: env.TRUESIFT_SITE_KEY,
      secretKey: env.TRUESIFT_SECRET_KEY,
      failOpen: false
    }
  );

  if (!proof.ok || !proof.allowed || proof.failOpen) {
    return json(
      { error: 'Verification rejected' },
      { status: proof.reason === 'unavailable' ? 503 : 403 }
    );
  }

  // The verification gate has passed. Validate and process the
  // contact request here, using your application's business logic.
  return json({ verified: true });
};

Das Beispiel validiert nur die Verifikation und führt noch keine E-Mail-, Formular- oder Datenbank-Aktion aus. Eigentliche Validierung und Verarbeitung sind nach erfolgreicher Proof-Prüfung zu implementieren. expectedAction, expectedPath und expectedOrigin dürfen niemals aus Browser-JSON stammen.

Darstellung: Modi und Größen

EigenschaftVerfügbare Werte
visualModecheckbox, banner, inline, badge
sizenormal, compact, flexible, half
themeauto, light, dark
localeauto, en, de, bg

Nur im Svelte SDK: size="half" begrenzt einen visualMode="banner" auf 500 px und passt sich schmaleren Bildschirmen an. Andere Modi verwenden dadurch kein besonderes Halbbreiten-Layout. Diese Größe fehlt im Next.js SDK v0.3.0.

svelte
<TrueSiftCheck action={data.truesiftAction} visualMode="banner" size="half" theme="auto" locale="de" />

Für einen eigenen Language-Switcher den gewünschten Wert explizit als locale übergeben. Benutzerdefinierte Beschriftungen lassen sich über labels teilweise überschreiben.

Callback und manuelle Steuerung

  • onReady() wird nach dem Mount aufgerufen.
  • onVerified(result) kann auch review, block oder Fail-Open enthalten. Nur status === 'verified', allowed, decision === 'allow' und der Token sind relevante Browserhinweise; die endgültige Entscheidung bleibt serverseitig.
  • onError(error) meldet Konfigurations-, Netzwerk- oder Request-Fehler.
  • Mit autoStart={false}, bind:this, start() und reset() können Sie die Prüfung manuell steuern.
svelte
<script lang="ts">
  import { TrueSiftCheck } from '@truesift/svelte/client';
  import type { TrueSiftCheckActions } from '@truesift/svelte/client';
  import type { PageData } from './$types';

  let { data }: { data: PageData } = $props();
  let check = $state<TrueSiftCheckActions | undefined>();
</script>

<TrueSiftCheck bind:this={check} action={data.truesiftAction} autoStart={false} />
<button type="button" onclick={() => void check?.start()}>Verify</button>
<button type="button" onclick={() => check?.reset()}>Reset</button>

Ein verwendeter Token ist auch nach reset() nicht erneut verwendbar.

Headless Svelte-5-Controller

Ohne die visuelle Komponente stellt createTrueSiftCheck die States state, challenge, verification, error, isLoading sowie activate(), start(), reset() und destroy() bereit.

svelte
<script lang="ts">
  import { onMount } from 'svelte';
  import { createTrueSiftCheck } from '@truesift/svelte/client';
  import type { PageData } from './$types';

  let { data }: { data: PageData } = $props();
  const check = createTrueSiftCheck(() => ({
    action: data.truesiftAction,
    path: '/contact'
  }));

  onMount(() => {
    check.activate();
    return () => check.destroy();
  });
</script>

<button type="button" disabled={check.isLoading} onclick={() => void check.start()}>
  Verify
</button>
<p aria-live="polite">{check.state}</p>

Animation, Datenschutz und Content Security Policy

Der TrueSift-Shield mit dotLottie-WebAssembly ist lokal im Paket enthalten, einschließlich Reduced-Motion-Unterstützung und SVG-Fallback. Ein externes Animations-CDN wird nicht benötigt. Für die lokale WASM-Laufzeit kann die CSP script-src 'self' 'wasm-unsafe-eval' benötigen; fügen Sie nicht pauschal 'unsafe-eval' hinzu.

Behavioral Signals v2

@truesift/svelte@0.2.0 verwendet aggregierte Behavioral Signals v2, abgestimmt auf @truesift/next@0.4.0. Ältere v1-Signale bleiben kompatibel. Der Svelte-5-Controller startet nach der Challenge eine abbrechbare 2.500-ms-Beobachtung (gemessen ab Beginn des Verifikationsversuchs) mit einer eigenen Interaktionsbasis pro Challenge. Reset, Unmount und Abbruch stoppen Wartezeiten und entfernen Event-Listener.

Die temporäre Momentaufnahme kann telemetryVersion: 2, elapsedMs, interactionCount, challengeInteractionCount, pointerMoveCount, pointerDownCount, scrollCount, touchCount, keyPressCount, focusCount, blurCount, visibilityChangeCount, firstInteractionDelayMs und activeDurationMs enthalten. Pointerbewegungen und Scrolls werden alle 75 ms abgetastet; Zähler sind auf 1.000 begrenzt. Ein Null-Interaktionssignal wird erst nach mindestens 1.000 ms sichtbarer Beobachtung ausgegeben. Ein einzelnes Signal beweist weder Mensch noch Bot.

Es werden keine Tasteneingaben, Pointerkoordinaten, Formularwerte, DOM-Ziele, Fingerprints oder dauerhaften Besucherkennungen erfasst. Die aggregierten Werte gelangen nur über die Same-Origin-SvelteKit-Routen zum Server. referrer und currentPageUrl enthalten ausschließlich die Origins, keine Pfade oder Query-Parameter. Der standardmäßige Proof-Pfad enthält nur location.pathname; explizit gesetzte path-Werte bleiben aus Kompatibilitätsgründen unverändert. Keine Zugangsdaten in benutzerdefinierte Pfade aufnehmen.

Absicherung der SvelteKit-BFF-Routen in 0.2.0

  • Challenge und Verify akzeptieren nur JSON-POST, prüfen den Browser-Origin streng, berücksichtigen Same-Origin-Fetch-Metadata und begrenzen den Request-Body auf 32 KiB. Reverse-Proxies müssen den öffentlichen Origin erhalten.
  • Behavioral-Felder sind validiert und begrenzt; durch Clients steuerbare Kontextwerte werden nicht in Upstream-Warnlogs aufgenommen. Rohe Upstream-Fehlermeldungen werden nicht weitergereicht.
  • Entfernte Upstreams erfordern HTTPS; HTTP ist nur für lokales Loopback (localhost, 127.0.0.1, [::1]) zulässig.
  • BFF-Antworten setzen Cache-Control: no-store und X-Content-Type-Options: nosniff.
  • Widersprüchliche Verifikationsentscheidungen und fehlerhafte Proof-Antworten werden abgelehnt; Protokollfehler dürfen kein Fail-Open auslösen.
  • Geschützte Geschäftsaktionen erfordern weiterhin einen serverseitig geprüften und einmalig verbrauchten Proof.

Versionshinweise: 0.2.0 (12.10.2026)

Behavioral Signals v2, abbrechbare Beobachtung, verschärfte Origin- und Größenprüfungen, URL-Datenschutz, robuste Fehlerbehandlung und zusätzliche adversariale Regressionstests. Die öffentliche Svelte-5-Komponenten-API und der Same-Origin-POST/JSON-Ablauf bleiben erhalten.

Proof-Ergebnisse und Sicherheitsregeln

  • ok: true, allowed: true, decision: 'allow', failOpen: false: autoritativer Proof.
  • reason: 'missing' | 'invalid' | 'expired' | 'replayed' | 'mismatch': nicht akzeptieren.
  • decision: 'review' | 'block' und reason: 'blocked': nicht akzeptieren.
  • reason: 'unavailable': bei failOpen: false ablehnen; bei failOpen: true kein autoritativer Proof trotz allowed: true.

Geschützte Aktionen nur bei proof.ok && proof.allowed && !proof.failOpen ausführen. Tokens sind kurzlebig, einmalig nutzbar und stets gegen die erwartete serverseitige Action, Origin und Path zu prüfen. CSRF, Autorisierung, Rate Limiting und die Validierung der Geschäftsaktion bleiben Aufgabe der Anwendung.

Lizenz

Der SDK-Code steht unter der MIT-Lizenz. Die mitgelieferte Animation von Maxence Chanel steht unter der separaten Lottie Simple License, nicht unter der MIT-Lizenz von WebDigiTech.