Przejdź do treści

Middleware HOWTO

Ten HOWTO zachowuje operacyjny materiał referencyjny, szkice konfiguracji, przykłady hooków i wzorce implementacyjne. Krótszy Middleware FAQ jest wejściem koncepcyjnym.

Tworzenie middleware channel JSON

Użyj standardowego adaptera Pythonowego z middleware-modules/lib/channel_module.py. Moduł dostarcza funkcje domenowe; adapter jest właścicielem hello/attach, ograniczonego framingu, korelacji, potwierdzeń observation, anulowania oraz mostu operatorskiego o kształcie HTTP. Minimalny entrypoint wygląda tak:

from channel_module import run_channel_module

run_channel_module(
    module_report=middleware_init_payload,
    middleware_invoke=middleware_invoke_payload,
    http_dispatch=channel_http_dispatch,
)

Daemon wstrzykuje ORBIPLEX_MIDDLEWARE_CHANNEL_URL, identyfikator uruchomienia i ścieżkę do pliku tokena. Nie utrwalaj, nie loguj ani nie wpisuj tych danych do konfiguracji paczki. Module report musi przejść standardowy schema gate. Route'y dostępne przez most deklaruj w api/surface, operator_surfaces albo rejestracji local routes; deklaracja jest opisowym claimem routingu, nie autoryzacją.

Proces rejestruj pod middleware_channel_services, podając identyfikatory modułu i komponentu, middleware home, politykę launch/sandbox/restart oraz ograniczenia channel. Używaj ścieżek względnych wobec paczki. Moduł host-only nie powinien w trybie kanałowym uruchamiać listenera. Moduł mieszany może zachować listener produktowy, ale nie może opierać na nim lifecycle ani dispatchu middleware hosta. Nie dodawaj konfiguracji rollbacku http_local_json: executor jest wycofany, a stare formy są odrzucane przed efektami. Kanał i zachowany listener produktowy nigdy nie mogą być właścicielami tej samej semantycznej route'y.

Dla modułu dostarczanego z repozytorium zapisz własność jawnie w factory config:

{
  "example_module": {
    "seed_config": true,
    "factory_executor": "channel_json",
    "product_listener_retained": false,
    "module_id": "example-module"
  }
}

Wpis channel-only nie może zawierać listen_host ani listen_port. Ustaw product_listener_retained na true tylko wtedy, gdy moduł świadomie zachowuje osobno ograniczoną powierzchnię produktową. Intentional network service nadal używa factory_executor = channel_json do nadzoru hosta, a jego listener produktowy ma osobnego właściciela. Wszystkie bieżące wpisy fabryczne wybierają channel_json; inwentarz nie jest allowlistą kompatybilności executora.

W trybie kanałowym mieszany moduł Python powinien wejść w scope retained_product_listener_marker(...) po zbindowaniu socketu produktowego. Scope zapisuje rzeczywisty endpoint do <middleware_home>/bind i usuwa marker podczas shutdownu. Nie używaj go dla modułu channel-only.

Każdy fabryczny retained listener musi mieć unikalną parę (listen_host, listen_port); sprawdzany inwentarz odrzuca kolizje. Supervisor zachowuje istniejący niepusty token albo tworzy losowy w <middleware_home>/authtok, przekazuje jego ścieżkę przez ORBIPLEX_MIDDLEWARE_AUTHTOK_FILE, a nazwę nagłówka przez ORBIPLEX_MIDDLEWARE_AUTH_HEADER. Serwer produktowy czyta ten lokalny bearer raz przy starcie i porównuje go przy każdym żądaniu. Ten kontrakt nie implikuje OAuth ani publicznego providera tożsamości.

Nie deklaruj middleware_http_local_services ani executora http_local_json w paczce operatorskiej. Konfiguracja daemona, utrwalone ustawienia, luźne artefakty konfiguracyjne i manifesty paczek zawierające te wycofane formy zawodzą przed efektami. Nie ma czytnika kompatybilności ani automatycznej konwersji.

Adaptery Inquirium używają run_channel_adapter(...), który zachowuje semantykę modelu i providera, wymieniając wyłącznie lokalny transport hosta. Wszystkie 18 modułów fabrycznych używa channel_json. Listenery produktowe zachowane przez Agora, Arca, Attestation, Contact Catalog, Dator, Messaging, Recovery i Whisper pozostają osobnymi usługami domenowymi.

Dla kandydata runtime Inquirium ustaw transport instancji adaptera na channel_json, podając module_id, zadeklarowaną w raporcie ścieżkę invoke_path i ograniczony timeout_ms. Nie kopiuj nazwy modelu providera ani autoryzacji do konfiguracji transportu: model binding i politykę rozwiązuje host przed wywołaniem.

Checklista migracji na kanał

  1. Zbuduj jedną wartość middleware-module-report.v1 i zwaliduj ją przez Node schema gate. Deklaruj tylko route'y i capability rzeczywiście obsługiwane kanałem.
  2. Sklasyfikuj stan docelowy jako channel-only albo jawny produktowy HTTP plus kanał kontrolny. Kanał i listener produktowy nie mogą być właścicielami tej samej route'y.
  3. Odwzoruj istniejące endpointy host-facing przez channel_http_dispatch. Zachowuj ograniczone query tylko wtedy, gdy jest wejściem domenowym; nie używaj go do routingu ani autoryzacji. Endpointy produktowe pozostaw na jawnym listenerze.
  4. Zarejestruj ograniczone parametry launch, restart, timeout i flow-control pod middleware_channel_services, a przed włączeniem cohortu uruchom testy cross-language i supervised peer.

Minimalny zestaw współdzielonych testów uruchamiany z repozytorium Node:

python3 tools/test_middleware_channel_python.py
cargo test -p orbiplex-node-middleware-supervisor supervised_channel_peer_attaches_proves_readiness_and_cleans_runtime_files
cargo test -p orbiplex-node-daemon daemon_starts_channel_middleware_and_resolves_transport_neutral_dispatch

Referencyjnym procesem bez zachowania domenowego jest middleware-modules/channel-conformance-peer/service.py. Realny moduł powinien używać tego samego adaptera transportowego, zastępując fixture handlers własnymi funkcjami domenowymi.

Deklarowanie zależności komponentów i odzyskiwania po efektach

Użyj middleware-component-contract.v1, gdy jeden nadzorowany komponent middleware zależy od innego komponentu albo wywołuje efekty, których uprzątnięcie ma znaczenie. Każdą dostarczaną i wymaganą capability zwiąż z digestem jej kanonicznego kontraktu. Sama nazwa capability nie jest deklaracją zgodności. Ustaw provider/component-id, gdy zamierzony jest dokładnie jeden provider; w przeciwnym razie host odmawia niejednoznacznego dopasowania zamiast wybierać według kolejności konfiguracji.

Host waliduje cały graf przed uruchomieniem. Brak wymagania, niezgodność digestu, niespełniony pin providera, niejednoznaczny provider i cykl są błędami konfiguracji. Wymaganie optional toleruje wyłącznie całkowity brak capability; jeżeli capability jest obecna pod innym digestem albo u innego providera niż zadeklarowany, host odmawia przyjęcia grafu zamiast ukrywać niezgodność. Komponenty uruchamiają się od providerów do zależnych, a zatrzymują w odwrotnej kolejności. Gdy wymagany provider znika w czasie działania, host najpierw usuwa zależne komponenty z routingu, wygasza i zatrzymuje je oraz raportuje dependency_unavailable. Dedykowana pętla cyklu życia może je uruchomić ponownie dopiero po zaobserwowaniu gotowości tej samej zadeklarowanej zależności; odczyt health albo statusu nigdy nie steruje tym przejściem. Oznacz wymaganie jako optional wyłącznie wtedy, gdy konsument ma jawny tryb degradacji, który nie dziedziczy po cichu równoważnej władzy.

Wartość effects jest mapą indeksowaną przez effect/id, a nie listą zawierającą edytowalne pola tożsamości lub właściciela. Każdy wpis należy do otaczającego go component/id. Taki kształt uniemożliwia duplikat identyfikatora w sparsowanej wartości i nie pozwala komponentowi przypisać obowiązku odzyskania komuś innemu.

Każdy zadeklarowany efekt używa jednej klasy odzyskiwania:

Klasa Zamierzony mechanizm odzyskiwania
ephemeral-revertible Typowany disposer hosta zwalnia lokalny timer, subskrypcję, route, katalog tymczasowy, binding, proces albo sesję kanału.
transactional-withheld Transakcja albo dziennik nie dopuszcza do ujawnienia niezatwierdzonego efektu trwałego, zewnętrznego lub federacyjnego.
compensatable Późniejszy fakt append-only kompensuje, oznacza tombstonem, wycofuje albo zastępuje widoczny efekt trwały, zewnętrzny lub federacyjny.
irreversible-external Efekt wymaga jawnej polityki zatwierdzania, ponieważ nie można uczciwie obiecać jego cofnięcia.

Nie przypisuj imperatywnego disposera do stanu trwałego, zewnętrznego ani federacyjnego. Zamknięcie procesu lub kanału zwalnia zasób lokalny; nie usuwa opublikowanego faktu, zdalnej obserwacji ani nie odwraca działania zewnętrznego. Pozytywną wartością referencyjną jest podstawowy kontrakt komponentu middleware.

Odpowiedzi operatorskich operacji start, stop i restart zawierają uporządkowane domknięcie dotkniętych komponentów. Sprawdź je, zanim potraktujesz polecenie jak obsługę jednego komponentu: zatrzymanie providera celowo najpierw wygasza i zatrzymuje wszystkich jego tranzytywnych konsumentów.

Jakie są rodzaje middleware'u?

Middleware Orbipleksu nie jest jednym webowym łańcuchem interceptorów. Jest hostowaną tkaniną rozszerzeń, w której każdy moduł albo deklaratywna definicja wnosi zachowanie przez jawne kontrakty, a host Node'a pozostaje właścicielem cyklu życia, dispatchu, walidacji, bram capability, śladów działania i semantyki awarii.

Główne typy wykonania i specjalizacji to:

  • middleware Rust działający w procesie,
  • czysty middleware JSON-e,
  • middleware JSON-e Flow,
  • middleware command/stdio,
  • niezarządzany lokalny middleware HTTP JSON,
  • nadzorowany middleware channel JSON,
  • middleware konektora Sensorium,
  • middleware-hosted runtime adapter Inquirium.

Legacy executor supervised HTTP jest wycofany. Wszystkie nadzorowane moduły fabryczne używają channel_json; niezależnie uzasadnione listenery produktowe HTTP pozostają osobnymi powierzchniami domenowymi, a nie executorami middleware.

Dystrybucja jest osobną osią: ten sam typ wykonania może być dostarczany fabrycznie, instalowany przez operatora albo materializowany z fragmentu profilu lub konfiguracji. Zobacz Modele dystrybucji.

Rust w procesie

Middleware Rust działający w procesie jest kompilowany do binarki Node'a albo do jednej z crate podłączonych do node'a. To najmniej izolowany i najbardziej uprzywilejowany kształt, więc rezerwujemy go dla zachowań hosta, które należą blisko granicy daemona. Ten typ ma sens, gdy zachowanie potrzebuje ciasnego dostępu do struktur runtime hosta, deterministycznego startu, niskiej latencji albo bardzo małej zaufanej powierzchni implementacyjnej. Nie powinien być używany tylko dlatego, że wygodnie jest dopisać kod Rust w daemonie. Jeżeli zachowanie da się wyrazić przez zadeklarowaną capability, szablon JSON-e albo supervised module, słabsza forma jest zwykle lepsza. Kontrakt nadal powinien wyglądać jak middleware: jawne wejście, jawne wyjście, śledzalna decyzja i walidacja należąca do hosta.

Kształt rejestracji

  • Crate albo moduł Rust kompilowany do workspace Node'a.
  • Rejestracja route'a, hooka albo capability należąca do kodu daemona.
  • Testy w crate, która jest właścicielem zachowania.

Zastosowania

  • Mosty dispatchu należące do hosta, które nie powinny zależeć od zewnętrznych procesów.
  • Małe bramy polityki ciasno powiązane ze stanem runtime daemona.
  • Adaptery niskiego poziomu, gdzie nadzór procesu nie dodawałby użytecznej granicy.

Przykłady

pub fn register_builtin_middleware(registry: &mut HostRegistry) {
    registry.register("example.builtin", |input| {
        MiddlewareDecision::continue_with(input)
    });
}

Czysty JSON-e

Czysty middleware json_e jest deklaratywnym transformatorem danych. Jego kontrakt executora jest zaimplementowany w middleware-runtime; daemon udostępnia dziś bezpośrednią rejestrację providerów z konfiguracji operatora dla json_e_flow, ale nie równoległą mapę usług czystego JSON-e. Czysty executor otrzymuje wyprojektowany przez operatora kontekst JSON, renderuje wartość JSON, a host waliduje tę wartość względem oczekiwanego kontraktu wyjściowego. Nie ma ambient authority: nie może otwierać plików, wołać sieci, mutować storage, wywoływać host capabilities ani oglądać danych, które nie zostały wyprojektowane do jego kontekstu. To właściwy domyślny wybór dla dopasowań, selekcji pól, małych przepisań, normalizacji, decyzji routingowych i budowania odpowiedzi wielkości wskaźnika. Jeżeli szablon zaczyna potrzebować efektów, polityki retry, długotrwałego stanu albo szerokiego rozgałęziania, należy przejść do JSON-e Flow albo supervised module.

Kształt rejestracji

  • JsonEExecutorConfig deklarujący tożsamość middleware, szablon, limity, projekcję kontekstu, profil helperów i kontrakt wyjściowy.
  • Bezpośrednie użycie przez executor middleware-runtime w integracjach na poziomie crate i testach.
  • Dla wdrożenia operatorskiego przez obecny daemon: bezefektowa definicja middleware_json_e_flow_services, używająca tylko render, validate i respond.

Zastosowania

  • Normalizacja przychodzącego payloadu zanim zobaczy go inny komponent.
  • Budowa middleware-decision.v1 z małego wyprojektowanego kontekstu.
  • Renderowanie prostego service-dispatch-response bez procesu.
  • Wybór route'a albo adnotacja żądania na podstawie jawnych danych.

Przykład

Kompletny kontrakt czystego executora znajduje się w HOWTO JSON-e i JSON-e Flows.

JSON-e Flow

Middleware json_e_flow jest flow należącym do hosta, zbudowanym wokół wejść kroków renderowanych przez JSON-e. Każda definicja flow jest operacyjnie osobnym, cienkim komponentem middleware z własną tożsamością, bindingami, limitami, dozwolonymi wywołaniami, rekordami trace, deklaracją raw-signal i statusem dla operatora. Współdzielony engine wykonuje flow, ale to definicja flow jest właścicielem granicy middleware. JSON-e renderuje wartości; host wykonuje zadeklarowane kroki takie jak render, validate, call, extract, respond i fail. Dzięki temu efekty pozostają poza szablonem, a flow nadal może wołać jawnie dopuszczone host capabilities. Używaj tego dla małych, ograniczonych adapterów, które potrzebują jednego lub kilku kontrolowanych efektów. Jeżeli flow staje się orkiestracją z dynamicznym generowaniem kroków, szerokim stanem roboczym albo złożoną polityką domenową, lepszy jest nadzorowany moduł channel_json.

Kształt rejestracji

  • Wpis konfiguracji daemona middleware_json_e_flow_services.
  • Szablony flow, definicje kroków, limity, dozwolone wywołania i projekcja kontekstu.
  • Opcjonalne fragmenty konfiguracji pakietu i metadane UI operatora.
  • Trace kroków i digests pod powierzchniami trace należącymi do daemona.

Zastosowania

  • Adaptacja żądania roli Datora do dyrektywy Sensorium.
  • Wywołanie memarium.write po wyrenderowaniu ograniczonego faktu.
  • Publikacja rekordu ukończenia kroku workflow po udanym wywołaniu capability.
  • Niskokodowy middleware dla operatorów, którzy nie powinni potrzebować skryptów na poziomie systemu operacyjnego.

Przykład

Aktualny względem schemy przepływ, mocki dry-run, pakowanie, obsługa operacji odroczonych i wzorce integracyjne znajdują się w HOWTO JSON-e i JSON-e Flows.

Command/Stdio

Middleware command/stdio uruchamia ograniczoną komendę jako jednorazowy proces. Jest silniejszy niż JSON-e, bo może wykonywać kod programu, ale nadal jest ograniczony timeoutem, wejściem, wyjściem i polityką hosta. Ten typ jest użyteczny dla małych narzędzi, które naturalnie mają kształt linii poleceń i nie muszą żyć między żądaniami. Nie powinien być używany dla długotrwałych usług, kolejkowania, streamingu ani złożonego lokalnego stanu. Host powinien traktować stdout, stderr, exit code, timeout i rozmiar wyjścia jako część kontraktu. Operatorzy powinni unikać dawania mu szerokiego dostępu do systemu plików lub sieci, chyba że use case wyraźnie tego wymaga.

Kształt rejestracji

  • Konfiguracja ścieżki komendy i argv.
  • Limity timeoutu i rozmiaru wyjścia.
  • Opcjonalny executable albo skrypt dostarczony przez pakiet.
  • Trace wywołania zawierający tożsamość komendy, exit status i ograniczone podsumowania wyjść.

Zastosowania

  • Wywołanie deterministycznego lokalnego konwertera.
  • Uruchomienie małego checkera nad ograniczonym payloadem JSON.
  • Opakowanie dojrzałego narzędzia CLI bez nadzorowania daemona.

Przykłady

{
  "executor": "command_stdio",
  "module_id": "example.slugify",
  "command": ["./bin/slugify"],
  "limits": {
    "timeout_ms": 1000,
    "stdout_max_bytes": 8192,
    "stderr_max_bytes": 4096
  }
}

Niezarządzany lokalny HTTP JSON

Niezarządzany lokalny middleware HTTP JSON używa już działającej lokalnej usługi. Host Node'a wie, jak wywołać endpoint, ale nie jest właścicielem cyklu życia tej usługi. Dzięki temu adapter pozostaje cienki i przydatny w developmentcie, integracji z lokalnymi usługami zarządzanymi przez operatora albo w przypadkach, gdzie proces ma już własnego supervisora. Jest słabszy operacyjnie niż supervised HTTP, bo readiness, polityka restartu, logi i shutdown są poza kontrolą Node'a. Daemon nadal powinien egzekwować kształt żądania, timeout, limit rozmiaru odpowiedzi, auth modułu i granice host capabilities. Używaj tego typu, gdy usługa naprawdę należy poza cykl życia Node'a.

Kształt rejestracji

  • URL endpointu, metoda, nagłówki, timeout i limit rozmiaru odpowiedzi.
  • Brak definicji procesu należącej do daemona.
  • Opcjonalny lokalny token auth albo polityka bindingu loopback.

Zastosowania

  • Połączenie z lokalną usługą uruchomioną przez developera podczas prototypowania.
  • Most do lokalnej usługi zarządzanej przez systemd, launchd, Dockera albo inny supervisor.
  • Integracja narzędzia, które ma własny cykl życia i model zdrowia.

Przykłady

{
  "executor": "local_http_json",
  "module_id": "example.external-service",
  "endpoint": "http://127.0.0.1:49110/v1/invoke",
  "method": "POST",
  "limits": {
    "timeout_ms": 2000,
    "response_max_bytes": 65536
  }
}

Nadzorowany channel JSON

Nadzorowany middleware channel JSON jest długotrwałym procesem uruchamianym, obserwowanym i zatrzymywanym przez host Node'a. To normalny kształt dla silnego middleware'u, który potrzebuje własnego runtime, stanu, kolejek, logiki domenowej, powierzchni operatorskiej albo kontaktu z sąsiednimi systemami. Moduł podłącza się do współdzielonego uwierzytelnionego kanału daemona; sesja przenosi jawne kontrakty i nie nadaje ambient daemon privilege. Readiness wynika z attach, schema-gated init/report oraz heartbeat. Ten typ jest właściwy dla modułów Python, Rust i innych procesów, których zachowanie jest zbyt bogate dla JSON-e Flow.

Chwilowa utrata listenera korzysta z ograniczonego reconnectu w ramach tego samego launchu. Generowane profile używają reconnect_grace_ms = 5000; operator może wybrać wartość 1..=60000 milisekund. Domyślne pięć sekund obejmuje zwykłą wymianę lokalnego listenera lub przeładowanie hosta bez długiego maskowania jego awarii; limit 60 sekund służy celowo wolniejszym środowiskom lokalnym, a nie jako budżet ponawiania awarii sieciowej. Sesja zastępcza staje się odtworzona dopiero po heartbeat aplikacyjnym, a każde oczekujące wywołanie z utraconej sesji kończy się błędem zamiast być odtwarzane. Wywołania podczas odłączenia zawodzą natychmiast. Odmowa uwierzytelnienia lub protokołu jest trwała dla bieżącego launchu i nie zużywa budżetu reconnectu transportowego.

Kształt rejestracji

  • Kod usługi dostarczony jako moduł wbudowany albo zainstalowany pakiet.
  • Wpis uruchomieniowy middleware_channel_services.
  • Uwierzytelniony attach kanału, init/report, heartbeat i ograniczony dispatch.
  • Raport modułu deklarujący route'y, capabilities i powierzchnie UI.
  • Pliki runtime pod <data-dir>/middleware/<module-id>/.

Zastosowania

  • Katalogi ofert i providery dispatchu podobne do Datora.
  • Orkiestracja workflow podobna do Arki.
  • Powierzchnie operatorskiego UI wymagające żywego server-rendered HTML.
  • Konektory wymagające kolejek, cache'y, retry albo dostępu do zewnętrznych narzędzi.

Przykłady

from channel_module import run_channel_module

run_channel_module(
    module_report=middleware_init_payload,
    middleware_invoke=middleware_invoke_payload,
    http_dispatch=channel_http_dispatch,
)

Jak sprawić, żeby endpoint middleware był widoczny w OpenAPI / Swaggerze?

Nie uruchamiaj osobnego serwera Swaggera wewnątrz middleware'u. Node daemon posiada jedną opisową projekcję OpenAPI 3.1 pod GET /v1/openapi.json; opcjonalny Swagger UI czyta tę projekcję daemona. Middleware dostarcza dane, a nie drugi runtime dokumentacyjny.

Dla nadzorowanego middleware dodaj sekcję api/surface do wartości init/report wysyłanej kanałem. Sekcja musi być zgodna z orbiplex.api-descriptor.v1.

Minimalny fragment raportu modułu:

{
  "schema_version": "v1",
  "module_id": "example.inquirium",
  "module_name": "Example Inquirium Adapter",
  "api/surface": {
    "schema": "orbiplex.api-descriptor.v1",
    "component/id": "example.inquirium",
    "base/path": "/",
    "endpoints": [
      {
        "method": "POST",
        "path": "/v1/inquirium/invoke",
        "summary": "Invoke the adapter through the host-owned Inquirium contract.",
        "tags": ["inquirium", "middleware"],
        "surface": "internal-loopback",
        "path/owner": "middleware-direct",
        "path/exposure": "internal-loopback",
        "loopback/path": "/v1/inquirium/invoke",
        "path/params": [],
        "request": {
          "schema_ref": "urn:orbiplex:schema:inquirium-adapter-invoke:v1"
        },
        "responses": {
          "200": {
            "schema_ref": "urn:orbiplex:schema:inquirium-adapter-response:v1"
          }
        },
        "x-orbiplex-auth": "module-authtok",
        "x-orbiplex-effect": "mutates-state",
        "x-orbiplex-idempotency": "optional",
        "x-orbiplex-authority": "descriptive-only"
      }
    ]
  }
}

Dla middleware'u w Pythonie preferuj jedną współdzieloną tabelę route'ów używaną zarówno przez dispatch, jak i przez generowanie deskryptora:

ROUTES = (
    {
        "method": "POST",
        "path": "/v1/inquirium/invoke",
        "handler": handle_inquirium_invoke,
        "summary": "Invoke the adapter through the host-owned Inquirium contract.",
        "tags": ["inquirium", "middleware"],
        "surface": "internal-loopback",
        "path_owner": "middleware-direct",
        "path_exposure": "internal-loopback",
        "request_schema_ref": "urn:orbiplex:schema:inquirium-adapter-invoke:v1",
        "response_schema_ref": "urn:orbiplex:schema:inquirium-adapter-response:v1",
    },
)

def api_surface_descriptor(module_id):
    return {
        "schema": "orbiplex.api-descriptor.v1",
        "component/id": module_id,
        "base/path": "/",
        "endpoints": [
            {
                "method": route["method"],
                "path": route["path"],
                "summary": route["summary"],
                "tags": route["tags"],
                "surface": route["surface"],
                "path/owner": route["path_owner"],
                "path/exposure": route["path_exposure"],
                "loopback/path": route["path"],
                "path/params": [],
                "request": {"schema_ref": route["request_schema_ref"]},
                "responses": {"200": {"schema_ref": route["response_schema_ref"]}},
                "x-orbiplex-auth": "module-authtok",
                "x-orbiplex-effect": "mutates-state",
                "x-orbiplex-idempotency": "optional",
                "x-orbiplex-authority": "descriptive-only",
            }
            for route in ROUTES
        ],
    }

Reguły praktyczne:

  • path to ścieżka eksponowana przez hosta i widoczna w projekcji daemona.
  • loopback/path to surowa lokalna ścieżka middleware'u; jest wymagana dla ekspozycji internal-loopback.
  • Parametry ścieżki używają kanonicznych segmentów OpenAPI {snake_case}, a każdy parametr ścieżki musi też występować w path/params.
  • Używaj schema_ref tylko dla schematów zapisanych w kanonicznym rejestrze. Mały schemat inline zostaw dla tymczasowych powierzchni kompatybilności.
  • Nigdy nie umieszczaj w api/surface wartości tokenów auth, sekretów, promptów, sealed payloads ani lokalnych ścieżek absolutnych.
  • x-orbiplex-auth jest opisową etykietą typu module-authtok, nie samym tokenem.
  • x-orbiplex-authority ma zawsze wartość descriptive-only; OpenAPI opisuje kształt, nie autorytet ani pełną politykę bezpieczeństwa.
  • Domyślna projekcja OpenAPI obejmuje tylko wpisy z powierzchni protocol. Inspekcja developerska/operatorska może jawnie dołączyć inne powierzchnie przez ?include=operator,developer,internal-loopback.

Dla zewnętrznie zarządzanych albo instalowanych pakietowo komponentów HTTP, które nie zwracają raportu modułu, daemon może też ładować zwalidowane sidecary deskryptorów z <data-dir>/api-descriptors/. Preferuj ścieżkę init-report, gdy komponent jest nadzorowany przez node.

Konektor Sensorium

Konektor Sensorium jest osobnym modułem middleware, który implementuje akcje za granicą organu Sensorium. sensorium-core jest mediatorem między daemonem a katalogami akcji konektorów; sam konektor, taki jak sensorium-os, nadal jest middleware'em. Akcja konektora nie jest osobnym modułem middleware: jest operacją zadeklarowaną przez konektor i mediowaną przez Sensorium Core. Ten kształt jest właściwy, gdy moduł potrzebuje kontrolowanego kontaktu z systemem operacyjnym, lokalnymi aplikacjami, sensorami, narzędziami albo innymi enactowanymi powierzchniami. Konsumenci powinni zależeć od capability Sensorium, klas akcji i kontraktów katalogu akcji, nie od twardo zakodowanej implementacji konkretnego konektora. Specjalizowany deployment może sklonować albo zastąpić konektor jako nowy moduł middleware, ale powinno to być widoczne jako nowa tożsamość modułu i katalog akcji.

Dla obecnego hard-MVP konektora sensorium-os dostępna powierzchnia runtime jest celowo wąska: skryptowe akcje C1/C2 mogą działać przez podpisany katalog, natomiast binarne C1 oraz C3-C7 są raportowane jako niedostępne i fail-closed do czasu istnienia ich enforcement envelopes. Autoryzowany wpis katalogu jest źródłem prawdy dla wykonania; lokalne wpisy allowlisty albo override'y host_policy z żądania są odrzucane.

Kształt rejestracji

  • Usługa albo pakiet konektora.
  • Katalog akcji Sensorium.
  • Raport modułu deklarujący capabilities konektora i powierzchnie operatorskie.
  • Opcjonalne skrypty akcji, szablony albo pliki polityki.

Zastosowania

  • Akcje na poziomie systemu operacyjnego, takie jak ograniczone sprawdzenia Git, deterministyczne lokalne skrypty albo wrappery specyficzne dla deploymentu, których klasa akcji i kontrakt wyniku są jawne w katalogu.
  • Bezpieczna mediacja między deklaratywnym middleware roli a silnymi lokalnymi efektami.
  • Konektory specyficzne dla deploymentu z ograniczonym katalogiem akcji.

Przykłady

{
  "module_id": "sensorium-os",
  "kind": "sensorium-connector",
  "actions": [
    {
      "action_id": "story009.publication.verify",
      "class": "allowlisted-script",
      "input_schema": "sensorium-directive.v1",
      "output_schema": "sensorium-directive-outcome.v1"
    }
  ]
}

Middleware-hosted runtime adapter Inquirium

Adapter runtime Inquirium może być middleware'em w sensie wykonania i hostowania, ale semantycznie pozostaje adapterem runtime Inquirium. To rozróżnienie jest celowe: typ wykonania odpowiada na pytanie "jak ten komponent działa?", a rola adaptera Inquirium odpowiada na pytanie "jakie tłumaczenie wykonania wolno mu robić?". Taki adapter może działać przez command_stdio, niezarządzany local_http_json, nadzorowany channel_json, handler w procesie albo późniejszy kompatybilny executor, ale nie dostaje przez to ogólnej władzy middleware'u nad route'ami, hookami, workflow ani polityką modelu.

Inquirium Core pozostaje właścicielem semantyki operacji takich jak generate, embed, classify, rerank, image.generate albo train.adapt. model-runtime zajmuje się katalogiem runtime'ów, lifecycle, health, supervision i transportem. Adapter tłumaczy request/result i szczegóły protokołu providera, a worker modelu wykonuje obliczenie bez authority Orbipleksu. Jeżeli adapter potrzebuje dostępu do dużych danych lokalnych, powinien dostać jawne lease'y i uchwyty artefaktów, nie ambient dostęp do filesystemu, sieci ani host capabilities.

Ten sam adapter nie musi oznaczać jednego modelu. Preferowany podział to: implementacja adaptera dla interfejsu, instancja adaptera dla konfiguracji lifecycle/trust boundary oraz osobny runtime/ref dla każdej routowalnej konfiguracji modelu. Dzięki temu jeden adapter instance może utrzymywać wspólną pulę HTTP, kolejkę, supervisor procesu albo cache klienta, a host nadal widzi każdy model jako osobny runtime candidate z własną polityką, zdrowiem, conformance i śladem.

To odpowiada częstemu wzorcowi warstw w agentowych orchestratorach: mechanika providera, tożsamość modelu, backend wykonawczy oraz kanał interakcji są osobnymi sprawami. Dla klasyfikacji middleware tylko backend wykonawczy może być middleware-hosted. Mechanika providera pozostaje sprawą adaptera, tożsamość modelu sprawą model binding, a kanał albo workflow orchestration pozostaje poza rolą adaptera.

Kształt rejestracji

  • Manifest adaptera Inquirium z adapter/ref, rodziną protokołu, listą operacji, modalnościami, limitami, polityką trace/retention i raportem conformance.
  • Opcjonalna konfiguracja middleware executora, np. command_stdio, local_http_json albo channel_json.
  • Health/status i init/report, jeżeli adapter jest attachable albo supervised.
  • Jawne lease'y, egress, sandbox i effects/allowed dla operacji z efektami.

Zastosowania

  • Most do lokalnego serwera modelu zarządzanego przez operatora albo host Node'a.
  • Jednorazowe opakowanie narzędzia CLI wykonującego ograniczoną inferencję.
  • Adapter zdalnego API wymagający polityki egress, sekretów, limitów i mapowania odmów.
  • Post-training, batch embedding albo przetwarzanie audio/wizji przez worker, który czyta i zapisuje wyłącznie przez scoped leases.

Przykłady

{
  "module_id": "inquirium.local-model-runtime",
  "kind": "inquirium-runtime-adapter",
  "executor": "channel_json",
  "adapter_manifest": {
    "adapter/ref": "adapter:local-model-runtime",
    "hosting/kind": "middleware-hosted",
    "operations": ["generate", "embed", "batch.embed"],
    "modalities/input": ["text"],
    "modalities/output": ["text", "embedding"],
    "effects/allowed": [
      { "kind": "fs/read", "lease/ref": "input-lease" },
      { "kind": "fs/write", "lease/ref": "artifact-output-lease" }
    ]
  }
}

Czym jest Role Middleware?

Role middleware to middleware, który pełni funkcję providera albo dyspozytora dla nazwanego kontraktu roli/usługi. Nie jest to typ wykonania taki jak nadzorowany channel JSON, JSON-e Flow, command/stdio albo Rust w procesie. To rola funkcjonalna: komponent otrzymuje ograniczone żądanie w stylu "wykonaj rolę editorial-review" albo "obsłuż rolę providera offer-catalog", wybiera właściwe zachowanie i zwraca wynik w stylu service-dispatch-response pod zadeklarowanym kontraktem. Ten sam wzorzec role middleware może być zaimplementowany różnymi typami wykonania, zależnie od tego, ile authority, stanu i złożoności runtime potrzebuje dana rola.

Praktyczne rozróżnienie jest takie:

  • typ wykonania odpowiada na pytanie "jak ten middleware działa?",
  • role middleware odpowiada na pytanie "jaką odpowiedzialność dispatchu ten middleware pełni?".

Role middleware nie powinien stawać się generycznym runnerem skryptów ani ukrytym serwerem aplikacyjnym. Powinien reklamować capability roli, którą udostępnia, walidować przychodzące żądanie roli, produkować śledzalną odpowiedź i używać host capabilities tylko przez jawne allowlisty. W Story-009 providerzy ról dla kompozycji szkicu, przygotowania ilustracji, przeglądu redakcyjnego, publikacji i weryfikacji są przykładami tego kształtu. Część z nich lepiej pasuje do JSON-e Flow, bo są ograniczonymi adapterami; inne mogą stać się supervised channel modules, jeśli potrzebują stanu, kolejek, bogatszej polityki albo operatorskiego UI.

Role middleware przez nadzorowany channel JSON

Nadzorowany channel role middleware jest użyteczny, gdy provider potrzebuje realnego procesu: trwałego lokalnego stanu, kolejkowania, nietrywialnej logiki domenowej, powierzchni HTML dla operatora albo integracji z sąsiednimi narzędziami. Daemon startuje i monitoruje proces, wykonuje handshake init/report kanału i dispatchuje żądania roli do modułu przez jawny kontrakt kanału. Moduł powinien rozgałęziać się po role capability albo service type w żądaniu, nie po ukrytym stanie daemona.

{
  "module_id": "story009-roles-http",
  "executor": "channel_json",
  "capabilities": [
    {
      "capability_id": "role/story009.editorial-review.execute",
      "kind": "service-dispatch-provider"
    }
  ],
  "invoke_path": "/v1/roles/dispatch"
}
def dispatch_role(request):
    role = request["role_capability_id"]

    if role == "role/story009.editorial-review.execute":
        return {
            "schema_version": "v1",
            "capability_id": "service_dispatch_execute",
            "status": "completed",
            "dispatch/id": request["dispatch/id"],
            "answer/content": {"decision": "accepted"},
            "answer/format": "json",
            "confidence/signal": 0.82,
            "human-linked-participation": False
        }

    return {
        "schema_version": "v1",
        "capability_id": "service_dispatch_execute",
        "status": "rejected-invalid-request",
        "dispatch/id": request.get("dispatch/id"),
        "reason": "unsupported role capability"
    }

Ten kształt pasuje do providerów podobnych do Datora, usług roli sąsiadujących z Arką albo modułów instalowanych przez operatora, których zachowanie jest zbyt bogate dla deklaratywnego flow.

Role middleware przez JSON-e Flow

JSON-e Flow role middleware jest preferowanym niskokodowym kształtem, gdy rola jest ograniczonym adapterem: wyrenderuj żądanie, opcjonalnie wywołaj dozwoloną host capability, wyciągnij wynik i odpowiedz. Każda definicja flow jest operacyjnie osobnym komponentem role middleware, nawet jeżeli działa przez współdzielony executor JSON-e Flow. Dzięki temu operator może zainstalować albo sprawdzić providera roli jako dane, bez dawania mu dostępu do systemu operacyjnego.

{
  "id": "story009.editorial.review",
  "module_id": "story009.editorial.review",
  "executor": "json_e_flow",
  "bindings": {
    "role_capability_id": "role/story009.editorial-review.execute"
  },
  "limits": { "timeout_ms": 500, "max_steps": 6 },
  "steps": [
    {
      "id": "respond",
      "kind": "respond",
      "template": {
        "schema_version": "v1",
        "capability_id": "service_dispatch_execute",
        "status": "completed",
        "dispatch/id": "${request.dispatch/id}",
        "completed-at": "${now}",
        "answer/content": {
          "decision": "accepted",
          "notes": "Rendered by a bounded JSON-e Flow role provider."
        },
        "answer/format": "json",
        "confidence/signal": 1.0,
        "human-linked-participation": false
      }
    }
  ]
}

Używaj JSON-e Flow role middleware dla adapterów roli, które da się opisać jako dane i których efekty są na tyle wąskie, że można je zadeklarować jako kroki należące do hosta. Przejdź do nadzorowanego channel JSON, gdy rola zaczyna wymagać bogatszej granicy runtime.

Gdzie middleware może wpinać się w ścieżkę danych node'a?

Middleware może wpinać się w różne miejsca ścieżki danych node'a. Hook mówi, w którym miejscu komunikat staje się widoczny dla komponentu i jakiego rodzaju decyzję komponent może zwrócić. Jest to osobne od typu wykonania: nadzorowany moduł channel, definicja JSON-e Flow albo handler Rust w procesie mogą uczestniczyć w dispatchu, ale każdy robi to przez powierzchnię należącą do hosta, z jawną walidacją, timeoutami, bramami capability i rekordami trace. Host pozostaje odpowiedzialny za routing i authority; middleware wnosi ograniczone zachowanie w zadeklarowanym punkcie podpięcia. Moduł powinien podpinać się do najwęższego hooka, który odpowiada jego realnej potrzebie, zamiast subskrybować szeroką fazę, bo jest to wygodne.

Ogólny słownik decyzji middleware jest zależny od hooka, ale obecne nazwy to:

  • allow - przepuść wejście do następnego etapu hosta albo handlera.
  • annotate - pozwól wejściu płynąć dalej, dodając metadane widoczne dla hosta tam, gdzie hook ma konkretny nośnik adnotacji.
  • rewrite - zastąp albo spatchuj payload widoczny dla hosta przed kontynuacją.
  • route - wybierz jawny cel albo następną trasę tam, gdzie hook to wspiera; obecne standardowe chain allowlists nie dopuszczają route jako samodzielnej decyzji, a lokalny routing używa dyrektyw trasy niesionych obok dozwolonych decyzji lokalnych.
  • return - przerwij ścieżkę odpowiedzią albo finalnym payloadem.
  • drop - zatrzymaj przetwarzanie bez udanej odpowiedzi.
  • defer - odmów podjęcia decyzji teraz i pozwól działać kolejnemu etapowi polityki hosta.
  • reject - odmów żądaniu z jawnym błędem/statusem.

Nie każdy hook może emitować każdą decyzję. Niektóre punkty podpięcia, takie jak wywołania host capabilities albo route'y operatorskiego UI, używają własnych kontraktów odpowiedzi zamiast middleware-decision.v1.

Hook fazy pre-input

pre-input jest pierwszym przejściem należącym do hosta, zanim żądanie wejdzie w konkretną rodzinę dispatchu. Służy do przekrojowego potraktowania przychodzącego triggera: normalizacji, redakcji, zachowania raw-signal, wczesnej klasyfikacji albo lokalnych checków polityki, które muszą wydarzyć się zanim daemon zdecyduje, czy wejście jest lokalnym żądaniem HTTP, wiadomością peer, zdarzeniem broadcast czy zadaniem workflow. Należy używać go oszczędnie, bo szerokie hooki zwiększają koszt poznawczy i mogą stać się ukrytym couplingiem. Uczestnik pre-input powinien zwykle adnotować albo przygotować kontekst trace, a nie przejmować obsługę domenową.

Zastosowania

  • Zachowanie raw_signal dla ścieżek wykonawczych, które go wymagają.
  • Inicjalizacja causality_id i component_path[].
  • Lokalna redakcja albo klasyfikacja zanim zacznie się węższy dispatch.

Przykładowe podpięcie

{
  "module_id": "example.pre-input-policy",
  "input_chains": ["pre-input"],
  "decision_contract": "middleware-decision.v1"
}

Możliwe decyzje

  • allow - kontynuuj do normalnego wyboru rodziny dispatchu.
  • annotate - dodaj lokalne metadane i kontynuuj tam, gdzie host może reprezentować adnotację. W obecnej peer-message ścieżce pre-input adnotacja jest przyjmowana przez słownik, ale nie jest jeszcze reprezentowana na PeerMessageEnvelope, więc efektywnie jest pass-through.
  • rewrite - znormalizuj albo zredaguj trigger zanim zacznie się węższy dispatch.
  • drop - zatrzymaj przetwarzanie zanim jakakolwiek konkretna rodzina dispatchu zobaczy wejście.

Szkic implementacji

Konfiguracja deklaruje szeroki hook; implementacja powinna pozostać mała:

{
  "module_id": "example.pre-input-policy",
  "input_chains": ["pre-input"],
  "executor": "json_e",
  "output_contract": "middleware-decision.v1"
}
{
  "decision": "annotate",
  "annotations": { "classification": "operator-local" },
  "diagnostics": {}
}

Znane użycia

  • Kontekst dispatchu raw-signal i component-path w daemonie.
  • Żaden obecny fabryczny middleware nie powinien polegać na tym jako na szerokim punkcie przechwytywania logiki biznesowej.

Kompatybilne typy middleware

  • Middleware Rust w procesie.
  • Czysty middleware JSON-e, dla czystej normalizacji albo renderowania decyzji.
  • Middleware JSON-e Flow, gdy faza potrzebuje ograniczonych efektów należących do hosta.
  • Middleware command/stdio, technicznie możliwy, ale zwykle zbyt ciężki dla tej szerokiej fazy.
  • Niezarządzany lokalny middleware HTTP JSON, dla lokalnych usług polityki należących do operatora.
  • Nadzorowany middleware channel JSON, dla silnych lokalnych usług polityki, które uzasadniają szeroki hook.
  • Middleware konektora Sensorium tylko pośrednio, gdy jest też supervised service i ma jawny powód uczestnictwa; szerokie podpięcie pre-input nie powinno być domyślne dla konektorów.

Zgłoszone lokalne route'y i inbound local hooks

Lokalny dispatch obejmuje żądania HTTP odbierane przez lokalnego daemona. Moduł może zgłosić wyłączny lokalny route, zwykle pod /v1/enact/*, albo uczestniczyć w bardziej generycznym łańcuchu inbound-local. Zgłoszone route'y są właściwe, gdy moduł jest właścicielem konkretnej lokalnej powierzchni API. Generyczne hooki inbound-local są lepsze dla małych adnotacji żądań, lokalnych decyzji polityki albo helperów routingu. Jeżeli zgłoszony route istnieje, ale jego moduł właściciel nie jest gotowy, host zwraca lokalną odpowiedź unavailable zamiast po cichu routować do innego komponentu.

Zastosowania

  • Udostępnianie lokalnych API należących do modułu.
  • Dodawanie operator-local request handling bez zmian w kodzie daemona.
  • Adaptacja wywołań lokalnej aplikacji do decyzji middleware.

Przykładowe podpięcie

{
  "module_id": "example.local-route",
  "claimed_routes": [
    { "method": "POST", "path": "/v1/enact/example.local-route/run" }
  ]
}

Możliwe decyzje

  • allow - pozwól lokalnemu żądaniu przejść do następnego lokalnego handlera.
  • rewrite - spatchuj albo zastąp payload lokalnego żądania przed kontynuacją.
  • return - przerwij ścieżkę lokalną odpowiedzią HTTP.
  • reject - odmów żądaniu z jawnym lokalnym błędem/statusem.

Szkic implementacji

Konfiguracja zgłasza route; żywy moduł obsługuje potem żądanie przez należący do daemona most modułu i dispatch kanałowy:

{
  "module_id": "example.local-route",
  "executor": "channel_json",
  "claimed_routes": [
    { "method": "POST", "path": "/v1/enact/example.local-route/run" }
  ]
}
def handle_run(request):
    return {
        "status": "ok",
        "result": {"echo": request["json"]}
    }

Znane użycia

  • Nadzorowany middleware channel publikujący route'y modułu przez middleware-module-report.
  • Pakiety instalowane przez operatora, które wnoszą metadane UI albo lokalnych route'ów.

Kompatybilne typy middleware

  • Middleware Rust w procesie.
  • Czysty middleware JSON-e, dla generycznych transformacji inbound-local; nie powinien samodzielnie posiadać bogatego zachowania HTTP route'a.
  • Middleware JSON-e Flow, dla lokalnych adapterów żądań z zadeklarowanymi krokami.
  • Middleware command/stdio, dla ograniczonych jednorazowych handlerów lokalnych żądań.
  • Niezarządzany lokalny middleware HTTP JSON, gdy inny supervisor posiada lokalną usługę.
  • Nadzorowany middleware channel JSON, normalny kształt dla zgłoszonych route'ów modułu.
  • Middleware konektora Sensorium, gdy konektor wystawia lokalne route'y modułu przez supervised service albo metadane pakietu.

Role and Service Dispatch

Role and service dispatch to rodzina hooków używana wtedy, gdy workflow prosi o usługę podobną do capability, a nie o surowe wywołanie procesu. Host routuje service-dispatch-request albo żądanie roli do providera, waliduje odpowiedź i zapisuje materiał trace. To naturalna powierzchnia dla workflow w stylu Datora i Arki, bo żądanie jest już sformułowane jako "wykonaj tę rolę/usługę pod tym kontraktem", a nie "obsłuż tę ścieżkę HTTP". Deklaratywne adaptery takie jak JSON-e Flow pasują tu dobrze, gdy potrzebują tylko przekształcić ograniczone żądanie roli i wywołać dozwolone host capabilities.

Zastosowania

  • Routowanie kroków workflow Arki do providerów odkrytych przez Datora.
  • Adaptacja żądań roli do dyrektyw Sensorium.
  • Zwracanie ograniczonych wartości service-dispatch-response ze śledzalną semantyką decyzji.

Przykładowe podpięcie

{
  "module_id": "story009.editorial.review",
  "role_capability_id": "role/story009.editorial-review.execute",
  "executor": "json_e_flow"
}

Możliwe decyzje

  • completed - provider ukończył żądanie i zwraca treść odpowiedzi, format odpowiedzi, sygnał pewności i metadane ukończenia.
  • rejected-invalid-request - provider odmówił żądaniu, bo nie spełniało jego kontraktu.
  • failed - provider podjął albo przyjął pracę, ale nie mógł jej ukończyć.

Szkic implementacji

Konfiguracja wiąże tożsamość providera z role capability; implementacją może być JSON-e Flow albo supervised service:

{
  "module_id": "story009.editorial.review",
  "executor": "json_e_flow",
  "bindings": {
    "role_capability_id": "role/story009.editorial-review.execute"
  },
  "steps": [
    {
      "id": "response",
      "kind": "respond",
      "template": {
        "schema_version": "v1",
        "capability_id": "service_dispatch_execute",
        "status": "completed",
        "dispatch/id": "dispatch:story009.editorial.review:example",
        "completed-at": "${now}",
        "answer/content": { "decision": "accepted" },
        "answer/format": "json",
        "confidence/signal": 1.0,
        "human-linked-participation": false
      }
    }
  ]
}

Znane użycia

  • arca - orkiestracja po stronie workflow i emisja żądań roli/usługi.
  • dator - katalog ofert i provider-side service dispatch.
  • Definicje roli JSON-e Flow w Story-009 - ograniczone adaptery między żądaniami roli a wywołaniami host capabilities.

Kompatybilne typy middleware

  • Middleware Rust w procesie, dla providerów roli należących do hosta.
  • Czysty middleware JSON-e, dla adapterów roli zwracających tylko odpowiedź i niewymagających efektów.
  • Middleware JSON-e Flow, preferowany deklaratywny kształt dla ograniczonych adapterów roli z dozwolonymi wywołaniami hosta.
  • Middleware command/stdio, dla jednorazowych providerów roli ze ścisłymi limitami.
  • Niezarządzany lokalny middleware HTTP JSON, dla providerów nadzorowanych zewnętrznie.
  • Nadzorowany middleware channel JSON, normalny kształt dla bogatych providerów takich jak Dator i usługi sąsiadujące z Arką.
  • Middleware konektora Sensorium zwykle uczestniczy za Sensorium Core, a nie jako bezpośredni provider roli.

Most Host Capability

Most host capability jest używany, gdy middleware potrzebuje ograniczonej operacji należącej do daemona albo organu, takiej jak zapis faktu do Memarium, emisja notyfikacji, dispatch wiadomości peer, wystawienie capability passport albo wywołanie akcji Sensorium. Moduł nie otrzymuje ambient daemon authority; otrzymuje tylko te wywołania hosta, które są zadeklarowane i przyznane przez lokalną konfigurację, auth modułu, capability passports i dispatch gates. Ten hook jest właściwym miejscem przejścia z zachowania middleware do efektów należących do hosta. Nie powinien być używany jako generyczna furtka do dowolnych wewnętrznych API.

Zastosowania

  • Zapis ograniczonego faktu do Memarium z kroku JSON-e Flow.
  • Emisja notyfikacji operatora po decyzji modułu.
  • Wywołanie Sensorium Core w celu mediacji akcji konektora Sensorium.
  • Dispatch wiadomości peer przez powierzchnie sieciowe należące do hosta.

Przykładowe podpięcie

{
  "module_id": "example.fact-writer",
  "allowed_calls": [
    { "capability": "memarium.write", "operation": "write" }
  ]
}

Możliwe decyzje

Middleware providera, który potrzebuje odkrywalnego capability passport, deklaruje stan pożądany przez capability.passport.reconcile; nie powinien posiadać kolejnej pętli issue/persist/publish/retry. Uwierzytelniony identyfikator modułu jest autorytatywny i musi być równy issued_for_module_id. Pominięcie publication oznacza fail-closed local-only:

{
  "schema_version": "v1",
  "capability_id": "capability_passport_reconcile",
  "issued_for_module_id": "contact-catalog-service",
  "requested_capability_id": "contact-catalog",
  "scope": {"catalog_kind": "contact"}
}

Publikacja w Seed Directory wymaga dodatkowo publication.mode = seed-directory oraz dokładnego policy ref/revision hosta. Moduły obserwują bounded retry, częściowy postęp endpointów, renewal i revocation przez GET /v1/capability-passport-publications?limit=N (N ma domyślnie 50 i maksimum 100); nie dostają autorytetu do publikowania dowolnego poznanego passport id. Niskopoziomowe capability.passport.issue i capability.passport.publish pozostają osobnymi efektami host-owned dla jawnych przepływów operatorskich/domenowych.

Operator dopuszcza dokładne pary moduł/capability w konfiguracji demona. Allowlista deklaracji kontroluje lokalne wydawanie przez reconciler, a lista Seed Directory jest dodatkową granicą egress:

{
  "passport_publication": {
    "declaration_allowlist": {
      "contact-catalog-service": ["contact-catalog"]
    },
    "seed_directory_allowlist": {
      "contact-catalog-service": ["contact-catalog"]
    },
    "max_declarations": 256,
    "max_declarations_per_module": 32
  }
}

Każda wpisana capability musi być passport-eligible w Capability Registry; wpisy publiczne muszą być też discovery-eligible. Pusta albo nieobecna admisja publiczna pozostaje fail-closed local-only. Operator może zawężać pojemności, ale nie może przekroczyć sufitów hosta: 4096 deklaracji łącznie i 256 na moduł.

Most host capability nie używa bezpośrednio middleware-decision.v1. Konkretne capabilities mają własne kontrakty odpowiedzi, ale zaimplementowane klasy wyniku to:

  • success - host capability zwraca HTTP 200, 201 albo 202 i w body odpowiedzi nie wykryto semantycznego statusu awarii.
  • host_capability_forbidden - caller/moduł nie ma prawa wywołać tej host capability.
  • host_capability_unavailable - nie ma zarejestrowanego handlera albo moduł handlera nie jest gotowy.
  • host_capability_dispatch_error - host nie mógł zbudować albo zautoryzować lokalnego żądania dispatchu.
  • host_capability_dispatch_failed - wywołanie handlera nie powiodło się albo nie dało się odczytać jego odpowiedzi.
  • semantyczne statusy awarii takie jak failed, error, timed_out, timeout, rejected, rejected-invalid-request, not_authorized, revocation_stale, passport_expired, passport_invalid, passport_revoked i policy_denied - traktowane przez JSON-e Flow jako nieudane wykonanie host capability.

Szkic implementacji

Konfiguracja deklaruje dozwolone wywołanie; implementacja wywołuje capability przez krok należący do hosta albo endpoint modułu, a nie przez importowanie wewnętrznych części daemona:

{
  "module_id": "example.fact-writer",
  "executor": "json_e_flow",
  "allowed_calls": [
    { "capability": "memarium.write", "operation": "write" }
  ],
  "steps": [
    {
      "id": "write-fact",
      "kind": "call",
      "capability": "memarium.write",
      "operation": "write"
    }
  ]
}

Znane użycia

  • Definicje ról JSON-e Flow w Story-009 - memarium.write i publikacja faktów workflow.
  • sensorium-core i sensorium-os - mediacja akcji Sensorium.
  • arca i dator - wywołania hosta związane z workflow, dispatch i publikacją.

Kompatybilne typy middleware

  • Middleware Rust w procesie.
  • Middleware JSON-e Flow, bo efekty są deklarowane jako kroki wykonywane przez hosta.
  • Middleware command/stdio, tylko przez wrapper hosta przyznający jawne wywołania capability; sama komenda nie powinna otrzymywać ambient authority.
  • Niezarządzany lokalny middleware HTTP JSON, gdy jest związany auth modułu i jawnymi dozwolonymi wywołaniami.
  • Nadzorowany middleware channel JSON, standardowy kształt procesowy dla konsumentów host capabilities.
  • Middleware konektora Sensorium, przez mediację capability/akcji należącą do Sensorium.
  • Czysty middleware JSON-e nie jest kompatybilny z bezpośrednimi wywołaniami host capability; użyj JSON-e Flow, gdy szablon potrzebuje efektów.

Peer Message Dispatch

Peer message dispatch to rodzina hooków dla wiadomości, które docierają po dekodowaniu sieci/sesji. Jest węższa niż lokalny routing HTTP i niesie kontrakty zorientowane na peer, takie jak wywołanie peer message, ustanowienie sesji, wymiana artefaktów albo prezentacja capability. Wbudowane handlery protokołu powinny posiadać prawdę protokołu, ale middleware może uczestniczyć tam, gdzie zachowanie rozszerzające jest jawnie zadeklarowane. Hook peer-message musi być szczególnie uważny na timeouty, semantykę replay, walidację wejścia i diagnostykę odmów, bo leży na granicy federacyjnej.

Zastosowania

  • Obsługa rozszerzeniowych wiadomości peer bez zmieniania głównego handlera protokołu.
  • Routowanie pracy inter-node artifact channel do uczestnika poza procesem.
  • Podpinanie katalogu ofert albo prezentacji capability na granicy peer.

Przykładowe podpięcie

{
  "module_id": "example.peer-handler",
  "input_chains": ["inbound-peer"],
  "message_kinds": ["example.peer-message.v1"]
}

Możliwe decyzje

  • allow - przekaż wiadomość peer do wbudowanych albo późniejszych handlerów peer.
  • rewrite - znormalizuj zdekodowaną wiadomość peer przed kontynuacją.
  • return - wyprodukuj odpowiedź peer i zatrzymaj dalszy dispatch peer.
  • drop - zatrzymaj przetwarzanie wiadomości peer bez udanej odpowiedzi.

Szkic implementacji

Konfiguracja subskrybuje chain peer i typ wiadomości; implementacja zwraca ograniczoną decyzję albo odpowiedź peer przez kontrakt hosta:

{
  "module_id": "example.peer-handler",
  "executor": "channel_json",
  "input_chains": ["inbound-peer"],
  "message_kinds": ["example.peer-message.v1"]
}
{
  "decision": "return",
  "annotations": {},
  "diagnostics": {},
  "patch_strategy": "json_merge_patch",
  "patch": { "response": { "status": "ok" } }
}

Znane użycia

  • Wbudowane handlery protokołu peer dla capability, schema, ledger i wymiany artefaktów.
  • Przyszłe handlery peer poza procesem używające podpięcia channel_json albo local_http_json.

Kompatybilne typy middleware

  • Middleware Rust w procesie, dla handlerów sąsiadujących z protokołem.
  • Czysty middleware JSON-e, dla wąskiej normalizacji wiadomości peer albo renderowania decyzji.
  • Middleware JSON-e Flow, dla ograniczonych adapterów wiadomości peer z zadeklarowanymi wywołaniami.
  • Middleware command/stdio, technicznie możliwy dla ograniczonych handlerów, ale zwykle zbyt kosztowny dla gorących ścieżek federacyjnych.
  • Niezarządzany lokalny middleware HTTP JSON, dla handlerów peer nadzorowanych zewnętrznie.
  • Nadzorowany middleware channel JSON, dla handlerów peer poza procesem z readiness i cyklem życia.
  • Middleware konektora Sensorium nie jest naturalnym hookiem peer-message; użyj roli, usługi albo mostu host capability, gdy wejście peer ma wywołać lokalne enaction.

Hooki broadcast

Hooki broadcast obserwują albo transformują zdarzenia broadcast zanim staną się lokalnymi efektami albo materiałem wychodzącego relay. Są użyteczne dla moderacji, adnotacji, lokalnej polityki i filtrowania, ale nie powinny stać się ukrytym globalnym porządkiem logiki biznesowej. Obsługa broadcast powinna zachować rozróżnienie między tym, że zdarzenie zostało zobaczone, lokalnie przyjęte, przekazane dalej albo zapisane. Jeżeli moduł potrzebuje trwałej interpretacji, zwykle powinien zapisać jawny fakt przez host capability, zamiast mutować broadcast w miejscu.

Zastosowania

  • Adnotacja albo klasyfikacja zdarzeń broadcast.
  • Zastosowanie lokalnej polityki moderacji przed forwardowaniem.
  • Drop albo kwarantanna materiału broadcast zgodnie z polityką operatora.

Przykładowe podpięcie

{
  "module_id": "example.broadcast-policy",
  "input_chains": ["inbound-broadcast"],
  "decision_contract": "middleware-decision.v1"
}

Możliwe decyzje

  • allow - przyjmij zdarzenie broadcast do następnego etapu hosta.
  • annotate - dodaj metadane polityki i kontynuuj.
  • rewrite - spatchuj payload widoczny dla broadcast przed kontynuacją.
  • drop - zatrzymaj lokalne przetwarzanie albo forwardowanie tego zdarzenia.
  • defer - odmów decyzji i pozwól działać kolejnemu etapowi polityki.

Szkic implementacji

Konfiguracja subskrybuje chain broadcast; implementacja emituje wąską decyzję polityki:

{
  "module_id": "example.broadcast-policy",
  "executor": "json_e",
  "input_chains": ["inbound-broadcast"],
  "output_contract": "middleware-decision.v1"
}
{
  "decision": "drop",
  "reason": "blocked by local relay policy",
  "annotations": {},
  "diagnostics": { "policy": "example.broadcast-policy" }
}

Znane użycia

  • Żaden produkcyjny fabryczny middleware nie jest obecnie udokumentowany jako właściciel dedykowanego hooka broadcast.
  • Komponenty skierowane do Agory są prawdopodobnymi przyszłymi użytkownikami powierzchni polityki relay i broadcast.

Kompatybilne typy middleware

  • Middleware Rust w procesie.
  • Czysty middleware JSON-e, dla adnotacji, klasyfikacji albo decyzji polityki.
  • Middleware JSON-e Flow, gdy obsługa broadcast potrzebuje ograniczonych efektów należących do hosta.
  • Middleware command/stdio, dla slow-path albo operator-local broadcast checks.
  • Niezarządzany lokalny middleware HTTP JSON, dla zewnętrznie nadzorowanych usług polityki.
  • Nadzorowany middleware channel JSON, dla bogatszych usług moderacji, relay albo polityki.
  • Middleware konektora Sensorium zwykle nie jest właściwy, chyba że zdarzenie broadcast celowo staje się lokalną akcją mediowaną przez Sensorium.

Hooki pre-send i egress

pre-send jest ostatnim punktem mutacji albo decyzji zanim odpowiedź, wiadomość peer albo zdarzenie broadcast opuści bieżącą ścieżkę dispatchu należącą do hosta. Nie jest miejscem do ponownego odkrywania znaczenia biznesowego; to finalna granica dla kształtowania odpowiedzi, metadanych, lokalnej redakcji albo decyzji deny/drop, które muszą wydarzyć się po tym, gdy główny handler wyprodukował wyjście. Ponieważ leży późno na ścieżce, powinien być deterministyczny i mały. Kosztowna praca należy do role/service dispatch, wywołań host capability albo bogatszego supervised module przed egress.

Zastosowania

  • Dodanie finalnych lokalnych metadanych przed egress.
  • Redakcja albo normalizacja wychodzących payloadów na granicy.
  • Drop wychodzącej wiadomości naruszającej lokalną politykę.

Przykładowe podpięcie

{
  "module_id": "example.pre-send-policy",
  "input_chains": ["pre-send"],
  "decision_contract": "middleware-decision.v1"
}

Możliwe decyzje

  • allow - wyślij bieżące wyjście bez zmian.
  • rewrite - spatchuj albo zastąp wychodzący payload przed wysłaniem.
  • drop - zatrzymaj wyjście przed opuszczeniem tej ścieżki dispatchu.

Szkic implementacji

Konfiguracja subskrybuje chain egress; implementacja zwraca małą decyzję brzegową:

{
  "module_id": "example.pre-send-policy",
  "executor": "json_e",
  "input_chains": ["pre-send"],
  "output_contract": "middleware-decision.v1"
}
{
  "decision": "rewrite",
  "patch_strategy": "json_merge_patch",
  "patch": { "headers": { "x-orbiplex-local-policy": "applied" } },
  "annotations": {},
  "diagnostics": {}
}

Znane użycia

  • Kształtowanie odpowiedzi, metadane, polityka deny/drop i finalna lokalna redakcja.
  • Żaden produkcyjny fabryczny middleware nie jest obecnie udokumentowany jako właściciel dedykowanego hooka pre-send.

Kompatybilne typy middleware

  • Middleware Rust w procesie.
  • Czysty middleware JSON-e, dla finalnych czystych transformacji.
  • Middleware JSON-e Flow, tylko gdy egress wymaga ograniczonych efektów należących do hosta.
  • Middleware command/stdio, technicznie możliwy, ale zwykle zbyt kosztowny dla tej późnej granicy.
  • Niezarządzany lokalny middleware HTTP JSON, dla usług polityki egress należących do operatora.
  • Nadzorowany middleware channel JSON, dla bogatszych powierzchni polityki egress, gdy latencja jest akceptowalna.
  • Middleware konektora Sensorium nie jest naturalnym hookiem egress; użyj mediacji akcji Sensorium wcześniej na ścieżce.

Powierzchnie operatorskiego UI

Powierzchnie operatorskiego UI nie są hookami data-plane, ale są ważnym punktem podpięcia middleware. Moduł albo pakiet może wnieść szablony UI, statyczne assety, metadane route'ów i workflow operatorskie, aby wbudowane Node UI nie musiało mieć twardo zakodowanej wiedzy o każdym przyszłym module. Daemon nadal jest właścicielem montowania route'ów, kontekstu sesji, autoryzacji i bezpiecznych granic renderowania. To właściwa powierzchnia dla dashboardów lokalnych dla modułu, ekranów przeglądu akcji, konfiguracji pakietu i czytelnych dla człowieka eksploratorów trace.

Zastosowania

  • Udostępnienie historii uruchomień i stron statusu modułu.
  • Dodanie stron konfiguracji operatora dla zainstalowanych pakietów.
  • Renderowanie specyficznych dla modułu ekranów przeglądu akcji albo workflow readiness.

Przykładowe podpięcie

{
  "module_id": "example.operator-ui",
  "operator_surfaces": [
    { "path": "/middleware/example/", "template": "ui/index.html" }
  ]
}

Możliwe decyzje

Powierzchnie operatorskiego UI nie używają middleware-decision.v1; ich wybory na poziomie kodu to tryby własności renderowania i wyniki HTTP/UI:

  • host-mediated - Node UI posiada renderowany HTML używając metadanych pakietu/modułu.
  • server-html - żywy moduł middleware posiada HTML, a Node UI proxy'uje powierzchnię same-origin.
  • unavailable - Node UI renderuje stronę unavailable/error, gdy nie da się rozwiązać pakietu, powierzchni albo modułu backing.
  • redirect-rewrite - redirecty same-origin z proxy'owanego server HTML mogą zostać przepisane pod zamontowaną powierzchnię; redirecty zewnętrzne są odrzucane.

Szkic implementacji

Konfiguracja deklaruje powierzchnię; implementacją może być statyczne UI pakietu albo żywy supervised route:

{
  "module_id": "example.operator-ui",
  "operator_surfaces": [
    {
      "path": "/middleware/example/",
      "template": "ui/index.html",
      "requires_operator_session": true
    }
  ]
}
<section>
  <h1>Example middleware</h1>
  <p>Status is rendered by the host from module report data.</p>
</section>

Znane użycia

  • arca - UI uruchomień workflow.
  • Powierzchnie trace JSON-e Flow.
  • Przykłady pakietów instalowanych przez operatora z materiałem ui/ i ui-op/.

Kompatybilne typy middleware

  • Middleware Rust w procesie, gdy UI należy do hosta.
  • Czysty middleware JSON-e, przez widoki metadanych i trace/config zamiast żywego serwera UI.
  • Middleware JSON-e Flow, przez powierzchnie trace, statusu i konfiguracji.
  • Middleware command/stdio, przez host-rendered status/config surfaces.
  • Niezarządzany lokalny middleware HTTP JSON, jeżeli operator jawnie akceptuje zewnętrznie zarządzany endpoint UI.
  • Nadzorowany middleware channel JSON, normalny kształt dla żywego UI modułu.
  • Middleware konektora Sensorium, przez katalogi akcji konektora i powierzchnie operatorskie należące do konektora.
  • Pakiety instalowane przez operatora i moduły dostarczane fabrycznie mogą wnosić assety UI; to kwestia modelu dystrybucji nałożona na typ wykonania.

Obserwatorzy i hooki audit

Obserwatorzy i hooki audit są powierzchniami widoczności. Mają zapisywać, co się wydarzyło, a nie stawać się kolejną ukrytą warstwą decyzji. Nowi konsumenci powinni preferować obserwatorów faz i obserwatorów post-chain dla widoczności; audit pozostaje powierzchnią kompatybilności po dispatchu. Rekordy trace powinny zachowywać causality, component path, wybrane podsumowania oraz skonfigurowane raw-signal albo szczegóły component I/O bez wyciekania sekretów lub niepotrzebnych payloadów. Jeżeli obserwator musi wpływać na zachowanie, powinien zostać zamodelowany jako prawdziwy hook dispatchu zamiast efektu ubocznego audytu.

Zastosowania

  • Zapis podsumowań component I/O trace.
  • Emisja rekordów audit dla decyzji autoryzacji albo dispatchu.
  • Diagnostyka operatorska bez zmiany zachowania runtime.

Przykładowe podpięcie

{
  "module_id": "example.trace-observer",
  "observes": ["pre-input", "inbound-local", "post-chain"],
  "mode": "observer"
}

Możliwe decyzje

  • allow - kompatybilnościowa odpowiedź oczekiwana przez legacy handlery audit invoke. Obserwacja nie może zmieniać funkcjonalnego wyniku dispatchu, a requesty obserwatorów używające peer-message-observe.v1 nie używają middleware-decision.v1.

Awarie wywołania obserwatora są polityką hosta, nie decyzjami middleware. Obecna ścieżka peer audit wywołuje obserwatorów asynchronicznie i loguje awarie bez zmiany wyniku widocznego dla callera.

Szkic implementacji

Konfiguracja deklaruje wyłącznie obserwację; implementacja zapisuje i nie zwraca decyzji biznesowej:

{
  "module_id": "example.trace-observer",
  "observes": ["pre-input", "inbound-local", "post-chain"],
  "mode": "observer"
}
fn record(event: TraceEvent) {
    tracing::info!(
        event = "middleware_trace_observed",
        component_path = ?event.component_path,
        decision = ?event.decision
    );
}

Znane użycia

  • Powierzchnie trace daemona dla dispatchu middleware.
  • Sinki audit autoryzacji i host capability.
  • Widoki trace i digest kroków JSON-e Flow.

Kompatybilne typy middleware

  • Middleware Rust w procesie, dla sinków audit i kolektorów trace należących do hosta.
  • Czysty middleware JSON-e, dla czystej projekcji podsumowań trace.
  • Middleware JSON-e Flow, dla ograniczonych workflow obserwatora.
  • Middleware command/stdio, dla ograniczonych zadań eksportu albo diagnostyki.
  • Niezarządzany lokalny middleware HTTP JSON, dla usług obserwowalności zarządzanych przez operatora.
  • Nadzorowany middleware channel JSON, dla bogatszych konsumentów audit/trace z własnym cyklem życia.
  • Middleware konektora Sensorium powinien zwykle emitować obserwacje przez Sensorium i powierzchnie audit hosta, a nie podpinać się jako generyczny obserwator, chyba że ta rola jest jawnie zadeklarowana.

Jak jeden nadzorowany middleware rozróżnia wywołania z wielu hooków?

Nadzorowany middleware channel może podpinać się do więcej niż jednego hooka. Na przykład jeden moduł może obsługiwać input chain i jednocześnie obserwować audit chain. Host może skierować oba wywołania do tego samego handlera modułu, jeżeli raport używa jednej zadeklarowanej ścieżki invoke dla obu rejestracji. To jest legalne, ale ścieżka route'a nie jest semantycznym rozróżnikiem. Kanonicznym rozróżnikiem jest koperta żądania, zwłaszcza chain_kind.

Dla ścieżek handlerów peer-message host wysyła PeerMessageInvokeRequest. Legacy powierzchnia peer-message audit również używa tego kształtu invoke, ale jej wynik jest obserwacyjny: zwrócone decyzje i awarie wywołania nie zmieniają wyniku dispatchu widocznego dla callera. Ten sam endpoint może otrzymywać wywołania inbound-peer i legacy audit:

{
  "schema_version": "v1",
  "envelope_kind": "peer-message",
  "msg": "example.message.v1",
  "chain_kind": "audit",
  "correlation_id": "corr:example",
  "remote_node_id": "node:did:key:z6Mk...",
  "payload": {
    "input_payload": { "example": true },
    "response": null,
    "elapsed_ms": 7
  }
}

Dla lokalnego dispatchu HTTP obowiązuje ta sama reguła przez lokalną kopertę invoke wejścia: moduł powinien rozgałęziać się po chain_kind, a nie po niejawnym założeniu o ścieżce, która została wywołana.

Dla obserwatorów faz i obserwatorów post-chain host wysyła peer-message-observe.v1 z envelope_kind = "peer-message-observe" oraz observation_kind = "phase" albo "post-chain". Te requesty obserwatorów są powierzchniami metadanych/trace, a nie punktami decyzyjnymi.

def handle_hook(request):
    chain = request["chain_kind"]

    if chain == "inbound-local":
        return handle_inbound_local(request)

    if chain == "audit":
        record_audit(request)
        return {
            "decision": "allow",
            "annotations": {},
            "diagnostics": {}
        }

    return {
        "decision": "reject",
        "reason": f"unsupported chain_kind {chain}",
        "annotations": {},
        "diagnostics": {}
    }

Użycie jednego endpointu jest rozsądne dla małego modułu z jednym wewnętrznym dispatcherem. Dla większych modułów oddzielne ścieżki HTTP są zwykle czytelniejsze operacyjnie:

{
  "module_id": "example.multi-hook",
  "hooks": [
    {
      "chain_kind": "inbound-peer",
      "invoke_url": "http://127.0.0.1:49120/hooks/inbound-peer"
    },
    {
      "chain_kind": "audit",
      "invoke_url": "http://127.0.0.1:49120/hooks/audit"
    }
  ]
}

Nawet wtedy chain_kind pozostaje częścią kontraktu. Ścieżka jest wygodą diagnostyczną i routingową; koperta jest źródłem prawdy.

Modele dystrybucji

Typ wykonania mówi, jak middleware działa. Model dystrybucji mówi, jak kod, definicja, konfiguracja albo pakiet trafia do node'a i jak operator to akceptuje. Te osie celowo się przecinają: nadzorowany moduł channel może być dostarczany fabrycznie albo instalowany przez operatora, a definicja JSON-e Flow może być dostarczana jako profil akceptacyjny bez stawania się samodzielnym modułem procesowym.

Middleware dostarczany fabrycznie

Bundled middleware jest dystrybuowany ze źródłami Node'a albo dystrybucją binarną. Nadal może być nadzorowanym channel JSON, JSON-e Flow, Rustem w procesie albo innym typem executora; "bundled" opisuje dystrybucję i postawę zaufania, nie mechanikę wykonania. Moduły bundled są użyteczne, gdy capability jest częścią systemu referencyjnego, ale powinna pozostać poza zaufanym rdzeniem daemona. Mogą otrzymać pierwszoklasowe pokrycie runbookiem, testy, domyślne fragmenty konfiguracji i integrację UI operatora. Bundling nie usuwa potrzeby raportów modułu, bram host capability, readiness, trace ani least privilege. Jeżeli moduł bundled nie jest wymagany przez deployment, operator powinien móc go wyłączyć.

Ścieżka dostarczenia

  • Kod źródłowy albo executable dostarczony w dystrybucji Node'a.
  • Domyślne fragmenty konfiguracji.
  • Profile akceptacyjne albo fixtures.
  • Testy, runbooki i assety operatorskiego UI, gdy dotyczy.

Middleware dostarczany fabrycznie

  • sensorium-core - bundled in-process Sensorium organ boundary.
  • sensorium-os - bundled middleware konektora Sensorium.
  • arca - bundled middleware workflow/orkiestracji.
  • dator - bundled katalog ofert i middleware dispatchu.
  • recovery-service - bundled middleware recovery-service.
  • snooper - bundled middleware obserwacyjny/debug.
  • whisper-intake - bundled middleware intake Whisper.
  • agora-service - bundled middleware usługi skierowanej do Agory.
  • agora-verifier - bundled middleware helpera weryfikacji Agory.
  • agora-demo - bundled demonstracyjny middleware Agory.

Zastosowania

  • Referencyjne moduły Arca, Dator, Sensorium OS albo Seed Directory.
  • Hard-MVP capabilities, które powinny działać od razu.
  • Moduły demonstracyjne używane przez historie akceptacyjne.

Przykłady

{
  "module_id": "example.bundled",
  "executor": "channel_json",
  "bundle": {
    "kind": "python-module",
    "entrypoint": "middleware-modules/example/service.py"
  },
  "enabled": true
}

Definicje dystrybuowane przez profil

Definicje dystrybuowane przez profil są definicjami middleware dostarczanymi jako część profilu akceptacyjnego, fixture runbooka albo fabrycznego szkieletu konfiguracji, a nie jako samodzielny pakiet modułu. Ten model dystrybucji jest użyteczny dla deklaratywnego middleware, zwłaszcza JSON-e Flow, gdzie operacyjnym komponentem jest sama definicja flow. Profil może materializować fragmenty konfiguracji do data directory podczas bootstrapu, ale stan runtime nadal należy do normalnych katalogów runtime zarządzanych przez daemona. Operatorzy powinni móc sprawdzić i zaakceptować zmaterializowane definicje zanim staną się aktywne w profilach podobnych do produkcyjnych. Ten model nie powinien ukrywać silnego zachowania: wywołania host capability, limity, dostęp raw-signal i route'y UI operatora pozostają jawne w definicji.

Ścieżka dostarczenia

  • Profil akceptacyjny, szkielet bootstrapu albo fixture runbooka.
  • Fabryczne fragmenty konfiguracji materializowane do katalogu danych node'a.
  • Deklaratywne definicje middleware takie jak wpisy usług JSON-e Flow.
  • Opcjonalnie wygenerowane paszporty, bindingi albo artefakty readiness potrzebne profilowi.

Middleware dostarczany fabrycznie

  • Definicje ról JSON-e Flow w Story-009 - bundled definicje flow profilu akceptacyjnego używane do adaptacji żądań roli do ograniczonych wywołań host capability.

Zastosowania

  • Dostarczenie pełnej historii albo profilu akceptacyjnego działającego bez dedykowanego modułu procesowego.
  • Materializacja niskokodowych adapterów roli jako danych.
  • Utrzymanie demonstracyjnego i bootstrapowego middleware'u w sposób reprodukowalny bez robienia z każdej definicji osobno wersjonowanego pakietu.

Przykłady

{
  "profile": "story-009",
  "materializes": [
    "middleware_json_e_flow_services.story009.editorial.review",
    "middleware_json_e_flow_services.story009.sensorium.prepare"
  ]
}

Pakiet instalowany przez operatora

Pakiet middleware instalowany przez operatora jest artefaktem umieszczanym pod <data-dir>/middleware-packages/<package-id>/. Może wnosić konfigurację modułu, statyczne fragmenty UI, metadane powierzchni operatorskich, skrypty, szablony i inne pliki należące do pakietu. Drzewo pakietu jest traktowane jako powierzchnia artefaktu: semantyczne pliki powinny być podpisane albo inaczej zatwierdzone, zanim daemon aktywuje wniesioną konfigurację. Stan runtime nie należy do drzewa pakietu; należy pod <data-dir>/middleware/<module-id>/. Ten typ jest użyteczny dla lokalnej rozszerzalności bez wymagania, aby wbudowane UI albo daemon znały każdy przyszły moduł. Pakiet może instalować deklaratywne flow, supervised service albo powierzchnie UI, ale host nadal jest właścicielem aktywacji i polityki.

Ścieżka dostarczenia

  • middleware.package.json.
  • Fragmenty konfiguracji pakietu config/*.json.
  • Statyczne host-rendered fragmenty UI w ui/.
  • Deklaracje powierzchni operatorskich w ui-op/.
  • Opcjonalne sidecary .signatures/.

Dołączone przykłady

  • Żaden produkcyjny pakiet nie jest obecnie traktowany jako fabrycznie zainstalowany pakiet operatora. Dokumentacja dostarcza przykłady pakietów takie jak middleware-package-ui, middleware-python-package-ui, role-module-http, role-module-json-e, json-e-flow-role i sensorium-connector.

Zastosowania

  • Instalacja zewnętrznego albo lokalnego middleware operatora.
  • Dodanie powierzchni operatorskiego UI bez zmian w kodzie Node UI.
  • Dostarczenie predefiniowanych adapterów JSON-e Flow jako danych.
  • Utrzymanie materiału pakietu osobno od stanu runtime.

Przykłady

middleware-packages/example-package/
  middleware.package.json
  config/
    50-example-flow.json
  ui/
    index.html
  ui-op/
    operator-surfaces.json
{
  "schema": "middleware.package.v1",
  "package_id": "example-package",
  "modules": [
    { "module_id": "example.flow", "config": "config/50-example-flow.json" }
  ]
}