[{"data":1,"prerenderedAt":3072},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fruntime\u002Fplugins":314,"\u002Fdocs\u002Fruntime\u002Fplugins-surround":3067},[4,38,86,149,176,219,257,273],{"title":5,"path":6,"stem":7,"children":8,"icon":37},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,17,22,27,32],{"title":10,"path":6,"stem":7,"icon":11},"Getting started","i-lucide-flag",{"title":13,"path":14,"stem":15,"icon":16},"Modules overview","\u002Fdocs\u002Fgetting-started\u002Fmodules-overview","1.docs\u002F1.getting-started\u002F2.modules-overview","i-lucide-boxes",{"title":18,"path":19,"stem":20,"icon":21},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","1.docs\u002F1.getting-started\u002F3.installation","i-lucide-download",{"title":23,"path":24,"stem":25,"icon":26},"License configuration","\u002Fdocs\u002Fgetting-started\u002Flicense-configuration","1.docs\u002F1.getting-started\u002F4.license-configuration","i-lucide-key-round",{"title":28,"path":29,"stem":30,"icon":31},"Your first app","\u002Fdocs\u002Fgetting-started\u002Ffirst-app","1.docs\u002F1.getting-started\u002F5.first-app","i-lucide-square-play",{"title":33,"path":34,"stem":35,"icon":36},"Project setup","\u002Fdocs\u002Fgetting-started\u002Fproject-setup","1.docs\u002F1.getting-started\u002F6.project-setup","i-lucide-package",false,{"title":39,"path":40,"stem":41,"children":42,"icon":45},"Core Concepts","\u002Fdocs\u002Fconcepts","1.docs\u002F2.concepts\u002F1.index",[43,46,51,56,61,66,71,76,81],{"title":44,"path":40,"stem":41,"icon":45},"Core concepts","i-lucide-book-open",{"title":47,"path":48,"stem":49,"icon":50},"Architecture","\u002Fdocs\u002Fconcepts\u002Farchitecture","1.docs\u002F2.concepts\u002F2.architecture","i-lucide-layers",{"title":52,"path":53,"stem":54,"icon":55},"Exactly-once semantics","\u002Fdocs\u002Fconcepts\u002Fexactly-once","1.docs\u002F2.concepts\u002F3.exactly-once","i-lucide-shield-check",{"title":57,"path":58,"stem":59,"icon":60},"Lanes and parallelism","\u002Fdocs\u002Fconcepts\u002Flanes-and-parallelism","1.docs\u002F2.concepts\u002F4.lanes-and-parallelism","i-lucide-split",{"title":62,"path":63,"stem":64,"icon":65},"State and thread-safety","\u002Fdocs\u002Fconcepts\u002Fstate-and-thread-safety","1.docs\u002F2.concepts\u002F5.state-and-thread-safety","i-lucide-lock",{"title":67,"path":68,"stem":69,"icon":70},"Event time and watermarks","\u002Fdocs\u002Fconcepts\u002Fevent-time-and-watermarks","1.docs\u002F2.concepts\u002F6.event-time-and-watermarks","i-lucide-clock",{"title":72,"path":73,"stem":74,"icon":75},"The configuration model","\u002Fdocs\u002Fconcepts\u002Fconfiguration-model","1.docs\u002F2.concepts\u002F7.configuration-model","i-lucide-sliders-horizontal",{"title":77,"path":78,"stem":79,"icon":80},"The error-handling model","\u002Fdocs\u002Fconcepts\u002Ferror-handling-model","1.docs\u002F2.concepts\u002F8.error-handling-model","i-lucide-triangle-alert",{"title":82,"path":83,"stem":84,"icon":85},"How StoatFlow differs from Kafka Streams","\u002Fdocs\u002Fconcepts\u002Fhow-stoatflow-differs-from-ks","1.docs\u002F2.concepts\u002F9.how-stoatflow-differs-from-ks","i-lucide-arrow-left-right",{"title":87,"path":88,"stem":89,"children":90,"icon":93},"Building Topologies","\u002Fdocs\u002Fbuilding","1.docs\u002F3.building\u002F1.index",[91,94,99,104,109,114,119,124,129,134,139,144],{"title":92,"path":88,"stem":89,"icon":93},"Building topologies","i-lucide-blocks",{"title":95,"path":96,"stem":97,"icon":98},"Error handling and DLQ","\u002Fdocs\u002Fbuilding\u002Ferror-handling-dlq","1.docs\u002F3.building\u002F10.error-handling-dlq","i-lucide-circle-x",{"title":100,"path":101,"stem":102,"icon":103},"State stores","\u002Fdocs\u002Fbuilding\u002Fstate-stores","1.docs\u002F3.building\u002F11.state-stores","i-lucide-database",{"title":105,"path":106,"stem":107,"icon":108},"Testing topologies","\u002Fdocs\u002Fbuilding\u002Ftesting","1.docs\u002F3.building\u002F12.testing","i-lucide-flask-conical",{"title":110,"path":111,"stem":112,"icon":113},"Sources and sinks","\u002Fdocs\u002Fbuilding\u002Fstreams-builder","1.docs\u002F3.building\u002F2.streams-builder","i-lucide-import",{"title":115,"path":116,"stem":117,"icon":118},"KStream and KTable operations","\u002Fdocs\u002Fbuilding\u002Fkstream-ktable","1.docs\u002F3.building\u002F3.kstream-ktable","i-lucide-waypoints",{"title":120,"path":121,"stem":122,"icon":123},"Aggregations","\u002Fdocs\u002Fbuilding\u002Faggregations","1.docs\u002F3.building\u002F4.aggregations","i-lucide-sigma",{"title":125,"path":126,"stem":127,"icon":128},"Windowing","\u002Fdocs\u002Fbuilding\u002Fwindowing","1.docs\u002F3.building\u002F5.windowing","i-lucide-calendar-clock",{"title":130,"path":131,"stem":132,"icon":133},"Joins","\u002Fdocs\u002Fbuilding\u002Fjoins","1.docs\u002F3.building\u002F6.joins","i-lucide-git-merge",{"title":135,"path":136,"stem":137,"icon":138},"The Processor API","\u002Fdocs\u002Fbuilding\u002Fprocessor-api","1.docs\u002F3.building\u002F7.processor-api","i-lucide-cpu",{"title":140,"path":141,"stem":142,"icon":143},"Scheduled sources","\u002Fdocs\u002Fbuilding\u002Fscheduled-sources","1.docs\u002F3.building\u002F8.scheduled-sources","i-lucide-timer",{"title":145,"path":146,"stem":147,"icon":148},"Serdes and Avro","\u002Fdocs\u002Fbuilding\u002Fserdes","1.docs\u002F3.building\u002F9.serdes","i-lucide-binary",{"title":150,"path":151,"stem":152,"children":153,"icon":75},"Configuration","\u002Fdocs\u002Fconfiguration","1.docs\u002F4.configuration\u002F1.index",[154,156,161,166,171],{"title":155,"path":151,"stem":152,"icon":75},"How configuration works",{"title":157,"path":158,"stem":159,"icon":160},"Engine configuration (:core)","\u002Fdocs\u002Fconfiguration\u002Fcore-config","1.docs\u002F4.configuration\u002F2.core-config","i-lucide-settings-2",{"title":162,"path":163,"stem":164,"icon":165},"Runtime configuration (:runtime)","\u002Fdocs\u002Fconfiguration\u002Fruntime-config","1.docs\u002F4.configuration\u002F3.runtime-config","i-lucide-server-cog",{"title":167,"path":168,"stem":169,"icon":170},"Defaults, adaptivity, and presets","\u002Fdocs\u002Fconfiguration\u002Fdefaults-and-presets","1.docs\u002F4.configuration\u002F4.defaults-and-presets","i-lucide-gauge",{"title":172,"path":173,"stem":174,"icon":175},"Kafka client configuration","\u002Fdocs\u002Fconfiguration\u002Fkafka-client-config","1.docs\u002F4.configuration\u002F5.kafka-client-config","i-lucide-plug",{"title":177,"path":178,"stem":179,"children":180,"icon":183},"Running in Production","\u002Fdocs\u002Fruntime","1.docs\u002F5.runtime\u002F1.index",[181,184,189,194,199,204,209,214],{"title":182,"path":178,"stem":179,"icon":183},"The runtime","i-lucide-server",{"title":185,"path":186,"stem":187,"icon":188},"The REST API","\u002Fdocs\u002Fruntime\u002Frest-api","1.docs\u002F5.runtime\u002F2.rest-api","i-lucide-globe",{"title":190,"path":191,"stem":192,"icon":193},"Health checks","\u002Fdocs\u002Fruntime\u002Fhealth-checks","1.docs\u002F5.runtime\u002F3.health-checks","i-lucide-heart-pulse",{"title":195,"path":196,"stem":197,"icon":198},"Metrics","\u002Fdocs\u002Fruntime\u002Fmetrics","1.docs\u002F5.runtime\u002F4.metrics","i-lucide-activity",{"title":200,"path":201,"stem":202,"icon":203},"Pause and resume","\u002Fdocs\u002Fruntime\u002Fpause-unpause","1.docs\u002F5.runtime\u002F5.pause-unpause","i-lucide-pause",{"title":205,"path":206,"stem":207,"icon":208},"Plugins and lifecycle hooks","\u002Fdocs\u002Fruntime\u002Fplugins","1.docs\u002F5.runtime\u002F6.plugins","i-lucide-puzzle",{"title":210,"path":211,"stem":212,"icon":213},"Docker images","\u002Fdocs\u002Fruntime\u002Fdocker","1.docs\u002F5.runtime\u002F7.docker","i-lucide-container",{"title":215,"path":216,"stem":217,"icon":218},"GraalVM native image","\u002Fdocs\u002Fruntime\u002Fnative-image","1.docs\u002F5.runtime\u002F8.native-image","i-lucide-zap",{"title":220,"path":221,"stem":222,"children":223,"icon":226},"Deploying & Operating","\u002Fdocs\u002Foperating","1.docs\u002F6.operating\u002F1.index",[224,227,232,237,242,247,252],{"title":225,"path":221,"stem":222,"icon":226},"Deploying and operating","i-lucide-life-buoy",{"title":228,"path":229,"stem":230,"icon":231},"Running on Kubernetes","\u002Fdocs\u002Foperating\u002Fkubernetes","1.docs\u002F6.operating\u002F2.kubernetes","i-lucide-ship",{"title":233,"path":234,"stem":235,"icon":236},"High availability","\u002Fdocs\u002Foperating\u002Fhigh-availability","1.docs\u002F6.operating\u002F3.high-availability","i-lucide-copy",{"title":238,"path":239,"stem":240,"icon":241},"Liveness and readiness probes","\u002Fdocs\u002Foperating\u002Fprobes","1.docs\u002F6.operating\u002F4.probes","i-lucide-stethoscope",{"title":243,"path":244,"stem":245,"icon":246},"Observability","\u002Fdocs\u002Foperating\u002Fobservability","1.docs\u002F6.operating\u002F5.observability","i-lucide-telescope",{"title":248,"path":249,"stem":250,"icon":251},"Tuning under load","\u002Fdocs\u002Foperating\u002Ftuning","1.docs\u002F6.operating\u002F6.tuning","i-lucide-sliders",{"title":253,"path":254,"stem":255,"icon":256},"Production checklist","\u002Fdocs\u002Foperating\u002Fproduction-checklist","1.docs\u002F6.operating\u002F7.production-checklist","i-lucide-clipboard-check",{"title":258,"path":259,"stem":260,"children":261,"icon":85},"Migrating from Kafka Streams","\u002Fdocs\u002Fmigration","1.docs\u002F7.migration\u002F1.index",[262,263,268],{"title":258,"path":259,"stem":260,"icon":85},{"title":264,"path":265,"stem":266,"icon":267},"Migration without carrying state","\u002Fdocs\u002Fmigration\u002Fwithout-data-migration","1.docs\u002F7.migration\u002F2.without-data-migration","i-lucide-sparkles",{"title":269,"path":270,"stem":271,"icon":272},"Migration carrying state","\u002Fdocs\u002Fmigration\u002Fwith-data-migration","1.docs\u002F7.migration\u002F3.with-data-migration","i-lucide-database-backup",{"title":274,"path":275,"stem":276,"children":277,"icon":279},"Reference","\u002Fdocs\u002Freference","1.docs\u002F8.reference\u002F1.index",[278,280,285,290,295,300,305,309],{"title":274,"path":275,"stem":276,"icon":279},"i-lucide-list",{"title":281,"path":282,"stem":283,"icon":284},"Configuration reference","\u002Fdocs\u002Freference\u002Fconfiguration-reference","1.docs\u002F8.reference\u002F2.configuration-reference","i-lucide-table",{"title":286,"path":287,"stem":288,"icon":289},"REST API reference","\u002Fdocs\u002Freference\u002Frest-api-reference","1.docs\u002F8.reference\u002F3.rest-api-reference","i-lucide-network",{"title":291,"path":292,"stem":293,"icon":294},"Gradle plugin reference","\u002Fdocs\u002Freference\u002Fgradle-plugin-reference","1.docs\u002F8.reference\u002F4.gradle-plugin-reference","i-lucide-box",{"title":296,"path":297,"stem":298,"icon":299},"Maven reference","\u002Fdocs\u002Freference\u002Fmaven-reference","1.docs\u002F8.reference\u002F5.maven-reference","i-simple-icons-apachemaven",{"title":301,"path":302,"stem":303,"icon":304},"Kafka Streams compatibility matrix","\u002Fdocs\u002Freference\u002Fks-compatibility-matrix","1.docs\u002F8.reference\u002F6.ks-compatibility-matrix","i-lucide-table-2",{"title":306,"path":307,"stem":308,"icon":170},"Metrics reference","\u002Fdocs\u002Freference\u002Fmetrics-reference","1.docs\u002F8.reference\u002F7.metrics-reference",{"title":310,"path":311,"stem":312,"icon":313},"Glossary","\u002Fdocs\u002Freference\u002Fglossary","1.docs\u002F8.reference\u002F8.glossary","i-lucide-book-a",{"id":315,"title":205,"body":316,"description":3060,"extension":3061,"meta":3062,"navigation":3063,"pageType":3064,"path":206,"seo":3065,"stem":207,"__hash__":3066},"docs\u002F1.docs\u002F5.runtime\u002F6.plugins.md",{"type":317,"value":318,"toc":3048},"minimark",[319,336,427,441,446,461,534,581,585,593,705,723,727,732,2056,2080,2085,2115,2119,2162,2166,2186,2234,2243,2247,2268,2305,2310,2375,2702,2736,2740,2750,2996,3006,3010,3044],[320,321,322,323,327,328,331,332,335],"p",{},"The ",[324,325,326],"code",{},":runtime"," module is a thin, extensible wrapper around the engine. Two extension points let you add your own behaviour without forking it: a ",[324,329,330],{},"RuntimePlugin"," runs at startup with access to the runtime's shared components (metrics registry, HTTP server, health registry), and a ",[324,333,334],{},"RuntimeLifecycleListener"," observes the runtime as it starts and stops.",[337,338,339],"tldr-panel",{},[340,341,342,363,387,407],"ul",{},[343,344,345,350,351,354,355,358,359,362],"li",{},[346,347,348],"strong",{},[324,349,330],{}," — ",[324,352,353],{},"id",", ",[324,356,357],{},"initialize(context)",", optional ",[324,360,361],{},"shutdown()",". Register custom metrics, HTTP handlers, and health indicators at startup.",[343,364,365,370,371,354,374,354,377,354,380,354,383,386],{},[346,366,367],{},[324,368,369],{},"PluginContext"," — what a plugin gets: ",[324,372,373],{},"meterRegistry()",[324,375,376],{},"httpServer()",[324,378,379],{},"metricsPrefix()",[324,381,382],{},"applicationId()",[324,384,385],{},"healthIndicatorRegistry()",".",[343,388,389,393,394,397,398,397,401,397,404,386],{},[346,390,391],{},[324,392,334],{}," — a callback fired on ",[324,395,396],{},"PRE_START"," \u002F ",[324,399,400],{},"POST_START",[324,402,403],{},"PRE_STOP",[324,405,406],{},"POST_STOP",[343,408,409,412,413,354,416,419,420,423,424,386],{},[346,410,411],{},"Register both"," via the runtime builder (",[324,414,415],{},"addPlugin",[324,417,418],{},"addLifecycleListener",") or the ",[324,421,422],{},"configure { ... }"," block on ",[324,425,426],{},"StoatFlowRuntime.fromConfig",[428,429,432],"callout",{"color":430,"icon":431},"info","i-lucide-info",[320,433,434,436,437,440],{},[324,435,369],{}," exposes the runtime's HTTP server, metrics registry, and health registry — ",[346,438,439],{},"not"," the stream-processing engine or a state-store handle. Plugins extend the operational surface (endpoints, metrics, probes); they cannot run interactive state queries against your topology.",[442,443,445],"h2",{"id":444},"the-runtimeplugin-interface","The RuntimePlugin interface",[320,447,448,449,451,452,454,455,457,458,460],{},"A plugin is a class implementing ",[324,450,330],{},". It has a stable ",[324,453,353],{},", an ",[324,456,357],{}," method called once during startup, and an optional ",[324,459,361],{}," for cleanup.",[462,463,468],"pre",{"className":464,"code":465,"language":466,"meta":467,"style":467},"language-kotlin shiki shiki-themes vitesse-light","interface RuntimePlugin {\n    val id: String\n    fun initialize(context: PluginContext)\n    fun shutdown() {}\n}\n","kotlin","",[324,469,470,487,499,517,528],{"__ignoreMap":467},[471,472,475,479,483],"span",{"class":473,"line":474},"line",1,[471,476,478],{"class":477},"sbBg2","interface",[471,480,482],{"class":481},"sUxyF"," RuntimePlugin",[471,484,486],{"class":485},"suHK_"," {\n",[471,488,490,493,496],{"class":473,"line":489},2,[471,491,492],{"class":477},"    val",[471,494,495],{"class":485}," id: ",[471,497,498],{"class":481},"String\n",[471,500,502,505,509,512,514],{"class":473,"line":501},3,[471,503,504],{"class":477},"    fun",[471,506,508],{"class":507},"sySUi"," initialize",[471,510,511],{"class":485},"(context: ",[471,513,369],{"class":481},[471,515,516],{"class":485},")\n",[471,518,520,522,525],{"class":473,"line":519},4,[471,521,504],{"class":477},[471,523,524],{"class":507}," shutdown",[471,526,527],{"class":485},"() {}\n",[471,529,531],{"class":473,"line":530},5,[471,532,533],{"class":485},"}\n",[340,535,536,553,567],{},[343,537,538,542,543,354,546,354,549,552],{},[346,539,540],{},[324,541,353],{}," — a short, descriptive name without spaces (e.g. ",[324,544,545],{},"\"prometheus\"",[324,547,548],{},"\"otel\"",[324,550,551],{},"\"audit-log\"","). Used for logging and diagnostics. Plugins initialize in registration order.",[343,554,555,559,560,563,564,566],{},[346,556,557],{},[324,558,357],{}," — called during startup, ",[346,561,562],{},"before"," the HTTP server starts and ",[346,565,562],{}," stream processing begins. The metrics registry exists, the HTTP server is created but not yet running. This is where you register HTTP handlers, metrics, and health indicators.",[343,568,569,573,574,577,578,386],{},[346,570,571],{},[324,572,361],{}," — called during shutdown, ",[346,575,576],{},"after"," stream processing has stopped. The metrics registry is still available; the HTTP server may still be running. Release anything you acquired in ",[324,579,580],{},"initialize",[442,582,584],{"id":583},"the-plugincontext-surface","The PluginContext surface",[320,586,587,589,590,592],{},[324,588,357],{}," receives a ",[324,591,369],{},". These are the only members:",[594,595,596,612],"table",{},[597,598,599],"thead",{},[600,601,602,606,609],"tr",{},[603,604,605],"th",{},"Member",[603,607,608],{},"Returns",[603,610,611],{},"Purpose",[613,614,615,634,657,675,691],"tbody",{},[600,616,617,622,627],{},[618,619,620],"td",{},[324,621,373],{},[618,623,624],{},[324,625,626],{},"MeterRegistry?",[618,628,629,630,633],{},"Micrometer registry for custom metrics. ",[324,631,632],{},"null"," if metrics are disabled.",[600,635,636,640,645],{},[618,637,638],{},[324,639,376],{},[618,641,642],{},[324,643,644],{},"RuntimeHttpServer?",[618,646,647,648,650,651,653,654,386],{},"The HTTP server, for registering custom endpoints. ",[324,649,632],{}," if HTTP is disabled. Handlers must be registered during ",[324,652,580],{}," — once the server starts, registration throws ",[324,655,656],{},"IllegalArgumentException",[600,658,659,663,668],{},[618,660,661],{},[324,662,379],{},[618,664,665],{},[324,666,667],{},"String",[618,669,670,671,674],{},"The configured metrics prefix (e.g. ",[324,672,673],{},"\"stoatflow\"","). Prefix your own meters with it for consistency.",[600,676,677,681,685],{},[618,678,679],{},[324,680,382],{},[618,682,683],{},[324,684,667],{},[618,686,322,687,690],{},[324,688,689],{},"stoatflow.application-id"," from configuration.",[600,692,693,697,702],{},[618,694,695],{},[324,696,385],{},[618,698,699],{},[324,700,701],{},"HealthIndicatorRegistry",[618,703,704],{},"Register custom health indicators for the liveness \u002F readiness probes.",[428,706,708],{"color":707,"icon":80},"warning",[320,709,710,712,713,715,716,718,719,722],{},[324,711,373],{}," and ",[324,714,376],{}," are nullable — they return ",[324,717,632],{}," when metrics or the HTTP server are disabled in config. Always guard the call (",[324,720,721],{},"?.let { ... }",") so a plugin degrades gracefully instead of throwing on a misconfigured runtime.",[442,724,726],{"id":725},"a-complete-plugin","A complete plugin",[320,728,729,730,386],{},"This plugin registers a custom counter, a custom HTTP endpoint, and a custom health indicator — exercising every part of the ",[324,731,369],{},[733,734,736,1233],"code-tabs",{"group":735},"lang",[462,737,740],{"className":464,"code":738,"filename":739,"language":466,"meta":467,"style":467},"package com.example\n\nimport io.stoatflow.runtime.health.Health\nimport io.stoatflow.runtime.health.HealthIndicator\nimport io.stoatflow.runtime.plugin.PluginContext\nimport io.stoatflow.runtime.plugin.RuntimePlugin\n\nclass AuditPlugin : RuntimePlugin {\n    override val id = \"audit\"\n\n    override fun initialize(context: PluginContext) {\n        val prefix = context.metricsPrefix()\n\n        \u002F\u002F 1. Custom metric — prefix it to match the built-in meters.\n        context.meterRegistry()?.let { registry ->\n            registry.counter(\"$prefix.audit.events\").increment()\n        }\n\n        \u002F\u002F 2. Custom HTTP endpoint (com.sun.net.httpserver.HttpHandler).\n        \u002F\u002F    Must be registered now — before the server starts.\n        context.httpServer()?.registerHandler(\"\u002Faudit\") { exchange ->\n            val body = \"\"\"{\"application\":\"${context.applicationId()}\"}\"\"\"\n                .toByteArray()\n            exchange.responseHeaders.add(\"Content-Type\", \"application\u002Fjson\")\n            exchange.sendResponseHeaders(200, body.size.toLong())\n            exchange.responseBody.use { it.write(body) }\n        }\n\n        \u002F\u002F 3. Custom health indicator — surfaces in \u002Fhealth\u002Fready and \u002Fhealth\u002Flive.\n        context.healthIndicatorRegistry().register(\"audit-sink\", AuditHealthIndicator())\n    }\n\n    override fun shutdown() {\n        \u002F\u002F Release anything acquired in initialize().\n    }\n}\n\nclass AuditHealthIndicator : HealthIndicator {\n    override fun health(): Health =\n        Health.up().withDetail(\"audit-sink\", \"connected\").build()\n}\n","Kotlin",[324,741,742,750,756,764,771,778,786,791,807,828,833,850,870,875,882,903,926,932,937,943,949,972,1004,1015,1036,1060,1078,1083,1088,1094,1120,1126,1131,1143,1149,1154,1159,1164,1179,1198,1228],{"__ignoreMap":467},[471,743,744,747],{"class":473,"line":474},[471,745,746],{"class":477},"package",[471,748,749],{"class":507}," com.example\n",[471,751,752],{"class":473,"line":489},[471,753,755],{"emptyLinePlaceholder":754},true,"\n",[471,757,758,761],{"class":473,"line":501},[471,759,760],{"class":477},"import",[471,762,763],{"class":507}," io.stoatflow.runtime.health.Health\n",[471,765,766,768],{"class":473,"line":519},[471,767,760],{"class":477},[471,769,770],{"class":507}," io.stoatflow.runtime.health.HealthIndicator\n",[471,772,773,775],{"class":473,"line":530},[471,774,760],{"class":477},[471,776,777],{"class":507}," io.stoatflow.runtime.plugin.PluginContext\n",[471,779,781,783],{"class":473,"line":780},6,[471,782,760],{"class":477},[471,784,785],{"class":507}," io.stoatflow.runtime.plugin.RuntimePlugin\n",[471,787,789],{"class":473,"line":788},7,[471,790,755],{"emptyLinePlaceholder":754},[471,792,794,797,800,803,805],{"class":473,"line":793},8,[471,795,796],{"class":477},"class",[471,798,799],{"class":481}," AuditPlugin",[471,801,802],{"class":485}," : ",[471,804,330],{"class":481},[471,806,486],{"class":485},[471,808,810,814,817,820,824],{"class":473,"line":809},9,[471,811,813],{"class":812},"si04Y","    override",[471,815,816],{"class":477}," val",[471,818,819],{"class":485}," id ",[471,821,823],{"class":822},"sYZai","=",[471,825,827],{"class":826},"spphp"," \"audit\"\n",[471,829,831],{"class":473,"line":830},10,[471,832,755],{"emptyLinePlaceholder":754},[471,834,836,838,841,843,845,847],{"class":473,"line":835},11,[471,837,813],{"class":812},[471,839,840],{"class":477}," fun",[471,842,508],{"class":507},[471,844,511],{"class":485},[471,846,369],{"class":481},[471,848,849],{"class":485},") {\n",[471,851,853,856,859,861,864,867],{"class":473,"line":852},12,[471,854,855],{"class":477},"        val",[471,857,858],{"class":485}," prefix ",[471,860,823],{"class":822},[471,862,863],{"class":485}," context.",[471,865,866],{"class":507},"metricsPrefix",[471,868,869],{"class":485},"()\n",[471,871,873],{"class":473,"line":872},13,[471,874,755],{"emptyLinePlaceholder":754},[471,876,878],{"class":473,"line":877},14,[471,879,881],{"class":880},"s8zF2","        \u002F\u002F 1. Custom metric — prefix it to match the built-in meters.\n",[471,883,885,888,891,894,897,900],{"class":473,"line":884},15,[471,886,887],{"class":485},"        context.",[471,889,890],{"class":507},"meterRegistry",[471,892,893],{"class":485},"()?.",[471,895,896],{"class":507},"let",[471,898,899],{"class":485}," { registry ",[471,901,902],{"class":822},"->\n",[471,904,906,909,912,915,918,921,924],{"class":473,"line":905},16,[471,907,908],{"class":485},"            registry.",[471,910,911],{"class":507},"counter",[471,913,914],{"class":485},"(",[471,916,917],{"class":826},"\"$prefix.audit.events\"",[471,919,920],{"class":485},").",[471,922,923],{"class":507},"increment",[471,925,869],{"class":485},[471,927,929],{"class":473,"line":928},17,[471,930,931],{"class":485},"        }\n",[471,933,935],{"class":473,"line":934},18,[471,936,755],{"emptyLinePlaceholder":754},[471,938,940],{"class":473,"line":939},19,[471,941,942],{"class":880},"        \u002F\u002F 2. Custom HTTP endpoint (com.sun.net.httpserver.HttpHandler).\n",[471,944,946],{"class":473,"line":945},20,[471,947,948],{"class":880},"        \u002F\u002F    Must be registered now — before the server starts.\n",[471,950,952,954,957,959,962,964,967,970],{"class":473,"line":951},21,[471,953,887],{"class":485},[471,955,956],{"class":507},"httpServer",[471,958,893],{"class":485},[471,960,961],{"class":507},"registerHandler",[471,963,914],{"class":485},[471,965,966],{"class":826},"\"\u002Faudit\"",[471,968,969],{"class":485},") { exchange ",[471,971,902],{"class":822},[471,973,975,978,981,983,986,989,992,995,998,1001],{"class":473,"line":974},22,[471,976,977],{"class":477},"            val",[471,979,980],{"class":485}," body ",[471,982,823],{"class":822},[471,984,985],{"class":826}," \"\"\"{\"application\":\"",[471,987,988],{"class":477},"${",[471,990,991],{"class":826},"context.",[471,993,994],{"class":507},"applicationId",[471,996,997],{"class":826},"()",[471,999,1000],{"class":477},"}",[471,1002,1003],{"class":826},"\"}\"\"\"\n",[471,1005,1007,1010,1013],{"class":473,"line":1006},23,[471,1008,1009],{"class":485},"                .",[471,1011,1012],{"class":507},"toByteArray",[471,1014,869],{"class":485},[471,1016,1018,1021,1024,1026,1029,1031,1034],{"class":473,"line":1017},24,[471,1019,1020],{"class":485},"            exchange.responseHeaders.",[471,1022,1023],{"class":507},"add",[471,1025,914],{"class":485},[471,1027,1028],{"class":826},"\"Content-Type\"",[471,1030,354],{"class":485},[471,1032,1033],{"class":826},"\"application\u002Fjson\"",[471,1035,516],{"class":485},[471,1037,1039,1042,1045,1047,1051,1054,1057],{"class":473,"line":1038},25,[471,1040,1041],{"class":485},"            exchange.",[471,1043,1044],{"class":507},"sendResponseHeaders",[471,1046,914],{"class":485},[471,1048,1050],{"class":1049},"s-TwI","200",[471,1052,1053],{"class":485},", body.size.",[471,1055,1056],{"class":507},"toLong",[471,1058,1059],{"class":485},"())\n",[471,1061,1063,1066,1069,1072,1075],{"class":473,"line":1062},26,[471,1064,1065],{"class":485},"            exchange.responseBody.",[471,1067,1068],{"class":507},"use",[471,1070,1071],{"class":485}," { it.",[471,1073,1074],{"class":507},"write",[471,1076,1077],{"class":485},"(body) }\n",[471,1079,1081],{"class":473,"line":1080},27,[471,1082,931],{"class":485},[471,1084,1086],{"class":473,"line":1085},28,[471,1087,755],{"emptyLinePlaceholder":754},[471,1089,1091],{"class":473,"line":1090},29,[471,1092,1093],{"class":880},"        \u002F\u002F 3. Custom health indicator — surfaces in \u002Fhealth\u002Fready and \u002Fhealth\u002Flive.\n",[471,1095,1097,1099,1102,1105,1108,1110,1113,1115,1118],{"class":473,"line":1096},30,[471,1098,887],{"class":485},[471,1100,1101],{"class":507},"healthIndicatorRegistry",[471,1103,1104],{"class":485},"().",[471,1106,1107],{"class":507},"register",[471,1109,914],{"class":485},[471,1111,1112],{"class":826},"\"audit-sink\"",[471,1114,354],{"class":485},[471,1116,1117],{"class":507},"AuditHealthIndicator",[471,1119,1059],{"class":485},[471,1121,1123],{"class":473,"line":1122},31,[471,1124,1125],{"class":485},"    }\n",[471,1127,1129],{"class":473,"line":1128},32,[471,1130,755],{"emptyLinePlaceholder":754},[471,1132,1134,1136,1138,1140],{"class":473,"line":1133},33,[471,1135,813],{"class":812},[471,1137,840],{"class":477},[471,1139,524],{"class":507},[471,1141,1142],{"class":485},"() {\n",[471,1144,1146],{"class":473,"line":1145},34,[471,1147,1148],{"class":880},"        \u002F\u002F Release anything acquired in initialize().\n",[471,1150,1152],{"class":473,"line":1151},35,[471,1153,1125],{"class":485},[471,1155,1157],{"class":473,"line":1156},36,[471,1158,533],{"class":485},[471,1160,1162],{"class":473,"line":1161},37,[471,1163,755],{"emptyLinePlaceholder":754},[471,1165,1167,1169,1172,1174,1177],{"class":473,"line":1166},38,[471,1168,796],{"class":477},[471,1170,1171],{"class":481}," AuditHealthIndicator",[471,1173,802],{"class":485},[471,1175,1176],{"class":481},"HealthIndicator",[471,1178,486],{"class":485},[471,1180,1182,1184,1186,1189,1192,1195],{"class":473,"line":1181},39,[471,1183,813],{"class":812},[471,1185,840],{"class":477},[471,1187,1188],{"class":507}," health",[471,1190,1191],{"class":485},"(): ",[471,1193,1194],{"class":481},"Health",[471,1196,1197],{"class":822}," =\n",[471,1199,1201,1204,1207,1209,1212,1214,1216,1218,1221,1223,1226],{"class":473,"line":1200},40,[471,1202,1203],{"class":485},"        Health.",[471,1205,1206],{"class":507},"up",[471,1208,1104],{"class":485},[471,1210,1211],{"class":507},"withDetail",[471,1213,914],{"class":485},[471,1215,1112],{"class":826},[471,1217,354],{"class":485},[471,1219,1220],{"class":826},"\"connected\"",[471,1222,920],{"class":485},[471,1224,1225],{"class":507},"build",[471,1227,869],{"class":485},[471,1229,1231],{"class":473,"line":1230},41,[471,1232,533],{"class":485},[462,1234,1239],{"className":1235,"code":1236,"filename":1237,"language":1238,"meta":467,"style":467},"language-java shiki shiki-themes vitesse-light","package com.example;\n\nimport io.stoatflow.runtime.health.Health;\nimport io.stoatflow.runtime.health.HealthIndicator;\nimport io.stoatflow.runtime.plugin.PluginContext;\nimport io.stoatflow.runtime.plugin.RuntimePlugin;\n\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\n\npublic class AuditPlugin implements RuntimePlugin {\n\n    @Override\n    public String getId() {\n        return \"audit\";\n    }\n\n    @Override\n    public void initialize(PluginContext context) {\n        String prefix = context.metricsPrefix();\n\n        \u002F\u002F 1. Custom metric — prefix it to match the built-in meters.\n        var registry = context.meterRegistry();\n        if (registry != null) {\n            registry.counter(prefix + \".audit.events\").increment();\n        }\n\n        \u002F\u002F 2. Custom HTTP endpoint (com.sun.net.httpserver.HttpHandler).\n        \u002F\u002F    Must be registered now — before the server starts.\n        var server = context.httpServer();\n        if (server != null) {\n            server.registerHandler(\"\u002Faudit\", exchange -> {\n                byte[] body = (\"{\\\"application\\\":\\\"\" + context.applicationId() + \"\\\"}\")\n                    .getBytes(StandardCharsets.UTF_8);\n                exchange.getResponseHeaders().add(\"Content-Type\", \"application\u002Fjson\");\n                exchange.sendResponseHeaders(200, body.length);\n                try (OutputStream os = exchange.getResponseBody()) {\n                    os.write(body);\n                }\n            });\n        }\n\n        \u002F\u002F 3. Custom health indicator — surfaces in \u002Fhealth\u002Fready and \u002Fhealth\u002Flive.\n        context.healthIndicatorRegistry().register(\"audit-sink\", new AuditHealthIndicator());\n    }\n\n    @Override\n    public void shutdown() {\n        \u002F\u002F Release anything acquired in initialize().\n    }\n}\n\nclass AuditHealthIndicator implements HealthIndicator {\n    @Override\n    public Health health() {\n        return Health.up().withDetail(\"audit-sink\", \"connected\").build();\n    }\n}\n","Java","java",[324,1240,1241,1256,1260,1288,1312,1337,1361,1365,1384,1407,1411,1428,1432,1440,1455,1472,1476,1480,1486,1509,1530,1534,1538,1556,1577,1607,1611,1615,1619,1623,1640,1657,1686,1745,1765,1799,1822,1849,1865,1870,1875,1879,1884,1889,1922,1927,1932,1939,1952,1957,1962,1967,1972,1986,1993,2007,2046,2051],{"__ignoreMap":467},[471,1242,1243,1245,1248,1250,1253],{"class":473,"line":474},[471,1244,746],{"class":477},[471,1246,1247],{"class":485}," com",[471,1249,386],{"class":822},[471,1251,1252],{"class":485},"example",[471,1254,1255],{"class":822},";\n",[471,1257,1258],{"class":473,"line":489},[471,1259,755],{"emptyLinePlaceholder":754},[471,1261,1262,1264,1267,1269,1272,1274,1277,1279,1282,1284,1286],{"class":473,"line":501},[471,1263,760],{"class":477},[471,1265,1266],{"class":485}," io",[471,1268,386],{"class":822},[471,1270,1271],{"class":485},"stoatflow",[471,1273,386],{"class":822},[471,1275,1276],{"class":485},"runtime",[471,1278,386],{"class":822},[471,1280,1281],{"class":485},"health",[471,1283,386],{"class":822},[471,1285,1194],{"class":485},[471,1287,1255],{"class":822},[471,1289,1290,1292,1294,1296,1298,1300,1302,1304,1306,1308,1310],{"class":473,"line":519},[471,1291,760],{"class":477},[471,1293,1266],{"class":485},[471,1295,386],{"class":822},[471,1297,1271],{"class":485},[471,1299,386],{"class":822},[471,1301,1276],{"class":485},[471,1303,386],{"class":822},[471,1305,1281],{"class":485},[471,1307,386],{"class":822},[471,1309,1176],{"class":485},[471,1311,1255],{"class":822},[471,1313,1314,1316,1318,1320,1322,1324,1326,1328,1331,1333,1335],{"class":473,"line":530},[471,1315,760],{"class":477},[471,1317,1266],{"class":485},[471,1319,386],{"class":822},[471,1321,1271],{"class":485},[471,1323,386],{"class":822},[471,1325,1276],{"class":485},[471,1327,386],{"class":822},[471,1329,1330],{"class":485},"plugin",[471,1332,386],{"class":822},[471,1334,369],{"class":485},[471,1336,1255],{"class":822},[471,1338,1339,1341,1343,1345,1347,1349,1351,1353,1355,1357,1359],{"class":473,"line":780},[471,1340,760],{"class":477},[471,1342,1266],{"class":485},[471,1344,386],{"class":822},[471,1346,1271],{"class":485},[471,1348,386],{"class":822},[471,1350,1276],{"class":485},[471,1352,386],{"class":822},[471,1354,1330],{"class":485},[471,1356,386],{"class":822},[471,1358,330],{"class":485},[471,1360,1255],{"class":822},[471,1362,1363],{"class":473,"line":788},[471,1364,755],{"emptyLinePlaceholder":754},[471,1366,1367,1369,1372,1374,1377,1379,1382],{"class":473,"line":793},[471,1368,760],{"class":477},[471,1370,1371],{"class":485}," java",[471,1373,386],{"class":822},[471,1375,1376],{"class":485},"io",[471,1378,386],{"class":822},[471,1380,1381],{"class":485},"OutputStream",[471,1383,1255],{"class":822},[471,1385,1386,1388,1390,1392,1395,1397,1400,1402,1405],{"class":473,"line":809},[471,1387,760],{"class":477},[471,1389,1371],{"class":485},[471,1391,386],{"class":822},[471,1393,1394],{"class":485},"nio",[471,1396,386],{"class":822},[471,1398,1399],{"class":485},"charset",[471,1401,386],{"class":822},[471,1403,1404],{"class":485},"StandardCharsets",[471,1406,1255],{"class":822},[471,1408,1409],{"class":473,"line":830},[471,1410,755],{"emptyLinePlaceholder":754},[471,1412,1413,1416,1419,1421,1424,1426],{"class":473,"line":835},[471,1414,1415],{"class":812},"public",[471,1417,1418],{"class":812}," class",[471,1420,799],{"class":481},[471,1422,1423],{"class":812}," implements",[471,1425,482],{"class":507},[471,1427,486],{"class":822},[471,1429,1430],{"class":473,"line":852},[471,1431,755],{"emptyLinePlaceholder":754},[471,1433,1434,1437],{"class":473,"line":872},[471,1435,1436],{"class":822},"    @",[471,1438,1439],{"class":812},"Override\n",[471,1441,1442,1445,1448,1451,1453],{"class":473,"line":877},[471,1443,1444],{"class":812},"    public",[471,1446,1447],{"class":485}," String ",[471,1449,1450],{"class":507},"getId",[471,1452,997],{"class":822},[471,1454,486],{"class":822},[471,1456,1457,1460,1464,1467,1470],{"class":473,"line":884},[471,1458,1459],{"class":477},"        return",[471,1461,1463],{"class":1462},"sSP4y"," \"",[471,1465,1466],{"class":826},"audit",[471,1468,1469],{"class":1462},"\"",[471,1471,1255],{"class":822},[471,1473,1474],{"class":473,"line":905},[471,1475,1125],{"class":822},[471,1477,1478],{"class":473,"line":928},[471,1479,755],{"emptyLinePlaceholder":754},[471,1481,1482,1484],{"class":473,"line":934},[471,1483,1436],{"class":822},[471,1485,1439],{"class":812},[471,1487,1488,1490,1493,1495,1497,1500,1504,1507],{"class":473,"line":939},[471,1489,1444],{"class":812},[471,1491,1492],{"class":812}," void",[471,1494,508],{"class":507},[471,1496,914],{"class":822},[471,1498,1499],{"class":485},"PluginContext ",[471,1501,1503],{"class":1502},"svycV","context",[471,1505,1506],{"class":822},")",[471,1508,486],{"class":822},[471,1510,1511,1514,1517,1520,1523,1525,1527],{"class":473,"line":945},[471,1512,1513],{"class":485},"        String",[471,1515,1516],{"class":1502}," prefix",[471,1518,1519],{"class":822}," =",[471,1521,1522],{"class":1502}," context",[471,1524,386],{"class":822},[471,1526,866],{"class":507},[471,1528,1529],{"class":822},"();\n",[471,1531,1532],{"class":473,"line":951},[471,1533,755],{"emptyLinePlaceholder":754},[471,1535,1536],{"class":473,"line":974},[471,1537,881],{"class":880},[471,1539,1540,1543,1546,1548,1550,1552,1554],{"class":473,"line":1006},[471,1541,1542],{"class":812},"        var",[471,1544,1545],{"class":1502}," registry",[471,1547,1519],{"class":822},[471,1549,1522],{"class":1502},[471,1551,386],{"class":822},[471,1553,890],{"class":507},[471,1555,1529],{"class":822},[471,1557,1558,1561,1564,1567,1570,1573,1575],{"class":473,"line":1017},[471,1559,1560],{"class":477},"        if",[471,1562,1563],{"class":822}," (",[471,1565,1566],{"class":485},"registry ",[471,1568,1569],{"class":812},"!=",[471,1571,1572],{"class":477}," null",[471,1574,1506],{"class":822},[471,1576,486],{"class":822},[471,1578,1579,1582,1584,1586,1588,1591,1594,1596,1599,1601,1603,1605],{"class":473,"line":1038},[471,1580,1581],{"class":1502},"            registry",[471,1583,386],{"class":822},[471,1585,911],{"class":507},[471,1587,914],{"class":822},[471,1589,1590],{"class":485},"prefix ",[471,1592,1593],{"class":812},"+",[471,1595,1463],{"class":1462},[471,1597,1598],{"class":826},".audit.events",[471,1600,1469],{"class":1462},[471,1602,920],{"class":822},[471,1604,923],{"class":507},[471,1606,1529],{"class":822},[471,1608,1609],{"class":473,"line":1062},[471,1610,931],{"class":822},[471,1612,1613],{"class":473,"line":1080},[471,1614,755],{"emptyLinePlaceholder":754},[471,1616,1617],{"class":473,"line":1085},[471,1618,942],{"class":880},[471,1620,1621],{"class":473,"line":1090},[471,1622,948],{"class":880},[471,1624,1625,1627,1630,1632,1634,1636,1638],{"class":473,"line":1096},[471,1626,1542],{"class":812},[471,1628,1629],{"class":1502}," server",[471,1631,1519],{"class":822},[471,1633,1522],{"class":1502},[471,1635,386],{"class":822},[471,1637,956],{"class":507},[471,1639,1529],{"class":822},[471,1641,1642,1644,1646,1649,1651,1653,1655],{"class":473,"line":1122},[471,1643,1560],{"class":477},[471,1645,1563],{"class":822},[471,1647,1648],{"class":485},"server ",[471,1650,1569],{"class":812},[471,1652,1572],{"class":477},[471,1654,1506],{"class":822},[471,1656,486],{"class":822},[471,1658,1659,1662,1664,1666,1668,1670,1673,1675,1678,1681,1684],{"class":473,"line":1128},[471,1660,1661],{"class":1502},"            server",[471,1663,386],{"class":822},[471,1665,961],{"class":507},[471,1667,914],{"class":822},[471,1669,1469],{"class":1462},[471,1671,1672],{"class":826},"\u002Faudit",[471,1674,1469],{"class":1462},[471,1676,1677],{"class":822},",",[471,1679,1680],{"class":485}," exchange ",[471,1682,1683],{"class":822},"->",[471,1685,486],{"class":822},[471,1687,1688,1691,1694,1697,1699,1701,1703,1706,1710,1713,1715,1718,1720,1722,1725,1727,1729,1731,1733,1735,1737,1739,1741,1743],{"class":473,"line":1133},[471,1689,1690],{"class":812},"                byte",[471,1692,1693],{"class":822},"[]",[471,1695,1696],{"class":1502}," body",[471,1698,1519],{"class":822},[471,1700,1563],{"class":822},[471,1702,1469],{"class":1462},[471,1704,1705],{"class":826},"{",[471,1707,1709],{"class":1708},"sEi1f","\\\"",[471,1711,1712],{"class":826},"application",[471,1714,1709],{"class":1708},[471,1716,1717],{"class":826},":",[471,1719,1709],{"class":1708},[471,1721,1469],{"class":1462},[471,1723,1724],{"class":812}," +",[471,1726,1522],{"class":1502},[471,1728,386],{"class":822},[471,1730,994],{"class":507},[471,1732,997],{"class":822},[471,1734,1724],{"class":812},[471,1736,1463],{"class":1462},[471,1738,1709],{"class":1708},[471,1740,1000],{"class":826},[471,1742,1469],{"class":1462},[471,1744,516],{"class":822},[471,1746,1747,1750,1753,1755,1757,1759,1762],{"class":473,"line":1145},[471,1748,1749],{"class":822},"                    .",[471,1751,1752],{"class":507},"getBytes",[471,1754,914],{"class":822},[471,1756,1404],{"class":1502},[471,1758,386],{"class":822},[471,1760,1761],{"class":1502},"UTF_8",[471,1763,1764],{"class":822},");\n",[471,1766,1767,1770,1772,1775,1777,1779,1781,1783,1786,1788,1790,1792,1795,1797],{"class":473,"line":1151},[471,1768,1769],{"class":1502},"                exchange",[471,1771,386],{"class":822},[471,1773,1774],{"class":507},"getResponseHeaders",[471,1776,1104],{"class":822},[471,1778,1023],{"class":507},[471,1780,914],{"class":822},[471,1782,1469],{"class":1462},[471,1784,1785],{"class":826},"Content-Type",[471,1787,1469],{"class":1462},[471,1789,1677],{"class":822},[471,1791,1463],{"class":1462},[471,1793,1794],{"class":826},"application\u002Fjson",[471,1796,1469],{"class":1462},[471,1798,1764],{"class":822},[471,1800,1801,1803,1805,1807,1809,1811,1813,1815,1817,1820],{"class":473,"line":1156},[471,1802,1769],{"class":1502},[471,1804,386],{"class":822},[471,1806,1044],{"class":507},[471,1808,914],{"class":822},[471,1810,1050],{"class":1049},[471,1812,1677],{"class":822},[471,1814,1696],{"class":1502},[471,1816,386],{"class":822},[471,1818,1819],{"class":1502},"length",[471,1821,1764],{"class":822},[471,1823,1824,1827,1829,1831,1834,1836,1839,1841,1844,1847],{"class":473,"line":1161},[471,1825,1826],{"class":477},"                try",[471,1828,1563],{"class":822},[471,1830,1381],{"class":485},[471,1832,1833],{"class":1502}," os",[471,1835,1519],{"class":822},[471,1837,1838],{"class":1502}," exchange",[471,1840,386],{"class":822},[471,1842,1843],{"class":507},"getResponseBody",[471,1845,1846],{"class":822},"())",[471,1848,486],{"class":822},[471,1850,1851,1854,1856,1858,1860,1863],{"class":473,"line":1166},[471,1852,1853],{"class":1502},"                    os",[471,1855,386],{"class":822},[471,1857,1074],{"class":507},[471,1859,914],{"class":822},[471,1861,1862],{"class":485},"body",[471,1864,1764],{"class":822},[471,1866,1867],{"class":473,"line":1181},[471,1868,1869],{"class":822},"                }\n",[471,1871,1872],{"class":473,"line":1200},[471,1873,1874],{"class":822},"            });\n",[471,1876,1877],{"class":473,"line":1230},[471,1878,931],{"class":822},[471,1880,1882],{"class":473,"line":1881},42,[471,1883,755],{"emptyLinePlaceholder":754},[471,1885,1887],{"class":473,"line":1886},43,[471,1888,1093],{"class":880},[471,1890,1892,1895,1897,1899,1901,1903,1905,1907,1910,1912,1914,1917,1919],{"class":473,"line":1891},44,[471,1893,1894],{"class":1502},"        context",[471,1896,386],{"class":822},[471,1898,1101],{"class":507},[471,1900,1104],{"class":822},[471,1902,1107],{"class":507},[471,1904,914],{"class":822},[471,1906,1469],{"class":1462},[471,1908,1909],{"class":826},"audit-sink",[471,1911,1469],{"class":1462},[471,1913,1677],{"class":822},[471,1915,1916],{"class":477}," new",[471,1918,1171],{"class":507},[471,1920,1921],{"class":822},"());\n",[471,1923,1925],{"class":473,"line":1924},45,[471,1926,1125],{"class":822},[471,1928,1930],{"class":473,"line":1929},46,[471,1931,755],{"emptyLinePlaceholder":754},[471,1933,1935,1937],{"class":473,"line":1934},47,[471,1936,1436],{"class":822},[471,1938,1439],{"class":812},[471,1940,1942,1944,1946,1948,1950],{"class":473,"line":1941},48,[471,1943,1444],{"class":812},[471,1945,1492],{"class":812},[471,1947,524],{"class":507},[471,1949,997],{"class":822},[471,1951,486],{"class":822},[471,1953,1955],{"class":473,"line":1954},49,[471,1956,1148],{"class":880},[471,1958,1960],{"class":473,"line":1959},50,[471,1961,1125],{"class":822},[471,1963,1965],{"class":473,"line":1964},51,[471,1966,533],{"class":822},[471,1968,1970],{"class":473,"line":1969},52,[471,1971,755],{"emptyLinePlaceholder":754},[471,1973,1975,1977,1979,1981,1984],{"class":473,"line":1974},53,[471,1976,796],{"class":812},[471,1978,1171],{"class":481},[471,1980,1423],{"class":812},[471,1982,1983],{"class":507}," HealthIndicator",[471,1985,486],{"class":822},[471,1987,1989,1991],{"class":473,"line":1988},54,[471,1990,1436],{"class":822},[471,1992,1439],{"class":812},[471,1994,1996,1998,2001,2003,2005],{"class":473,"line":1995},55,[471,1997,1444],{"class":812},[471,1999,2000],{"class":485}," Health ",[471,2002,1281],{"class":507},[471,2004,997],{"class":822},[471,2006,486],{"class":822},[471,2008,2010,2012,2015,2017,2019,2021,2023,2025,2027,2029,2031,2033,2035,2038,2040,2042,2044],{"class":473,"line":2009},56,[471,2011,1459],{"class":477},[471,2013,2014],{"class":1502}," Health",[471,2016,386],{"class":822},[471,2018,1206],{"class":507},[471,2020,1104],{"class":822},[471,2022,1211],{"class":507},[471,2024,914],{"class":822},[471,2026,1469],{"class":1462},[471,2028,1909],{"class":826},[471,2030,1469],{"class":1462},[471,2032,1677],{"class":822},[471,2034,1463],{"class":1462},[471,2036,2037],{"class":826},"connected",[471,2039,1469],{"class":1462},[471,2041,920],{"class":822},[471,2043,1225],{"class":507},[471,2045,1529],{"class":822},[471,2047,2049],{"class":473,"line":2048},57,[471,2050,1125],{"class":822},[471,2052,2054],{"class":473,"line":2053},58,[471,2055,533],{"class":822},[428,2057,2058],{"color":430,"icon":431},[320,2059,2060,2061,2063,2064,2067,2068,2071,2072,2075,2076,2079],{},"In Kotlin the ",[324,2062,353],{}," is an abstract ",[324,2065,2066],{},"val","; in Java it surfaces as ",[324,2069,2070],{},"getId()",". The HTTP handler is the JDK's ",[324,2073,2074],{},"com.sun.net.httpserver.HttpHandler"," — a functional interface taking an ",[324,2077,2078],{},"HttpExchange",", so it accepts a lambda directly.",[2081,2082,2084],"h3",{"id":2083},"registering-a-custom-metric","Registering a custom metric",[320,2086,2087,2088,2091,2092,354,2094,354,2097,2100,2101,2104,2105,2111,2112,2114],{},"Call ",[324,2089,2090],{},"context.meterRegistry()"," and use the standard Micrometer API (",[324,2093,911],{},[324,2095,2096],{},"gauge",[324,2098,2099],{},"timer",", …). Prefix meter names with ",[324,2102,2103],{},"context.metricsPrefix()"," so they sit alongside the built-in StoatFlow meters and inherit the runtime's common tags. The new meters appear on the ",[2106,2107,2108],"a",{"href":196},[324,2109,2110],{},"\u002Fmetrics"," scrape endpoint. See ",[2106,2113,195],{"href":196}," for the built-in catalogue.",[2081,2116,2118],{"id":2117},"registering-an-http-handler","Registering an HTTP handler",[320,2120,2087,2121,2124,2125,2127,2128,2130,2131,2134,2135,2137,2138,2141,2142,2144,2145,2148,2149,2151,2152,2154,2155,2157,2158,2161],{},[324,2122,2123],{},"context.httpServer()?.registerHandler(path, handler)"," during ",[324,2126,580],{},". The path is a context root (e.g. ",[324,2129,1672],{},"); the underlying JDK ",[324,2132,2133],{},"HttpServer"," routes by longest-prefix match, so ",[324,2136,1672],{}," also catches ",[324,2139,2140],{},"\u002Faudit\u002Fanything",". The handler is a ",[324,2143,2074],{},"; the server runs each request on a virtual thread. Registration ",[346,2146,2147],{},"must"," happen during ",[324,2150,580],{}," — calling ",[324,2153,961],{}," after the server has started throws ",[324,2156,656],{},". See ",[2106,2159,2160],{"href":186},"REST API"," for the built-in endpoints.",[2081,2163,2165],{"id":2164},"registering-a-health-indicator","Registering a health indicator",[320,2167,2087,2168,2171,2172,2174,2175,2178,2179,2182,2183,2185],{},[324,2169,2170],{},"context.healthIndicatorRegistry().register(name, indicator)",". A ",[324,2173,1176],{}," implements ",[324,2176,2177],{},"health()"," (readiness) and may override ",[324,2180,2181],{},"livenessHealth()"," (liveness — defaults to the same as ",[324,2184,2177],{},"). The registry offers three registration methods:",[594,2187,2188,2198],{},[597,2189,2190],{},[600,2191,2192,2195],{},[603,2193,2194],{},"Method",[603,2196,2197],{},"Effect",[613,2199,2200,2214,2224],{},[600,2201,2202,2207],{},[618,2203,2204],{},[324,2205,2206],{},"register(name, indicator)",[618,2208,2209,2210,2213],{},"Registers for ",[346,2211,2212],{},"both"," liveness and readiness.",[600,2215,2216,2221],{},[618,2217,2218],{},[324,2219,2220],{},"registerReadiness(name, indicator)",[618,2222,2223],{},"Readiness probe only.",[600,2225,2226,2231],{},[618,2227,2228],{},[324,2229,2230],{},"registerLiveness(name, indicator)",[618,2232,2233],{},"Liveness probe only.",[320,2235,2236,2237,2239,2240,2242],{},"Use readiness-only when a dependency being down should take the instance out of the load-balancer pool but ",[346,2238,439],{}," trigger a restart; use liveness-only for conditions where a restart is the right remedy. See ",[2106,2241,190],{"href":191}," for the built-in indicators and the probe semantics.",[442,2244,2246],{"id":2245},"lifecycle-hooks","Lifecycle hooks",[320,2248,2249,2250,2252,2253,2256,2257,2259,2260,2263,2264,2267],{},"A ",[324,2251,334],{}," is a single-method callback (a Kotlin ",[324,2254,2255],{},"fun interface",") invoked as the runtime transitions through its lifecycle. Unlike a plugin, it has no ",[324,2258,369],{}," — it receives the ",[324,2261,2262],{},"LifecycleEvent"," and the ",[324,2265,2266],{},"StoatFlowRuntime"," instance (for state queries).",[462,2269,2271],{"className":464,"code":2270,"language":466,"meta":467,"style":467},"fun interface RuntimeLifecycleListener {\n    fun onEvent(event: LifecycleEvent, runtime: StoatFlowRuntime)\n}\n",[324,2272,2273,2282,2301],{"__ignoreMap":467},[471,2274,2275,2277,2280],{"class":473,"line":474},[471,2276,2255],{"class":477},[471,2278,2279],{"class":481}," RuntimeLifecycleListener",[471,2281,486],{"class":485},[471,2283,2284,2286,2289,2292,2294,2297,2299],{"class":473,"line":489},[471,2285,504],{"class":477},[471,2287,2288],{"class":507}," onEvent",[471,2290,2291],{"class":485},"(event: ",[471,2293,2262],{"class":481},[471,2295,2296],{"class":485},", runtime: ",[471,2298,2266],{"class":481},[471,2300,516],{"class":485},[471,2302,2303],{"class":473,"line":501},[471,2304,533],{"class":485},[320,2306,2307,2309],{},[324,2308,2262],{}," has four values, emitted in order:",[594,2311,2312,2325],{},[597,2313,2314],{},[600,2315,2316,2319,2322],{},[603,2317,2318],{},"Event",[603,2320,2321],{},"When",[603,2323,2324],{},"State at this point",[613,2326,2327,2339,2351,2363],{},[600,2328,2329,2333,2336],{},[618,2330,2331],{},[324,2332,396],{},[618,2334,2335],{},"Before initialization",[618,2337,2338],{},"Config validated; no resources allocated yet.",[600,2340,2341,2345,2348],{},[618,2342,2343],{},[324,2344,400],{},[618,2346,2347],{},"After successful startup",[618,2349,2350],{},"HTTP server running, metrics collecting, stream processing active.",[600,2352,2353,2357,2360],{},[618,2354,2355],{},[324,2356,403],{},[618,2358,2359],{},"Before shutdown begins",[618,2361,2362],{},"Shutdown requested; processing still active.",[600,2364,2365,2369,2372],{},[618,2366,2367],{},[324,2368,406],{},[618,2370,2371],{},"After shutdown completes",[618,2373,2374],{},"Processing stopped, HTTP server stopped, metrics registry closed.",[733,2376,2377,2509],{"group":735},[462,2378,2380],{"className":464,"code":2379,"filename":739,"language":466,"meta":467,"style":467},"import io.stoatflow.runtime.LifecycleEvent\nimport io.stoatflow.runtime.StoatFlowRuntime\nimport io.stoatflow.runtime.plugin.RuntimeLifecycleListener\n\nval listener = RuntimeLifecycleListener { event, runtime ->\n    when (event) {\n        LifecycleEvent.POST_START -> println(\"Started: ${runtime.state()}\")\n        LifecycleEvent.PRE_STOP -> println(\"Stopping: ${runtime.state()}\")\n        else -> {}\n    }\n}\n",[324,2381,2382,2389,2396,2403,2407,2423,2431,2462,2490,2501,2505],{"__ignoreMap":467},[471,2383,2384,2386],{"class":473,"line":474},[471,2385,760],{"class":477},[471,2387,2388],{"class":507}," io.stoatflow.runtime.LifecycleEvent\n",[471,2390,2391,2393],{"class":473,"line":489},[471,2392,760],{"class":477},[471,2394,2395],{"class":507}," io.stoatflow.runtime.StoatFlowRuntime\n",[471,2397,2398,2400],{"class":473,"line":501},[471,2399,760],{"class":477},[471,2401,2402],{"class":507}," io.stoatflow.runtime.plugin.RuntimeLifecycleListener\n",[471,2404,2405],{"class":473,"line":519},[471,2406,755],{"emptyLinePlaceholder":754},[471,2408,2409,2411,2414,2416,2418,2421],{"class":473,"line":530},[471,2410,2066],{"class":477},[471,2412,2413],{"class":485}," listener ",[471,2415,823],{"class":822},[471,2417,2279],{"class":507},[471,2419,2420],{"class":485}," { event, runtime ",[471,2422,902],{"class":822},[471,2424,2425,2428],{"class":473,"line":780},[471,2426,2427],{"class":477},"    when",[471,2429,2430],{"class":485}," (event) {\n",[471,2432,2433,2436,2438,2441,2443,2446,2448,2451,2454,2456,2458,2460],{"class":473,"line":788},[471,2434,2435],{"class":485},"        LifecycleEvent.POST_START ",[471,2437,1683],{"class":822},[471,2439,2440],{"class":507}," println",[471,2442,914],{"class":485},[471,2444,2445],{"class":826},"\"Started: ",[471,2447,988],{"class":477},[471,2449,2450],{"class":826},"runtime.",[471,2452,2453],{"class":507},"state",[471,2455,997],{"class":826},[471,2457,1000],{"class":477},[471,2459,1469],{"class":826},[471,2461,516],{"class":485},[471,2463,2464,2467,2469,2471,2473,2476,2478,2480,2482,2484,2486,2488],{"class":473,"line":793},[471,2465,2466],{"class":485},"        LifecycleEvent.PRE_STOP ",[471,2468,1683],{"class":822},[471,2470,2440],{"class":507},[471,2472,914],{"class":485},[471,2474,2475],{"class":826},"\"Stopping: ",[471,2477,988],{"class":477},[471,2479,2450],{"class":826},[471,2481,2453],{"class":507},[471,2483,997],{"class":826},[471,2485,1000],{"class":477},[471,2487,1469],{"class":826},[471,2489,516],{"class":485},[471,2491,2492,2495,2498],{"class":473,"line":809},[471,2493,2494],{"class":477},"        else",[471,2496,2497],{"class":822}," ->",[471,2499,2500],{"class":485}," {}\n",[471,2502,2503],{"class":473,"line":830},[471,2504,1125],{"class":485},[471,2506,2507],{"class":473,"line":835},[471,2508,533],{"class":485},[462,2510,2512],{"className":1235,"code":2511,"filename":1237,"language":1238,"meta":467,"style":467},"import io.stoatflow.runtime.LifecycleEvent;\nimport io.stoatflow.runtime.plugin.RuntimeLifecycleListener;\n\nRuntimeLifecycleListener listener = (event, runtime) -> {\n    switch (event) {\n        case POST_START -> System.out.println(\"Started: \" + runtime.state());\n        case PRE_STOP -> System.out.println(\"Stopping: \" + runtime.state());\n        default -> { }\n    }\n};\n",[324,2513,2514,2534,2558,2562,2587,2600,2642,2680,2693,2697],{"__ignoreMap":467},[471,2515,2516,2518,2520,2522,2524,2526,2528,2530,2532],{"class":473,"line":474},[471,2517,760],{"class":477},[471,2519,1266],{"class":485},[471,2521,386],{"class":822},[471,2523,1271],{"class":485},[471,2525,386],{"class":822},[471,2527,1276],{"class":485},[471,2529,386],{"class":822},[471,2531,2262],{"class":485},[471,2533,1255],{"class":822},[471,2535,2536,2538,2540,2542,2544,2546,2548,2550,2552,2554,2556],{"class":473,"line":489},[471,2537,760],{"class":477},[471,2539,1266],{"class":485},[471,2541,386],{"class":822},[471,2543,1271],{"class":485},[471,2545,386],{"class":822},[471,2547,1276],{"class":485},[471,2549,386],{"class":822},[471,2551,1330],{"class":485},[471,2553,386],{"class":822},[471,2555,334],{"class":485},[471,2557,1255],{"class":822},[471,2559,2560],{"class":473,"line":501},[471,2561,755],{"emptyLinePlaceholder":754},[471,2563,2564,2566,2569,2571,2573,2576,2578,2581,2583,2585],{"class":473,"line":519},[471,2565,334],{"class":485},[471,2567,2568],{"class":1502}," listener",[471,2570,1519],{"class":822},[471,2572,1563],{"class":822},[471,2574,2575],{"class":485},"event",[471,2577,1677],{"class":822},[471,2579,2580],{"class":485}," runtime",[471,2582,1506],{"class":822},[471,2584,2497],{"class":822},[471,2586,486],{"class":822},[471,2588,2589,2592,2594,2596,2598],{"class":473,"line":530},[471,2590,2591],{"class":477},"    switch",[471,2593,1563],{"class":822},[471,2595,2575],{"class":485},[471,2597,1506],{"class":822},[471,2599,486],{"class":822},[471,2601,2602,2605,2608,2610,2613,2615,2618,2620,2623,2625,2627,2630,2632,2634,2636,2638,2640],{"class":473,"line":780},[471,2603,2604],{"class":477},"        case",[471,2606,2607],{"class":485}," POST_START ",[471,2609,1683],{"class":822},[471,2611,2612],{"class":1502}," System",[471,2614,386],{"class":822},[471,2616,2617],{"class":1502},"out",[471,2619,386],{"class":822},[471,2621,2622],{"class":507},"println",[471,2624,914],{"class":822},[471,2626,1469],{"class":1462},[471,2628,2629],{"class":826},"Started: ",[471,2631,1469],{"class":1462},[471,2633,1724],{"class":812},[471,2635,2580],{"class":1502},[471,2637,386],{"class":822},[471,2639,2453],{"class":507},[471,2641,1921],{"class":822},[471,2643,2644,2646,2649,2651,2653,2655,2657,2659,2661,2663,2665,2668,2670,2672,2674,2676,2678],{"class":473,"line":788},[471,2645,2604],{"class":477},[471,2647,2648],{"class":485}," PRE_STOP ",[471,2650,1683],{"class":822},[471,2652,2612],{"class":1502},[471,2654,386],{"class":822},[471,2656,2617],{"class":1502},[471,2658,386],{"class":822},[471,2660,2622],{"class":507},[471,2662,914],{"class":822},[471,2664,1469],{"class":1462},[471,2666,2667],{"class":826},"Stopping: ",[471,2669,1469],{"class":1462},[471,2671,1724],{"class":812},[471,2673,2580],{"class":1502},[471,2675,386],{"class":822},[471,2677,2453],{"class":507},[471,2679,1921],{"class":822},[471,2681,2682,2685,2687,2690],{"class":473,"line":793},[471,2683,2684],{"class":477},"        default",[471,2686,2497],{"class":822},[471,2688,2689],{"class":822}," {",[471,2691,2692],{"class":822}," }\n",[471,2694,2695],{"class":473,"line":809},[471,2696,1125],{"class":822},[471,2698,2699],{"class":473,"line":830},[471,2700,2701],{"class":822},"};\n",[428,2703,2706],{"color":2704,"icon":2705},"neutral","i-lucide-lightbulb",[320,2707,2708,2709,2712,2713,2715,2716,2718,2719,2721,2722,2263,2725,2721,2727,2729,2730,2732,2733,2735],{},"Use a ",[346,2710,2711],{},"lifecycle listener"," for one-off side effects tied to startup\u002Fshutdown transitions (notify an external system, flush a buffer). Use a ",[346,2714,1330],{}," when you need the ",[324,2717,369],{}," to register endpoints, metrics, or health indicators. A plugin's ",[324,2720,580],{},"\u002F",[324,2723,2724],{},"shutdown",[324,2726,400],{},[324,2728,406],{}," events overlap but differ in timing — ",[324,2731,580],{}," runs before the HTTP server starts; ",[324,2734,400],{}," fires after it is running.",[442,2737,2739],{"id":2738},"registering-plugins-and-listeners","Registering plugins and listeners",[320,2741,2742,2743,2746,2747,2749],{},"Register both on the runtime builder. With the batteries-included ",[324,2744,2745],{},"StoatFlowRuntime.fromConfig(...)"," entry point, reach the builder through the ",[324,2748,422],{}," block; with the explicit builder, call the methods directly.",[733,2751,2752,2862],{"group":735},[462,2753,2755],{"className":464,"code":2754,"filename":739,"language":466,"meta":467,"style":467},"import io.stoatflow.runtime.StoatFlowRuntime\n\n\u002F\u002F Via fromConfig — configure block exposes the builder\nval runtime = StoatFlowRuntime.fromConfig(\n    topologyBuilder = { buildTopology(it) },\n    configure = {\n        addPlugin(AuditPlugin())\n        addLifecycleListener(listener)\n    },\n)\nruntime.start()\nruntime.awaitTermination()\n",[324,2756,2757,2763,2767,2772,2790,2806,2815,2827,2835,2840,2844,2853],{"__ignoreMap":467},[471,2758,2759,2761],{"class":473,"line":474},[471,2760,760],{"class":477},[471,2762,2395],{"class":507},[471,2764,2765],{"class":473,"line":489},[471,2766,755],{"emptyLinePlaceholder":754},[471,2768,2769],{"class":473,"line":501},[471,2770,2771],{"class":880},"\u002F\u002F Via fromConfig — configure block exposes the builder\n",[471,2773,2774,2776,2779,2781,2784,2787],{"class":473,"line":519},[471,2775,2066],{"class":477},[471,2777,2778],{"class":485}," runtime ",[471,2780,823],{"class":822},[471,2782,2783],{"class":485}," StoatFlowRuntime.",[471,2785,2786],{"class":507},"fromConfig",[471,2788,2789],{"class":485},"(\n",[471,2791,2792,2795,2797,2800,2803],{"class":473,"line":530},[471,2793,2794],{"class":485},"    topologyBuilder ",[471,2796,823],{"class":822},[471,2798,2799],{"class":485}," { ",[471,2801,2802],{"class":507},"buildTopology",[471,2804,2805],{"class":485},"(it) },\n",[471,2807,2808,2811,2813],{"class":473,"line":780},[471,2809,2810],{"class":485},"    configure ",[471,2812,823],{"class":822},[471,2814,486],{"class":485},[471,2816,2817,2820,2822,2825],{"class":473,"line":788},[471,2818,2819],{"class":507},"        addPlugin",[471,2821,914],{"class":485},[471,2823,2824],{"class":507},"AuditPlugin",[471,2826,1059],{"class":485},[471,2828,2829,2832],{"class":473,"line":793},[471,2830,2831],{"class":507},"        addLifecycleListener",[471,2833,2834],{"class":485},"(listener)\n",[471,2836,2837],{"class":473,"line":809},[471,2838,2839],{"class":485},"    },\n",[471,2841,2842],{"class":473,"line":830},[471,2843,516],{"class":485},[471,2845,2846,2848,2851],{"class":473,"line":835},[471,2847,2450],{"class":485},[471,2849,2850],{"class":507},"start",[471,2852,869],{"class":485},[471,2854,2855,2857,2860],{"class":473,"line":852},[471,2856,2450],{"class":485},[471,2858,2859],{"class":507},"awaitTermination",[471,2861,869],{"class":485},[462,2863,2865],{"className":1235,"code":2864,"filename":1237,"language":1238,"meta":467,"style":467},"import io.stoatflow.runtime.StoatFlowRuntime;\n\n\u002F\u002F Via fromConfig — configure block exposes the builder\nvar runtime = StoatFlowRuntime.fromConfig(\n    Main::buildTopology,\n    builder -> {\n        builder.addPlugin(new AuditPlugin());\n        builder.addLifecycleListener(listener);\n    }\n);\nruntime.start();\nruntime.awaitTermination();\n",[324,2866,2867,2887,2891,2895,2913,2926,2935,2953,2968,2972,2976,2986],{"__ignoreMap":467},[471,2868,2869,2871,2873,2875,2877,2879,2881,2883,2885],{"class":473,"line":474},[471,2870,760],{"class":477},[471,2872,1266],{"class":485},[471,2874,386],{"class":822},[471,2876,1271],{"class":485},[471,2878,386],{"class":822},[471,2880,1276],{"class":485},[471,2882,386],{"class":822},[471,2884,2266],{"class":485},[471,2886,1255],{"class":822},[471,2888,2889],{"class":473,"line":489},[471,2890,755],{"emptyLinePlaceholder":754},[471,2892,2893],{"class":473,"line":501},[471,2894,2771],{"class":880},[471,2896,2897,2900,2902,2904,2907,2909,2911],{"class":473,"line":519},[471,2898,2899],{"class":812},"var",[471,2901,2580],{"class":1502},[471,2903,1519],{"class":822},[471,2905,2906],{"class":1502}," StoatFlowRuntime",[471,2908,386],{"class":822},[471,2910,2786],{"class":507},[471,2912,2789],{"class":822},[471,2914,2915,2918,2921,2923],{"class":473,"line":530},[471,2916,2917],{"class":485},"    Main",[471,2919,2920],{"class":477},"::",[471,2922,2802],{"class":485},[471,2924,2925],{"class":822},",\n",[471,2927,2928,2931,2933],{"class":473,"line":780},[471,2929,2930],{"class":485},"    builder ",[471,2932,1683],{"class":822},[471,2934,486],{"class":822},[471,2936,2937,2940,2942,2944,2946,2949,2951],{"class":473,"line":788},[471,2938,2939],{"class":1502},"        builder",[471,2941,386],{"class":822},[471,2943,415],{"class":507},[471,2945,914],{"class":822},[471,2947,2948],{"class":477},"new",[471,2950,799],{"class":507},[471,2952,1921],{"class":822},[471,2954,2955,2957,2959,2961,2963,2966],{"class":473,"line":793},[471,2956,2939],{"class":1502},[471,2958,386],{"class":822},[471,2960,418],{"class":507},[471,2962,914],{"class":822},[471,2964,2965],{"class":485},"listener",[471,2967,1764],{"class":822},[471,2969,2970],{"class":473,"line":809},[471,2971,1125],{"class":822},[471,2973,2974],{"class":473,"line":830},[471,2975,1764],{"class":822},[471,2977,2978,2980,2982,2984],{"class":473,"line":835},[471,2979,1276],{"class":1502},[471,2981,386],{"class":822},[471,2983,2850],{"class":507},[471,2985,1529],{"class":822},[471,2987,2988,2990,2992,2994],{"class":473,"line":852},[471,2989,1276],{"class":1502},[471,2991,386],{"class":822},[471,2993,2859],{"class":507},[471,2995,1529],{"class":822},[320,2997,2998,2999,3002,3003,386],{},"The builder also offers ",[324,3000,3001],{},"addHealthIndicator(name, indicator)"," as a shortcut for registering a health indicator without writing a plugin — it registers for both liveness and readiness. For finer control (readiness-only, liveness-only, or dynamic registration), go through a plugin and ",[324,3004,3005],{},"context.healthIndicatorRegistry()",[442,3007,3009],{"id":3008},"next-steps","Next steps",[340,3011,3012,3022,3029,3036],{},[343,3013,3014,3018,3019,3021],{},[346,3015,3016],{},[2106,3017,195],{"href":196}," — the built-in Micrometer catalogue and the ",[324,3020,2110],{}," scrape format your custom meters join.",[343,3023,3024,3028],{},[346,3025,3026],{},[2106,3027,190],{"href":191}," — built-in indicators and the liveness vs readiness semantics your custom indicators participate in.",[343,3030,3031,3035],{},[346,3032,3033],{},[2106,3034,2160],{"href":186}," — the built-in HTTP endpoints alongside which your custom handlers are served.",[343,3037,3038,3043],{},[346,3039,3040],{},[2106,3041,3042],{"href":163},"Runtime configuration"," — toggling the HTTP server and metrics that your plugin depends on.",[3045,3046,3047],"style",{},"html pre.shiki code .sbBg2, html code.shiki .sbBg2{--shiki-default:#1E754F}html pre.shiki code .sUxyF, html code.shiki .sUxyF{--shiki-default:#2E8F82}html pre.shiki code .suHK_, html code.shiki .suHK_{--shiki-default:#393A34}html pre.shiki code .sySUi, html code.shiki .sySUi{--shiki-default:#59873A}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .si04Y, html code.shiki .si04Y{--shiki-default:#AB5959}html pre.shiki code .sYZai, html code.shiki .sYZai{--shiki-default:#999999}html pre.shiki code .spphp, html code.shiki .spphp{--shiki-default:#B56959}html pre.shiki code .s8zF2, html code.shiki .s8zF2{--shiki-default:#A0ADA0}html pre.shiki code .s-TwI, html code.shiki .s-TwI{--shiki-default:#2F798A}html pre.shiki code .sSP4y, html code.shiki .sSP4y{--shiki-default:#B5695977}html pre.shiki code .svycV, html code.shiki .svycV{--shiki-default:#B07D48}html pre.shiki code .sEi1f, html code.shiki .sEi1f{--shiki-default:#A65E2B}",{"title":467,"searchDepth":489,"depth":489,"links":3049},[3050,3051,3052,3057,3058,3059],{"id":444,"depth":489,"text":445},{"id":583,"depth":489,"text":584},{"id":725,"depth":489,"text":726,"children":3053},[3054,3055,3056],{"id":2083,"depth":501,"text":2084},{"id":2117,"depth":501,"text":2118},{"id":2164,"depth":501,"text":2165},{"id":2245,"depth":489,"text":2246},{"id":2738,"depth":489,"text":2739},{"id":3008,"depth":489,"text":3009},"Extend the StoatFlow runtime with custom metrics, HTTP endpoints, and health indicators via the RuntimePlugin interface and lifecycle listeners.","md",{},{"icon":208},"how-to",{"title":205,"description":3060},"XU_wmjGMYUscXkGw0u1nfX1979F25nQvYR6gigeVX2A",[3068,3070],{"title":200,"path":201,"stem":202,"description":3069,"icon":203,"children":-1},"Pause and resume record processing at runtime with POST \u002Fpause and \u002Funpause — for maintenance windows and downstream backpressure, without restarting the instance.",{"title":210,"path":211,"stem":212,"description":3071,"icon":213,"children":-1},"Build a container image for your StoatFlow app with the StoatFlow build conventions (Jib) — base image, JVM flags, reproducible builds, container-aware heap sizing, ports and env — on Gradle or Maven.",1783713361810]