Back to "StyloBot Release Series: Architecture de la Sidecar"

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: Architecture de la Sidecar

Monday, 01 June 2026

StyloBot's moteur de détection est ASPM SK1NET Core. Cet article explique comment ce moteur se connecte aux passerelles GoMSC3 NodeMNK4js applicationsMST5 et tout autre stack par l'entremise d'un sidecar gRPC MST6 un type Go SDKMSR7 et un plugin CaddyMSL8 sans qu'aucun de ces consommateurs n'ait besoin de savoir quoi que ce soit sur les MSSK9NET internesMSS10

StyloBot

L'article 5 de la Loi sur l'immigration et le statut des réfugiés github.com/scottgal/stylobot-go SDK, le github.com/scottgal/caddy-stylobot plugin, et le Mostlylucid.BotDetection.Sidecar le contenant sera publié prochainement. Tout ce qui suit décrit la surface qu’ils exposeront.

StyloBot Release Series

  1. Comportement, Non identité: pourquoi StyloBot modele les clients de façon comportementale
  2. Behaviour-Aware ASP.NET UI: le serveur- la surface rendue pour les applications .NET
  3. Trouver et rétablir une croissance sans limites dans les services longs-Running .NET: la discipline de fiabilité qui maintient le moteur borne dans la production
  4. Behaviour-Aware TypeScript UI: ExpressM SK1 Fastify, et composants de navigateur
  5. L'architecture de la Sidecar: cet article
  6. Apprentissage pour devenir plus rapide: le système d'apprentissage adaptatif , quatre- mémoire de niveauM SK3 et cache des verdicts
  7. Étudier ce qui ne va pas se tenir: la discipline de vérification : un fichier BDF conduit à la régressionM SK2 charge, et à l'étalonnage
  8. StyloExtract - un convertisseur HTML d'apprentissage local à Markdown: la couche de marquage HTML→ qui se joigne au détecteur , le bug lucidVIEW du fauteuil roulant qui a capté l’image

Pourquoi une voiture latérale

Un proxy inverse est le lieu évident pour faire fonctionner la détection des bots: il se trouve devant tout , voit toutes les demandes , et peut bloquer avant que n’ait été lancée une application quelconqueM SK3 Cette logique se maintient jusqu’à ce que vous demandez quelle application devrait être utilisée de faire différemment basée sur qui fait la demande. Une passerelle qui bloque tout à fait est une passerelle ; ce dont la plupart des applications ont réellement besoin, c'est un verdict qu'elles peuvent appliquer de multiples façons à la fois: tourner l'APIM SK3 personnaliser l'interface de l'U.I.

Le modèle de la chariot sidecar distingue les deux préoccupations. La passerelle demeure rapide et sans étatM SK1 La chariote sidecar maintient l’état de session, les ancrages archétypiques , l’État de dériveMSC4 et le cache des verdicts qui rendent la détection exacte MST5le type archetypique MST6le modèle d’ancrageM ST7 le système d’apprentissage à niveau perM st8le cache des vérdicts sur les empreintes digitales M ST9 et les quatre niveauxM S T 10 sont couverts par Apprentissage pour devenir plus rapide). Les deux communiquent sur le réseau local (le même hôte ou le même Pod) de sorte que l’étape rondeM SK3est de microsecondes à un seul MSC4millisecondes de chiffresMSC5 non pas celle d’un appel API éloignéMSM6msMMSC7 Ce budget de latence est ce qui rend la détection des demandes par SMC8réclamation pratique en tout

Ce n’est pas un nouveau modèle. Envoyé proxy Ce n’est pas le cas pour les services -mesh concerns (mTLSM SK2 retries, circuit breakingMSC4 Dapr a-t-il pour l'État et la pub/subM SK1 le Collecteur de télémétrie ouverte utilise-t-il pour la télémétrie?. Linkerd, Consul ConnectM SK2 et AWS App Mesh suivent tous le même modèle?

Pourquoi ne pas l’impliquer simplement comme bibliothèque?

La solution de rechange consiste à compiler la détection directement dans la passerelle . Pour Go, c'est-à-dire une réimplementation pure -Go ou une liaison CGo avec une bibliothèque C

Ce n’est pas réaliste pour un moteur de cette complexité. StyloBot a des détecteurs 49 organisés sur quatre exécutions les ondes (Les vagues subséquentes ne feront feu que lorsque les signaux antérieurs le justifient ; un certificat de crédibilité M SK2une tentative d’affaissement déclenche différents détecteurs qu’un crawl Googlebot ). Il est maintenu par session Vektors de chaîne Markov Dans un espace dimensionnel, une chaîne Markov est simplement un modèle de probabilité par rapport à la dernière chose que cette séance a faite. Détection communautaire de Leiden sur ces vecteurs: un graphique - algorithme de fragmentation qui regroupe les sessions comportant des comportements similaires, c'est ainsi que StyloBot repère le réseau d'un bot même lorsque les séances individuelles semblent parfaitesMSC3 Et il persiste tout cela à SQLite entre demandesM SK4 Cet état a besoin d'une vieillesse indépendante~. Il ne peut pas remarrer avec le processus Node ou se détruire quand la passerelle recharge sa configuration~.

Une voiture de côté permet à chaque composant de faire ce qui est bon à :

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

La passerelle appelle le sidecar, injecte le résultat sous forme de titres HTTP , et bloque optionnellement. L'application en amont lit les titres et agit sur le verdictM SK3 Le sidecar maintient l'état entre les demandesMSC4 Le pipeline de détection fonctionne à l'intérieur du seul appel gRPC et son résultat se propage sous la forme de neuf titresHTTPMNK5

L'automobile de sidecar

Mostlylucid.BotDetection.Sidecar est un ASP minimal.Procédure de base NET. Il n’a pas d’interface M SK2 aucun service de fichier statique , et aucune routage au-delà des points finals gRPC et REST M SK4 Il démarre le moteur de détection complet et expose deux portsMSC5

  • :5090: HTTPM SK1 pour les clients gRPC ( passerelles, Proxies d'acheminement
  • :5091: HTTP /1.1 pour les clients de RESTM SK2 re-exportation /api/v1/* Les points de départ sont les suivants :

gRPC

gRPC est un cadre d’appels de procédure à distance haute performance, développé par Google. Les tampons du protocole (protobuf) en tant que format de câble : un codage binaire compact qui est plus rapide à serialiser et plus petit sur le câble que JSONM SK3 gRPC fonctionne par HTTPMSC4 ce qui signifie qu'il obtient des multiplexings (multiple requests over one TCP connectionMNK6 for freeMRK7

L'interface est définie dans un .proto file. De ce fichier, les générateurs de code, produisent des articles sur le client et le serveur typés dans n'importe quelle langue supportée. .proto les fichiers, de sorte que toute langue avec une mise en œuvre gRPC peut appeler le sidecar: Go, NodeM SK2 Python , Rust

L'interface gRPC

Le service comporte trois PCR:

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

Detect est le par-route chaude de la demande . Méthode d'acheminement , routeM SK3 entêtesMSC4 IP éloignéMNK5 et données optionnelles sur les empreintes digitales TLSMRK6 Il gère le pipeline des ondes MNK7seulement les détecteurs que la requête MMK8 garantit ses signaux MGK9 met à jour le vecteur de session MBK10 donne un score contre l'entrepôt de réputation MDK11 et retourne un verdictMBK12

DetectBatch exécute plusieurs demandes séquentiellement. Utilisé pour le replay du log et l'analyse hors ligneM SK1 non par-utilisation de la passerelle de demande .

RenderWidget accepte une chaîne de modèle liquide, un verdict facultatifM SK1 et une clé- carte de valeur des variables additionnelles , puis rend le serveur de templateMSC4 côté et retourne HTMLMNK5 C'est ainsi que les appelants non MST6NET produisent un botMSP7aware HTML sans mettre en place un processus distinct de renderingMSV8 détails dans la section RenderWidget ci-dessousMSSK9

Ce qui se passe à l'intérieur d'un appel de détection

Un glossaire rapide avant le diagramme, puisque ces noms apparaîtront:

  • Tableau noir: un sac de valeur par - clé de demande request.ip.is_datacenter, detection.useragent.confidence). Les détecteurs envoient des signaux à celui-ci ; les détectrices subséquentes dans la même vague les lisent. Il n'existe que pendant une seule demandeM SK3 PII brut PSK4IPMSC5 String UAMNK6 demeure dans le contexte de la demande et ne tombe jamais sur le plancher blackboard.
  • Contexte Http synthétique: un peuplementM SK1in HttpContext le service gRPC est construit à partir des champs de demande proto. Le moteur de détection a été conçu pour ASP.NET middleware et attend d’être lu par HttpContext; synthétiser un permet de faire fonctionner le même moteur indépendamment à l'intérieur d'un appel gRPC
  • Contributions de détection / Données générées: les détecteurs de sorties ( le signal écrit des deltas de confiance
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, ... }

L’ensemble du pipeline se déroule à l’intérieur d’un seul appel gRPC. Il n’y a pas de travail async après que la réponse retourne.

Le SDK Go

Le code de passerelle dans Go ne peut pas importer l’ASP.NET sidecar. Ce qu’il peut faire, c’est l’appeler par gRPCM SK2 Le SDK Go (github.com/scottgal/stylobot-go) fournit une interface type qui cache complètement les types de protobuf générés des appelants.

Pourquoi le SDK cache les types de protobuf

Protobuf-le code généré est verbose et a un API inhabituel . Les enums sont représentés comme des integers. Les chaînes apparaissent sous les noms de proto-enums bruts M SK3RISK_BAND_HIGH, non "High"). Les noms de champ sont camelCase dans certains générateurs et serpentM SK1case dans d’autres. L’exposition des types de proto dans votre API public signifie que vos appelants doivent comprendre tout cela .

Le SDK traduit une fois à la limite (enums proto en chaînes canoniques,structures proto en structures Go simplesM SK2 et les appelants ne le voient jamaisMSC3

// 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 et Verdict sont des structures de Go simples:

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
}

Création d'un client et exploitation de la détection:

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
}

Connexion lente et sécurité de démarrage

grpc.NewClient create a client channel but does not establish a TCP connection immediately. The connection happens on the first RPC callM SK1 This means your gateway process starts successfully even if the sidecar hasn't started yet. The first request after startup may fail (and should be handled with failureMST4openMSP5 but every subsequent request works normally once the side car is runningMSC6

Cela diffère des clients HTTP, où vous connectez habituellement lors de la création. gRPC Documentation pour aller couvre le cycle de vie en détail.

Interaction de temps-out

WithTimeout sur NewClient définit un délai par -appel par défaut appliqué à chacun des appels Detect appel. Si votre code d'appel M SK1ou un outil de middleware tel que le plugin Caddy ) dérive déjà une date limite- contexte lié à la demande incombantMSC4 le SDK s'applique quels que soient les délais qui expirent le plus tôt. WithTimeout de NewClient et laisser le plug-in le contrôler. Pour l'utilisation autonome (un gestionnaire appelant directement le SDK ), la mettre en place NewClient comme il est indiqué ci-dessus.

Le plugin Caddy

Caddy est un serveur Web basé sur Go- et un proxy inverse avec HTTPS automatisé. xcaddy pour construire un binaire Caddy personnalisé qui inclut vos plugins, produisant un seul self-contained binary with no runtime dependency on shared libraries . Ceci diffère de nginxM SK3s dynamic module system MSC4.so files loaded at runtime). Le plugin StyloBot (github.com/scottgal/caddy-stylobot) enregistre un gestionnaire de middleware qui appele le Go SDK à chaque demande

Configuration du Caddyfile:

{
    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
}

Le plugin injecte neuf titres de verdict sur chaque demande transmise:

Intitulé Champ de source MSC2
X-StyloBot-IsBot isBot (boolM SK1
X-StyloBot-Probability botProbability (0.0-1.0)
X-StyloBot-Confidence confidence (0.0-1.0)
X-StyloBot-BotType eM SK1g. AiBot, Scraper, GoodBot
X-StyloBot-BotName eM SK1g. GPTBot, Googlebot
X-StyloBot-RiskBand VeryLow ... VeryHigh
X-StyloBot-Action Allow / Throttle / Challenge / Block
X-StyloBot-ThreatScore numérique
X-StyloBot-ThreatBand None ... Critical

Demande de savoir où isBot=true et Action=Block sont arrêtés à la passerelle avec un 403 et ne parviennent jamais jusqu’à l’amont de l’embranchement. Throttle ou Challenge recommandation) est transmis avec tous les neuf titres intacts . C'est la division prévue: le passage de port agit sur des blocs dursM SK3 l'avant-marché agit en fonction des nuances

on_block modifie le code de situation utilisé lorsque la passerelle bloque (defaultM SK1 403). Set on_block 503 supprimer la logique de réessai dans les scrapers qui traitent 403 comme retryable.

Que fait le middleware à chaque demande

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

Étape 1, têtes d'entrée de bande . Un client qui connaît X-StyloBot-* les noms de l’en-tête pourraient eux-mêmes -injecter un verdict favorable et le faire survivre à la défaillance -pathe ouverte

Étape 2, Délai de mise en œuvre du contexteM SK1 Le temps d'arrêt est dérivé de r.Context() utilisation context.WithTimeout (qui prend une durée relative context.WithDeadline Il faut un temps absolu; ils sont équivalents ). D'après r.Context() au lieu de context.Background() est le point clef: si le client se déconnecte avant que l'appel gRPC ne soit terminé , l'annulation se propage et la voiture de sidecar cesse de traiter tôt

Étapes 3 et 4, détection et injection Les neuf champs de verdict sont devenus neuf X-StyloBot-* headers. Les en-têtes sont réglés avant la vérification des blocs. styloBotMiddleware({ mode: 'headers' }) pour toutes les demandes non bloquées-. Demandes où isBot=true et recommendedAction=Block sont retournés comme 403 à la passerelle; tout le reste avance avec les titres de verdict complets attachés

La mise en œuvre:

// 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)
}

Établissement avec xcaddy

Les plugins Caddy doivent être compilés dans le binaire en utilisant xcaddy. Le Dockerfile dans les essais d’intégration montre le modèle

# 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

La directive de remplacement xcaddy

Le plugin's go.mod contient:

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

Cela indique à la chaîne d’outils Go " quand vous voyez stylobot-go, utiliser le répertoire local au lieu de télécharger du proxy module." Il fonctionne pour go build et go test dans le répertoire des plugins.

xcaddy crée un nouveau module Go temporaire pour sa construction. Ce module ne hérite pas replace directives du plugin's go.mod. Sans la seconde --with argument, xcaddy essaiera de télécharger stylobot-go de pkg.go.dev (dans les cas où il n’est pas encore publié

L'article 5 de la Loi sur l'immigration et le statut des réfugiés --with module=path argument est l'équivalent natif de xcaddy's d'un replace directive: il représente un chemin de module vers un répertoire local au moment de la constructionM SK1 Les deux modules locaux doivent être désignés explicitement.

RenderWidget: Modèles liquides sur gRPC

RenderWidget est un RPC gRPC sur l'automobile qui accepte une chaîne de modèle liquide, la rend avec le contexte de détection , et retourne HTMLM SK2 Cela permet à tout appelant (Go proxyMSC4 Node SSR layerMST5 batch pipelineMSP6 produce botMSSK7aware HTML without running a separate render process

Formulaires sur les liquides

Liquide est un langage de templat créé par Shopify, utilisé par les thèmes de Shopify+, Jekyll+M SK2 Pages GitHub+, et bien d'autres systèmes+ . Ses propriétés clés=: sécuritaire à exploiter avec l'utilisateur+MSC6+templates fournies+MNK7+sans exécution arbitraire du code+MRK8+faible pour les développeurs non--+pour écrire+MEK10+et largement comprise+MSEK11+ Utilise StyloBot Fluid.Core, une performance élevée -une mise en œuvre NET de Liquid, pour rendre un serveur de templates

Mise en œuvre de l’automobile sidecar:

// 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 entretient un cache interne compilé de templates. FluidParser est statique et partagée par tous les appels gRPC.

Le node StyloBotGrpcClient.renderWidget() exemple et la référence complète de la variable template sont dans le Article TypeScript SDK.

En appelant de 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)
}

La syntaxe du modèle est identique si vous appelez RenderWidget de Go, Node, ou utiliser <sb-widget> dans le navigateur: le même moteur liquideM SK1 les mêmes noms de variables , la même voie de traduction.

Répartition de la production

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

L'application en amont appelle directement le sidecar pour la représentation des widgets, parpasser la passerelle. La représentation des Widgets a besoin du contexte de verdict complet et se produit après que la demande ait déjà passé la détermination de la passerellesM SK2, il n'y a donc pas de double emploi de détection .

Défaut-ouvrir à chaque couche

Caddy plugin, Node middleware, and Go SDK all fail openM SK2 a sidecar timeout or error becomes a warning log and a permissive empty verdictMNK3 not a MMK4xxMRK5 The 50ms Caddy deadline is a coldMBK7start safety marginMKB8 the steadyMZK9state cost on a warm connection is 1–5msMGK11

Le commerce - qui encode ce message : bloque le trafic légitime parce que la détection n’est pas disponible est pire qu’un manque de trafic bot lors d’une panne d’autos secondaires.


La série de diffusion se poursuit. D’autres articles sur les dispositifs internes de détectionM SK1 Les modèles de déploiement , l’observabilité M SK3 et la topologie commerciale sont encore à venir. L’arc jusqu’à présentMSC5

Source de la mise en œuvre: github.comM SK1scottgal/stylobot. moteur en direct , tableaux de bord M SK2 et contrôles commerciaux à stylobot.net.

logo

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