Back to "स्टिलियोबोट रिलीज श्रृंखला: साईडकार वास्तुकला"

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

स्टिलियोबोट रिलीज श्रृंखला: साईडकार वास्तुकला

Monday, 01 June 2026

स्टेलोबोट का पता लगाने वाला इंजन ASP है।

StyloBot

** github.com/scottgal/stylobot-go SDK, github.com/scottgal/caddy-stylobot प्लगइन, और Mostlylucid.BotDetection.Sidecar कन्टेनर शीघ्र प्रकाशित होगा** नीचे सब कुछ उन सतह का वर्णन करता है जो वे प्रदर्शित करेंगे

स्टिलियोबोट रिलेस शृंखला

  1. व्यवहार, पहचान नहीं: क्यों StyloBot ग्राहक व्यवहारिक रूप से मॉडल करता है
  2. व्यवहार-Aware ASP.NET UIसर्वर : NET अनुप्रयोगों के लिए प्रदान किया गया सतह
  3. लंबे समय तक चलाने वाले नेट सेवाओं में असीमित विकास का पता लगाना और स्थिर करना: विश्वसनीयता अनुशासन जो उत्पादन में इंजन को नीरस बनाए रखता है
  4. व्यवहार-Aware TypeScript UI: Express , Fastify, और ब्राउज़र घटक
  5. साइडकार वास्तुकला: इस लेख
  6. तेजी से सीखनाअनुकूलन सीखने की प्रणाली
  7. उस चीज़ का परीक्षण जो सफल नहीं हो रही है: सत्यापन अनुशासन
  8. स्टिलियोएक्सट्रैक्ट - एक स्थानीय सीखने के HTML को मार्कडाउन रूपांतरक में: एचटीएमएल\→\मार्कडाउन परत जो डिटेक्टर के साथ जोड़ता है,\walker bug lucidVIEW पकड़ा\M SK3\ और कुत्तेfood लूप कि यह ईमानदार बना

छेदगाड़ी क्यों

एक उल्टा प्रॉक्सी बॉट पता चलाने के लिए स्पष्ट स्थान है। अलग-अलग करना जो अनुरोध कर रहा है के आधार पर | . | एक गेटवे जो सीधे ब्लॉक करता है |; | अधिकांश अनुप्रयोगों को वास्तव में एक निर्णय की आवश्यकता होती है, जिस पर वे एक ही समय में कई तरह से कार्य कर सकते हैं

छेदगाड़ी पैटर्न दो चिंताओं को अलग करता है। तेजी से सीखनादो स्थानीय नेटवर्क पर संचार करते हैं. एक ही होस्ट या एक ही पोड.

यह नया नमूना नहीं है प्रेषक प्रॉक्सी सेवा के लिए यह ठीक करता है-मेश संबंधित M SK1mTLS, रिट्रीस दमर यह राज्य और pub के लिए करता है ओपन टेलीमीटरी कलेक्टर टेलीमीटरी के लिए यह करता है।

इसे केवल एक लाइब्रेरी के रूप में क्यों नहीं सम्मिलित करें

वैकल्पिक है गेटवे में सीधे पता लगाने को कम्पाइल करना।

इस जटिलता के एक इंजन के लिए भी यथार्थ नहीं है . स्टिलोबोट में चार निष्पादनों पर \49\सूचक संगठित हैं लहरें (अगामी तरंगों को तभी चलाया जाता है जब पहले से आने वाले संकेत इसे गारंटी देते हैं। मार्कोव श्रृंखला वेक्टर एक 129-आयामी अंतरिक्ष में\ .\ A Markov श्रृंखला केवल एक संभाव्यता मॉडल के ऊपर है & # 160; "\ given the last thing this session did\ M SK3\ what comes next\ ?",\ and 129\ dimensions captures enough page\ MSC6\ transition shape to tell humans and bots apart\ MST7\ It runs लीडन समुदाय पहचान उन सदिशों के ऊपर-:, एक ग्राफ, -, समूहबद्ध एल्गोरिथ्म जो सत्रों को समान रूप से व्यवहार करते हैं समूहित करता है,,, जो कि है StyloBot एक बॉट नेटवर्क को पता लगाता है जब व्यक्तिगत सत्र ठीक दिखते हैं तब भी।

एक पार्श्वगाड़ी प्रत्येक अवयव को ऐसा करने देता है जो यह अच्छी है

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

गेटवे छेदगाड़ी को कॉल करता है, परिणाम HTTP हेडर के रूप में इंजेक्शन करता है।

छेदगाड़ी

Mostlylucid.BotDetection.Sidecar एक न्यूनतम ASP है

  • :5090के लिए HTTP
  • :5091REST क्लाइंटों के लिए : HTTP/1.1 /api/v1/* अंतबिन्दु

जीआरपीसी

जीआरपीसी Google में विकसित एक उच्च निष्पादन दूरस्थ प्रक्रिया कॉल फ्रेमवर्क है प्रोटोकॉल बफर अपने तार प्रारूप के रूप में (protobuf) एक संक्षिप्त द्विआधारी एनकोडिंग जो JSON की तुलना में तार पर क्रमबद्ध करने में तेजी से और छोटा होता है।

इंटरफेस एक में परिभाषित किया गया है .proto फ़ाइल. उस फ़ाइल से कूट जनरेटर किसी भी समर्थित भाषा में टाइप किए गए क्लाइंट और सर्वर स्टब्स को उत्पन्न करते हैं .proto कोई भी भाषा जो जीआरपीसी क्रियान्वयन के साथ है, साइडकार को बुला सकता है

जीआरपीसी इंटरफेस

सेवा में तीन आरपीसी हैं

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

Detect per है। - request hot path; .; Pass it method; ,; path;,; headers; MSC4; remote IP; M SK5; and optional TLS fingerprint data; MRK6; It runs the wave pipeline; (; only the detectors that the request; MST8; signals warrant; M ST9; updates the session vector; Mst10; scores against the reputation store; M st11; and returns a verdict.

DetectBatch अनुक्रमिक रूप से बहुविध अनुरोध चलाता है. लॉग रिप्ले और ऑफ़लाइन विश्लेषण के लिए उपयोग किया जाता है

RenderWidget एक तरल टेम्प्लेट स्ट्रिंग को स्वीकार करता है।

खोज कॉल के अंदर क्या होता है

चित्र से पहले एक त्वरित शब्दकोश, क्योंकि ये नाम प्रकट होंगे

  • ब्लैकबोर्डएक प्रति-request key -value bag request.ip.is_datacenter, detection.useragent.confidence). डिटेक्टर इसके लिए संकेत लिखते हैं, ; बाद में एक ही तरंग के डिटेक्टर उन्हें पढ़ते हैं।
  • संश्लेषित एचटीटीपी संदर्भ: एक स्टैंड HttpContext जीआरपीसी सेवा प्रोटो अनुरोध क्षेत्रों से बनाता है HttpContextएक संश्लेषण करने से एक जीआरपीसी कॉल के अंदर एक ही इंजन बिना बदले चलाता है
  • पता लगाना योगदान / संयुक्त साक्ष्यडिटेक्टर आउटपुट (सिग्नल लिखता है
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, ... }

पूरी पाइपलाइन एकल जीआरपीसी कॉल के अंदर चलती है

जीओ एसडीके

गो में गेटवे कोड ASP को आयात नहीं कर सकता हैgithub.com/scottgal/stylobot-goएक टाइप्ड इंटरफेस प्रदान करता है जो कलर से उत्पन्न प्रोटोबॉफ़ प्रकारों को पूरी तरह छुपाता है

क्यों SDK प्रोटोबॉफ़ प्रकार छिपाता है

प्रोटोबॉफ़-generated code is verbose and has an unusual APIRISK_BAND_HIGH, नहीं "High"). क्षेत्र नाम ऊंट हैं कुछ जेनेरेटरों में केस और सांप के साथ-साथ

SDK एक बार सीमा पर (प्रो एनम को कैनोनिक स्ट्रिंगों में अनुवाद करता है

// 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 और Verdict सादा Go स्ट्रक्चर हैं

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
}

एक क्लाइंट बनाना और पता चलाना

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
}

लचीला कनेक्शन और स्टार्टअप सुरक्षा

grpc.NewClient एक क्लाइंट चैनल बनाता है लेकिन तत्काल टीसीपी कनेक्शन स्थापित नहीं करता।

यह HTTP क्लाइंटों से भिन्न है, जहाँ आप आमतौर पर निर्माण पर जुड़ते हैं जीआरपीसी गो दस्तावेज़ीकरण जीवन चक्र को विस्तृत रूप से शामिल करता है

समय समाप्ति क्रिया

WithTimeout पर NewClient प्रत्येक के भीतर लागू एक डिफ़ॉल्ट per-call तारीख़ सेट करता है Detect कॉल. यदि आपका कॉल कोड |( |या Caddy प्लगइन जैसे मिडवेअर | ) |आगत अनुरोध से सीमाबद्ध संदर्भ | МSK3 |से पहले समाप्त होने वाले किसी भी समयावधि को लागू करता है WithTimeout से NewClient और प्लगइन को इसे नियंत्रित करने दें NewClient जैसा कि ऊपर दिखाया गया है

Caddy प्लगइन

कैडीCity name (optional, probably does not need a translation) एक Go-आधारित वेब सर्वर और स्वचालित HTTPS के साथ उल्टे प्रॉक्सी है एक्सकैडी एक अनुकूल Caddy द्विआधारी बनाने के लिए जो आपके प्लगइनों को शामिल करता है.so रनटाइम पर लोड फ़ाइलें). स्टेलोबोट प्लगइनgithub.com/scottgal/caddy-stylobotप्रत्येक अनुरोध पर गो एसडीके को कॉल करने वाले मध्यवेयर हैंडलर को पंजीकृत करता है

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
}

प्लगइन प्रत्येक अग्रेषित अनुरोध पर नौ निर्णायक हेडर डालता है

हेडर |---|---| | X-StyloBot-IsBot | isBot 0 bool | X-StyloBot-Probability | botProbability (0.0-1.0) | | X-StyloBot-Confidence | confidence (0.0-1.0) | | X-StyloBot-BotType | AiBot, Scraper, GoodBot | | X-StyloBot-BotName | GPTBot, Googlebot | | X-StyloBot-RiskBand | VeryLow ... VeryHigh | | X-StyloBot-Action | Allow / Throttle / Challenge / Block | | X-StyloBot-ThreatScore संख्यात्मक | X-StyloBot-ThreatBand | None ... Critical |

जहाँ अनुरोध करता है isBot=true और Action=Block एक 403 के साथ गेटवे पर रोके जाते हैं और कभी ऊपरी धारा तक नहीं पहुँचते हैं Throttle या Challenge सिफारिश) सभी नौ शीर्ष intact के साथ अग्रेषित किया जाता है

on_block जब गेटवे ब्लॉक करता है तब उपयोग की जाने वाली स्थिति कोड बदलता है on_block 503 scrapers में पुनः प्रयास तर्क को दबाने के लिए जो 403 को पुनः प्रयास योग्य के रूप में देखता है

हर अनुरोध पर मिडलवेयर क्या करता है

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

चरण 1, स्ट्रिप इनबाउंड हेडर एक ग्राहक जो जानता है कि X-StyloBot-* हेडर नाम स्वंयः---एक अनुकूल निर्णय दे सकता है और यह असफलता से बच सकता है।

चरण 2, संदर्भ समय-सीमा समय समाप्ति से व्युत्पन्न है r.Context() उपयोग context.WithTimeout (जो एक सापेक्ष अवधि लेता है context.WithDeadline एक पूर्ण समय लेता है r.Context() बजाय context.Background() कुंजी बिंदु है

चरण 3 और 4, पता लगाना और इंजेक्शन करें नौ निर्णायक क्षेत्र नौ हो जाते हैं X-StyloBot-* हेडर. हेडर ब्लॉक जाँच से पहले सेट किए जाते हैं styloBotMiddleware({ mode: 'headers' }) सभी non-blocked अनुरोधों के लिए isBot=true और recommendedAction=Block गेटवे पर 403 के रूप में लौटाया जाता है

क्रियान्वयन

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

xcaddy के साथ निर्माण

केडीई प्लगइन को द्विआधारी में कम्पाइल किया जाना चाहिए एक्सकैडीएकीकरण परीक्षणों में Dockerfile पैटर्न दिखाता है

# 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

एक्सकैडी प्रतिस्थापन निर्देशिका

प्लगइन go.mod समाहित है:

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

यह कहता है कि जब आप देखते हैं Go औजार श्रृंखला " stylobot-goमॉड्यूल प्रॉक्सी से लाने के बजाय स्थानीय निर्देशिका का उपयोग करें go build और go test प्लगइन निर्देशिका में

xcaddy अपने निर्माण के लिए एक नया अस्थायी जा मॉड्यूल बनाता है replace प्लगइन से निर्देश go.mod. बिना दूसरे --with तर्क, xcaddy डाउनलोड करने की कोशिश करेगा stylobot-go से pkg.go.dev (जहां यह अभी प्रकाशित नहीं हुआ है

--with module=path तर्क xcaddy's एक का मूल समकक्ष है replace निर्देशिका: यह निर्माण समय पर स्थानीय निर्देशिका में एक मॉड्यूल पथ का नक्शा बनाता है

gRPC पर रेंडरवाइडजेट: तरल टेम्प्लेट्स

RenderWidget एक जीआरपीसी आरपीसी है जो एक तरल टेम्प्लेट स्ट्रिंग को स्वीकार करता है।

तरल टेम्प्लेट

तरल Shopify द्वारा बनाया गया एक टेम्पलेटिंग भाषा है। तरल, एक उच्च

पार्श्वगाड़ी कार्यान्वयन

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

तरल.Core एक आंतरिक कम्पाइलित टेम्प्लेट कैश रखता है FluidParser स्थिर है और सभी जीआरपीसी कॉलों में साझा है

नोड StyloBotGrpcClient.renderWidget() उदाहरण और पूरा टेम्प्लेट चल संदर्भ में हैं टाइपस्क्रिप्ट एसडीके अनुच्छेद.

इसे 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)
}

टेम्प्लेट वाक्य संरचना समान है चाहे आप कॉल करें RenderWidget से Go, Node, या उपयोग <sb-widget> ब्राउज़र में

उत्पादन सजावट

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

उपप्रणाली अनुप्रयोग छेदकार को सीधे विजेट रेंडरिंग के लिए बुलाता है, गेटवे को पार करने के लिए

हर परत पर खोलें

Caddy प्लगइन, नोड मिडिल्डवेयर, और Go SDK सभी खोलने में विफल होते हैं।

व्यापार से यह एनकोड करता है। legitimate traffic blocking because detection is unavailable worse than missing bot traffic during a sidecar outage


जारी जारी जारी है

कार्यान्वयन के लिए स्रोत github.comM SK1scottgal/stylobot. लाइव इंजन stylobot.net.

logo

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