Back to "StyloBot Release Series - Die Sidecar-Architektur"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Architecture ASP.NET Core Go gRPC StyloBot

StyloBot Release Series - Die Sidecar-Architektur

Monday, 01 June 2026

StyloBot-'-Detektions-Engine ist ASP..-NET Core-MSC2-Dieser Beitrag erklärt, wie dieser Motor mit Go Gateways verbunden ist.,-Node-.-js-Anwendungen. ,- und jeder andere Stapel über einen gRPC Sidecar.MSC6- ein typisiertes Go SDKK7- und ein Caddy-Plugin, ohne dass die Verbraucher etwas über die internen Systeme von .-NET wissen müssen.

StyloBot

Das github.com/scottgal/stylobot-go SDK-,- das github.com/scottgal/caddy-stylobot plugin-,- und die Mostlylucid.BotDetection.Sidecar Container wird in Kürze veröffentlicht Alles unten beschreibt die Oberfläche, die sie aussetzen werden

StyloBot Release Series

  1. Verhalten nicht IdentitätWarum StyloBot die Kunden verhaltensmäßig modelliert
  2. Verhalten Bewusst WERDENDie Server-Oberfläche von -, die für Anwendungen von . oder NET bestimmt ist
  3. Ungebundenes Wachstum in langen --Diensten finden und behebenDie Zuverlässigkeitsdisziplin, die den Motor in der Produktion langweilig hält
  4. Verhalten--Bewusstsein TypScript UIKomponenten von , und Browsern
  5. Die Sidecar-ArchitekturDieser Artikel
  6. Schneller lernenDas adaptive Lernsystem (MSK) und der Urteilscache
  7. Testen der Sache, die Won-' sitt StillEine BDF-Datei treibt Regression an.
  8. StyloExtrahieren Sie einen lokalen HTML zu Markdown KonverterDie HTML-Markdown-Schicht, die sich mit dem Detektor verbindet, hat den Walker Bug lucidVIEW gefangen und die Hundefutterschleife, die es ehrlich machte.

Warum ein Beiwagen?

Ein umgekehrter Proxy ist der offensichtliche Ort, um Bot-Detektion zu starten.:. Er sitzt vor allem und sieht jede Anfrage und kann blocken bevor jeder Anwendungscode läuft.MSC3. Diese Logik hält richtig, bis Sie fragen, was Ihre Anwendung sollte anders machen based on who is make the request- - ein Gateway, das direkt blockiert ist ein gate- -;-, was die meisten Anwendungen tatsächlich brauchen, ist ein Urteil können sie auf mehrere Weisen sofort handeln - : drosseln die API-,-Personalisierung der UI-MSC4-exclude Traffic aus analytics - ,- fügen Sie einen Reibungsschritt bei Checkout-.-Ein Gate kann nichts von dem tun - . - Ein Urteil Pipeline can -MSC8-

Das Sidecar-Muster trennt die beiden Belange.Das Gateway bleibt schnell und staatenlos.Das Sidecar hält den Sitzungszustand aufrecht., archetype anker, , drift state,, quitting cache, das die Detektion präzise macht. Schneller lernenDie beiden kommunizieren über das lokale Netzwerk .. . .(samer Host oder gleiches Pod... so ist der runde -" Trip Mikrosekunden zu einzelnen -"stelligen Millisekunden " . ," nicht die . 50-200"ms eines entfernten API-Aufrufs " .. Das Latenzbudget ist, was per... -"Erkennung praktisch macht .

Dies ist kein neuartiges Muster. Gesandter Proxy tut genau dies für den Dienst ,- ,mesh betrifft , ( , mTLS , , , Retries , ., , Stromkreisbruch , Dapr tut es für Zustand und pub-/-sub OpenTelemetrie Sammler does it for telemetry- - .-Linkerd-,-Consul Connect- und AWS App Mesh folgen alle dem gleichen Modell. Das Muster erscheint immer, weil es ein echtes Problem löst.: - Sie wollen komplexe stateful Verhalten, das Sprachgrenzen überschreitet, ohne es in jeder Sprache umzuimplementieren.

Warum nicht einfach als Bibliothek einbetten?

Die Alternative ist das Kompilieren von Detektion direkt in die Gateway-Bibliothek, d.h. eine reine Pressemitteilung oder eine CGo-Bindung an eine C-Bibliothek.

Ebenso wenig ist realistisch für einen Motor dieser Komplexität... StyloBot hat über vier Ausführungs-Detektoren organisiert. Wellen (-spätere Wellen feuern nur dann, wenn frühere Signale es rechtfertigen. ;-skalierender Versuch löst andere Detektoren aus als ein Googlebot crawling. Markov-Kettenvektoren Eine Markov-Kette ist nur ein Wahrscheinlichkeitsmodell über ", gegeben das letzte, was diese Sitzung getan hat,, was als nächstes kommt?", und 129, Dimensionen erfasst genug Seite-, transition Form Menschen zu sagen und Bots auseinander ., Es läuft Nachweis der Leiden-Gemeinschaft über die Vektoren ,: , ein Graph , - , Clustering-Algorithmus , dass Gruppen Sitzungen gleichen Verhaltens , , , das ist wie StyloBot ein Bot-Netzwerk auch wenn einzelne Sessions fein aussehen , und es bleibt all dies auf SQLite zwischen Anfragen ,. , Dieser Zustand braucht einen unabhängigen Lebenszyklus .. , Es kann nicht mit dem Knoten-Prozess neu starten oder abgerissen werden, wenn das Gateway reloads seine Konfiguration , . ,

Ein Sidecar lässt jede Komponente tun, was es gut ist

graph TD
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px
    classDef good fill:none,stroke:#22c55e,stroke-width:2px
    classDef store fill:none,stroke:#f59e0b,stroke-width:2px

    GW["Gateway<br/>Caddy / YARP / nginx"]:::input
    SD["StyloBot Sidecar · ASP.NET Core<br/>gRPC :5090 · REST :5091<br/>≤50ms per Detect RPC"]:::async
    APP["Upstream Application<br/>Node / Go / ASP.NET<br/>reads req.stylobot.verdict"]:::good
    DB[("SQLite<br/>sessions · signatures · reputation")]:::store

    GW --> SD
    SD <--> DB
    GW --> APP

Das Gateway ruft den Sidecar auf und injiziert das Ergebnis als HTTP-Header.,. Optional blockiert die Upstream-Anwendung die Header und wirkt nach dem Urteil.MS K3. Das Sidecar hält an Zustand zwischen Anfragen... Die Detektionspipeline läuft innerhalb des einzelnen gRPC-Aufrufs und sein Ergebnis propagiert sich als neun HTTP-Kopfzeilen.

Der Beiwagen

Mostlylucid.BotDetection.Sidecar ist ein minimaler ASP-.-NET-Kernprozess, der keine UI-MSC2 ist, keine statische Datei dient und kein Routing über die Endpunkte gRPC und REST hinaus verfügt. Es startet den Volldetektionsmotor und setzt zwei Ports aus.

  • :5090HTTP®/2 für gRPC-Clients ( gateways, , gehen Proxies, , der Knoten gRPc-Client ) ,
  • :5091HTTP-Ausfuhr für REST-Clients /api/v1/* Endpunkte

gRPC

gRPC ist ein hochleistungsfähiges Remote-Procedure-Call Framework, das bei Google entwickelt wurde. Protokollpuffer Eine kompakte binäre Kodierung, die schneller zu Serialisierung und kleiner auf dem Draht ist als JSON-.. gRPC läuft über HTTP./2,, was bedeutet, dass es Multiplexing Mehrfache Anfragen über eine TCP-Verbindung erhält.

Die Schnittstelle ist in einem .proto aus dieser Datei erzeugen typisierte Client- und Server-Stubs in jeder unterstützten Sprache. .proto Dateien, so dass jede Sprache mit einer gRPC-Implementierung das Sidecar nennen kann.

Die gRPC-Schnittstelle

Der Dienst hat drei RPCs.

service DetectionService {
  rpc Detect(DetectRequest)             returns (DetectResponse);
  rpc DetectBatch(DetectBatchRequest)   returns (DetectBatchResponse);
  rpc RenderWidget(RenderWidgetRequest) returns (RenderWidgetResponse);
}

Detect ist der per---Request-Hot Path?.-Pass es Methode ,,-Pfad , ,-Header , ,-Remote IP , und optional TLS Fingerabdruckdaten , .-Es läuft die Welle Pipeline nur die Detektoren, dass die Anfrage ,'-Signale Warrant , die Sitzung vector , ,-Scores gegen den Reputation Store .,- und gibt ein Urteil ,

DetectBatch läuft mehrere Anfragen sequentiell ,. , verwendet für Protokoll-Wiedergabe und Offline-Analyse , , , nicht per , - , anfordern Gateway verwenden .. ,

RenderWidget akzeptiert eine Liquid-Schablone-String-Strings-,, ein optionales Urteilsurteil und eine Schlüsselkarte von zusätzlichen Variablen.,, macht dann den Vorlagenserver wieder und gibt HTML.MSC5 zurück.Dies ist die Art und Weise, wie Nicht-MSC6-NET-Anrufer Bot?-aware-HTML produzieren, ohne einen separaten Renderprozess aufzustellen.;, Details im Abschnitt RenderWidget weiter unten.

Was in einem Detect-Anruf passiert

Ein schnelles Glossar vor dem Diagramm, da diese Namen erscheinen werden.

  • Tafel:, ein Per--, Schlüssel anfordern -, Wertbeutel, ., g. request.ip.is_datacenter, detection.useragent.confidenceDetektoren schreiben Signale zu ihr;. Spätere Detektor in der gleichen Welle lesen sie.. Lebt nur für die Dauer einer Anfrage und landet nie auf der Tafel...
  • Synthetisch HttpKontext:" ein Ständer ,-"in HttpContext der gRPC-Service baut aus den proto request fields. Die Detektionsmaschine wurde für ASP ,.'NET Middleware entwickelt und erwartet von HttpContextDie Synthese lässt den gleichen Motor in einem gRPC-Ruf unverändert laufen.
  • NachweisBeiträge / Aggregierte EvidenzDetektorausgänge ( signal schreibt Vertrauen Deltas ) und ihr zusammengeführtes Ergebnis .
sequenceDiagram
    participant GW as Gateway (Caddy)
    participant SD as gRPC Service
    participant ORC as BlackboardOrchestrator
    participant DET as Detectors (up to 49, 4 waves)
    participant DB as SQLite

    GW->>SD: Detect RPC { method, path, headers, remoteIp }
    SD->>ORC: DetectAsync(syntheticHttpContext)
    ORC->>DET: Wave 0 - Identity + ContentSequence
    ORC->>DET: Wave 1 - Fast path <1ms: UA, Header, IP, Heuristic ...
    ORC->>DET: Wave 2 - Session vectors, Behavioural waveform
    ORC->>DET: Wave 3 - Slow path: DNS, advanced fingerprinting
    DET-->>ORC: DetectionContributions (signals, confidence deltas)
    ORC->>DB: update session vector and reputation score
    DB-->>ORC: ok
    ORC-->>SD: AggregatedEvidence { botProbability, riskBand, ... }
    SD-->>GW: DetectResponse { isBot, riskBand, recommendedAction, ... }

Die gesamte Pipeline läuft innerhalb des einzelnen gRPC-Aufrufs...Es gibt keine Async-Arbeit, nachdem die Antwort zurückgegeben wurde.

Das Go SDK

Gateway-Code in Go kann nicht importieren, die ASP .. .NET Sidecar ?. . Was es tun kann, ist es über gRPC nennen .github.com/scottgal/stylobot-go-) , bietet eine getippte Schnittstelle, die die erzeugten Protobuf Typen von Anrufern vollständig versteckt

Warum das SDK Protobuf Typen versteckt

Protobuf---generierter Code ist verbose und hat eine ungewöhnliche API..-Enums werden als Integer dargestellt.MSC2-Strings kommen als rohe proto enum Namen.RISK_BAND_HIGHNicht "High"Feldnamen sind Kamelfall in einigen Generatoren und Schlangenfall in anderen Ausstellen von Prototypen in Ihrer öffentlichen API bedeutet, dass Ihre Anrufer all dies verstehen müssen

Das SDK übersetzt einmal an der Grenze .( enums to canonical strings . , . proto structs to plain Go stucts ,) . und Anrufer nie sehen es .

// the only interface you depend on: no proto imports required
type Client interface {
    Detect(ctx context.Context, req DetectRequest) (*Verdict, error)
    DetectBatch(ctx context.Context, reqs []DetectRequest) ([]*Verdict, error)
    RenderWidget(ctx context.Context, req RenderRequest) (*RenderResponse, error)
    Close() error
}

DetectRequest und Verdict sind einfache Go-Strukturen

type DetectRequest struct {
    Method   string
    Path     string
    Headers  map[string]string
    RemoteIP string
    Protocol string  // "http" or "https"; defaults to "https" if empty
    TLS      *TLSInfo
}

type Verdict struct {
    IsBot             bool
    BotProbability    float32
    Confidence        float32
    BotType           string   // "AiBot", "Scraper", "GoodBot", ...
    BotName           string
    RiskBand          string   // "VeryLow", "Low", "Elevated", "Medium", "High", "VeryHigh"
    RecommendedAction string   // "Allow", "Throttle", "Challenge", "Block"
    ThreatScore       float32
    ThreatBand        string
    ProcessingTimeMs  float32
    DetectorsRun      int32
    Reasons           []Reason
}

Erstellen eines Clients und laufende Erkennung

import (
    stylobot "github.com/scottgal/stylobot-go"
    "context"
    "time"
)

client, err := stylobot.NewClient(
    "localhost:5090",
    stylobot.WithTimeout(50 * time.Millisecond),
    stylobot.WithAPIKey(os.Getenv("SB_API_KEY")),
)
if err != nil {
    log.Fatal(err)
}
defer client.Close()

verdict, err := client.Detect(ctx, stylobot.DetectRequest{
    Method:   r.Method,
    Path:     r.URL.RequestURI(),
    RemoteIP: r.RemoteAddr,
    Headers:  extractHeaders(r),
    Protocol: "https",
})
if err != nil {
    // fail open: log and continue
    log.Printf("stylobot detect failed: %v", err)
    return next(w, r)
}

if verdict.RecommendedAction == "Block" {
    http.Error(w, "Forbidden", http.StatusForbidden)
    return
}

Lazy Verbindung und Startsicherheit

grpc.NewClient erstellt einen Client-Kanal, baut aber nicht sofort eine TCP-Verbindung auf Die Verbindung erfolgt beim ersten RPC-Anruf .. . Dies bedeutet, dass Ihr Gateway-Prozess erfolgreich startet, auch wenn das Sidecar noch nicht gestartet ist . . . Die erste Anfrage nach dem Start kann fehlschlagen .( . und sollte mit failant behandelt werden .

Dies unterscheidet sich von HTTP-Clients, in denen Sie typischerweise bei der Erstellung verbinden. gRPC Go Dokumentation deckt den Lebenszyklus im Detail ab

Timeout-Interaktion

WithTimeout am NewClient setzt eine Standard-Per---Call-Deadline, die in jedem Detect call-.-Wenn Ihr aufrufender Code ( oder Middleware wie das Caddy-Plugin )- bereits eine Frist ableitet, die von der eingehenden Anfrage , eingeblendet wird, gilt das SDK, welche Frist zuerst abläuft..-Wenn das Cadody-Plugin verwendet wird, kann das Plugin die Frist 50-ms nicht mehr verwenden. WithTimeout von NewClient und lassen Sie das Plugin es steuern...... Für den Standalone-Einsatz....... ein Handler, der das SDK direkt anruft.... NewClient wie oben gezeigt

Das Caddy-Modul

Caddy ist ein Go®--basierter Webserver und Reverse Proxy mit automatischer HTTPS®MS K1. Sein Plugin-System wird kompiliert.-?time?:? xcaddy um eine benutzerdefinierte Caddy-Binärdatei zu erstellen, die Ihre Plugins enthält, die ein einziges selbst produzierendes Binärmodul ohne Laufzeitabhängigkeit von geteilten Bibliotheken erzeugen, das sich von dem dynamischen Modulsystem nginx-' unterscheidet..so Dateien, die zur Laufzeit geladen werden... das StyloBot-Plugin...github.com/scottgal/caddy-stylobot) registriert einen Middleware-Handler, der das Go SDK auf jede Anfrage ruft

Die Konfiguration der Caddyfiles

{
    order stylobot before respond
}

:80 {
    stylobot {
        endpoint localhost:5090   # gRPC host:port of the sidecar
        timeout   50ms            # per-request deadline; fails open on expiry
        # on_block 503            # optional: change the block status code (default: 403)
    }
    reverse_proxy upstream:3000
}

Das Plugin injiziert neun Urteil-Header auf jede übermittelte Anfrage.

Quelle Feld |---|---| | X-StyloBot-IsBot | isBot (-Bool-)-|- | X-StyloBot-Probability | botProbability (0.0-1.0) | | X-StyloBot-Confidence | confidence (0.0-1.0) | | X-StyloBot-BotType |" e-, ." g, ,." AiBot, Scraper, GoodBot | | X-StyloBot-BotName |" e-, ." g, ,." GPTBot, Googlebot | | X-StyloBot-RiskBand | VeryLow ... VeryHigh | | X-StyloBot-Action | Allow / Throttle / Challenge / Block | | X-StyloBot-ThreatScore ,| , numerisch , | | X-StyloBot-ThreatBand | None ... Critical |

Anträge auf isBot=true und Action=Block werden am Gateway mit einem 403 stoppt und erreichen nie die vorgelagerten ., alles andere, einschließlich Bots mit Throttle oder Challenge Empfehlung: Das Gateway wird mit allen neun Headern intakt weitergeleitet.Das ist der beabsichtigte Split. Das Gateway verarbeitet harte Blöcke.

on_block ändert den Statuscode, der verwendet wird, wenn das Gateway blockiert. on_block 503 zu unterdrücken retry Logik in Schabern, die als wiederverwendbar behandeln

Was die Middleware auf jede Anfrage macht

flowchart TD
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px
    classDef good fill:none,stroke:#22c55e,stroke-width:2px

    A["1. Strip inbound X-StyloBot-* headers"]:::input
    B["2. context.WithTimeout(r.Context(), 50ms)"]:::input
    C["3. sbClient.Detect(ctx, DetectRequest)"]:::async
    D{error?}
    E["log warn - fail open<br/>forward unchanged"]:::good
    F["4. injectHeaders<br/>X-StyloBot-IsBot, Probability, Confidence,<br/>BotType, BotName, RiskBand, Action,<br/>ThreatScore, ThreatBand"]:::input
    I["next.ServeHTTP - forward to upstream<br/>with all verdict headers injected"]:::good

    A --> B --> C --> D
    D -->|yes| E --> I
    D -->|no| F --> I

Schritt - Streifen inbound Kopfzeilen Ein Kunde, der die X-StyloBot-* Header-Namen konnten sich selbst aussenden ein günstiges Urteil und haben es überleben die fail-?open path .? Stripping sie zuerst bedeutet das Urteil der Upstream sieht immer aus dem Sidecar?.?

Schritt: Kontext-Deadline Der Timeout ist abgeleitet von r.Context() Verwendung context.WithTimeout .( , die eine relative Dauer dauert . context.WithDeadline nimmt eine absolute Zeit... sie sind äquivalent... r.Context() eher als context.Background() ist der Schlüsselpunkt, wenn sich der Client vor dem gRPC-Aufruf trennt und die Absage propagiert und das Sidecar die Verarbeitung vorzeitig beendet.

Schritte 3 und 4, erkennen und injizieren Die neun Urteilsfelder werden zu neun X-StyloBot-* headers-.-Header werden vor dem Blockcheck gesetzt, also liest der Upstream sie über styloBotMiddleware({ mode: 'headers' }) für alle nicht geblockten Anfragen isBot=true und recommendedAction=Block werden am Gateway als 403 zurückgegeben, alles andere vorwärts mit den vollständigen Urteil-Headern angehängt.

Die Implementierung

// from sdk/caddy/stylobot.go
func (s *StyloBot) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
    for _, name := range stylobotHeaders {
        r.Header.Del(name)
    }

    ctx, cancel := context.WithTimeout(r.Context(), s.timeout)
    defer cancel()

    verdict, err := s.sbClient.Detect(ctx, sb.DetectRequest{
        Method:   r.Method,
        Path:     r.URL.RequestURI(),
        RemoteIP: ExtractIP(r),
        Protocol: r.Proto,
        Headers:  ExtractHeaders(r),
    })
    if err != nil {
        s.logger.Warn("stylobot detect failed, failing open", zap.Error(err))
        return next.ServeHTTP(w, r)
    }

    injectHeaders(r, verdict)

    if verdict.IsBot && s.OnBlock > 0 && verdict.RecommendedAction == "Block" {
        http.Error(w, "Forbidden", s.OnBlock)
        return nil
    }
    return next.ServeHTTP(w, r)
}

Gebäude mit xcaddy

Caddy-Plugins müssen in die binäre kompiliert werden xcaddyDas Dockerfile in den Integrationstests zeigt das Muster.

# from tests/integration/caddy-sidecar/Dockerfile
FROM caddy:2-builder AS builder

WORKDIR /build
COPY sdk/caddy/ caddy-plugin/
COPY sdk/go/    go/

WORKDIR /build/caddy-plugin

RUN xcaddy build \
    --with github.com/scottgal/caddy-stylobot=/build/caddy-plugin \
    --with github.com/scottgal/stylobot-go=/build/go

FROM caddy:2
COPY --from=builder /build/caddy-plugin/caddy /usr/bin/caddy
COPY tests/integration/caddy-sidecar/Caddyfile /etc/caddy/Caddyfile

Die xcaddy-Richtlinie wird ersetzt

Das Plugin„'" go.mod enthält:

replace github.com/scottgal/stylobot-go => ../go

Dies sagt die Go-Toolchain, wenn Sie sehen stylobot-goVerwenden Sie das lokale Verzeichnis, anstatt aus dem Modul Proxy zu holen. Es funktioniert für go build und go test im Plugin-Verzeichnis

xcaddy erstellt ein frisches temporäres Go-Modul für sein build-.-Das Modul erbt nicht replace Direktiven aus dem Plugin go.modOhne die zweite --with Argument: xcaddy würde versuchen, herunterladen stylobot-go von pkg.go.dev (, wo es noch nicht veröffentlicht wird, ) und fail-.

Das --with module=path argument ist xcaddy.'.s natives Äquivalent einer replace Direktive: : , es zeigt einen Modulpfad zu einem lokalen Verzeichnis zum Zeitpunkt des Erstellens an.. , Beide lokale Module müssen explizit benannt werden.

RenderWidget-:® Flüssige Vorlagen über gRPC

RenderWidget ist ein gRPC RPC auf dem Sidecar, der eine Liquid-Schablone-String akzeptiert, das es mit dem Erkennungskontext rendert,, und HTML-Street zurückgibt.Dies erlaubt jedem Anrufer einen Proxy ,, Node SSR Layer,, Batch Pipeline ), Bot--aware-HTML zu produzieren ohne einen separaten Renderprozess auszuführen..

Flüssige Vorlagen

flüssig ist eine templating Sprache, die von Shopify erstellt wurde., used by Shopify themes............... und viele andere Systeme...... seine wichtigsten Eigenschaften...... sicher mit Benutzer zu laufen...... ...-... mitgelieferte Vorlagen...... keine willkürliche Code-Ausführung...... ..),... einfach genug für Nicht---Entwickler zum Schreiben...... Fluid-.-KörnerEine hohe Leistung von - ist die Implementierung von Liquid-, zum Rendern von Vorlagen.

Die Sidecar-Implementierung

// from src/Mostlylucid.BotDetection.Sidecar/Services/DetectionGrpcService.cs
private static readonly FluidParser Parser = new();  // static, shared, compiled templates cached

public override async Task<Proto.RenderWidgetResponse> RenderWidget(
    Proto.RenderWidgetRequest request, ServerCallContext context)
{
    if (!Parser.TryParse(request.Template, out var template, out var error))
        return new Proto.RenderWidgetResponse { Success = false, Error = error };

    var ctx = new TemplateContext();
    if (request.Verdict is { } v)
    {
        ctx.SetValue("isBot",             v.IsBot);
        ctx.SetValue("botProbability",    (double)v.BotProbability);
        ctx.SetValue("botType",           v.BotType);
        ctx.SetValue("botName",           v.BotName);
        ctx.SetValue("riskBand",          v.RiskBand.ToString());
        ctx.SetValue("recommendedAction", v.RecommendedAction.ToString());
        ctx.SetValue("threatScore",       (double)v.ThreatScore);
        ctx.SetValue("threatBand",        v.ThreatBand.ToString());
    }
    foreach (var kv in request.Vars)
        ctx.SetValue(kv.Key, kv.Value);

    var html = await template.RenderAsync(ctx);
    return new Proto.RenderWidgetResponse { Html = html, Success = true };
}

Fluid-.-Core pflegt einen internen kompilierten Vorlagencache... Wiederholte Renders des gleichen Template-Strings überspringen re...-.parsing ?.. FluidParser ist statisch und über alle gRPC-Aufrufe verteilt

Der Knoten StyloBotGrpcClient.renderWidget() Beispiel und die vollständige Vorlage Variable Referenz sind in der TypeScript SDK Artikel.

Ich rufe es von Go-:.

rendered, err := client.RenderWidget(ctx, stylobot.RenderRequest{
    Template: `{% if isBot %}<p class="warning">Bot: {{ botType }}</p>{% endif %}`,
    Verdict:  verdict,
    Vars:     map[string]string{"locale": "en-GB"},
})
if err == nil && rendered.Success {
    fmt.Fprint(w, rendered.HTML)
}

Die Syntax der Vorlage ist identisch, ob Sie aufrufen RenderWidget von Go-,-Knoten oder verwenden <sb-widget> im Browser die gleiche Flüssige Engine die gleichen variablen Namen die gleiche Rendering-Pfad

Produktionsplan

graph LR
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px
    classDef good fill:none,stroke:#22c55e,stroke-width:2px
    classDef store fill:none,stroke:#f59e0b,stroke-width:2px

    INT([Internet])
    CF["Cloudflare<br/>Tunnel / CDN"]
    CA["Caddy<br/>+ caddy-stylobot"]:::input
    SD["StyloBot Sidecar<br/>:5090 gRPC  ·  :5091 REST"]:::async
    WEB["Upstream App<br/>Node / Go / ASP.NET"]:::good
    DB[("SQLite<br/>sessions · reputation")]:::store

    INT --> CF --> CA
    CA -->|"gRPC Detect<br/>≤50ms"| SD
    SD <-->|"persist"| DB
    CA -->|"X-StyloBot-* headers"| WEB
    WEB -->|"/_stylobot/partials/render<br/>(widget rendering)"| SD

Die vorgelagerte Anwendung ruft das Sidecar direkt für Widget-Rendering auf, um das Gateway zu umgehen... Widget Rendering benötigt den vollständigen Urteilskontext und passiert, nachdem die Anfrage bereits eine Gateway-Erkennung bestanden hat.

Fehler beim Öffnen auf jeder Ebene

Caddy plugin-,-Node Middleware - ,- und Go SDK alle ausfallen offen :-ein Sidecar-Timeout oder Fehler wird zu einem Warnprotokoll und eine permissive leere Urteil , nicht ein , 5 xx , die ,50-ms Caddy Deadline ist ein kalt - Start Sicherheitsmarge -;- die steady , --state Kosten auf einer warmen Verbindung ist , ist .

Der trade---off dieses codiert encodes-:-Blockierung legitimen Verkehr, weil die Erkennung nicht verfügbar ist schlimmer als fehlenden Bot-Verkehr während eines Sidecar-Ausfälles


Die Release-Serie geht weiter... mehr Beiträge auf Detektion Internals... ,... Bereitstellungsmuster...,... Beobachtbarkeit... und die kommerzielle Topologie sind noch nicht da.

Quelle für die Implementierung github-.-com./-scottgal./-stylobotLive-Engine, Dashboard und kommerzielle Steuerungen am Stylobot-.-net.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.