Back to "Серія випуску StyloBot: Архітектура 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: Архітектура Sidecar

Monday, 01 June 2026

Стівлобота з детектором - ASP, ., NET Core, МSK2. Цей пост пояснює, як цей двигун підключений доGateways, M SK3, Node, Applications for Java, , та будь-якого іншого стека через sidecar gRPC. МSK6, вбудований Go SDK,, і Caddy плагин, без жодного з цих споживачів, які не мають нічого знати про . NET інтерфейси.

StyloBot

І github.com/scottgal/stylobot-go SDK, github.com/scottgal/caddy-stylobot плагин, і Mostlylucid.BotDetection.Sidecar контейнер буде опубліковано незабаром. Все, що зображено на екрані, описує поверхню, яку вони покриють.

Серія випуску StyloBot

  1. Подібність, Не ідентичність: чому StyloBot моделює клієнтів поведінково
  2. Подібність-Усвідомлення ASP.NET UI: сервер - віддалена поверхня для МSK2NET застосувань
  3. Винайти та виправити безмежний ріст у довгостроковій-Running M SK1NET Services: - дисципліна надійності, яка робить двигун нудним в процесі виробництва
  4. Подібність-Усвідомлення інтерфейсу TypScript: Express , Fastify, і компоненти браузера
  5. Архітектура Sidecar: цей artykuł
  6. Навчитися швидше: адаптивна система навчання , чотири МSK2 пам 'ять на рівні M SK3 і кэш вердикта
  7. Випробовуємо те, що не зупиняє нас: контрольна дисципліна : один БДФ файл керує регресією МSK2 завантаженням МСК3 та калібрацією
  8. StyloExtract - локальний інтерфейс навчання HTML для конвертера Markdown: HTML → Марка-даун, який поєднується з детектором МSK2 walker bug lucidVIEW caught МSK3 and the dogfood loop that made it honest

Чому хідник

Перевернутий прокси є очевидним місцем для запуску розпізнавання роботів : він сидить перед всіма МSK1 бачить кожну просьбу МСК2 і може блокувати перед запуском будь-якого коду aplikacji М СК3 Ця логіка працює аж доки ви не запитуєте, що ваші програми мають робити зробити інакше на основі того, хто робить прохання. Мережа, яка блокує напряму - це воротаM SK1 те, що більшість програм насправді потребує, це рішення, яке вони можуть робити одночасно в багатьох способах : перекрутити API МSK3 персоналізувати інтерфейсу M SK4 виключити трафик з аналітики , додати крок тремтіння при перевірці MSC6 ворота не можуть зробити нічого з цього Mska7 Тріт для прийняття рішення може M Ska8

Модель канатної дороги розділяє ці дві проблеми: . Перехідник залишається швидким і безладним . канатна машина підтримує стан сеансу МSK2 засадники архетипу \ , стан дрейфу \ МSK4 та резервуар вердикта, що роблять розпізнавання точним Навчитися швидше). Обидві зв 'язуються через локальну мережу ( той самий хост чи той самій Pod МSK2 так, що кругла \ - подорож є мікросекундами до одного \ МSK4 \ цифри мілісекунд , \ а не МSK6 \ ms віддаленого API дзвінка \

Це не нова модель. Прокси посла робить саме це для обслуговування-обговорення про мережу M SK1mTLS, ретриси , розбіжність ланцюгаMSC4 Дапр чи робить це для держави та pub/subM SK1 Коллекционер OpenTelemetry чи для телеметрії? . Linkerd, Consul ConnectM SK2 і AWS App Mesh всі підтримують один і той самий модель. МSK3 Модель постійно з 'являється, тому що вона вирішує реальну проблему.

Чому б не просто вмонтувати її як бібліотеку?

Альтернативою є компіляція розпізнавання безпосередньо в порті. Для Go це означає чистуM SK1 Переimplementацію Go або CGo прив 'язування до C-ліbraрії. Для Нода це означає запуск розпізнання уMSC3процесі поряд з програмоюMスク4

Ни одна з них не є реалістичною для двигуна такої складності . StyloBot має 49 детектори, організовані на чотири етапи хвилі (пізніші хвилі реагують тільки тоді, коли попередні сигнали підтверджують це ; довіраність МSK2 спроба поглинання запускає різні детектори, ніж пошук Googlebotу Вектори ланцюга Маркова у просторі 129-вимірного розміру . Марковський ланцюг - це лише модель ймовірності над МSK2 беручи до уваги останні речі, які зробили на цій сеансі МSK3 те, що прийде далі МСК4 і ММСК5 виміри захоплюють достатньо сторінок MСК6 форми переходу, щоб розрізнити людей та роботів Розпізнання спільноти Лейдена над цими векторами: графом - заплутаний алгоритм, який об 'єднує сеанси, які поводяться однаково МSK2 це те, як StyloBot знаходить мережу роботів навіть тоді, коли окремі сеансі виглядають непогано M SK3 І все це продовжується до SQLite між посиланнями . Цей стан потребує незалежного життєвого циклу MSC5 Він не може розпочати процес узла або розірватися, коли порт перезагрузить свою конфігурацію

Автомобіль дозволяє кожному компоненту робити те, що йому добре вдається.

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 заголовки , і опціонально блокуєM SK2 Пристрій зворотнього потоку читає заголовку та діє на вердикті. Шляховий vagon зберігає стан між запитамиMSC4 Піплін для виявлення запускається всередині одного виклику gRPC, а його результат розповсюджується як дев 'ять HTTP закінчений заголовківМSK5

Автомобіль

Mostlylucid.BotDetection.Sidecar є мінімальним ASP. NET Кораовий процес . Він не має інтерфейсу МSK2 жодного статистичного файлу, який служить МСК3 і немає маршрутізації за межами gRPC та кінцевих точок REST М СК4 Він запускає повний двигун розпізнавання і відкриває два порти M СК5

  • :5090: HTTP /2 для gRPC-клиентів
  • :5091: HTTP /1.1 для klientów REST /api/v1/* кінцеві точки

gRPC

gRPC це високоефективний віддалений процедурний зв 'язок, розроблений в Google Буфери протоколу МSK0protobuf) як його формат дроту : компактний бінарний кодування, який швидший для серійизації і менший на дроті, ніж JSONM SK3 gRPC працює над HTTP/2, що означає, що він отримує мультиплексування MSC5множина запитів над одним TCP підключенням МSK6 безкоштовно

В інтерфейсі .proto файл. З цього файлуM SK1 генератори коду виробляють задруковані clients і серверні штуки в будь-якому піддерживаному мові . StyloBot публікує .proto файли, так що будь-яка мова з інсталяцією gRPC може називати sidecar .

інтерфейс gRPC

Служба має три РПК:

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

Detect це про per-горячий шлях запитуM SK1 Metodа передачі його , шлях МSK3 заголовки M SK4 віддалений IP , і опціональні дані пальцевих відбитків TLS MSC6 Він запускає хвилі каналу MST7 тільки детектори, які гарантують сигнали запиту \ MST8\ M ST9 оновлює вектор сеансу M ST10 балує від репутації в магазині репутаціях М ST11 та повертає вирок \ М ST12 \

DetectBatch запускає декілька запитів послідовно. Використовується для відтворення журналу та аналізу в офлайні

RenderWidget приймає стрічку рідкісного шаблону, опціональний вердикт , і ключM SK2 मानову мапу додаткових змінних МSK3 потім відображає сервер шаблонів МСК4 на стороні і повертає HTML М СК5 Ось як не-MСК6 NET викликачі виробляють бота MСК7 знають HTML без створення окремого процесу відтворювання M СК8 подібні деталі в sekції RenderWidget низу МSК9

Що відбувається всередині виклику Detect

Короткий словник перед діаграмою, оскільки ці назви будуть з 'являтьсяM SK1

  • Чёрна доска: один шт. request.ip.is_datacenter, detection.useragent.confidence). Детектори пишуть йому сигнали, МSK1 пізніше детектори в тій самій хвилі читають їхM SK2 Живе лише протягом одного запиту МSK3 Raw PII (IPMSC5 UA стрічка ) залишається у контексті запиту і ніколи не приземляється на чорну дошку M SK7
  • Синтетичний HttpContext: столикM SK1в HttpContext usługа gRPC будується з поля прото-запросів. детектор був розроблений для ASPM SK1 NET middleware і сподівається читати з HttpContext; синтезуючи один, той самий двигун запускається без змін всередині gRPC
  • Доноси до розпізнавання / АgregatedEvidence: виходи детектора МSK1 сигнал записує + confident deltas МSK3 і їх об 'єднаний результат
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, ... }

Весь канал запускається всередині одного gRPC виклику. Не існує асинкної роботи після того, як відповідь повертається.

Go SDK

Шляховий код в Go не може импортувати ASP.NET sidecarM SK1 Він може викликати його за допомогою gRPC. Go SDK (github.com/scottgal/stylobot-go) - це вбудований інтерфейс, який повністю приховує генеровані типи протобуфів від викликачів

Чому SDK приховує типи протобуфів

Protobuf-генерований код verbose і має незвичайний API . Еноми представлені як цілковиті M SK2 Стрини з 'являються як сирові ім' я прото-енумів МSK3RISK_BAND_HIGH, не "High"). Field names are camelCase in some generators and snake

SDK перекладає один раз на граничному рівні (proto-енуми на канонічні струни ,protо-структури на прості Го-структурі МSK2 і викликачі ніколи їх не бачать

// 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 створює клієнтський канал, але не встановлює негайного TCP зв 'язку. Зв' язок відбувається на першому дзвінку до РПКМSK1 Це означає, що ваш портовий процес запускається успішно, навіть якщо автомагістраль ще не запущена

Це відрізняється від HTTP-клиентів,, де ви зазвичай зв 'язуєтесь на створеннях. gRPC Рухаємось до документації деталізує життєвий цикл.

Взаємодія на часі

WithTimeout на NewClient встановлює початковий термін для кожного дзвінка Detect подзвонити. Якщо ваш код подзвону | ( | або середній інструмент, як, наприклад, плагин Caddy | МSK2 | вже має кінцевий термін |- | обмежений контекст з приchodzącego запиту \ , | SDK застосовує той термін, який перший закінчується |МSK5 | Коли вживається плагин Kaddy WithTimeout від NewClient і дозволити плагин контролювати його. Для автономного використання M SK1гортач, що викликає SDK напряму ), встановив його NewClient як показано вище.

плагин Caddy

Кадді це веб-сервер на основі Go- і зворотній прокси з автоматичним HTTPS xcaddy для створення custom Caddy бінарного коду, який включає в себе ваші плагини, виробляючи один セルб MSC1континентний бінар без залежності від часу запуску від спільних бібліотек. Це відрізняється від nginxM SK3 динамічної модулячої системи (.so files loaded at runtime). плагин StyloBot (github.com/scottgal/caddy-stylobot) записує middleware handler, який називає Go SDK на кожному запиті

Конфигурація 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
}

плагин вводить дев 'ять заголовків вердикту на кожен просуваний запит:

Заголовок МSK1 Поле джерела МSK2
X-StyloBot-IsBot isBot МSK0bool) M SK2
X-StyloBot-Probability botProbability (0.0-1.0)
X-StyloBot-Confidence confidence (0.0-1.0)
X-StyloBot-BotType МSK0 e.gM SK2 AiBot, Scraper, GoodBot
X-StyloBot-BotName МSK0 e.gM SK2 GPTBot, Googlebot
X-StyloBot-RiskBand VeryLow ... VeryHigh
X-StyloBot-Action Allow / Throttle / Challenge / Block
X-StyloBot-ThreatScore МSK0 числові
X-StyloBot-ThreatBand None ... Critical

Прохання, де isBot=true і Action=Block зупиняються на шлюзі з 403 і ніколи не дістаються до верхнього потоку . Все інше МSK2 включаючи роботів з Throttle або Challenge Рекомендація) передають з усіма дев 'ятьма заголовками непошкодженими . Це намірний розрив МSK2 ворота управляють важкими блоками МСК3 верхні потоки управляються нюансами MСК4

on_block змінює код статусу, який використовується, коли порт блокує (defaultM SK1 403). Установка on_block 503 для подавлення логіки повторного спробування у скрукерах, які сприймають 403 як повторне спробування

Що робить middleware на кожному запиті

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, стрічковий входний заголовок МSK1 Клиент, який знає X-StyloBot-* імена заголовків могли б самі внести позитивний результат, щоб він пережив невдачі.

Шаг 2, кінцевий термін в контексті МSK1 Часовий відрізок походить з r.Context() використовуючи context.WithTimeout (, що займає відносно тривалістьM SK1 context.WithDeadline приймає абсолютний час; вони є еквівалентніM SK1 Виходить з r.Context() замість context.Background() є ключовим моментом: якщо klient відключається перед тим, як завершити дзвінок gRPC , аннулація розповсюджується, а автозаправка зупиняється на ранній стадії МSK2

Шаг 3 і МSK1 виявити та ввести Дев 'ять вердиктів перетворюються на дев' ять X-StyloBot-* заголовки. Заголовки встановлені перед блоковою перевіркоюM SK1 так, що їх доштовхує styloBotMiddleware({ mode: 'headers' }) для всіх неблокованих запитів- isBot=true і recommendedAction=Block повертаються як 403 на порозі МSK1 все інше рухається вперед з повними заголовками вердикту

Implementacja:

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

Вставники Caddy повинні бути компільовані в бінарну систему, використовуючи 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

xcaddy замінює директиву

плагин's go.mod містить:

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

Це вказує на ланцюг інструментів Go ", коли ви бачите stylobot-go, використовувати локальний каталог замість того, щоб брати з модуля прокси go build і go test в каталогі плагинів.

xcaddy створює новий тимчасовий модуль Go для свого створення. Цей модуль не успадковується replace директиви з плагина's go.mod. Без секунди --with argument, xcaddy спробує завантажити stylobot-go від pkg.go.dev ( там, де ще не було опубліковано

І --with module=path аргумент xcaddy'с природним еквівалентом replace директива: він відображає шлях модуля до локального каталогу в час будуванняM SK1 Обидві локальні модулі повинні мати конкретне ім 'я.

RenderWidget: Шаблони Liquid над gRPC

RenderWidget це gRPC RPC на сторонній машині, яка приймає стрічку шаблону Liquid, відтворює її з контекстом розпізнаванняM SK1 і повертає HTML. Це дозволяє будь-кому викликателю ( Переходити на проксиMSC4 Лаyer узла SSRMska5 пакетна трубкаM Ska6 виробляти BotMска7 мати доступ до HTML без запуску окремого процесу відтворенняMSKA8

Шаблони:Пульти

Рід це templating мова, створена Shopify, використовується Shopify themes, JekyllM SK2 Pages of GitHub , і багато інших системMSC4 Цілові характеристикиMST5 безпечна для запуску разом з користувачамиМSK6 вбудовані шаблони MSL7 без произвольного виконання кодуMNK8 достатньо проста для не-- розробників, щоб написатиMRK10 та широко зрозумілиMسک11 StyloBot використовує Fluid.Core, високий МSK1конкурентність МSK2NET-implementacja Liquid , для відтворення сервера шаблонів

Втілення бокового вагону:

// 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 підтримує внутрішній компілятор шаблонівM SK1 Повторні відтвори того ж стрічки шаблону перемикаються назад FluidParser є статичним і поширюється на всіх викликах gRPC.

Зв 'язок StyloBotGrpcClient.renderWidget() приклад і повна змінна шаблона є в Artykuł про TypScript SDK.

Від 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

Верхня програма звертає сторонню машину безпосередньо для відтворювання виджета, перехиляючи воротаM SK1 Відтворення виджету потребує повного контексту вердикту і відбувається після того, як запити вже пройшли розпізнавання ворот, тож не існує дублування розпізнанняMSC3

Неможливе-открити на кожному шарі

плагин Caddy, Node middleware, and Go SDK all fail openM SK2 a sidecar timeout or error becomes a warning log and a permissive empty verdictMSC3 not a MST4xxMSL5 The 50ms Caddy deadline is a coldMSP7start safety marginMSV8 the steadyМСП9state cost on a warm connection is МSP10msMСП11

Торгівля "-" - це кодування МSK1 , блокування законного руху через те, що його не можна виявити - гірше, ніж пропущений рух роботів під час аварії


Серія релізів продовжується. Більше статей про інтерфейси розпізнаванняМSK1 схеми використанняM SK2 спостереженнямість, і комерційна топологія ще попереду

Источник implementу: github.comM SK1scottgal/stylobot. Живий двигун , панель управления МSK2 та коммерціальні прилади stylobot.net.

logo

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