[{"data":1,"prerenderedAt":11348},["ShallowReactive",2],{"navigation_docs":3,"-docs-reference-go-framework":968,"-docs-reference-go-framework-surround":11344},[4,27,63,129,287,311,893,902,950,964],{"title":5,"path":6,"stem":7,"children":8,"order":10,"icon":11},"Overview","\u002Fdocs\u002Foverview","docs\u002F1.overview\u002Findex",[9,12,18,24],{"title":5,"path":6,"stem":7,"order":10,"icon":11},1,"i-lucide-info",{"title":13,"path":14,"stem":15,"order":16,"icon":17},"Getting Started","\u002Fdocs\u002Foverview\u002Fgetting-started","docs\u002F1.overview\u002F2.getting-started",2,"i-lucide-rocket",{"title":19,"path":20,"stem":21,"order":22,"icon":23},"Contributing","\u002Fdocs\u002Foverview\u002Fcontributing","docs\u002F1.overview\u002F3.contributing",3,"i-lucide-heart",{"title":5,"path":25,"stem":26,"order":10},"\u002Fdocs\u002Foverview\u002F_dir","docs\u002F1.overview\u002F_dir",{"title":28,"path":29,"stem":30,"children":31,"page":62},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[32,37,42,47,53,59],{"title":33,"path":34,"stem":35,"order":10,"icon":36},"Events","\u002Fdocs\u002Fconcepts\u002Fevents","docs\u002F2.concepts\u002F1.events","i-lucide-zap",{"title":38,"path":39,"stem":40,"order":16,"icon":41},"Transports","\u002Fdocs\u002Fconcepts\u002Ftransports","docs\u002F2.concepts\u002F2.transports","i-lucide-network",{"title":43,"path":44,"stem":45,"order":22,"icon":46},"Module System","\u002Fdocs\u002Fconcepts\u002Fmodules","docs\u002F2.concepts\u002F3.modules","i-lucide-puzzle",{"title":48,"path":49,"stem":50,"order":51,"icon":52},"Flows","\u002Fdocs\u002Fconcepts\u002Fflows","docs\u002F2.concepts\u002F4.flows",4,"i-lucide-workflow",{"title":54,"path":55,"stem":56,"order":57,"icon":58},"Scheduler","\u002Fdocs\u002Fconcepts\u002Fscheduler","docs\u002F2.concepts\u002F5.scheduler",5,"i-lucide-clock",{"title":28,"path":60,"stem":61,"order":16},"\u002Fdocs\u002Fconcepts\u002F_dir","docs\u002F2.concepts\u002F_dir",false,{"title":64,"path":65,"stem":66,"children":67,"page":62},"Guides","\u002Fdocs\u002Fguides","docs\u002F3.guides",[68,73,77,81,85,89,93,98,104,110,116,122,126],{"title":69,"path":70,"stem":71,"order":10,"icon":72},"Creating a Go App","\u002Fdocs\u002Fguides\u002Fcreating-go-app","docs\u002F3.guides\u002F1.creating-go-app","i-lucide-terminal",{"title":74,"path":75,"stem":76},"E2E tests & demo videos","\u002Fdocs\u002Fguides\u002Fe2e-and-demo-videos","docs\u002F3.guides\u002F10.e2e-and-demo-videos",{"title":78,"path":79,"stem":80,"icon":17},"Deploying to production","\u002Fdocs\u002Fguides\u002Fdeploying-to-production","docs\u002F3.guides\u002F11.deploying-to-production",{"title":82,"path":83,"stem":84,"order":16,"icon":46},"Creating a Go Module","\u002Fdocs\u002Fguides\u002Fcreating-go-module","docs\u002F3.guides\u002F2.creating-go-module",{"title":86,"path":87,"stem":88,"order":22,"icon":36},"Adding Events","\u002Fdocs\u002Fguides\u002Fadding-events-to-go-module","docs\u002F3.guides\u002F3.adding-events-to-go-module",{"title":90,"path":91,"stem":92,"order":51,"icon":52},"FlowDSL to Service","\u002Fdocs\u002Fguides\u002Fflowdsl-to-redelay","docs\u002F3.guides\u002F4.flowdsl-to-redelay",{"title":94,"path":95,"stem":96,"order":57,"icon":97},"Choosing a Transport","\u002Fdocs\u002Fguides\u002Fchoosing-a-transport","docs\u002F3.guides\u002F5.choosing-a-transport","i-lucide-git-branch",{"title":99,"path":100,"stem":101,"order":102,"icon":103},"Module Browser (\u002Fmodules)","\u002Fdocs\u002Fguides\u002Fmodules-browser","docs\u002F3.guides\u002F6.modules-browser",6,"i-lucide-layout-dashboard",{"title":105,"path":106,"stem":107,"order":108,"icon":109},"Running Flows","\u002Fdocs\u002Fguides\u002Frunning-flows","docs\u002F3.guides\u002F7.running-flows",7,"i-lucide-play-circle",{"title":111,"path":112,"stem":113,"order":114,"icon":115},"Trace + Live Observability","\u002Fdocs\u002Fguides\u002Fobservability","docs\u002F3.guides\u002F8.observability",8,"i-lucide-activity",{"title":117,"path":118,"stem":119,"order":120,"icon":121},"Project AI Assistant","\u002Fdocs\u002Fguides\u002Fai-assistant","docs\u002F3.guides\u002F9.ai-assistant",9,"i-lucide-sparkles",{"title":123,"path":124,"stem":125},"Debug taps — trace live data through any node","\u002Fdocs\u002Fguides\u002Fdebug-taps","docs\u002F3.guides\u002F9.debug-taps",{"title":64,"path":127,"stem":128,"order":22},"\u002Fdocs\u002Fguides\u002F_dir","docs\u002F3.guides\u002F_dir",{"title":130,"path":131,"stem":132,"children":133,"page":62},"Reference","\u002Fdocs\u002Freference","docs\u002F4.reference",[134,139,143,148,153,158,163,168,172,177,182,187,193,199,205,210,215,219,225,230,236,242,247,253,259,263,267,273,278,284],{"title":135,"path":136,"stem":137,"order":10,"icon":138},"Go Framework","\u002Fdocs\u002Freference\u002Fgo-framework","docs\u002F4.reference\u002F01.go-framework","i-simple-icons-go",{"title":140,"path":141,"stem":142,"order":16,"icon":46},"Built-in Modules","\u002Fdocs\u002Freference\u002Fgo-modules","docs\u002F4.reference\u002F02.go-modules",{"title":144,"path":145,"stem":146,"order":22,"icon":147},"Domain Addon Modules","\u002Fdocs\u002Freference\u002Fgo-modules-addons","docs\u002F4.reference\u002F03.go-modules-addons","i-lucide-blocks",{"title":149,"path":150,"stem":151,"order":51,"icon":152},"Go Events","\u002Fdocs\u002Freference\u002Fgo-events","docs\u002F4.reference\u002F04.go-events","i-lucide-radio",{"title":154,"path":155,"stem":156,"order":57,"icon":157},"Email Module","\u002Fdocs\u002Freference\u002Fgo-module-email","docs\u002F4.reference\u002F05.go-module-email","i-lucide-mail",{"title":159,"path":160,"stem":161,"order":102,"icon":162},"Go Core & IR","\u002Fdocs\u002Freference\u002Fgo-core","docs\u002F4.reference\u002F06.go-core","i-lucide-cpu",{"title":164,"path":165,"stem":166,"order":108,"icon":167},"Module Definition Files","\u002Fdocs\u002Freference\u002Fgo-module-files","docs\u002F4.reference\u002F07.go-module-files","i-lucide-file-code",{"title":169,"path":170,"stem":171,"order":114,"icon":72},"CLI Reference","\u002Fdocs\u002Freference\u002Fgo-cli","docs\u002F4.reference\u002F08.go-cli",{"title":173,"path":174,"stem":175,"order":120,"icon":176},"Go Formats","\u002Fdocs\u002Freference\u002Fgo-formats","docs\u002F4.reference\u002F09.go-formats","i-lucide-file-json",{"title":178,"path":179,"stem":180,"order":181,"icon":152},"AsyncAPI Module","\u002Fdocs\u002Freference\u002Fgo-asyncapi","docs\u002F4.reference\u002F10.go-asyncapi",10,{"title":183,"path":184,"stem":185,"order":186,"icon":52},"Go FlowDSL","\u002Fdocs\u002Freference\u002Fgo-flowdsl","docs\u002F4.reference\u002F11.go-flowdsl",11,{"title":188,"path":189,"stem":190,"order":191,"icon":192},"FlowDSL Examples","\u002Fdocs\u002Freference\u002Fflowdsl-examples","docs\u002F4.reference\u002F12.flowdsl-examples",12,"i-lucide-file-code-2",{"title":194,"path":195,"stem":196,"order":197,"icon":198},"FlowDSL Sync","\u002Fdocs\u002Freference\u002Fflowdsl-sync","docs\u002F4.reference\u002F13.flowdsl-sync",13,"i-lucide-refresh-cw",{"title":200,"path":201,"stem":202,"order":203,"icon":204},"Node.js Framework","\u002Fdocs\u002Freference\u002Fjs-framework","docs\u002F4.reference\u002F14.js-framework",14,"i-simple-icons-nodedotjs",{"title":206,"path":207,"stem":208,"order":203,"icon":209},"Python Framework","\u002Fdocs\u002Freference\u002Fpy-framework","docs\u002F4.reference\u002F14.py-framework","i-simple-icons-python",{"title":211,"path":212,"stem":213,"order":214,"icon":204},"Node.js Events","\u002Fdocs\u002Freference\u002Fjs-events","docs\u002F4.reference\u002F15.js-events",15,{"title":216,"path":217,"stem":218,"order":214,"icon":209},"Python Events","\u002Fdocs\u002Freference\u002Fpy-events","docs\u002F4.reference\u002F15.py-events",{"title":220,"path":221,"stem":222,"order":223,"icon":224},"go-ai — LLM Providers, Guard, Ledger","\u002Fdocs\u002Freference\u002Fgo-ai","docs\u002F4.reference\u002F16.go-ai",16,"i-lucide-brain-circuit",{"title":226,"path":227,"stem":228,"order":229,"icon":41},"Infrastructure Modules","\u002Fdocs\u002Freference\u002Fgeneric-infra-modules","docs\u002F4.reference\u002F17.generic-infra-modules",17,{"title":231,"path":232,"stem":233,"order":234,"icon":235},"Flow Templates","\u002Fdocs\u002Freference\u002Fflow-templates","docs\u002F4.reference\u002F18.flow-templates",18,"i-lucide-library-big",{"title":237,"path":238,"stem":239,"order":240,"icon":241},"Assistant (chat + handoff)","\u002Fdocs\u002Freference\u002Fassistant","docs\u002F4.reference\u002F19.assistant",19,"i-lucide-messages-square",{"title":243,"path":244,"stem":245,"order":246,"icon":41},"Coordination","\u002Fdocs\u002Freference\u002Fcoordination","docs\u002F4.reference\u002F20.coordination",20,{"title":248,"path":249,"stem":250,"order":251,"icon":252},"Search (vector)","\u002Fdocs\u002Freference\u002Fsearch","docs\u002F4.reference\u002F21.search",21,"i-lucide-search",{"title":254,"path":255,"stem":256,"order":257,"icon":258},"Profiles","\u002Fdocs\u002Freference\u002Fprofiles","docs\u002F4.reference\u002F22.profiles",22,"i-lucide-bookmark",{"title":260,"path":261,"stem":262},"Flow-driven HTTP endpoints","\u002Fdocs\u002Freference\u002Fflow-driven-endpoints","docs\u002F4.reference\u002F23.flow-driven-endpoints",{"title":264,"path":265,"stem":266},"Deployments","\u002Fdocs\u002Freference\u002Fdeployments","docs\u002F4.reference\u002F24.deployments",{"title":268,"path":269,"stem":270,"order":271,"icon":272},"i18n (translations)","\u002Fdocs\u002Freference\u002Fi18n","docs\u002F4.reference\u002F25.i18n",25,"i-lucide-languages",{"title":274,"path":275,"stem":276,"order":277,"icon":152},"WebSockets","\u002Fdocs\u002Freference\u002Fwebsockets","docs\u002F4.reference\u002F26.websockets",26,{"title":279,"path":280,"stem":281,"order":282,"icon":283},"Localized content","\u002Fdocs\u002Freference\u002Flocalized-content","docs\u002F4.reference\u002F27.localized-content",27,"i-lucide-file-text",{"title":130,"path":285,"stem":286,"order":51},"\u002Fdocs\u002Freference\u002F_dir","docs\u002F4.reference\u002F_dir",{"title":288,"path":289,"stem":290,"children":291,"page":62},"Integrations","\u002Fdocs\u002Fintegrations","docs\u002F5.integrations",[292,296,300,304,308],{"title":293,"path":294,"stem":295,"order":10,"icon":52},"FlowDSL","\u002Fdocs\u002Fintegrations\u002Fflowdsl","docs\u002F5.integrations\u002F1.flowdsl",{"title":297,"path":298,"stem":299,"order":16,"icon":283},"OpenAPI","\u002Fdocs\u002Fintegrations\u002Fopenapi","docs\u002F5.integrations\u002F2.openapi",{"title":301,"path":302,"stem":303,"order":22,"icon":152},"AsyncAPI","\u002Fdocs\u002Fintegrations\u002Fasyncapi","docs\u002F5.integrations\u002F3.asyncapi",{"title":305,"path":306,"stem":307},"MCP Integration","\u002Fdocs\u002Fintegrations\u002Fmcp","docs\u002F5.integrations\u002F4.mcp",{"title":288,"path":309,"stem":310,"order":57},"\u002Fdocs\u002Fintegrations\u002F_dir","docs\u002F5.integrations\u002F_dir",{"title":312,"path":313,"stem":314,"children":315},"FlowDSL Node Catalog","\u002Fdocs\u002Fnodes","docs\u002F5.nodes\u002Findex",[316,317,321,325,329,333,337,341,345,349,353,357,361,365,369,373,377,381,385,389,393,397,401,405,409,413,417,421,425,429,433,437,441,445,449,453,457,461,465,469,473,477,481,485,489,493,497,501,505,509,513,517,521,525,529,533,537,541,545,549,553,557,561,565,569,573,577,581,585,589,593,597,601,605,609,613,617,621,625,629,633,637,641,645,649,653,657,661,665,669,673,677,681,685,689,693,697,701,705,709,713,717,721,725,729,733,737,741,745,749,753,757,761,765,769,773,777,781,785,789,793,797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865,869,873,877,881,885,889],{"title":312,"path":313,"stem":314},{"title":318,"path":319,"stem":320,"order":57},"Node Catalog","\u002Fdocs\u002Fnodes\u002F_dir","docs\u002F5.nodes\u002F_dir",{"title":322,"path":323,"stem":324},"Record Alert","\u002Fdocs\u002Fnodes\u002Falerts_record","docs\u002F5.nodes\u002Falerts_record",{"title":326,"path":327,"stem":328},"Log to API Log","\u002Fdocs\u002Fnodes\u002Fapilog_record","docs\u002F5.nodes\u002Fapilog_record",{"title":330,"path":331,"stem":332},"ClickHouse Exec","\u002Fdocs\u002Fnodes\u002Fclickhouse_exec","docs\u002F5.nodes\u002Fclickhouse_exec",{"title":334,"path":335,"stem":336},"ClickHouse Insert","\u002Fdocs\u002Fnodes\u002Fclickhouse_insert","docs\u002F5.nodes\u002Fclickhouse_insert",{"title":338,"path":339,"stem":340},"ClickHouse Query","\u002Fdocs\u002Fnodes\u002Fclickhouse_query","docs\u002F5.nodes\u002Fclickhouse_query",{"title":342,"path":343,"stem":344},"Array Op","\u002Fdocs\u002Fnodes\u002Fcore_array-ops","docs\u002F5.nodes\u002Fcore_array-ops",{"title":346,"path":347,"stem":348},"Assert","\u002Fdocs\u002Fnodes\u002Fcore_assert","docs\u002F5.nodes\u002Fcore_assert",{"title":350,"path":351,"stem":352},"Audit Emit","\u002Fdocs\u002Fnodes\u002Fcore_audit-emit","docs\u002F5.nodes\u002Fcore_audit-emit",{"title":354,"path":355,"stem":356},"Catch","\u002Fdocs\u002Fnodes\u002Fcore_catch","docs\u002F5.nodes\u002Fcore_catch",{"title":358,"path":359,"stem":360},"DateTime","\u002Fdocs\u002Fnodes\u002Fcore_datetime","docs\u002F5.nodes\u002Fcore_datetime",{"title":362,"path":363,"stem":364},"Dead Letter","\u002Fdocs\u002Fnodes\u002Fcore_dead-letter","docs\u002F5.nodes\u002Fcore_dead-letter",{"title":366,"path":367,"stem":368},"Delay","\u002Fdocs\u002Fnodes\u002Fcore_delay","docs\u002F5.nodes\u002Fcore_delay",{"title":370,"path":371,"stem":372},"ForEach","\u002Fdocs\u002Fnodes\u002Fcore_foreach","docs\u002F5.nodes\u002Fcore_foreach",{"title":374,"path":375,"stem":376},"Idempotency Key","\u002Fdocs\u002Fnodes\u002Fcore_idempotency-key","docs\u002F5.nodes\u002Fcore_idempotency-key",{"title":378,"path":379,"stem":380},"If","\u002Fdocs\u002Fnodes\u002Fcore_if","docs\u002F5.nodes\u002Fcore_if",{"title":382,"path":383,"stem":384},"Inspect","\u002Fdocs\u002Fnodes\u002Fcore_inspect","docs\u002F5.nodes\u002Fcore_inspect",{"title":386,"path":387,"stem":388},"JSON Merge","\u002Fdocs\u002Fnodes\u002Fcore_json-merge","docs\u002F5.nodes\u002Fcore_json-merge",{"title":390,"path":391,"stem":392},"JSON Schema Validate","\u002Fdocs\u002Fnodes\u002Fcore_json-schema-validate","docs\u002F5.nodes\u002Fcore_json-schema-validate",{"title":394,"path":395,"stem":396},"JSONPath Extract","\u002Fdocs\u002Fnodes\u002Fcore_jsonpath-extract","docs\u002F5.nodes\u002Fcore_jsonpath-extract",{"title":398,"path":399,"stem":400},"Log","\u002Fdocs\u002Fnodes\u002Fcore_log","docs\u002F5.nodes\u002Fcore_log",{"title":402,"path":403,"stem":404},"Math","\u002Fdocs\u002Fnodes\u002Fcore_math","docs\u002F5.nodes\u002Fcore_math",{"title":406,"path":407,"stem":408},"Merge","\u002Fdocs\u002Fnodes\u002Fcore_merge","docs\u002F5.nodes\u002Fcore_merge",{"title":410,"path":411,"stem":412},"Metric Counter","\u002Fdocs\u002Fnodes\u002Fcore_metric-counter","docs\u002F5.nodes\u002Fcore_metric-counter",{"title":414,"path":415,"stem":416},"Metric Gauge","\u002Fdocs\u002Fnodes\u002Fcore_metric-gauge","docs\u002F5.nodes\u002Fcore_metric-gauge",{"title":418,"path":419,"stem":420},"Mock","\u002Fdocs\u002Fnodes\u002Fcore_mock","docs\u002F5.nodes\u002Fcore_mock",{"title":422,"path":423,"stem":424},"No-op","\u002Fdocs\u002Fnodes\u002Fcore_noop","docs\u002F5.nodes\u002Fcore_noop",{"title":426,"path":427,"stem":428},"Object Omit","\u002Fdocs\u002Fnodes\u002Fcore_object-omit","docs\u002F5.nodes\u002Fcore_object-omit",{"title":430,"path":431,"stem":432},"Object Pick","\u002Fdocs\u002Fnodes\u002Fcore_object-pick","docs\u002F5.nodes\u002Fcore_object-pick",{"title":434,"path":435,"stem":436},"Parallel Fan-Out","\u002Fdocs\u002Fnodes\u002Fcore_parallel","docs\u002F5.nodes\u002Fcore_parallel",{"title":438,"path":439,"stem":440},"Raise","\u002Fdocs\u002Fnodes\u002Fcore_raise","docs\u002F5.nodes\u002Fcore_raise",{"title":442,"path":443,"stem":444},"Rate Limit Check","\u002Fdocs\u002Fnodes\u002Fcore_rate-limit-check","docs\u002F5.nodes\u002Fcore_rate-limit-check",{"title":446,"path":447,"stem":448},"Regex Extract","\u002Fdocs\u002Fnodes\u002Fcore_regex-extract","docs\u002F5.nodes\u002Fcore_regex-extract",{"title":450,"path":451,"stem":452},"Require Fields","\u002Fdocs\u002Fnodes\u002Fcore_require-fields","docs\u002F5.nodes\u002Fcore_require-fields",{"title":454,"path":455,"stem":456},"Rethrow","\u002Fdocs\u002Fnodes\u002Fcore_rethrow","docs\u002F5.nodes\u002Fcore_rethrow",{"title":458,"path":459,"stem":460},"Return","\u002Fdocs\u002Fnodes\u002Fcore_return","docs\u002F5.nodes\u002Fcore_return",{"title":462,"path":463,"stem":464},"HMAC Signature Verify","\u002Fdocs\u002Fnodes\u002Fcore_signature-verify","docs\u002F5.nodes\u002Fcore_signature-verify",{"title":466,"path":467,"stem":468},"String Op","\u002Fdocs\u002Fnodes\u002Fcore_string-ops","docs\u002F5.nodes\u002Fcore_string-ops",{"title":470,"path":471,"stem":472},"Subflow Invoke","\u002Fdocs\u002Fnodes\u002Fcore_subflow-invoke","docs\u002F5.nodes\u002Fcore_subflow-invoke",{"title":474,"path":475,"stem":476},"Switch","\u002Fdocs\u002Fnodes\u002Fcore_switch","docs\u002F5.nodes\u002Fcore_switch",{"title":478,"path":479,"stem":480},"Template Render","\u002Fdocs\u002Fnodes\u002Fcore_template-render","docs\u002F5.nodes\u002Fcore_template-render",{"title":482,"path":483,"stem":484},"Throttle","\u002Fdocs\u002Fnodes\u002Fcore_throttle","docs\u002F5.nodes\u002Fcore_throttle",{"title":486,"path":487,"stem":488},"Timeout","\u002Fdocs\u002Fnodes\u002Fcore_timeout","docs\u002F5.nodes\u002Fcore_timeout",{"title":490,"path":491,"stem":492},"Type Cast","\u002Fdocs\u002Fnodes\u002Fcore_type-cast","docs\u002F5.nodes\u002Fcore_type-cast",{"title":494,"path":495,"stem":496},"Webhook Respond","\u002Fdocs\u002Fnodes\u002Fcore_webhook-respond","docs\u002F5.nodes\u002Fcore_webhook-respond",{"title":498,"path":499,"stem":500},"File Delete","\u002Fdocs\u002Fnodes\u002Ffilesys_delete","docs\u002F5.nodes\u002Ffilesys_delete",{"title":502,"path":503,"stem":504},"Directory List","\u002Fdocs\u002Fnodes\u002Ffilesys_list","docs\u002F5.nodes\u002Ffilesys_list",{"title":506,"path":507,"stem":508},"File Read","\u002Fdocs\u002Fnodes\u002Ffilesys_read","docs\u002F5.nodes\u002Ffilesys_read",{"title":510,"path":511,"stem":512},"File Stat","\u002Fdocs\u002Fnodes\u002Ffilesys_stat","docs\u002F5.nodes\u002Ffilesys_stat",{"title":514,"path":515,"stem":516},"File Write","\u002Fdocs\u002Fnodes\u002Ffilesys_write","docs\u002F5.nodes\u002Ffilesys_write",{"title":518,"path":519,"stem":520},"FTP Delete","\u002Fdocs\u002Fnodes\u002Fftp_delete","docs\u002F5.nodes\u002Fftp_delete",{"title":522,"path":523,"stem":524},"FTP Download","\u002Fdocs\u002Fnodes\u002Fftp_download","docs\u002F5.nodes\u002Fftp_download",{"title":526,"path":527,"stem":528},"FTP List","\u002Fdocs\u002Fnodes\u002Fftp_list","docs\u002F5.nodes\u002Fftp_list",{"title":530,"path":531,"stem":532},"FTP Upload","\u002Fdocs\u002Fnodes\u002Fftp_upload","docs\u002F5.nodes\u002Fftp_upload",{"title":534,"path":535,"stem":536},"HTTP DELETE","\u002Fdocs\u002Fnodes\u002Fhttp_delete","docs\u002F5.nodes\u002Fhttp_delete",{"title":538,"path":539,"stem":540},"HTTP GET","\u002Fdocs\u002Fnodes\u002Fhttp_get","docs\u002F5.nodes\u002Fhttp_get",{"title":542,"path":543,"stem":544},"HTTP POST","\u002Fdocs\u002Fnodes\u002Fhttp_post","docs\u002F5.nodes\u002Fhttp_post",{"title":546,"path":547,"stem":548},"HTTP PUT","\u002Fdocs\u002Fnodes\u002Fhttp_put","docs\u002F5.nodes\u002Fhttp_put",{"title":550,"path":551,"stem":552},"HTTP Request","\u002Fdocs\u002Fnodes\u002Fhttp_request","docs\u002F5.nodes\u002Fhttp_request",{"title":554,"path":555,"stem":556},"Fill Translations","\u002Fdocs\u002Fnodes\u002Fi18n_fill-translations","docs\u002F5.nodes\u002Fi18n_fill-translations",{"title":558,"path":559,"stem":560},"Resolve Locale","\u002Fdocs\u002Fnodes\u002Fi18n_resolve-locale","docs\u002F5.nodes\u002Fi18n_resolve-locale",{"title":562,"path":563,"stem":564},"Translate","\u002Fdocs\u002Fnodes\u002Fi18n_translate","docs\u002F5.nodes\u002Fi18n_translate",{"title":566,"path":567,"stem":568},"Mongo AGGREGATE","\u002Fdocs\u002Fnodes\u002Fmongoops_aggregate","docs\u002F5.nodes\u002Fmongoops_aggregate",{"title":570,"path":571,"stem":572},"Mongo DELETE","\u002Fdocs\u002Fnodes\u002Fmongoops_delete","docs\u002F5.nodes\u002Fmongoops_delete",{"title":574,"path":575,"stem":576},"Mongo FIND","\u002Fdocs\u002Fnodes\u002Fmongoops_find","docs\u002F5.nodes\u002Fmongoops_find",{"title":578,"path":579,"stem":580},"Mongo FIND ONE","\u002Fdocs\u002Fnodes\u002Fmongoops_find-one","docs\u002F5.nodes\u002Fmongoops_find-one",{"title":582,"path":583,"stem":584},"Mongo INSERT","\u002Fdocs\u002Fnodes\u002Fmongoops_insert","docs\u002F5.nodes\u002Fmongoops_insert",{"title":586,"path":587,"stem":588},"Mongo UPDATE","\u002Fdocs\u002Fnodes\u002Fmongoops_update","docs\u002F5.nodes\u002Fmongoops_update",{"title":590,"path":591,"stem":592},"Postgres COPY FROM","\u002Fdocs\u002Fnodes\u002Fpostgres_copy-from","docs\u002F5.nodes\u002Fpostgres_copy-from",{"title":594,"path":595,"stem":596},"Postgres Exec","\u002Fdocs\u002Fnodes\u002Fpostgres_exec","docs\u002F5.nodes\u002Fpostgres_exec",{"title":598,"path":599,"stem":600},"Postgres Query","\u002Fdocs\u002Fnodes\u002Fpostgres_query","docs\u002F5.nodes\u002Fpostgres_query",{"title":602,"path":603,"stem":604},"Postgres Transaction","\u002Fdocs\u002Fnodes\u002Fpostgres_transaction","docs\u002F5.nodes\u002Fpostgres_transaction",{"title":606,"path":607,"stem":608},"RabbitMQ Consume","\u002Fdocs\u002Fnodes\u002Frabbitmq_consume","docs\u002F5.nodes\u002Frabbitmq_consume",{"title":610,"path":611,"stem":612},"RabbitMQ Declare","\u002Fdocs\u002Fnodes\u002Frabbitmq_declare","docs\u002F5.nodes\u002Frabbitmq_declare",{"title":614,"path":615,"stem":616},"RabbitMQ Publish","\u002Fdocs\u002Fnodes\u002Frabbitmq_publish","docs\u002F5.nodes\u002Frabbitmq_publish",{"title":618,"path":619,"stem":620},"Auth Login","\u002Fdocs\u002Fnodes\u002Fredelay_auth-login","docs\u002F5.nodes\u002Fredelay_auth-login",{"title":622,"path":623,"stem":624},"Require Permission","\u002Fdocs\u002Fnodes\u002Fredelay_auth-require-permission","docs\u002F5.nodes\u002Fredelay_auth-require-permission",{"title":626,"path":627,"stem":628},"Validate Token","\u002Fdocs\u002Fnodes\u002Fredelay_auth-validate-token","docs\u002F5.nodes\u002Fredelay_auth-validate-token",{"title":630,"path":631,"stem":632},"Render Email Template","\u002Fdocs\u002Fnodes\u002Fredelay_email-render-template","docs\u002F5.nodes\u002Fredelay_email-render-template",{"title":634,"path":635,"stem":636},"Send Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send","docs\u002F5.nodes\u002Fredelay_email-send",{"title":638,"path":639,"stem":640},"Send Templated Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send-templated","docs\u002F5.nodes\u002Fredelay_email-send-templated",{"title":642,"path":643,"stem":644},"Event Source","\u002Fdocs\u002Fnodes\u002Fredelay_event-source","docs\u002F5.nodes\u002Fredelay_event-source",{"title":646,"path":647,"stem":648},"Create Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-create-group","docs\u002F5.nodes\u002Fredelay_groups-create-group",{"title":650,"path":651,"stem":652},"Delete Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-delete-group","docs\u002F5.nodes\u002Fredelay_groups-delete-group",{"title":654,"path":655,"stem":656},"Resolve Permissions","\u002Fdocs\u002Fnodes\u002Fredelay_groups-resolve-permissions","docs\u002F5.nodes\u002Fredelay_groups-resolve-permissions",{"title":658,"path":659,"stem":660},"Update Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-update-group","docs\u002F5.nodes\u002Fredelay_groups-update-group",{"title":662,"path":663,"stem":664},"HTTP Endpoint","\u002Fdocs\u002Fnodes\u002Fredelay_http-endpoint","docs\u002F5.nodes\u002Fredelay_http-endpoint",{"title":666,"path":667,"stem":668},"JSON Stream Source","\u002Fdocs\u002Fnodes\u002Fredelay_json-stream-source","docs\u002F5.nodes\u002Fredelay_json-stream-source",{"title":670,"path":671,"stem":672},"JSON → Event","\u002Fdocs\u002Fnodes\u002Fredelay_json-to-event","docs\u002F5.nodes\u002Fredelay_json-to-event",{"title":674,"path":675,"stem":676},"Kafka Produce","\u002Fdocs\u002Fnodes\u002Fredelay_kafka-produce","docs\u002F5.nodes\u002Fredelay_kafka-produce",{"title":678,"path":679,"stem":680},"Create Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-create","docs\u002F5.nodes\u002Fredelay_links-create",{"title":682,"path":683,"stem":684},"Delete Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-delete","docs\u002F5.nodes\u002Fredelay_links-delete",{"title":686,"path":687,"stem":688},"Resolve Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-resolve","docs\u002F5.nodes\u002Fredelay_links-resolve",{"title":690,"path":691,"stem":692},"LLM Chat","\u002Fdocs\u002Fnodes\u002Fredelay_llm-chat","docs\u002F5.nodes\u002Fredelay_llm-chat",{"title":694,"path":695,"stem":696},"LLM Embed","\u002Fdocs\u002Fnodes\u002Fredelay_llm-embed","docs\u002F5.nodes\u002Fredelay_llm-embed",{"title":698,"path":699,"stem":700},"LLM Guard","\u002Fdocs\u002Fnodes\u002Fredelay_llm-guard","docs\u002F5.nodes\u002Fredelay_llm-guard",{"title":702,"path":703,"stem":704},"LLM Tool Router","\u002Fdocs\u002Fnodes\u002Fredelay_llm-tool-router","docs\u002F5.nodes\u002Fredelay_llm-tool-router",{"title":706,"path":707,"stem":708},"NATS Publish","\u002Fdocs\u002Fnodes\u002Fredelay_nats-publish","docs\u002F5.nodes\u002Fredelay_nats-publish",{"title":710,"path":711,"stem":712},"Mark Notification Read","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-mark-read","docs\u002F5.nodes\u002Fredelay_notifications-mark-read",{"title":714,"path":715,"stem":716},"Send Notification","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-send","docs\u002F5.nodes\u002Fredelay_notifications-send",{"title":718,"path":719,"stem":720},"Redis Stream XADD","\u002Fdocs\u002Fnodes\u002Fredelay_redis-stream-publish","docs\u002F5.nodes\u002Fredelay_redis-stream-publish",{"title":722,"path":723,"stem":724},"S3 Copy Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-copy-object","docs\u002F5.nodes\u002Fredelay_s3-copy-object",{"title":726,"path":727,"stem":728},"S3 Delete Many","\u002Fdocs\u002Fnodes\u002Fredelay_s3-delete-many","docs\u002F5.nodes\u002Fredelay_s3-delete-many",{"title":730,"path":731,"stem":732},"S3 Head Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-head-object","docs\u002F5.nodes\u002Fredelay_s3-head-object",{"title":734,"path":735,"stem":736},"S3 List Objects","\u002Fdocs\u002Fnodes\u002Fredelay_s3-list-objects","docs\u002F5.nodes\u002Fredelay_s3-list-objects",{"title":738,"path":739,"stem":740},"Create Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-create-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-create-schedule",{"title":742,"path":743,"stem":744},"Cron Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-cron-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-cron-trigger",{"title":746,"path":747,"stem":748},"Delete Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-delete-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-delete-schedule",{"title":750,"path":751,"stem":752},"Interval Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-interval-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-interval-trigger",{"title":754,"path":755,"stem":756},"Search Query","\u002Fdocs\u002Fnodes\u002Fredelay_search-query","docs\u002F5.nodes\u002Fredelay_search-query",{"title":758,"path":759,"stem":760},"Search Upsert","\u002Fdocs\u002Fnodes\u002Fredelay_search-upsert","docs\u002F5.nodes\u002Fredelay_search-upsert",{"title":762,"path":763,"stem":764},"Delete Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-delete","docs\u002F5.nodes\u002Fredelay_settings-delete",{"title":766,"path":767,"stem":768},"Get Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-get","docs\u002F5.nodes\u002Fredelay_settings-get",{"title":770,"path":771,"stem":772},"Set Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-set","docs\u002F5.nodes\u002Fredelay_settings-set",{"title":774,"path":775,"stem":776},"Delete File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-delete","docs\u002F5.nodes\u002Fredelay_storage-delete",{"title":778,"path":779,"stem":780},"Download File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-download","docs\u002F5.nodes\u002Fredelay_storage-download",{"title":782,"path":783,"stem":784},"Generate Presigned URL","\u002Fdocs\u002Fnodes\u002Fredelay_storage-presign-url","docs\u002F5.nodes\u002Fredelay_storage-presign-url",{"title":786,"path":787,"stem":788},"Upload File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-upload","docs\u002F5.nodes\u002Fredelay_storage-upload",{"title":790,"path":791,"stem":792},"Subworkflow","\u002Fdocs\u002Fnodes\u002Fredelay_subworkflow","docs\u002F5.nodes\u002Fredelay_subworkflow",{"title":794,"path":795,"stem":796},"Activate User","\u002Fdocs\u002Fnodes\u002Fredelay_users-activate","docs\u002F5.nodes\u002Fredelay_users-activate",{"title":798,"path":799,"stem":800},"Create Pending User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-pending","docs\u002F5.nodes\u002Fredelay_users-create-pending",{"title":802,"path":803,"stem":804},"Create User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-user","docs\u002F5.nodes\u002Fredelay_users-create-user",{"title":806,"path":807,"stem":808},"Delete User","\u002Fdocs\u002Fnodes\u002Fredelay_users-delete-user","docs\u002F5.nodes\u002Fredelay_users-delete-user",{"title":810,"path":811,"stem":812},"Find User By Email","\u002Fdocs\u002Fnodes\u002Fredelay_users-find-by-email","docs\u002F5.nodes\u002Fredelay_users-find-by-email",{"title":814,"path":815,"stem":816},"Get User","\u002Fdocs\u002Fnodes\u002Fredelay_users-get-user","docs\u002F5.nodes\u002Fredelay_users-get-user",{"title":818,"path":819,"stem":820},"List Users","\u002Fdocs\u002Fnodes\u002Fredelay_users-list-users","docs\u002F5.nodes\u002Fredelay_users-list-users",{"title":822,"path":823,"stem":824},"Update User","\u002Fdocs\u002Fnodes\u002Fredelay_users-update-user","docs\u002F5.nodes\u002Fredelay_users-update-user",{"title":826,"path":827,"stem":828},"Request Email Verification (legacy)","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request","docs\u002F5.nodes\u002Fredelay_verification-request",{"title":830,"path":831,"stem":832},"Request Email Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-email","docs\u002F5.nodes\u002Fredelay_verification-request-email",{"title":834,"path":835,"stem":836},"Request SMS Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-sms","docs\u002F5.nodes\u002Fredelay_verification-request-sms",{"title":838,"path":839,"stem":840},"Verify Token","\u002Fdocs\u002Fnodes\u002Fredelay_verification-verify","docs\u002F5.nodes\u002Fredelay_verification-verify",{"title":842,"path":843,"stem":844},"Redis DEL","\u002Fdocs\u002Fnodes\u002Fredisops_del","docs\u002F5.nodes\u002Fredisops_del",{"title":846,"path":847,"stem":848},"Redis EXPIRE","\u002Fdocs\u002Fnodes\u002Fredisops_expire","docs\u002F5.nodes\u002Fredisops_expire",{"title":850,"path":851,"stem":852},"Redis GET","\u002Fdocs\u002Fnodes\u002Fredisops_get","docs\u002F5.nodes\u002Fredisops_get",{"title":854,"path":855,"stem":856},"Redis HGET","\u002Fdocs\u002Fnodes\u002Fredisops_hget","docs\u002F5.nodes\u002Fredisops_hget",{"title":858,"path":859,"stem":860},"Redis HSET","\u002Fdocs\u002Fnodes\u002Fredisops_hset","docs\u002F5.nodes\u002Fredisops_hset",{"title":862,"path":863,"stem":864},"Redis INCR","\u002Fdocs\u002Fnodes\u002Fredisops_incr","docs\u002F5.nodes\u002Fredisops_incr",{"title":866,"path":867,"stem":868},"Redis LPOP","\u002Fdocs\u002Fnodes\u002Fredisops_lpop","docs\u002F5.nodes\u002Fredisops_lpop",{"title":870,"path":871,"stem":872},"Redis LPUSH","\u002Fdocs\u002Fnodes\u002Fredisops_lpush","docs\u002F5.nodes\u002Fredisops_lpush",{"title":874,"path":875,"stem":876},"Redis PUBLISH","\u002Fdocs\u002Fnodes\u002Fredisops_publish","docs\u002F5.nodes\u002Fredisops_publish",{"title":878,"path":879,"stem":880},"Redis SET","\u002Fdocs\u002Fnodes\u002Fredisops_set","docs\u002F5.nodes\u002Fredisops_set",{"title":882,"path":883,"stem":884},"SFTP Download","\u002Fdocs\u002Fnodes\u002Fsftp_download","docs\u002F5.nodes\u002Fsftp_download",{"title":886,"path":887,"stem":888},"SFTP Upload","\u002Fdocs\u002Fnodes\u002Fsftp_upload","docs\u002F5.nodes\u002Fsftp_upload",{"title":890,"path":891,"stem":892},"Send SMS","\u002Fdocs\u002Fnodes\u002Fsms_send","docs\u002F5.nodes\u002Fsms_send",{"title":894,"path":895,"stem":896,"children":897,"page":62},"Rfcs","\u002Fdocs\u002Frfcs","docs\u002F6.rfcs",[898],{"title":899,"path":900,"stem":901},"RFC 0001 — Worker Assignment for Flow Deployments","\u002Fdocs\u002Frfcs\u002F0001-worker-assignment","docs\u002F6.rfcs\u002F0001-worker-assignment",{"title":903,"path":904,"stem":905,"children":906,"page":62},"Admin","\u002Fdocs\u002Fadmin","docs\u002F7.admin",[907,912,917,922,927,932,937,941,946],{"title":5,"path":908,"stem":909,"order":910,"icon":911},"\u002Fdocs\u002Fadmin\u002Foverview","docs\u002F7.admin\u002F00.overview",0,"i-lucide-compass",{"title":913,"path":914,"stem":915,"order":10,"icon":916},"Getting started","\u002Fdocs\u002Fadmin\u002Fgetting-started","docs\u002F7.admin\u002F01.getting-started","i-lucide-play",{"title":918,"path":919,"stem":920,"order":16,"icon":921},"Layered architecture","\u002Fdocs\u002Fadmin\u002Flayered-architecture","docs\u002F7.admin\u002F02.layered-architecture","i-lucide-layers",{"title":923,"path":924,"stem":925,"order":22,"icon":926},"Building a custom admin project","\u002Fdocs\u002Fadmin\u002Fbuilding-a-custom-admin-project","docs\u002F7.admin\u002F03.building-a-custom-admin-project","i-lucide-folder-plus",{"title":928,"path":929,"stem":930,"order":51,"icon":931},"Building admin modules","\u002Fdocs\u002Fadmin\u002Fbuilding-admin-modules","docs\u002F7.admin\u002F04.building-admin-modules","i-lucide-package-plus",{"title":933,"path":934,"stem":935,"order":57,"icon":936},"Base module reference","\u002Fdocs\u002Fadmin\u002Freference-base-modules","docs\u002F7.admin\u002F05.reference-base-modules","i-lucide-boxes",{"title":938,"path":939,"stem":940,"order":102,"icon":17},"Deployment","\u002Fdocs\u002Fadmin\u002Fdeployment","docs\u002F7.admin\u002F06.deployment",{"title":942,"path":943,"stem":944,"order":108,"icon":945},"Consuming the base layer (versioned)","\u002Fdocs\u002Fadmin\u002Fconsuming-the-base-layer","docs\u002F7.admin\u002F07.consuming-the-base-layer","i-lucide-package",{"title":947,"path":948,"stem":949,"order":108},"Admin UI","\u002Fdocs\u002Fadmin\u002F_dir","docs\u002F7.admin\u002F_dir",{"title":951,"path":952,"stem":953,"children":954,"page":62},"Services","\u002Fdocs\u002Fservices","docs\u002F8.services",[955,960],{"title":956,"path":957,"stem":958,"order":10,"icon":959},"Custom development & support services","\u002Fdocs\u002Fservices\u002Fcustom-development","docs\u002F8.services\u002F1.custom-development","i-lucide-briefcase",{"title":951,"path":961,"stem":962,"icon":959,"order":963},"\u002Fdocs\u002Fservices\u002F_dir","docs\u002F8.services\u002F_dir",80,{"title":965,"path":966,"stem":967,"order":910},"Dir","\u002Fdocs\u002F_dir","docs\u002F_dir",{"id":969,"title":135,"body":970,"description":11337,"extension":11338,"links":11339,"meta":11340,"navigation":11341,"path":136,"seo":11342,"stem":137,"__hash__":11343},"docs\u002Fdocs\u002F4.reference\u002F01.go-framework.md",{"type":971,"value":972,"toc":11216},"minimark",[973,981,984,1005,1029,1034,1045,1082,1085,1090,1137,1141,1174,1178,1198,1202,1227,1243,1247,1251,1365,1369,1384,1388,1575,1579,1586,1590,1637,1641,1693,1697,1786,1790,1832,1836,1865,1869,1872,1910,1914,1918,1995,1999,2008,2042,2045,2109,2127,2131,2141,2170,2211,2219,2230,2334,2362,2365,2405,2409,2415,2443,2524,2530,2633,2637,2676,2680,2686,2715,2718,2727,2731,2752,2800,2819,2823,2826,2872,2897,2912,2937,2952,2956,2962,3078,3084,3132,3138,3147,3263,3272,3286,3297,3312,3316,3343,3479,3482,3533,3537,3540,3565,3570,3627,3644,3664,3672,3829,3846,3873,3878,3941,3946,4009,4014,4068,4073,4116,4121,4203,4208,4223,4236,4262,4275,4280,4327,4329,4345,4354,4422,4432,4555,4565,4569,4651,4655,4707,4712,4715,4723,4727,4794,4799,4802,4816,4823,4877,4909,4913,4920,4943,5016,5023,5027,5031,5130,5138,5151,5161,5171,5190,5207,5211,5217,5220,5254,5264,5277,5284,5297,5306,5330,5360,5366,5373,5384,5458,5468,5493,5509,5513,5524,5702,5723,5727,5738,5800,5803,5828,5832,5842,5846,5851,5860,5863,5893,5897,5958,5962,6017,6021,6027,6095,6100,6203,6207,6210,6287,6296,6300,6324,6327,6331,6334,6472,6476,6493,6496,6525,6529,6532,6616,6620,6626,6727,6730,6734,6740,6875,6879,6882,6944,6948,6957,6963,7005,7012,7016,7057,7060,7065,7089,7092,7201,7207,7211,7215,7290,7294,7392,7396,7517,7531,7535,7664,7672,7676,7751,7761,7765,7769,7784,7788,7797,7804,7822,7856,7863,7867,7874,7878,7885,7964,7968,7971,8060,8064,8080,8086,8090,8184,8188,8461,8464,8475,8480,8484,8518,8522,9020,9024,9118,9122,9139,9145,9149,9235,9239,9325,9329,9351,9355,9364,9369,9427,9461,9464,9473,9478,9497,9500,9660,9664,9673,9678,9707,9769,9773,9782,9787,9849,9853,9859,9862,9881,9888,9891,9900,9903,9918,9922,9931,9937,9940,9964,9967,9976,9978,9986,9990,10171,10175,10181,10185,10224,10228,10476,10480,10566,10583,10587,10597,10671,10674,10680,10689,10755,10775,10782,10837,10843,10849,10889,10900,10906,10911,10915,11129,11133,11212],[974,975,976,977],"p",{},"Module: ",[978,979,980],"code",{},"github.com\u002Fredelay\u002Fgo-framework",[974,982,983],{},"Full-featured Go framework providing a module system with auto-discovery, built-in\nauth\u002Fusers\u002Fgroups modules with self-contained HTTP routes, a transport-agnostic\nbootstrap system with pluggable runners (HTTP, Kafka, FlowDSL, gRPC, WebSocket),\ngeneric MongoDB and ClickHouse CRUD, ClickHouse batch writer, Redis-backed distributed\nprimitives (lock, semaphore), JWT + password management, OpenAPI 3.0 spec generation\nwith Swagger UI \u002F Scalar \u002F Redoc, MCP server, and config loaders.",[974,985,986,987,993,994,997,998,1000,1001,1004],{},"The canonical IR types, FlowDSL parser, compilation pipeline, structural validation,\ndependency resolution, code generation, and SVG tooling have been extracted to the\nstandalone ",[988,989,990],"strong",{},[978,991,992],{},"go-flowdsl"," library (",[978,995,996],{},"github.com\u002Fredelay\u002Fgo-flowdsl","). The framework\nintegrates it without re-exporting — use ",[978,999,992],{}," directly for IR\u002FDSL work.\nAsyncAPI and OpenAPI format adapters remain in ",[978,1002,1003],{},"go-framework\u002Fcore",".",[1006,1007,1009,1012,1013,1016,1017,1016,1020,1016,1023,1016,1026],"callout",{"icon":1008},"i-lucide-book-open",[988,1010,1011],{},"See also:"," ",[1014,1015,140],"a",{"href":141}," · ",[1014,1018,1019],{"href":165},"Module Files & Tooling",[1014,1021,1022],{"href":160},"Core IR & Compilation",[1014,1024,1025],{"href":174},"Format Import\u002FExport",[1014,1027,1028],{"href":170},"redelayctl CLI",[1030,1031,1033],"h2",{"id":1032},"bootstrap-runner-architecture","Bootstrap & Runner architecture",[974,1035,1036,1037,1040,1041,1044],{},"The framework separates ",[988,1038,1039],{},"application lifecycle"," from ",[988,1042,1043],{},"transport",":",[1046,1047,1048,1058,1076],"ol",{},[1049,1050,1051,1054,1055],"li",{},[978,1052,1053],{},"app.Bootstrap()"," — connects Mongo, discovers modules, starts them → returns ",[978,1056,1057],{},"*Instance",[1049,1059,1060,1061,1064,1065,1068,1069,1072,1073],{},"Transport runners (",[978,1062,1063],{},"server.Server",", future ",[978,1066,1067],{},"consumer.KafkaRunner",", ",[978,1070,1071],{},"grpc.Server",", etc.) implement ",[978,1074,1075],{},"app.Runner",[1049,1077,1078,1081],{},[978,1079,1080],{},"app.Run(inst, runners...)"," — starts all runners in parallel, handles signals, shuts down cleanly",[974,1083,1084],{},"This means the same module system powers HTTP, Kafka consumers, FlowDSL workers, gRPC, and WebSocket — without any code changes in modules.",[1086,1087,1089],"h3",{"id":1088},"quick-start-http-only","Quick start (HTTP only)",[1091,1092,1097],"pre",{"className":1093,"code":1094,"language":1095,"meta":1096,"style":1096},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import (\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fapp\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\"\n)\n\ninst, srv, err := server.Default()  \u002F\u002F bootstrap + HTTP runner\napp.Run(inst, srv)                  \u002F\u002F signal handling + graceful shutdown\n","go","",[978,1098,1099,1106,1111,1116,1121,1127,1132],{"__ignoreMap":1096},[1100,1101,1103],"span",{"class":1102,"line":10},"line",[1100,1104,1105],{},"import (\n",[1100,1107,1108],{"class":1102,"line":16},[1100,1109,1110],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fapp\"\n",[1100,1112,1113],{"class":1102,"line":22},[1100,1114,1115],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\"\n",[1100,1117,1118],{"class":1102,"line":51},[1100,1119,1120],{},")\n",[1100,1122,1123],{"class":1102,"line":57},[1100,1124,1126],{"emptyLinePlaceholder":1125},true,"\n",[1100,1128,1129],{"class":1102,"line":102},[1100,1130,1131],{},"inst, srv, err := server.Default()  \u002F\u002F bootstrap + HTTP runner\n",[1100,1133,1134],{"class":1102,"line":108},[1100,1135,1136],{},"app.Run(inst, srv)                  \u002F\u002F signal handling + graceful shutdown\n",[1086,1138,1140],{"id":1139},"multiple-runners-http-kafka","Multiple runners (HTTP + Kafka)",[1091,1142,1144],{"className":1093,"code":1143,"language":1095,"meta":1096,"style":1096},"inst, err := app.Bootstrap()  \u002F\u002F transport-agnostic\n\nhttpSrv := server.FromInstance(inst, server.LoadConfig())\nkafkaRunner := consumer.NewKafkaRunner(inst, consumer.LoadConfig())  \u002F\u002F future\n\napp.Run(inst, httpSrv, kafkaRunner)  \u002F\u002F both in parallel\n",[978,1145,1146,1151,1155,1160,1165,1169],{"__ignoreMap":1096},[1100,1147,1148],{"class":1102,"line":10},[1100,1149,1150],{},"inst, err := app.Bootstrap()  \u002F\u002F transport-agnostic\n",[1100,1152,1153],{"class":1102,"line":16},[1100,1154,1126],{"emptyLinePlaceholder":1125},[1100,1156,1157],{"class":1102,"line":22},[1100,1158,1159],{},"httpSrv := server.FromInstance(inst, server.LoadConfig())\n",[1100,1161,1162],{"class":1102,"line":51},[1100,1163,1164],{},"kafkaRunner := consumer.NewKafkaRunner(inst, consumer.LoadConfig())  \u002F\u002F future\n",[1100,1166,1167],{"class":1102,"line":57},[1100,1168,1126],{"emptyLinePlaceholder":1125},[1100,1170,1171],{"class":1102,"line":102},[1100,1172,1173],{},"app.Run(inst, httpSrv, kafkaRunner)  \u002F\u002F both in parallel\n",[1086,1175,1177],{"id":1176},"no-http-at-all-consumer-only-service","No HTTP at all (consumer-only service)",[1091,1179,1181],{"className":1093,"code":1180,"language":1095,"meta":1096,"style":1096},"inst, err := app.Bootstrap()\nkafkaRunner := consumer.NewKafkaRunner(inst, consumer.LoadConfig())\napp.Run(inst, kafkaRunner)\n",[978,1182,1183,1188,1193],{"__ignoreMap":1096},[1100,1184,1185],{"class":1102,"line":10},[1100,1186,1187],{},"inst, err := app.Bootstrap()\n",[1100,1189,1190],{"class":1102,"line":16},[1100,1191,1192],{},"kafkaRunner := consumer.NewKafkaRunner(inst, consumer.LoadConfig())\n",[1100,1194,1195],{"class":1102,"line":22},[1100,1196,1197],{},"app.Run(inst, kafkaRunner)\n",[1086,1199,1201],{"id":1200},"runner-interface","Runner interface",[1091,1203,1205],{"className":1093,"code":1204,"language":1095,"meta":1096,"style":1096},"\u002F\u002F Any transport adapter implements this:\ntype Runner interface {\n    Run(ctx context.Context) error\n}\n",[978,1206,1207,1212,1217,1222],{"__ignoreMap":1096},[1100,1208,1209],{"class":1102,"line":10},[1100,1210,1211],{},"\u002F\u002F Any transport adapter implements this:\n",[1100,1213,1214],{"class":1102,"line":16},[1100,1215,1216],{},"type Runner interface {\n",[1100,1218,1219],{"class":1102,"line":22},[1100,1220,1221],{},"    Run(ctx context.Context) error\n",[1100,1223,1224],{"class":1102,"line":51},[1100,1225,1226],{},"}\n",[974,1228,1229,1230,1232,1233,1068,1235,1068,1237,1068,1240,1004],{},"Planned runners: ",[978,1231,1063],{}," (HTTP\u002FREST), ",[978,1234,1067],{},[978,1236,1071],{},[978,1238,1239],{},"ws.Server",[978,1241,1242],{},"flowdsl.Runner",[1030,1244,1246],{"id":1245},"app-package","app package",[1086,1248,1250],{"id":1249},"bootstrap","Bootstrap",[1091,1252,1254],{"className":1093,"code":1253,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fapp\"\n\n\u002F\u002F Zero-config from env vars:\ninst, err := app.Bootstrap()\n\n\u002F\u002F With EventBus and Redis:\ninst, err := app.Bootstrap(\n    app.WithEventBus(bus),   \u002F\u002F *eventbus.EventBus from go-events\n    app.WithRedis(rdb),      \u002F\u002F *redis.Client\n)\n\n\u002F\u002F Or with explicit config:\ncfg := app.LoadBootstrapConfig()\ninst, err := app.BootstrapWith(cfg)\n\n\u002F\u002F Instance fields:\ninst.App         \u002F\u002F *app.App\ninst.MongoClient \u002F\u002F *mongo.Client\ninst.Logger      \u002F\u002F *zap.Logger\ninst.Config      \u002F\u002F *app.BootstrapConfig\n\n\u002F\u002F Shutdown:\ninst.Shutdown(ctx)\n",[978,1255,1256,1261,1265,1270,1274,1278,1283,1288,1293,1298,1302,1306,1311,1316,1321,1325,1330,1335,1340,1345,1350,1354,1359],{"__ignoreMap":1096},[1100,1257,1258],{"class":1102,"line":10},[1100,1259,1260],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fapp\"\n",[1100,1262,1263],{"class":1102,"line":16},[1100,1264,1126],{"emptyLinePlaceholder":1125},[1100,1266,1267],{"class":1102,"line":22},[1100,1268,1269],{},"\u002F\u002F Zero-config from env vars:\n",[1100,1271,1272],{"class":1102,"line":51},[1100,1273,1187],{},[1100,1275,1276],{"class":1102,"line":57},[1100,1277,1126],{"emptyLinePlaceholder":1125},[1100,1279,1280],{"class":1102,"line":102},[1100,1281,1282],{},"\u002F\u002F With EventBus and Redis:\n",[1100,1284,1285],{"class":1102,"line":108},[1100,1286,1287],{},"inst, err := app.Bootstrap(\n",[1100,1289,1290],{"class":1102,"line":114},[1100,1291,1292],{},"    app.WithEventBus(bus),   \u002F\u002F *eventbus.EventBus from go-events\n",[1100,1294,1295],{"class":1102,"line":120},[1100,1296,1297],{},"    app.WithRedis(rdb),      \u002F\u002F *redis.Client\n",[1100,1299,1300],{"class":1102,"line":181},[1100,1301,1120],{},[1100,1303,1304],{"class":1102,"line":186},[1100,1305,1126],{"emptyLinePlaceholder":1125},[1100,1307,1308],{"class":1102,"line":191},[1100,1309,1310],{},"\u002F\u002F Or with explicit config:\n",[1100,1312,1313],{"class":1102,"line":197},[1100,1314,1315],{},"cfg := app.LoadBootstrapConfig()\n",[1100,1317,1318],{"class":1102,"line":203},[1100,1319,1320],{},"inst, err := app.BootstrapWith(cfg)\n",[1100,1322,1323],{"class":1102,"line":214},[1100,1324,1126],{"emptyLinePlaceholder":1125},[1100,1326,1327],{"class":1102,"line":223},[1100,1328,1329],{},"\u002F\u002F Instance fields:\n",[1100,1331,1332],{"class":1102,"line":229},[1100,1333,1334],{},"inst.App         \u002F\u002F *app.App\n",[1100,1336,1337],{"class":1102,"line":234},[1100,1338,1339],{},"inst.MongoClient \u002F\u002F *mongo.Client\n",[1100,1341,1342],{"class":1102,"line":240},[1100,1343,1344],{},"inst.Logger      \u002F\u002F *zap.Logger\n",[1100,1346,1347],{"class":1102,"line":246},[1100,1348,1349],{},"inst.Config      \u002F\u002F *app.BootstrapConfig\n",[1100,1351,1352],{"class":1102,"line":251},[1100,1353,1126],{"emptyLinePlaceholder":1125},[1100,1355,1356],{"class":1102,"line":257},[1100,1357,1358],{},"\u002F\u002F Shutdown:\n",[1100,1360,1362],{"class":1102,"line":1361},23,[1100,1363,1364],{},"inst.Shutdown(ctx)\n",[1086,1366,1368],{"id":1367},"run","Run",[1091,1370,1372],{"className":1093,"code":1371,"language":1095,"meta":1096,"style":1096},"\u002F\u002F Start runners, wait for SIGINT\u002FSIGTERM, shut down:\nerr := app.Run(inst, runner1, runner2, ...)\n",[978,1373,1374,1379],{"__ignoreMap":1096},[1100,1375,1376],{"class":1102,"line":10},[1100,1377,1378],{},"\u002F\u002F Start runners, wait for SIGINT\u002FSIGTERM, shut down:\n",[1100,1380,1381],{"class":1102,"line":16},[1100,1382,1383],{},"err := app.Run(inst, runner1, runner2, ...)\n",[1086,1385,1387],{"id":1386},"bootstrapconfig-env-vars","BootstrapConfig env vars",[1389,1390,1391,1407],"table",{},[1392,1393,1394],"thead",{},[1395,1396,1397,1401,1404],"tr",{},[1398,1399,1400],"th",{},"Variable",[1398,1402,1403],{},"Default",[1398,1405,1406],{},"Description",[1408,1409,1410,1426,1441,1456,1471,1485,1500,1529,1559],"tbody",{},[1395,1411,1412,1418,1423],{},[1413,1414,1415],"td",{},[978,1416,1417],{},"APP_NAME",[1413,1419,1420],{},[978,1421,1422],{},"redelay",[1413,1424,1425],{},"Application name",[1395,1427,1428,1433,1438],{},[1413,1429,1430],{},[978,1431,1432],{},"ENV",[1413,1434,1435],{},[978,1436,1437],{},"development",[1413,1439,1440],{},"Environment name",[1395,1442,1443,1448,1453],{},[1413,1444,1445],{},[978,1446,1447],{},"DEBUG",[1413,1449,1450],{},[978,1451,1452],{},"false",[1413,1454,1455],{},"Debug mode",[1395,1457,1458,1463,1468],{},[1413,1459,1460],{},[978,1461,1462],{},"MONGO_URI",[1413,1464,1465],{},[978,1466,1467],{},"mongodb:\u002F\u002Flocalhost:27017",[1413,1469,1470],{},"MongoDB connection",[1395,1472,1473,1478,1482],{},[1413,1474,1475],{},[978,1476,1477],{},"MONGO_DATABASE",[1413,1479,1480],{},[978,1481,1422],{},[1413,1483,1484],{},"Database name",[1395,1486,1487,1492,1497],{},[1413,1488,1489],{},[978,1490,1491],{},"MONGO_TIMEOUT_SECONDS",[1413,1493,1494],{},[978,1495,1496],{},"15",[1413,1498,1499],{},"Connection timeout",[1395,1501,1502,1507,1512],{},[1413,1503,1504],{},[978,1505,1506],{},"MONGO_READ_PREFERENCE",[1413,1508,1509],{},[978,1510,1511],{},"primary",[1413,1513,1514,1068,1516,1068,1519,1068,1522,1068,1525,1528],{},[978,1515,1511],{},[978,1517,1518],{},"primaryPreferred",[978,1520,1521],{},"secondary",[978,1523,1524],{},"secondaryPreferred",[978,1526,1527],{},"nearest",". Unknown values are refused at startup",[1395,1530,1531,1536,1542],{},[1413,1532,1533],{},[978,1534,1535],{},"MONGO_READ_CONCERN",[1413,1537,1538],{},[1539,1540,1541],"em",{},"(driver default)",[1413,1543,1544,1068,1547,1068,1550,1068,1553,1068,1556],{},[978,1545,1546],{},"local",[978,1548,1549],{},"majority",[978,1551,1552],{},"available",[978,1554,1555],{},"linearizable",[978,1557,1558],{},"snapshot",[1395,1560,1561,1566,1570],{},[1413,1562,1563],{},[978,1564,1565],{},"MONGO_WRITE_CONCERN",[1413,1567,1568],{},[1539,1569,1541],{},[1413,1571,1572,1574],{},[978,1573,1549],{}," or a number of members",[1030,1576,1578],{"id":1577},"server-package-http-runner","Server package (HTTP runner)",[974,1580,1581,1582,1585],{},"The ",[978,1583,1584],{},"server"," package is the HTTP\u002FREST runner. It wires chi router, middleware,\nmodule routes, and MCP server.",[1086,1587,1589],{"id":1588},"factory-methods","Factory methods",[1389,1591,1592,1601],{},[1392,1593,1594],{},[1395,1595,1596,1599],{},[1398,1597,1598],{},"Method",[1398,1600,1406],{},[1408,1602,1603,1617,1627],{},[1395,1604,1605,1610],{},[1413,1606,1607],{},[978,1608,1609],{},"Default()",[1413,1611,1612,1613,1616],{},"Returns ",[978,1614,1615],{},"(*Instance, *Server, error)"," — bootstrap + HTTP",[1395,1618,1619,1624],{},[1413,1620,1621],{},[978,1622,1623],{},"FromInstance(inst, cfg)",[1413,1625,1626],{},"Create HTTP runner from existing Instance",[1395,1628,1629,1634],{},[1413,1630,1631],{},[978,1632,1633],{},"LoadConfig()",[1413,1635,1636],{},"Read HTTP-specific config from env vars",[1086,1638,1640],{"id":1639},"server-methods","Server methods",[1389,1642,1643,1651],{},[1392,1644,1645],{},[1395,1646,1647,1649],{},[1398,1648,1598],{},[1398,1650,1406],{},[1408,1652,1653,1666,1680],{},[1395,1654,1655,1660],{},[1413,1656,1657],{},[978,1658,1659],{},"Run(ctx)",[1413,1661,1662,1663,1665],{},"Implements ",[978,1664,1075],{}," — start HTTP, block until ctx done",[1395,1667,1668,1673],{},[1413,1669,1670],{},[978,1671,1672],{},"Router()",[1413,1674,1675,1676,1679],{},"Access the ",[978,1677,1678],{},"chi.Mux"," for custom routes",[1395,1681,1682,1687],{},[1413,1683,1684],{},[978,1685,1686],{},"Handler()",[1413,1688,1689,1690],{},"Get the ",[978,1691,1692],{},"http.Handler",[1086,1694,1696],{"id":1695},"http-config-env-vars","HTTP Config env vars",[1389,1698,1699,1709],{},[1392,1700,1701],{},[1395,1702,1703,1705,1707],{},[1398,1704,1400],{},[1398,1706,1403],{},[1398,1708,1406],{},[1408,1710,1711,1726,1741,1756,1771],{},[1395,1712,1713,1718,1723],{},[1413,1714,1715],{},[978,1716,1717],{},"HOST",[1413,1719,1720],{},[978,1721,1722],{},"0.0.0.0",[1413,1724,1725],{},"Listen host",[1395,1727,1728,1733,1738],{},[1413,1729,1730],{},[978,1731,1732],{},"PORT",[1413,1734,1735],{},[978,1736,1737],{},"8000",[1413,1739,1740],{},"Listen port",[1395,1742,1743,1748,1753],{},[1413,1744,1745],{},[978,1746,1747],{},"PUBLIC_URL",[1413,1749,1750],{},[978,1751,1752],{},"http:\u002F\u002Flocalhost:8000",[1413,1754,1755],{},"Public URL",[1395,1757,1758,1763,1768],{},[1413,1759,1760],{},[978,1761,1762],{},"ROUTE_PREFIX",[1413,1764,1765],{},[978,1766,1767],{},"\u002Fapi\u002Fv1",[1413,1769,1770],{},"API route prefix",[1395,1772,1773,1778,1783],{},[1413,1774,1775],{},[978,1776,1777],{},"CORS_ORIGINS",[1413,1779,1780],{},[978,1781,1782],{},"*",[1413,1784,1785],{},"Comma-separated allowed origins",[1086,1787,1789],{"id":1788},"what-the-http-runner-sets-up","What the HTTP runner sets up",[1046,1791,1792,1795,1801,1807,1816,1826],{},[1049,1793,1794],{},"chi router with RealIP, RequestID, Recoverer, CORS",[1049,1796,1797,1800],{},[978,1798,1799],{},"auth.OptionalAuth"," — parse JWT if present",[1049,1802,1803,1806],{},[978,1804,1805],{},"{ROUTE_PREFIX}\u002Fhealth"," endpoint",[1049,1808,1809,1810,1813,1814],{},"All ",[978,1811,1812],{},"RoutesProvider"," module routes under ",[978,1815,1762],{},[1049,1817,1818,1819,1822,1823],{},"OpenAPI spec at ",[978,1820,1821],{},"\u002Fopenapi.json"," + API reference UI at ",[978,1824,1825],{},"\u002Freference",[1049,1827,1828,1829],{},"MCP server at ",[978,1830,1831],{},"\u002Fmcp",[1030,1833,1835],{"id":1834},"httputil-package","httputil package",[1091,1837,1839],{"className":1093,"code":1838,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fhttputil\"\n\nhttputil.WriteJSON(w, http.StatusOK, data)        \u002F\u002F JSON response\nhttputil.ReadJSON(r, &input)                       \u002F\u002F decode request body\nhttputil.Error(w, http.StatusBadRequest, \"detail\") \u002F\u002F {\"detail\": \"...\"}\n",[978,1840,1841,1846,1850,1855,1860],{"__ignoreMap":1096},[1100,1842,1843],{"class":1102,"line":10},[1100,1844,1845],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fhttputil\"\n",[1100,1847,1848],{"class":1102,"line":16},[1100,1849,1126],{"emptyLinePlaceholder":1125},[1100,1851,1852],{"class":1102,"line":22},[1100,1853,1854],{},"httputil.WriteJSON(w, http.StatusOK, data)        \u002F\u002F JSON response\n",[1100,1856,1857],{"class":1102,"line":51},[1100,1858,1859],{},"httputil.ReadJSON(r, &input)                       \u002F\u002F decode request body\n",[1100,1861,1862],{"class":1102,"line":57},[1100,1863,1864],{},"httputil.Error(w, http.StatusBadRequest, \"detail\") \u002F\u002F {\"detail\": \"...\"}\n",[1030,1866,1868],{"id":1867},"app-container-low-level","App container (low-level)",[974,1870,1871],{},"For full manual control without Bootstrap:",[1091,1873,1875],{"className":1093,"code":1874,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fapp\"\n\napplication := app.New(\"myapp\", logger)\napplication.DiscoverAndRegister(modules.ModuleDeps{DB: db, Logger: logger})\napplication.Start(ctx)\nauthMod := application.Registry().Get(\"auth\").(*auth.Module)\napplication.Shutdown(ctx)\n",[978,1876,1877,1881,1885,1890,1895,1900,1905],{"__ignoreMap":1096},[1100,1878,1879],{"class":1102,"line":10},[1100,1880,1260],{},[1100,1882,1883],{"class":1102,"line":16},[1100,1884,1126],{"emptyLinePlaceholder":1125},[1100,1886,1887],{"class":1102,"line":22},[1100,1888,1889],{},"application := app.New(\"myapp\", logger)\n",[1100,1891,1892],{"class":1102,"line":51},[1100,1893,1894],{},"application.DiscoverAndRegister(modules.ModuleDeps{DB: db, Logger: logger})\n",[1100,1896,1897],{"class":1102,"line":57},[1100,1898,1899],{},"application.Start(ctx)\n",[1100,1901,1902],{"class":1102,"line":102},[1100,1903,1904],{},"authMod := application.Registry().Get(\"auth\").(*auth.Module)\n",[1100,1906,1907],{"class":1102,"line":108},[1100,1908,1909],{},"application.Shutdown(ctx)\n",[1030,1911,1913],{"id":1912},"module-system","Module system",[1086,1915,1917],{"id":1916},"core-interfaces","Core interfaces",[1091,1919,1921],{"className":1093,"code":1920,"language":1095,"meta":1096,"style":1096},"\u002F\u002F Every module must implement:\ntype Module interface {\n    Manifest() *Manifest\n    Startup(ctx context.Context) error\n    Shutdown(ctx context.Context) error\n}\n\n\u002F\u002F Optional interfaces:\ntype Configurable interface { Configure(registry *Registry) error }\ntype RoutesProvider interface { Routes(router Router) }\ntype MCPProvider interface { MCPTools() []MCPTool; MCPResources() []MCPResource }\ntype EventsProvider interface {\n    Events() []*ir.Event                         \u002F\u002F IR metadata — fed to modspec + AsyncAPI\n    Consumers() []*ConsumerRegistration          \u002F\u002F runtime consumer wiring\n}\n",[978,1922,1923,1928,1933,1938,1943,1948,1952,1956,1961,1966,1971,1976,1981,1986,1991],{"__ignoreMap":1096},[1100,1924,1925],{"class":1102,"line":10},[1100,1926,1927],{},"\u002F\u002F Every module must implement:\n",[1100,1929,1930],{"class":1102,"line":16},[1100,1931,1932],{},"type Module interface {\n",[1100,1934,1935],{"class":1102,"line":22},[1100,1936,1937],{},"    Manifest() *Manifest\n",[1100,1939,1940],{"class":1102,"line":51},[1100,1941,1942],{},"    Startup(ctx context.Context) error\n",[1100,1944,1945],{"class":1102,"line":57},[1100,1946,1947],{},"    Shutdown(ctx context.Context) error\n",[1100,1949,1950],{"class":1102,"line":102},[1100,1951,1226],{},[1100,1953,1954],{"class":1102,"line":108},[1100,1955,1126],{"emptyLinePlaceholder":1125},[1100,1957,1958],{"class":1102,"line":114},[1100,1959,1960],{},"\u002F\u002F Optional interfaces:\n",[1100,1962,1963],{"class":1102,"line":120},[1100,1964,1965],{},"type Configurable interface { Configure(registry *Registry) error }\n",[1100,1967,1968],{"class":1102,"line":181},[1100,1969,1970],{},"type RoutesProvider interface { Routes(router Router) }\n",[1100,1972,1973],{"class":1102,"line":186},[1100,1974,1975],{},"type MCPProvider interface { MCPTools() []MCPTool; MCPResources() []MCPResource }\n",[1100,1977,1978],{"class":1102,"line":191},[1100,1979,1980],{},"type EventsProvider interface {\n",[1100,1982,1983],{"class":1102,"line":197},[1100,1984,1985],{},"    Events() []*ir.Event                         \u002F\u002F IR metadata — fed to modspec + AsyncAPI\n",[1100,1987,1988],{"class":1102,"line":203},[1100,1989,1990],{},"    Consumers() []*ConsumerRegistration          \u002F\u002F runtime consumer wiring\n",[1100,1992,1993],{"class":1102,"line":214},[1100,1994,1226],{},[1086,1996,1998],{"id":1997},"moduledeps","ModuleDeps",[974,2000,2001,2003,2004,2007],{},[978,2002,1998],{}," is passed to every factory function. Fields that are ",[978,2005,2006],{},"nil"," when the\ncorresponding option is not configured.",[1091,2009,2011],{"className":1093,"code":2010,"language":1095,"meta":1096,"style":1096},"type ModuleDeps struct {\n    DB       *mongo.Database  \u002F\u002F always set if MONGO_URI is configured\n    Logger   *zap.Logger      \u002F\u002F always set\n    EventBus EventBus         \u002F\u002F nil when no transport is configured\n    Redis    *redis.Client    \u002F\u002F nil when REDIS_URL is not set\n}\n",[978,2012,2013,2018,2023,2028,2033,2038],{"__ignoreMap":1096},[1100,2014,2015],{"class":1102,"line":10},[1100,2016,2017],{},"type ModuleDeps struct {\n",[1100,2019,2020],{"class":1102,"line":16},[1100,2021,2022],{},"    DB       *mongo.Database  \u002F\u002F always set if MONGO_URI is configured\n",[1100,2024,2025],{"class":1102,"line":22},[1100,2026,2027],{},"    Logger   *zap.Logger      \u002F\u002F always set\n",[1100,2029,2030],{"class":1102,"line":51},[1100,2031,2032],{},"    EventBus EventBus         \u002F\u002F nil when no transport is configured\n",[1100,2034,2035],{"class":1102,"line":57},[1100,2036,2037],{},"    Redis    *redis.Client    \u002F\u002F nil when REDIS_URL is not set\n",[1100,2039,2040],{"class":1102,"line":102},[1100,2041,1226],{},[974,2043,2044],{},"Wire optional deps at bootstrap:",[1091,2046,2048],{"className":1093,"code":2047,"language":1095,"meta":1096,"style":1096},"import (\n    \"github.com\u002Fredelay\u002Fgo-events\u002Feventbus\"\n    \"github.com\u002Fredelay\u002Fgo-events\u002Ftransport\u002Fkafka\"\n)\n\nt, _ := kafka.New(kafka.Config{Brokers: []string{\"kafka:9092\"}})\nbus := eventbus.New(t)\nif err := bus.Start(ctx); err != nil { log.Fatal(err) }\n\ninst, err := app.Bootstrap(\n    app.WithEventBus(bus),\n    \u002F\u002F app.WithRedis(redisClient),\n)\n",[978,2049,2050,2054,2059,2064,2068,2072,2077,2082,2087,2091,2095,2100,2105],{"__ignoreMap":1096},[1100,2051,2052],{"class":1102,"line":10},[1100,2053,1105],{},[1100,2055,2056],{"class":1102,"line":16},[1100,2057,2058],{},"    \"github.com\u002Fredelay\u002Fgo-events\u002Feventbus\"\n",[1100,2060,2061],{"class":1102,"line":22},[1100,2062,2063],{},"    \"github.com\u002Fredelay\u002Fgo-events\u002Ftransport\u002Fkafka\"\n",[1100,2065,2066],{"class":1102,"line":51},[1100,2067,1120],{},[1100,2069,2070],{"class":1102,"line":57},[1100,2071,1126],{"emptyLinePlaceholder":1125},[1100,2073,2074],{"class":1102,"line":102},[1100,2075,2076],{},"t, _ := kafka.New(kafka.Config{Brokers: []string{\"kafka:9092\"}})\n",[1100,2078,2079],{"class":1102,"line":108},[1100,2080,2081],{},"bus := eventbus.New(t)\n",[1100,2083,2084],{"class":1102,"line":114},[1100,2085,2086],{},"if err := bus.Start(ctx); err != nil { log.Fatal(err) }\n",[1100,2088,2089],{"class":1102,"line":120},[1100,2090,1126],{"emptyLinePlaceholder":1125},[1100,2092,2093],{"class":1102,"line":181},[1100,2094,1287],{},[1100,2096,2097],{"class":1102,"line":186},[1100,2098,2099],{},"    app.WithEventBus(bus),\n",[1100,2101,2102],{"class":1102,"line":191},[1100,2103,2104],{},"    \u002F\u002F app.WithRedis(redisClient),\n",[1100,2106,2107],{"class":1102,"line":197},[1100,2108,1120],{},[2110,2111,2112],"blockquote",{},[974,2113,2114,2117,2118,1012,2121,1012,2124,2126],{},[988,2115,2116],{},"Important:"," Call ",[978,2119,2120],{},"bus.Start(ctx)",[988,2122,2123],{},"before",[978,2125,1053],{},". Start wires all registered\nconsumer subscriptions to the transport — no messages are consumed until it is called.",[1086,2128,2130],{"id":2129},"eventbus-interface","EventBus interface",[974,2132,2133,2134,2137,2138,2140],{},"Modules receive a narrow ",[978,2135,2136],{},"EventBus"," interface in ",[978,2139,1998],{}," — they can publish events\nbut cannot register new consumers or inspect internal state:",[1091,2142,2144],{"className":1093,"code":2143,"language":1095,"meta":1096,"style":1096},"\u002F\u002F modules.EventBus — the interface injected into ModuleDeps.\ntype EventBus interface {\n    Publish(ctx context.Context, msg *EventMessage) error\n    Close() error\n}\n",[978,2145,2146,2151,2156,2161,2166],{"__ignoreMap":1096},[1100,2147,2148],{"class":1102,"line":10},[1100,2149,2150],{},"\u002F\u002F modules.EventBus — the interface injected into ModuleDeps.\n",[1100,2152,2153],{"class":1102,"line":16},[1100,2154,2155],{},"type EventBus interface {\n",[1100,2157,2158],{"class":1102,"line":22},[1100,2159,2160],{},"    Publish(ctx context.Context, msg *EventMessage) error\n",[1100,2162,2163],{"class":1102,"line":51},[1100,2164,2165],{},"    Close() error\n",[1100,2167,2168],{"class":1102,"line":57},[1100,2169,1226],{},[974,2171,2172,2173,2176,2177,2180,2181,1068,2184,1068,2187,2190,2191,2194,2195,2198,2199,2202,2203,2206,2207,2210],{},"The concrete ",[978,2174,2175],{},"eventbus.EventBus"," in ",[978,2178,2179],{},"go-events"," adds ",[978,2182,2183],{},"Register",[978,2185,2186],{},"Start",[978,2188,2189],{},"Stop",",\n",[978,2192,2193],{},"UsePublish",", and ",[978,2196,2197],{},"UseConsume",". Modules only ever see the narrow interface; consumers\nreturned from ",[978,2200,2201],{},"EventsProvider.Consumers()"," are registered on the full struct and the bus is\nstarted by the ",[978,2204,2205],{},"go-events\u002Fmodule"," lifecycle module (blank-import it; ",[978,2208,2209],{},"TRANSPORT"," picks the\nbackend).",[1086,2212,2214,2215,2218],{"id":2213},"eventdefinition-go-eventstyped","EventDefinition (",[978,2216,2217],{},"go-events\u002Ftyped",")",[974,2220,2221,2222,2225,2226,2229],{},"Compile-time-safe event descriptor. Define once per event, publish from any handler.\nThe canonical import is ",[978,2223,2224],{},"github.com\u002Fredelay\u002Fgo-events\u002Ftyped"," (implementation in\n",[978,2227,2228],{},"go-framework\u002Fruntime\u002Fevents",").",[1091,2231,2233],{"className":1093,"code":2232,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-events\u002Ftyped\"\n\ntype OrderCreatedPayload struct {\n    OrderID string  `json:\"order_id\"`\n    Total   float64 `json:\"total\"`\n}\n\nvar OrderCreatedEvent = typed.EventDefinition[OrderCreatedPayload]{\n    Name:       \"order.created\",\n    EntityType: \"order\",\n    Action:     \"created\",\n    Topic:      \"order.created\",\n}\n\n\u002F\u002F Publish from a handler (deps.EventBus injected at bootstrap):\nmsg, _ := OrderCreatedEvent.NewMessage(\n    order.ID,\n    typed.Actor{Type: typed.ActorTypeUser, ID: userID},\n    OrderCreatedPayload{OrderID: order.ID, Total: order.Total},\n)\ndeps.EventBus.Publish(ctx, msg)\n",[978,2234,2235,2240,2244,2249,2254,2259,2263,2267,2272,2277,2282,2287,2292,2296,2300,2305,2310,2315,2320,2325,2329],{"__ignoreMap":1096},[1100,2236,2237],{"class":1102,"line":10},[1100,2238,2239],{},"import \"github.com\u002Fredelay\u002Fgo-events\u002Ftyped\"\n",[1100,2241,2242],{"class":1102,"line":16},[1100,2243,1126],{"emptyLinePlaceholder":1125},[1100,2245,2246],{"class":1102,"line":22},[1100,2247,2248],{},"type OrderCreatedPayload struct {\n",[1100,2250,2251],{"class":1102,"line":51},[1100,2252,2253],{},"    OrderID string  `json:\"order_id\"`\n",[1100,2255,2256],{"class":1102,"line":57},[1100,2257,2258],{},"    Total   float64 `json:\"total\"`\n",[1100,2260,2261],{"class":1102,"line":102},[1100,2262,1226],{},[1100,2264,2265],{"class":1102,"line":108},[1100,2266,1126],{"emptyLinePlaceholder":1125},[1100,2268,2269],{"class":1102,"line":114},[1100,2270,2271],{},"var OrderCreatedEvent = typed.EventDefinition[OrderCreatedPayload]{\n",[1100,2273,2274],{"class":1102,"line":120},[1100,2275,2276],{},"    Name:       \"order.created\",\n",[1100,2278,2279],{"class":1102,"line":181},[1100,2280,2281],{},"    EntityType: \"order\",\n",[1100,2283,2284],{"class":1102,"line":186},[1100,2285,2286],{},"    Action:     \"created\",\n",[1100,2288,2289],{"class":1102,"line":191},[1100,2290,2291],{},"    Topic:      \"order.created\",\n",[1100,2293,2294],{"class":1102,"line":197},[1100,2295,1226],{},[1100,2297,2298],{"class":1102,"line":203},[1100,2299,1126],{"emptyLinePlaceholder":1125},[1100,2301,2302],{"class":1102,"line":214},[1100,2303,2304],{},"\u002F\u002F Publish from a handler (deps.EventBus injected at bootstrap):\n",[1100,2306,2307],{"class":1102,"line":223},[1100,2308,2309],{},"msg, _ := OrderCreatedEvent.NewMessage(\n",[1100,2311,2312],{"class":1102,"line":229},[1100,2313,2314],{},"    order.ID,\n",[1100,2316,2317],{"class":1102,"line":234},[1100,2318,2319],{},"    typed.Actor{Type: typed.ActorTypeUser, ID: userID},\n",[1100,2321,2322],{"class":1102,"line":240},[1100,2323,2324],{},"    OrderCreatedPayload{OrderID: order.ID, Total: order.Total},\n",[1100,2326,2327],{"class":1102,"line":246},[1100,2328,1120],{},[1100,2330,2331],{"class":1102,"line":251},[1100,2332,2333],{},"deps.EventBus.Publish(ctx, msg)\n",[974,2335,2336,2337,1068,2340,1068,2343,1068,2346,2349,2350,2353,2354,2357,2358,2361],{},"Actor types: ",[978,2338,2339],{},"typed.ActorTypeUser",[978,2341,2342],{},"ActorTypeAdmin",[978,2344,2345],{},"ActorTypeSystem",[978,2347,2348],{},"ActorTypeService",";\n",[978,2351,2352],{},"typed.SystemActor"," is ",[978,2355,2356],{},"Actor{Type: ActorTypeSystem, ID: \"system\"}",". ",[978,2359,2360],{},"MustNewMessage"," panics\ninstead of returning an error.",[974,2363,2364],{},"Well-known definitions:",[1389,2366,2367,2382],{},[1392,2368,2369],{},[1395,2370,2371,2373,2376,2379],{},[1398,2372,1400],{},[1398,2374,2375],{},"Package",[1398,2377,2378],{},"Event name",[1398,2380,2381],{},"Topic",[1408,2383,2384],{},[1395,2385,2386,2391,2396,2401],{},[1413,2387,2388],{},[978,2389,2390],{},"EmailSendEvent",[1413,2392,2393],{},[978,2394,2395],{},"github.com\u002Fredelay\u002Fgo-module-email\u002Fevents",[1413,2397,2398],{},[978,2399,2400],{},"email.send",[1413,2402,2403],{},[978,2404,2400],{},[1086,2406,2408],{"id":2407},"eventsprovider","EventsProvider",[974,2410,2411,2412,2414],{},"Implement ",[978,2413,2408],{}," when a module publishes or consumes events. It serves two purposes:",[1046,2416,2417,2431],{},[1049,2418,2419,2422,2423,2426,2427,2430],{},[988,2420,2421],{},"IR metadata"," — ",[978,2424,2425],{},"Events()"," feeds ",[978,2428,2429],{},"modspec"," and AsyncAPI spec generation",[1049,2432,2433,2422,2436,2439,2440],{},[988,2434,2435],{},"Runtime wiring",[978,2437,2438],{},"Consumers()"," is called by the app to register handlers before ",[978,2441,2442],{},"bus.Start()",[1091,2444,2446],{"className":1093,"code":2445,"language":1095,"meta":1096,"style":1096},"func (m *OrderModule) Events() []*ir.Event {\n    return []*ir.Event{\n        {Name: \"order.created\", EntityType: \"order\", Action: \"created\"},\n    }\n}\n\nfunc (m *OrderModule) Consumers() []*modules.ConsumerRegistration {\n    return []*modules.ConsumerRegistration{\n        {\n            EventName: \"payment.completed\",\n            Topic:     \"payment.completed\",\n            GroupID:   \"orders\",\n            Handler:   m.handlePaymentCompleted,\n        },\n    }\n}\n",[978,2447,2448,2453,2458,2463,2468,2472,2476,2481,2486,2491,2496,2501,2506,2511,2516,2520],{"__ignoreMap":1096},[1100,2449,2450],{"class":1102,"line":10},[1100,2451,2452],{},"func (m *OrderModule) Events() []*ir.Event {\n",[1100,2454,2455],{"class":1102,"line":16},[1100,2456,2457],{},"    return []*ir.Event{\n",[1100,2459,2460],{"class":1102,"line":22},[1100,2461,2462],{},"        {Name: \"order.created\", EntityType: \"order\", Action: \"created\"},\n",[1100,2464,2465],{"class":1102,"line":51},[1100,2466,2467],{},"    }\n",[1100,2469,2470],{"class":1102,"line":57},[1100,2471,1226],{},[1100,2473,2474],{"class":1102,"line":102},[1100,2475,1126],{"emptyLinePlaceholder":1125},[1100,2477,2478],{"class":1102,"line":108},[1100,2479,2480],{},"func (m *OrderModule) Consumers() []*modules.ConsumerRegistration {\n",[1100,2482,2483],{"class":1102,"line":114},[1100,2484,2485],{},"    return []*modules.ConsumerRegistration{\n",[1100,2487,2488],{"class":1102,"line":120},[1100,2489,2490],{},"        {\n",[1100,2492,2493],{"class":1102,"line":181},[1100,2494,2495],{},"            EventName: \"payment.completed\",\n",[1100,2497,2498],{"class":1102,"line":186},[1100,2499,2500],{},"            Topic:     \"payment.completed\",\n",[1100,2502,2503],{"class":1102,"line":191},[1100,2504,2505],{},"            GroupID:   \"orders\",\n",[1100,2507,2508],{"class":1102,"line":197},[1100,2509,2510],{},"            Handler:   m.handlePaymentCompleted,\n",[1100,2512,2513],{"class":1102,"line":203},[1100,2514,2515],{},"        },\n",[1100,2517,2518],{"class":1102,"line":214},[1100,2519,2467],{},[1100,2521,2522],{"class":1102,"line":223},[1100,2523,1226],{},[974,2525,1581,2526,2529],{},[978,2527,2528],{},"ConsumerRegistration"," fields:",[1389,2531,2532,2544],{},[1392,2533,2534],{},[1395,2535,2536,2539,2542],{},[1398,2537,2538],{},"Field",[1398,2540,2541],{},"Type",[1398,2543,1406],{},[1408,2545,2546,2561,2574,2588,2603,2618],{},[1395,2547,2548,2553,2558],{},[1413,2549,2550],{},[978,2551,2552],{},"EventName",[1413,2554,2555],{},[978,2556,2557],{},"string",[1413,2559,2560],{},"Human name (used in modspec)",[1395,2562,2563,2567,2571],{},[1413,2564,2565],{},[978,2566,2381],{},[1413,2568,2569],{},[978,2570,2557],{},[1413,2572,2573],{},"Transport topic to subscribe to",[1395,2575,2576,2581,2585],{},[1413,2577,2578],{},[978,2579,2580],{},"GroupID",[1413,2582,2583],{},[978,2584,2557],{},[1413,2586,2587],{},"Consumer group ID",[1395,2589,2590,2595,2600],{},[1413,2591,2592],{},[978,2593,2594],{},"Handler",[1413,2596,2597],{},[978,2598,2599],{},"func(ctx, *EventMessage) error",[1413,2601,2602],{},"Message handler",[1395,2604,2605,2610,2615],{},[1413,2606,2607],{},[978,2608,2609],{},"BatchSize",[1413,2611,2612],{},[978,2613,2614],{},"int",[1413,2616,2617],{},"Non-zero enables batch delivery",[1395,2619,2620,2625,2630],{},[1413,2621,2622],{},[978,2623,2624],{},"Filter",[1413,2626,2627],{},[978,2628,2629],{},"func(*EventMessage) bool",[1413,2631,2632],{},"Optional message filter",[1086,2634,2636],{"id":2635},"manifest","Manifest",[1091,2638,2640],{"className":1093,"code":2639,"language":1095,"meta":1096,"style":1096},"&modules.Manifest{\n    Name:        \"mymodule\",\n    Description: \"My custom module\",\n    Version:     \"1.0.0\",\n    DependsOn:   []string{\"auth\"},     \u002F\u002F started after auth\n    Provides:    []string{\"mymodule\"}, \u002F\u002F what this module provides\n}\n",[978,2641,2642,2647,2652,2657,2662,2667,2672],{"__ignoreMap":1096},[1100,2643,2644],{"class":1102,"line":10},[1100,2645,2646],{},"&modules.Manifest{\n",[1100,2648,2649],{"class":1102,"line":16},[1100,2650,2651],{},"    Name:        \"mymodule\",\n",[1100,2653,2654],{"class":1102,"line":22},[1100,2655,2656],{},"    Description: \"My custom module\",\n",[1100,2658,2659],{"class":1102,"line":51},[1100,2660,2661],{},"    Version:     \"1.0.0\",\n",[1100,2663,2664],{"class":1102,"line":57},[1100,2665,2666],{},"    DependsOn:   []string{\"auth\"},     \u002F\u002F started after auth\n",[1100,2668,2669],{"class":1102,"line":102},[1100,2670,2671],{},"    Provides:    []string{\"mymodule\"}, \u002F\u002F what this module provides\n",[1100,2673,2674],{"class":1102,"line":108},[1100,2675,1226],{},[1086,2677,2679],{"id":2678},"auto-discovery","Auto-discovery",[974,2681,2682,2683,1044],{},"Modules register factories in ",[978,2684,2685],{},"init()",[1091,2687,2689],{"className":1093,"code":2688,"language":1095,"meta":1096,"style":1096},"func init() {\n    modules.RegisterFactory(\"mymodule\", func(deps modules.ModuleDeps) (modules.Module, error) {\n        return NewModule(deps.DB, deps.Logger), nil\n    })\n}\n",[978,2690,2691,2696,2701,2706,2711],{"__ignoreMap":1096},[1100,2692,2693],{"class":1102,"line":10},[1100,2694,2695],{},"func init() {\n",[1100,2697,2698],{"class":1102,"line":16},[1100,2699,2700],{},"    modules.RegisterFactory(\"mymodule\", func(deps modules.ModuleDeps) (modules.Module, error) {\n",[1100,2702,2703],{"class":1102,"line":22},[1100,2704,2705],{},"        return NewModule(deps.DB, deps.Logger), nil\n",[1100,2707,2708],{"class":1102,"line":51},[1100,2709,2710],{},"    })\n",[1100,2712,2713],{"class":1102,"line":57},[1100,2714,1226],{},[974,2716,2717],{},"Blank-import the package to trigger registration:",[1091,2719,2721],{"className":1093,"code":2720,"language":1095,"meta":1096,"style":1096},"import _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fauth\"\n",[978,2722,2723],{"__ignoreMap":1096},[1100,2724,2725],{"class":1102,"line":10},[1100,2726,2720],{},[1086,2728,2730],{"id":2729},"registry-dual-key-indexing-by-name-and-id","Registry — dual-key indexing by Name and ID",[974,2732,2733,2736,2737,1012,2740,2743,2744,2747,2748,2751],{},[978,2734,2735],{},"Registry.Register"," indexes each module by ",[988,2738,2739],{},"both",[978,2741,2742],{},"manifest.Name"," and\n",[978,2745,2746],{},"manifest.ID"," when they differ. This lets admin sibling modules reach the core\nmodule by its canonical ID regardless of how the original author capitalised\nthe ",[978,2749,2750],{},"Name"," field:",[1091,2753,2755],{"className":1093,"code":2754,"language":1095,"meta":1096,"style":1096},"\u002F\u002F core module\n&modules.Manifest{ ID: \"users\", Name: \"Users\" }\n\n\u002F\u002F admin sibling — Configure hook\nfunc (m *Module) Configure(r *modules.Registry) error {\n    peer, _ := r.Get(\"users\")          \u002F\u002F lookup by manifest.ID — always works\n    m.core = peer.(*usersmod.Module)\n    return nil\n}\n",[978,2756,2757,2762,2767,2771,2776,2781,2786,2791,2796],{"__ignoreMap":1096},[1100,2758,2759],{"class":1102,"line":10},[1100,2760,2761],{},"\u002F\u002F core module\n",[1100,2763,2764],{"class":1102,"line":16},[1100,2765,2766],{},"&modules.Manifest{ ID: \"users\", Name: \"Users\" }\n",[1100,2768,2769],{"class":1102,"line":22},[1100,2770,1126],{"emptyLinePlaceholder":1125},[1100,2772,2773],{"class":1102,"line":51},[1100,2774,2775],{},"\u002F\u002F admin sibling — Configure hook\n",[1100,2777,2778],{"class":1102,"line":57},[1100,2779,2780],{},"func (m *Module) Configure(r *modules.Registry) error {\n",[1100,2782,2783],{"class":1102,"line":102},[1100,2784,2785],{},"    peer, _ := r.Get(\"users\")          \u002F\u002F lookup by manifest.ID — always works\n",[1100,2787,2788],{"class":1102,"line":108},[1100,2789,2790],{},"    m.core = peer.(*usersmod.Module)\n",[1100,2792,2793],{"class":1102,"line":114},[1100,2794,2795],{},"    return nil\n",[1100,2797,2798],{"class":1102,"line":120},[1100,2799,1226],{},[974,2801,2802,2804,2805,2807,2808,2810,2811,2814,2815,2818],{},[978,2803,2746],{}," defaults to ",[978,2806,2742],{}," when unset, so legacy modules that\nonly set ",[978,2809,2750],{}," keep working with no changes. When both are set and differ,\nboth keys resolve to the same ",[978,2812,2813],{},"Module"," pointer; the ",[978,2816,2817],{},"ID"," alias loses to an\nexisting entry with the same key (Name always wins on collision).",[1086,2820,2822],{"id":2821},"public-admin-split-convention","Public \u002F admin split convention",[974,2824,2825],{},"Every module that exposes mutating HTTP endpoints splits into two packages:",[1389,2827,2828,2840],{},[1392,2829,2830],{},[1395,2831,2832,2834,2837],{},[1398,2833,2375],{},[1398,2835,2836],{},"Blank-imported by",[1398,2838,2839],{},"Registers",[1408,2841,2842,2856],{},[1395,2843,2844,2850,2853],{},[1413,2845,2846,2849],{},[978,2847,2848],{},"foo\u002F"," (core)",[1413,2851,2852],{},"api + admin-api",[1413,2854,2855],{},"Public self-service routes (signup, GET \u002Fme, PATCH \u002Fme, user-facing writes) + read-only listings safe for authed users",[1395,2857,2858,2863,2869],{},[1413,2859,2860],{},[978,2861,2862],{},"foo\u002Fadmin\u002F",[1413,2864,2865,2866],{},"admin-api ",[988,2867,2868],{},"only",[1413,2870,2871],{},"Admin-gated writes (list-all, create-for-other-user, update-any, delete, toggle-state, administrative reads of other users' data)",[974,2873,2874,2875,2878,2879,2882,2883,2176,2886,2889,2890,2892,2893,2896],{},"The factory name convention is ",[978,2876,2877],{},"\u003Cmodule>-admin",". The admin package does ",[988,2880,2881],{},"not","\nre-register the core module's factory — it depends on the core module being\nimported too (",[978,2884,2885],{},"depends_on: \u003Cmodule>",[978,2887,2888],{},"module.yaml","). Admin handlers reach\ncore state via accessor methods on the core ",[978,2891,2813],{}," struct (Logger, Service,\nStore, etc.), or by asking the ",[978,2894,2895],{},"Registry"," for the peer module.",[974,2898,2899,2900,2903,2904,2907,2908,2911],{},"Admin routes sit under the ",[978,2901,2902],{},"AdminPrefix()"," (",[978,2905,2906],{},"\u002Fadmin"," by default, override via\n",[978,2909,2910],{},"ADMIN_ROUTE_PREFIX",") and are wrapped in:",[1091,2913,2915],{"className":1093,"code":2914,"language":1095,"meta":1096,"style":1096},"r.Group(modules.AdminPrefix()+\"\u002F\u003Cname>\", func(g modules.Router) {\n    g.Use(auth.RequireActiveUser, auth.RequirePermission(\"admin:access\"))\n    \u002F\u002F ... admin writes ...\n})\n",[978,2916,2917,2922,2927,2932],{"__ignoreMap":1096},[1100,2918,2919],{"class":1102,"line":10},[1100,2920,2921],{},"r.Group(modules.AdminPrefix()+\"\u002F\u003Cname>\", func(g modules.Router) {\n",[1100,2923,2924],{"class":1102,"line":16},[1100,2925,2926],{},"    g.Use(auth.RequireActiveUser, auth.RequirePermission(\"admin:access\"))\n",[1100,2928,2929],{"class":1102,"line":22},[1100,2930,2931],{},"    \u002F\u002F ... admin writes ...\n",[1100,2933,2934],{"class":1102,"line":51},[1100,2935,2936],{},"})\n",[974,2938,2939,2940,2943,2944,2947,2948,2951],{},"This convention is why the backend ships two binaries: ",[978,2941,2942],{},"cmd\u002Fapi"," pulls in core\nmodules only (never the ",[978,2945,2946],{},"admin\u002F"," siblings), so its route table simply cannot\nrespond to admin-only mutations. ",[978,2949,2950],{},"cmd\u002Fadmin-api"," pulls both and lives behind\nthe operator's VPN \u002F Tailscale.",[1086,2953,2955],{"id":2954},"module-inventory-shared-bundles-per-profile","Module inventory — shared bundles per profile",[974,2957,2958,2959,1044],{},"Every backend binary blank-imports exactly one or two \"bundle\" packages that\nenumerate which modules land in that profile. The bundles live under\n",[978,2960,2961],{},"backend\u002Fimports\u002F",[1389,2963,2964,2977],{},[1392,2965,2966],{},[1395,2967,2968,2971,2974],{},[1398,2969,2970],{},"Bundle",[1398,2972,2973],{},"Used by",[1398,2975,2976],{},"Contains",[1408,2978,2979,2996,3036,3059],{},[1395,2980,2981,2986,2993],{},[1413,2982,2983],{},[978,2984,2985],{},"backend\u002Fimports\u002Fcommon",[1413,2987,2988,2990,2991],{},[978,2989,2942],{}," + ",[978,2992,2950],{},[1413,2994,2995],{},"Every module that should appear on BOTH public and admin HTTP surfaces — framework, FlowDSL nodes, addons, AI, application modules.",[1395,2997,2998,3003,3009],{},[1413,2999,3000],{},[978,3001,3002],{},"backend\u002Fimports\u002Faiwire",[1413,3004,3005,2990,3007],{},[978,3006,2942],{},[978,3008,2950],{},[1413,3010,3011,3012,3015,3016,3019,3020,3023,3024,3027,3028,3031,3032,3035],{},"Non-import bundle — exposes ",[978,3013,3014],{},"aiwire.Wire()"," which both HTTP ",[978,3017,3018],{},"main.go","s call after ",[978,3021,3022],{},"server.Default()"," and before ",[978,3025,3026],{},"app.Run()",". Builds the LLM provider from ",[978,3029,3030],{},"LLM_PROVIDER",", registers go-ai FlowDSL node handlers with the flowexec engine, and binds the LLM usage ledger to flowexec's ",[978,3033,3034],{},"UsageSink",". Idempotent — safe to call more than once.",[1395,3037,3038,3043,3048],{},[1413,3039,3040],{},[978,3041,3042],{},"backend\u002Fimports\u002Fcli",[1413,3044,3045],{},[978,3046,3047],{},"cmd\u002Fredelayctl",[1413,3049,3050,3051,3054,3055,3058],{},"Narrow set that contributes ",[978,3052,3053],{},"CLIProvider"," commands (auth, users, groups, health, assistant, …). Keeps CLI startup fast. A legacy ",[978,3056,3057],{},"cmd\u002Fcli"," entry point remains as a deprecation shim with identical behaviour.",[1395,3060,3061,3066,3071],{},[1413,3062,3063],{},[978,3064,3065],{},"backend\u002Fcmd\u002Fadmin-api\u002Fadmin_imports.go",[1413,3067,3068,3070],{},[978,3069,2950],{}," only",[1413,3072,3073,3074,3077],{},"Admin submodules (",[978,3075,3076],{},"*\u002Fadmin\u002F","). Sibling file to admin-api's main.go, stays out of common so the public api cannot reach any admin path.",[974,3079,3080,3083],{},[988,3081,3082],{},"Adding a new module"," is a one-line change:",[1046,3085,3086,3100,3119],{},[1049,3087,3088,3091,3092,1004],{},[988,3089,3090],{},"Shared by api + admin-api"," → add blank-import to ",[1014,3093,3097],{"href":3094,"rel":3095},"https:\u002F\u002Fgithub.com\u002Fredelay\u002Fredelay\u002Fblob\u002Fmain\u002Fbackend\u002Fimports\u002Fcommon\u002Fcommon.go",[3096],"nofollow",[978,3098,3099],{},"backend\u002Fimports\u002Fcommon\u002Fcommon.go",[1049,3101,3102,3105,3106,3108,3109,3112,3113,1004],{},[988,3103,3104],{},"Admin-only HTTP surface"," (has ",[978,3107,2862],{}," with ",[978,3110,3111],{},"id: foo-admin",") → add to ",[1014,3114,3117],{"href":3115,"rel":3116},"https:\u002F\u002Fgithub.com\u002Fredelay\u002Fredelay\u002Fblob\u002Fmain\u002Fbackend\u002Fcmd\u002Fadmin-api\u002Fadmin_imports.go",[3096],[978,3118,3065],{},[1049,3120,3121,3124,3125,1004],{},[988,3122,3123],{},"CLI-only command provider"," → add to ",[1014,3126,3129],{"href":3127,"rel":3128},"https:\u002F\u002Fgithub.com\u002Fredelay\u002Fredelay\u002Fblob\u002Fmain\u002Fbackend\u002Fimports\u002Fcli\u002Fcli.go",[3096],[978,3130,3131],{},"backend\u002Fimports\u002Fcli\u002Fcli.go",[974,3133,3134,3135,3137],{},"No other file needs editing. Each ",[978,3136,3018],{}," is just five lines of wiring plus\nthe bundle imports.",[974,3139,3140,3143,3144,3146],{},[988,3141,3142],{},"Leak enforcement — three layers, same intent."," No route or middleware from\nan admin package can reach ",[978,3145,2942],{},"'s surface. Each layer catches a class of\nmistake the others miss, so they complement rather than duplicate.",[1389,3148,3149,3162],{},[1392,3150,3151],{},[1395,3152,3153,3156,3159],{},[1398,3154,3155],{},"Layer",[1398,3157,3158],{},"Test",[1398,3160,3161],{},"What it catches",[1408,3163,3164,3202,3229],{},[1395,3165,3166,3169,3182],{},[1413,3167,3168],{},"Path",[1413,3170,3171,3178,3179],{},[1014,3172,3175],{"href":3173,"rel":3174},"https:\u002F\u002Fgithub.com\u002Fredelay\u002Fredelay\u002Fblob\u002Fmain\u002Fbackend\u002Fcmd\u002Fapi\u002Fadmin_leak_test.go",[3096],[978,3176,3177],{},"backend\u002Fcmd\u002Fapi\u002Fadmin_leak_test.go"," → ",[978,3180,3181],{},"TestPublicAPIHasNoAdminDeps",[1413,3183,3184,3185,3188,3189,3191,3192,3194,3195,3198,3199,1004],{},"Runs ",[978,3186,3187],{},"go list -deps"," over ",[978,3190,2942],{}," and fails if any import path ends in ",[978,3193,2906],{}," or contains ",[978,3196,3197],{},"\u002Fadmin\u002F",". Catches accidental blank imports in ",[978,3200,3201],{},"common",[1395,3203,3204,3207,3219],{},[1413,3205,3206],{},"Middleware",[1413,3208,3209,3178,3216],{},[1014,3210,3213],{"href":3211,"rel":3212},"https:\u002F\u002Fgithub.com\u002Fredelay\u002Fredelay\u002Fblob\u002Fmain\u002Fbackend\u002Fcmd\u002Fapi\u002Fadmin_middleware_leak_test.go",[3096],[978,3214,3215],{},"backend\u002Fcmd\u002Fapi\u002Fadmin_middleware_leak_test.go",[978,3217,3218],{},"TestPublicAPIHasNoAdminMiddleware",[1413,3220,3221,3222,3225,3226,3228],{},"Boots the public api's module registry and fails if any public route's middleware chain contains ",[978,3223,3224],{},"RequirePermission(\"admin:access\")",". Catches the subtler mistake of a non-",[978,3227,3197],{}," package registering an admin-gated mutation.",[1395,3230,3231,3234,3246],{},[1413,3232,3233],{},"Route",[1413,3235,3236,3178,3243],{},[1014,3237,3240],{"href":3238,"rel":3239},"https:\u002F\u002Fgithub.com\u002Fredelay\u002Fredelay\u002Fblob\u002Fmain\u002Fbackend\u002Fcmd\u002Fapi\u002Fopenapi_no_flow_admin_test.go",[3096],[978,3241,3242],{},"backend\u002Fcmd\u002Fapi\u002Fopenapi_no_flow_admin_test.go",[978,3244,3245],{},"TestOpenAPIHasNoFlowAdminPaths",[1413,3247,3248,3249,3251,3252,1068,3255,3258,3259,3262],{},"Renders ",[978,3250,2942],{},"'s OpenAPI spec and fails if any path matches ",[978,3253,3254],{},"\u002Fflows",[978,3256,3257],{},"\u002Fruns",", or ",[978,3260,3261],{},"\u002Fdeployments",". Domain-specific backstop for flowexec — if either of the other two passes but flowexec routes still leak, this one fires.",[974,3264,3265,3266,3268,3269,3271],{},"Adding an admin blank-import to ",[978,3267,2942],{},", to ",[978,3270,2985],{},", or to\nany transitive dependency of either, trips one of these tests with a\nhuman-readable message listing the leaking paths \u002F routes.",[974,3273,3274,3277,3278,3281,3282,3285],{},[988,3275,3276],{},"Why a shared package instead of duplicated lists per binary?"," Two or three\nbinaries keeping parallel import lists in sync is a known failure mode —\nsomeone adds a module to ",[978,3279,3280],{},"cmd\u002Fapi\u002Fmain.go"," and forgets ",[978,3283,3284],{},"cmd\u002Fadmin-api\u002Fmain.go",",\nthen prod admin-api silently misses a feature. The bundle pattern makes the\nmistake structurally impossible: adding a module means editing the one file\nthat both binaries already import.",[974,3287,3288,3289,3292,3293,3296],{},"When the shared inventory grows past ~60 imports or the admin inventory past\n~20, a ",[978,3290,3291],{},"go generate"," tool that scans ",[978,3294,3295],{},"*\u002Fmodule.yaml"," and regenerates these\nbundles becomes worthwhile. At current scale the manual lists are simpler to\naudit and diff.",[974,3298,3299,3300,1068,3304,2190,3307,3311],{},"See the audit in any core module (e.g. ",[1014,3301,3303],{"href":3302},"#users","users",[1014,3305,3306],{"href":238},"assistant",[1014,3308,3310],{"href":3309},"\u002Fdocs\u002Freference\u002Fgo-flowdsl#flowexec","flowexec",") for the concrete public\u002Fadmin split.",[1086,3313,3315],{"id":3314},"testing-ci-guardrails-testing-ci-guardrails","Testing & CI guardrails {#testing-ci-guardrails}",[974,3317,3318,3319,1068,3322,1068,3324,2190,3326,1068,3329,1068,3332,1068,3335,3338,3339,3342],{},"The monorepo has many Go modules (",[978,3320,3321],{},"go-framework",[978,3323,992],{},[978,3325,2179],{},[978,3327,3328],{},"go-modules",[978,3330,3331],{},"go-module-email",[978,3333,3334],{},"go-ai",[978,3336,3337],{},"backend",", …). ",[978,3340,3341],{},"infra\u002FMakefile","\nprovides single-command entry points that iterate the full set so CI and\npre-push checks never miss a module:",[1389,3344,3345,3358],{},[1392,3346,3347],{},[1395,3348,3349,3352,3355],{},[1398,3350,3351],{},"Target",[1398,3353,3354],{},"Runs",[1398,3356,3357],{},"Purpose",[1408,3359,3360,3376,3391,3405,3421,3444,3459],{},[1395,3361,3362,3367,3373],{},[1413,3363,3364],{},[978,3365,3366],{},"make vet-all",[1413,3368,3369,3372],{},[978,3370,3371],{},"go vet .\u002F..."," in every module",[1413,3374,3375],{},"Static analysis across the whole monorepo — fails on first non-clean module.",[1395,3377,3378,3383,3388],{},[1413,3379,3380],{},[978,3381,3382],{},"make test-all",[1413,3384,3385,3372],{},[978,3386,3387],{},"go test .\u002F...",[1413,3389,3390],{},"Test suite — fails on first non-passing module.",[1395,3392,3393,3398,3402],{},[1413,3394,3395],{},[978,3396,3397],{},"make test-admin-leak",[1413,3399,3400,3070],{},[978,3401,3181],{},[1413,3403,3404],{},"Fast, path-based regression (seconds).",[1395,3406,3407,3412,3418],{},[1413,3408,3409],{},[978,3410,3411],{},"make check-admin-leak",[1413,3413,3414,3415,3417],{},"All three leak tests + live ",[978,3416,3187],{}," scan",[1413,3419,3420],{},"Exhaustive admin-isolation audit (path + middleware + route layers described above).",[1395,3422,3423,3428,3437],{},[1413,3424,3425],{},[978,3426,3427],{},"make check-openapi-paths",[1413,3429,3430,3431,3433,3434],{},"Boots admin-api against an ephemeral DB, fetches ",[978,3432,1821],{},", greps for ",[978,3435,3436],{},"\u002F\u002F",[1413,3438,3439,3440,3443],{},"Catches route-prefix joining regressions like the ",[978,3441,3442],{},"\u002Fapi\u002Fv1\u002F\u002Fflows"," bug.",[1395,3445,3446,3451,3456],{},[1413,3447,3448],{},[978,3449,3450],{},"make check",[1413,3452,3453],{},[978,3454,3455],{},"vet-all + test-all + check-admin-leak + check-openapi-paths",[1413,3457,3458],{},"Full CI-grade gate. Run before pushing.",[1395,3460,3461,3466,3476],{},[1413,3462,3463],{},[978,3464,3465],{},"make gen-docs",[1413,3467,3468,3469,3472,3473,3475],{},"Regenerates ",[978,3470,3471],{},"spec\u002Fdocs\u002F5.nodes\u002F"," from live ",[978,3474,2888],{}," files",[1413,3477,3478],{},"Rebuilds the FlowDSL node catalog. Commit the diff.",[974,3480,3481],{},"Recent hardening worth knowing about:",[3483,3484,3485,3510],"ul",{},[1049,3486,3487,2903,3492,3495,3496,3498,3499,3502,3503,3506,3507,3509],{},[988,3488,3489],{},[978,3490,3491],{},"openapi.joinRoutePrefix",[978,3493,3494],{},"go-framework\u002Fcore\u002Fopenapi\u002Fspec.go",") joins two\nroute-path segments without ever producing ",[978,3497,3436],{},". Locked in by\n",[978,3500,3501],{},"TestJoinRoutePrefix"," and ",[978,3504,3505],{},"TestCollectRoutes_NoDoubleSlashInRealPattern"," —\nthese catch the regression class the ",[978,3508,3427],{}," target\nguards against at the spec level.",[1049,3511,3512,2903,3518,3521,3522,3525,3526,3529,3530,1004],{},[988,3513,3514,3515],{},"Router adapter ",[978,3516,3517],{},"Group(\"\u002F\", fn)",[978,3519,3520],{},"go-framework\u002Fserver\u002Fadapter.go",") now\nuses ",[978,3523,3524],{},"chi.Router.Group"," (middleware-only inline subrouter, no mount) when\nthe prefix is empty. Multiple modules can apply admin gating via\n",[978,3527,3528],{},"r.Group(\"\u002F\", g.Use(auth.RequireActiveUser, auth.RequirePermission(\"admin:access\")))","\non the same parent router without colliding on chi's mount-point check.\nNon-empty prefixes still use ",[978,3531,3532],{},"chi.Router.Route",[1030,3534,3536],{"id":3535},"built-in-modules","Built-in modules",[1086,3538,3539],{"id":3539},"auth",[974,3541,3542,3543,3546,3547,3550,3551,3554,3555,3557,3558,3561,3562],{},"Factory name: ",[978,3544,3545],{},"\"auth\""," · DependsOn: ",[978,3548,3549],{},"[\"users\"]"," · Provides: ",[978,3552,3553],{},"[\"auth\", \"jwt\", \"tokens\"]","\nImplements: ",[978,3556,1812],{}," (login, refresh, revoke under ",[978,3559,3560],{},"\u002Fauth","), ",[978,3563,3564],{},"MiddlewareProvider",[974,3566,3567],{},[988,3568,3569],{},"Routes (auto-registered):",[1389,3571,3572,3582],{},[1392,3573,3574],{},[1395,3575,3576,3578,3580],{},[1398,3577,1598],{},[1398,3579,3168],{},[1398,3581,1406],{},[1408,3583,3584,3599,3613],{},[1395,3585,3586,3591,3596],{},[1413,3587,3588],{},[978,3589,3590],{},"POST",[1413,3592,3593],{},[978,3594,3595],{},"\u002Fauth\u002Flogin",[1413,3597,3598],{},"Login with email + password (JSON or OAuth2 form)",[1395,3600,3601,3605,3610],{},[1413,3602,3603],{},[978,3604,3590],{},[1413,3606,3607],{},[978,3608,3609],{},"\u002Fauth\u002Frefresh",[1413,3611,3612],{},"Refresh token pair",[1395,3614,3615,3619,3624],{},[1413,3616,3617],{},[978,3618,3590],{},[1413,3620,3621],{},[978,3622,3623],{},"\u002Fauth\u002Frevoke",[1413,3625,3626],{},"Revoke a refresh token",[974,3628,3629,3630,2903,3633,3636,3637,2903,3640,3643],{},"The login endpoint supports both ",[978,3631,3632],{},"application\u002Fjson",[978,3634,3635],{},"{\\\"email\\\", \\\"password\\\"}",") and\n",[978,3638,3639],{},"application\u002Fx-www-form-urlencoded",[978,3641,3642],{},"username=&password=",") for OAuth2 password flow\ncompatibility with Swagger UI.",[974,3645,3646,3649,3650,3653,3654,3656,3657,3659,3660,3663],{},[988,3647,3648],{},"Configurable mount prefix."," The auth routes mount at ",[978,3651,3652],{},"AUTH_ROUTE_PREFIX","\n(default ",[978,3655,3560],{},", relative to ",[978,3658,1762],{},"). A project porting a legacy\nsurface points it elsewhere — e.g. ",[978,3661,3662],{},"AUTH_ROUTE_PREFIX=\u002Flogin"," — and every auth\nroute moves with it, no per-handler change.",[974,3665,3666,3669,3670,1004],{},[988,3667,3668],{},"Extended login surface (passwordless + TOTP)."," Beyond password login, the\nmodule registers a fuller authentication surface. All paths are relative to\n",[978,3671,3652],{},[1389,3673,3674,3684],{},[1392,3675,3676],{},[1395,3677,3678,3680,3682],{},[1398,3679,1598],{},[1398,3681,3168],{},[1398,3683,1406],{},[1408,3685,3686,3700,3714,3731,3745,3759,3773,3787,3801,3815],{},[1395,3687,3688,3692,3697],{},[1413,3689,3690],{},[978,3691,3590],{},[1413,3693,3694],{},[978,3695,3696],{},"\u002Foauth",[1413,3698,3699],{},"OAuth2 password-flow login (form-encoded)",[1395,3701,3702,3706,3711],{},[1413,3703,3704],{},[978,3705,3590],{},[1413,3707,3708],{},[978,3709,3710],{},"\u002Fmagic\u002F{email}",[1413,3712,3713],{},"Request a magic-link login token (emailed)",[1395,3715,3716,3720,3728],{},[1413,3717,3718],{},[978,3719,3590],{},[1413,3721,3722,1016,3725],{},[978,3723,3724],{},"\u002Fclaim",[978,3726,3727],{},"\u002Fclaim\u002F{email}",[1413,3729,3730],{},"Redeem a claim token → JWT pair",[1395,3732,3733,3737,3742],{},[1413,3734,3735],{},[978,3736,3590],{},[1413,3738,3739],{},[978,3740,3741],{},"\u002Fvalidate",[1413,3743,3744],{},"Validate a claim token without redeeming it",[1395,3746,3747,3751,3756],{},[1413,3748,3749],{},[978,3750,3590],{},[1413,3752,3753],{},[978,3754,3755],{},"\u002Fverify\u002F{email}",[1413,3757,3758],{},"Verify an email-confirmation token",[1395,3760,3761,3765,3770],{},[1413,3762,3763],{},[978,3764,3590],{},[1413,3766,3767],{},[978,3768,3769],{},"\u002Frecover\u002F{email}",[1413,3771,3772],{},"Start a password recovery (emails a reset token)",[1395,3774,3775,3779,3784],{},[1413,3776,3777],{},[978,3778,3590],{},[1413,3780,3781],{},[978,3782,3783],{},"\u002Freset",[1413,3785,3786],{},"Complete a password reset with a claim token",[1395,3788,3789,3793,3798],{},[1413,3790,3791],{},[978,3792,3590],{},[1413,3794,3795],{},[978,3796,3797],{},"\u002Ftotp",[1413,3799,3800],{},"Complete a login that requires a second TOTP factor",[1395,3802,3803,3808,3812],{},[1413,3804,3805],{},[978,3806,3807],{},"PUT",[1413,3809,3810],{},[978,3811,3797],{},[1413,3813,3814],{},"Enrol TOTP for the current user (returns the secret\u002FQR)",[1395,3816,3817,3822,3826],{},[1413,3818,3819],{},[978,3820,3821],{},"DELETE",[1413,3823,3824],{},[978,3825,3797],{},[1413,3827,3828],{},"Disable TOTP for the current user",[974,3830,3831,2903,3834,3837,3838,3841,3842,3845],{},[988,3832,3833],{},"TOTP",[978,3835,3836],{},"totp.go",") is RFC 6238, stdlib-only: it accepts a ±1-period clock\nskew and compares codes in constant time. A login for a TOTP-enrolled user\nreturns a short-lived claim token instead of a full session; the client\ncompletes it at ",[978,3839,3840],{},"POST \u002Ftotp",", and the issued ",[978,3843,3844],{},"Claims.TOTPValid"," records that the\nsecond factor was satisfied.",[974,3847,3848,2903,3851,3854,3855,3858,3859,1068,3862,3258,3865,3868,3869,3872],{},[988,3849,3850],{},"Claim tokens",[978,3852,3853],{},"claim.go",") are purpose-scoped, short-lived JWTs — a token\nminted for ",[978,3856,3857],{},"totp"," cannot be redeemed as a ",[978,3860,3861],{},"magic",[978,3863,3864],{},"verify",[978,3866,3867],{},"reset"," token.\n",[978,3870,3871],{},"Service.ParseClaim(tokenStr, purpose)"," enforces the purpose, so a leaked\nmagic-link token cannot be repurposed to reset a password.",[974,3874,3875],{},[988,3876,3877],{},"API schemas:",[1389,3879,3880,3889],{},[1392,3881,3882],{},[1395,3883,3884,3887],{},[1398,3885,3886],{},"Schema",[1398,3888,3357],{},[1408,3890,3891,3901,3911,3921,3931],{},[1395,3892,3893,3898],{},[1413,3894,3895],{},[978,3896,3897],{},"AuthLoginInput",[1413,3899,3900],{},"Login request body",[1395,3902,3903,3908],{},[1413,3904,3905],{},[978,3906,3907],{},"AuthRefreshInput",[1413,3909,3910],{},"Refresh token request body",[1395,3912,3913,3918],{},[1413,3914,3915],{},[978,3916,3917],{},"AuthRevokeInput",[1413,3919,3920],{},"Revoke token request body",[1395,3922,3923,3928],{},[1413,3924,3925],{},[978,3926,3927],{},"AuthTokenResponse",[1413,3929,3930],{},"JWT access + refresh token pair",[1395,3932,3933,3938],{},[1413,3934,3935],{},[978,3936,3937],{},"AuthRevokeResponse",[1413,3939,3940],{},"Revoke confirmation",[974,3942,3943],{},[988,3944,3945],{},"Service methods:",[1389,3947,3948,3957],{},[1392,3949,3950],{},[1395,3951,3952,3954],{},[1398,3953,1598],{},[1398,3955,3956],{},"Signature",[1408,3958,3959,3969,3979,3989,3999],{},[1395,3960,3961,3964],{},[1413,3962,3963],{},"Login",[1413,3965,3966],{},[978,3967,3968],{},"Login(ctx, email, password) (access, refresh string, err error)",[1395,3970,3971,3974],{},[1413,3972,3973],{},"Refresh",[1413,3975,3976],{},[978,3977,3978],{},"Refresh(ctx, refreshToken) (access, refresh string, err error)",[1395,3980,3981,3984],{},[1413,3982,3983],{},"Revoke",[1413,3985,3986],{},[978,3987,3988],{},"Revoke(ctx, refreshToken) error",[1395,3990,3991,3994],{},[1413,3992,3993],{},"Validate",[1413,3995,3996],{},[978,3997,3998],{},"ValidateAccessTokenStr(tokenStr) (*Claims, error)",[1395,4000,4001,4004],{},[1413,4002,4003],{},"JWT config",[1413,4005,4006],{},[978,4007,4008],{},"GetJWTConfig() *JWTConfig",[974,4010,4011],{},[988,4012,4013],{},"Claims struct:",[1091,4015,4017],{"className":1093,"code":4016,"language":1095,"meta":1096,"style":1096},"type Claims struct {\n    UserID      string   `json:\"sub\"`\n    Email       string   `json:\"email\"`\n    IsSuperuser bool     `json:\"is_superuser\"`\n    TokenType   TokenType\n    TOTPValid   bool\n    Permissions []string `json:\"permissions,omitempty\"`\n    Extra       map[string]any\n    jwt.RegisteredClaims\n}\n",[978,4018,4019,4024,4029,4034,4039,4044,4049,4054,4059,4064],{"__ignoreMap":1096},[1100,4020,4021],{"class":1102,"line":10},[1100,4022,4023],{},"type Claims struct {\n",[1100,4025,4026],{"class":1102,"line":16},[1100,4027,4028],{},"    UserID      string   `json:\"sub\"`\n",[1100,4030,4031],{"class":1102,"line":22},[1100,4032,4033],{},"    Email       string   `json:\"email\"`\n",[1100,4035,4036],{"class":1102,"line":51},[1100,4037,4038],{},"    IsSuperuser bool     `json:\"is_superuser\"`\n",[1100,4040,4041],{"class":1102,"line":57},[1100,4042,4043],{},"    TokenType   TokenType\n",[1100,4045,4046],{"class":1102,"line":102},[1100,4047,4048],{},"    TOTPValid   bool\n",[1100,4050,4051],{"class":1102,"line":108},[1100,4052,4053],{},"    Permissions []string `json:\"permissions,omitempty\"`\n",[1100,4055,4056],{"class":1102,"line":114},[1100,4057,4058],{},"    Extra       map[string]any\n",[1100,4060,4061],{"class":1102,"line":120},[1100,4062,4063],{},"    jwt.RegisteredClaims\n",[1100,4065,4066],{"class":1102,"line":181},[1100,4067,1226],{},[974,4069,4070],{},[988,4071,4072],{},"Password functions:",[1389,4074,4075,4084],{},[1392,4076,4077],{},[1395,4078,4079,4082],{},[1398,4080,4081],{},"Function",[1398,4083,1406],{},[1408,4085,4086,4096,4106],{},[1395,4087,4088,4093],{},[1413,4089,4090],{},[978,4091,4092],{},"HashPassword(password)",[1413,4094,4095],{},"Argon2id hash (preferred)",[1395,4097,4098,4103],{},[1413,4099,4100],{},[978,4101,4102],{},"VerifyPassword(password, hash)",[1413,4104,4105],{},"Auto-detects Argon2id or bcrypt",[1395,4107,4108,4113],{},[1413,4109,4110],{},[978,4111,4112],{},"NeedsRehash(hash)",[1413,4114,4115],{},"True if hash uses legacy bcrypt",[974,4117,4118],{},[988,4119,4120],{},"Middleware:",[1389,4122,4123,4131],{},[1392,4124,4125],{},[1395,4126,4127,4129],{},[1398,4128,4081],{},[1398,4130,1406],{},[1408,4132,4133,4143,4153,4163,4173,4183,4193],{},[1395,4134,4135,4140],{},[1413,4136,4137],{},[978,4138,4139],{},"AuthMiddleware(cfg)",[1413,4141,4142],{},"Require valid JWT",[1395,4144,4145,4150],{},[1413,4146,4147],{},[978,4148,4149],{},"OptionalAuth(cfg)",[1413,4151,4152],{},"Parse JWT if present",[1395,4154,4155,4160],{},[1413,4156,4157],{},[978,4158,4159],{},"RequireActiveUser",[1413,4161,4162],{},"Ensure claims in context",[1395,4164,4165,4170],{},[1413,4166,4167],{},[978,4168,4169],{},"RequireSuperuser",[1413,4171,4172],{},"Require superuser",[1395,4174,4175,4180],{},[1413,4176,4177],{},[978,4178,4179],{},"RequirePermission(perm)",[1413,4181,4182],{},"Check specific permission",[1395,4184,4185,4190],{},[1413,4186,4187],{},[978,4188,4189],{},"RequireAnyPermission(perms...)",[1413,4191,4192],{},"Check any permission",[1395,4194,4195,4200],{},[1413,4196,4197],{},[978,4198,4199],{},"APISecretMiddleware(secret)",[1413,4201,4202],{},"Validate X-API-Secret",[974,4204,4205],{},[988,4206,4207],{},"Context helpers:",[1091,4209,4211],{"className":1093,"code":4210,"language":1095,"meta":1096,"style":1096},"ctx = auth.ContextWithClaims(ctx, claims)\nclaims, ok := auth.ClaimsFromContext(ctx)\n",[978,4212,4213,4218],{"__ignoreMap":1096},[1100,4214,4215],{"class":1102,"line":10},[1100,4216,4217],{},"ctx = auth.ContextWithClaims(ctx, claims)\n",[1100,4219,4220],{"class":1102,"line":16},[1100,4221,4222],{},"claims, ok := auth.ClaimsFromContext(ctx)\n",[974,4224,4225,2422,4228,4231,4232,4235],{},[988,4226,4227],{},"Token source precedence",[978,4229,4230],{},"AuthMiddleware"," (and ",[978,4233,4234],{},"OptionalAuth",") looks for\nthe access token in two places, in order:",[1046,4237,4238,4244],{},[1049,4239,4240,4243],{},[978,4241,4242],{},"Authorization: Bearer \u003Cjwt>"," header (canonical).",[1049,4245,4246,4249,4250,4253,4254,4257,4258,4261],{},[978,4247,4248],{},"?access_token=\u003Cjwt>"," query-string fallback (RFC 6750 §2.3 \u002F OAuth2 SSE\nidiom). Needed because the browser ",[978,4251,4252],{},"EventSource"," API cannot set custom\nheaders — SSE endpoints like ",[978,4255,4256],{},"\u002Fruns\u002F{id}\u002Fevents"," or ",[978,4259,4260],{},"\u002Fflows\u002F{id}\u002Flive","\nwould otherwise be unreachable from a browser without proxying the token\nthrough a cookie.",[974,4263,4264,4265,4268,4269,2190,4272,1004],{},"If both are present, the header wins. Deployments that terminate TLS at the\nedge should ensure access logs redact the ",[978,4266,4267],{},"access_token"," query parameter.\nRegression tests: ",[978,4270,4271],{},"TestAuthMiddleware_QueryParamFallback",[978,4273,4274],{},"TestAuthMiddleware_HeaderWinsOverQueryParam",[974,4276,4277],{},[988,4278,4279],{},"Cross-module interfaces:",[1091,4281,4283],{"className":1093,"code":4282,"language":1095,"meta":1096,"style":1096},"\u002F\u002F Implement in your users module:\ntype LookupProvider interface {\n    AsAuthLookup() UserLookup\n}\n\n\u002F\u002F Implement in your groups module (optional):\ntype PermissionsProvider interface {\n    AsPermissionsResolver() PermissionsResolver\n}\n",[978,4284,4285,4290,4295,4300,4304,4308,4313,4318,4323],{"__ignoreMap":1096},[1100,4286,4287],{"class":1102,"line":10},[1100,4288,4289],{},"\u002F\u002F Implement in your users module:\n",[1100,4291,4292],{"class":1102,"line":16},[1100,4293,4294],{},"type LookupProvider interface {\n",[1100,4296,4297],{"class":1102,"line":22},[1100,4298,4299],{},"    AsAuthLookup() UserLookup\n",[1100,4301,4302],{"class":1102,"line":51},[1100,4303,1226],{},[1100,4305,4306],{"class":1102,"line":57},[1100,4307,1126],{"emptyLinePlaceholder":1125},[1100,4309,4310],{"class":1102,"line":102},[1100,4311,4312],{},"\u002F\u002F Implement in your groups module (optional):\n",[1100,4314,4315],{"class":1102,"line":108},[1100,4316,4317],{},"type PermissionsProvider interface {\n",[1100,4319,4320],{"class":1102,"line":114},[1100,4321,4322],{},"    AsPermissionsResolver() PermissionsResolver\n",[1100,4324,4325],{"class":1102,"line":120},[1100,4326,1226],{},[1086,4328,3303],{"id":3303},[974,4330,3542,4331,3550,4334,3554,4336,4338,4339,3561,4342],{},[978,4332,4333],{},"\"users\"",[978,4335,3549],{},[978,4337,1812],{}," (self-service under ",[978,4340,4341],{},"\u002Fusers",[978,4343,4344],{},"auth.LookupProvider",[974,4346,4347,4350,4351,4353],{},[988,4348,4349],{},"Public routes"," (registered by core ",[978,4352,3303],{}," module — reach any binary that\nblank-imports it):",[1389,4355,4356,4366],{},[1392,4357,4358],{},[1395,4359,4360,4362,4364],{},[1398,4361,1598],{},[1398,4363,3168],{},[1398,4365,1406],{},[1408,4367,4368,4393,4408],{},[1395,4369,4370,4374,4379],{},[1413,4371,4372],{},[978,4373,3590],{},[1413,4375,4376],{},[978,4377,4378],{},"\u002Fusers\u002Fsignup",[1413,4380,4381,4382,4385,4386,4385,4389,4392],{},"Public signup — creates a non-superuser account. ",[978,4383,4384],{},"is_superuser"," \u002F ",[978,4387,4388],{},"is_active",[978,4390,4391],{},"email_validated"," cannot be set from this surface.",[1395,4394,4395,4400,4405],{},[1413,4396,4397],{},[978,4398,4399],{},"GET",[1413,4401,4402],{},[978,4403,4404],{},"\u002Fusers\u002Fme",[1413,4406,4407],{},"Current user profile",[1395,4409,4410,4415,4419],{},[1413,4411,4412],{},[978,4413,4414],{},"PATCH",[1413,4416,4417],{},[978,4418,4404],{},[1413,4420,4421],{},"Update the authenticated user. Privileged fields silently stripped.",[974,4423,4424,4427,4428,4431],{},[988,4425,4426],{},"Admin routes"," (registered by the ",[978,4429,4430],{},"users\u002Fadmin"," sibling module — blank-import\nonly in admin-api):",[1389,4433,4434,4444],{},[1392,4435,4436],{},[1395,4437,4438,4440,4442],{},[1398,4439,1598],{},[1398,4441,3168],{},[1398,4443,1406],{},[1408,4445,4446,4460,4474,4488,4502,4515,4528,4542],{},[1395,4447,4448,4452,4457],{},[1413,4449,4450],{},[978,4451,4399],{},[1413,4453,4454],{},[978,4455,4456],{},"\u002F{ADMIN_PREFIX}\u002Fusers\u002Fall",[1413,4458,4459],{},"List users",[1395,4461,4462,4466,4471],{},[1413,4463,4464],{},[978,4465,3590],{},[1413,4467,4468],{},[978,4469,4470],{},"\u002F{ADMIN_PREFIX}\u002Fusers\u002F",[1413,4472,4473],{},"Create user with full privilege set",[1395,4475,4476,4480,4485],{},[1413,4477,4478],{},[978,4479,3590],{},[1413,4481,4482],{},[978,4483,4484],{},"\u002F{ADMIN_PREFIX}\u002Fusers\u002Ftoggle-state",[1413,4486,4487],{},"Enable \u002F disable user",[1395,4489,4490,4494,4499],{},[1413,4491,4492],{},[978,4493,4399],{},[1413,4495,4496],{},[978,4497,4498],{},"\u002F{ADMIN_PREFIX}\u002Fusers\u002F{id}",[1413,4500,4501],{},"Get user by ID",[1395,4503,4504,4508,4512],{},[1413,4505,4506],{},[978,4507,3807],{},[1413,4509,4510],{},[978,4511,4498],{},[1413,4513,4514],{},"Update user (any fields)",[1395,4516,4517,4521,4525],{},[1413,4518,4519],{},[978,4520,3821],{},[1413,4522,4523],{},[978,4524,4498],{},[1413,4526,4527],{},"Delete user",[1395,4529,4530,4534,4539],{},[1413,4531,4532],{},[978,4533,3590],{},[1413,4535,4536],{},[978,4537,4538],{},"\u002F{ADMIN_PREFIX}\u002Fusers\u002F{id}\u002Fgroups\u002F{groupID}",[1413,4540,4541],{},"Grant group membership",[1395,4543,4544,4548,4552],{},[1413,4545,4546],{},[978,4547,3821],{},[1413,4549,4550],{},[978,4551,4538],{},[1413,4553,4554],{},"Revoke group membership",[974,4556,4557,4558,2990,4561,4564],{},"Every admin route is gated on ",[978,4559,4560],{},"admin:access",[978,4562,4563],{},"auth:active-user",". The public\napi binary has no write surface for other users; the admin api binary exposes\nboth the public self-service routes AND the admin administration routes.",[974,4566,4567],{},[988,4568,3945],{},[1389,4570,4571,4579],{},[1392,4572,4573],{},[1395,4574,4575,4577],{},[1398,4576,1598],{},[1398,4578,3956],{},[1408,4580,4581,4591,4601,4611,4621,4631,4641],{},[1395,4582,4583,4586],{},[1413,4584,4585],{},"Create",[1413,4587,4588],{},[978,4589,4590],{},"Create(ctx, *UserCreateInput) (*User, error)",[1395,4592,4593,4596],{},[1413,4594,4595],{},"GetByID",[1413,4597,4598],{},[978,4599,4600],{},"GetByID(ctx, id) (*User, error)",[1395,4602,4603,4606],{},[1413,4604,4605],{},"GetByEmail",[1413,4607,4608],{},[978,4609,4610],{},"GetByEmail(ctx, email) (*User, error)",[1395,4612,4613,4616],{},[1413,4614,4615],{},"List",[1413,4617,4618],{},[978,4619,4620],{},"List(ctx, filter, page, pageSize) (*PaginatedResult[*User], error)",[1395,4622,4623,4626],{},[1413,4624,4625],{},"Update",[1413,4627,4628],{},[978,4629,4630],{},"Update(ctx, id, *UserUpdateInput) (*User, error)",[1395,4632,4633,4636],{},[1413,4634,4635],{},"Delete",[1413,4637,4638],{},[978,4639,4640],{},"Delete(ctx, id) error",[1395,4642,4643,4646],{},[1413,4644,4645],{},"Authenticate",[1413,4647,4648],{},[978,4649,4650],{},"Authenticate(ctx, email, password) (*User, error)",[974,4652,4653],{},[988,4654,3877],{},[1389,4656,4657,4665],{},[1392,4658,4659],{},[1395,4660,4661,4663],{},[1398,4662,3886],{},[1398,4664,3357],{},[1408,4666,4667,4677,4687,4697],{},[1395,4668,4669,4674],{},[1413,4670,4671],{},[978,4672,4673],{},"UserCreateInput",[1413,4675,4676],{},"Create user request body",[1395,4678,4679,4684],{},[1413,4680,4681],{},[978,4682,4683],{},"UserUpdateInput",[1413,4685,4686],{},"Partial update request body (pointer fields)",[1395,4688,4689,4694],{},[1413,4690,4691],{},[978,4692,4693],{},"UserResponse",[1413,4695,4696],{},"API response (no sensitive fields)",[1395,4698,4699,4704],{},[1413,4700,4701],{},[978,4702,4703],{},"UserListResponse",[1413,4705,4706],{},"Paginated list response",[974,4708,1662,4709,4711],{},[978,4710,4344],{}," automatically.",[1086,4713,4714],{"id":4714},"groups",[974,4716,3542,4717,3550,4720],{},[978,4718,4719],{},"\"groups\"",[978,4721,4722],{},"[\"groups\", \"permissions\"]",[974,4724,4725],{},[988,4726,3945],{},[1389,4728,4729,4737],{},[1392,4730,4731],{},[1395,4732,4733,4735],{},[1398,4734,1598],{},[1398,4736,3956],{},[1408,4738,4739,4748,4757,4767,4777,4786],{},[1395,4740,4741,4743],{},[1413,4742,4585],{},[1413,4744,4745],{},[978,4746,4747],{},"Create(ctx, name, description, permissions) (*UserGroup, error)",[1395,4749,4750,4752],{},[1413,4751,4595],{},[1413,4753,4754],{},[978,4755,4756],{},"GetByID(ctx, id) (*UserGroup, error)",[1395,4758,4759,4762],{},[1413,4760,4761],{},"GetByIDs",[1413,4763,4764],{},[978,4765,4766],{},"GetByIDs(ctx, ids) ([]*UserGroup, error)",[1395,4768,4769,4772],{},[1413,4770,4771],{},"GetPermissionsForGroups",[1413,4773,4774],{},[978,4775,4776],{},"GetPermissionsForGroups(ctx, ids) ([]string, error)",[1395,4778,4779,4781],{},[1413,4780,4625],{},[1413,4782,4783],{},[978,4784,4785],{},"Update(ctx, id, update) (*UserGroup, error)",[1395,4787,4788,4790],{},[1413,4789,4635],{},[1413,4791,4792],{},[978,4793,4640],{},[974,4795,1662,4796,4711],{},[978,4797,4798],{},"auth.PermissionsProvider",[1086,4800,4801],{"id":4801},"health",[974,4803,3542,4804,4807,4808,1068,4810,2190,4813,1004],{},[978,4805,4806],{},"\"health\""," · Implements ",[978,4809,1812],{},[978,4811,4812],{},"MountProvider",[978,4814,4815],{},"DynamicRoutesProvider",[974,4817,4818,4819,4822],{},"Serves the same health check at ",[988,4820,4821],{},"two"," places:",[1389,4824,4825,4837],{},[1392,4826,4827],{},[1395,4828,4829,4831,4834],{},[1398,4830,3168],{},[1398,4832,4833],{},"Via",[1398,4835,4836],{},"For",[1408,4838,4839,4858],{},[1395,4840,4841,4850,4855],{},[1413,4842,4843,4846,4847,2218],{},[978,4844,4845],{},"ROUTE_PREFIX\u002Fhealth"," (e.g. ",[978,4848,4849],{},"\u002Fapi\u002Fv1\u002Fhealth",[1413,4851,4852],{},[978,4853,4854],{},"Routes",[1413,4856,4857],{},"The versioned API surface (Redelay convention)",[1395,4859,4860,4866,4871],{},[1413,4861,4862,4865],{},[978,4863,4864],{},"\u002Fhealth"," (root, outside the prefix)",[1413,4867,4868],{},[978,4869,4870],{},"Mounts",[1413,4872,4873,4874,4876],{},"Load-balancer and Kubernetes probes, which hit ",[978,4875,4864],{}," without knowing the app's API version prefix",[974,4878,4879,4880,4883,4884,4886,4887,4889,4890,2990,4893,4896,4897,4900,4901,4904,4905,4908],{},"The root path is ",[978,4881,4882],{},"HEALTH_ROOT_PATH"," (default ",[978,4885,4864],{},"); set it empty to disable\nthe root mount. It is advertised in ",[978,4888,1821],{}," at its absolute path via\n",[978,4891,4892],{},"DynamicRoutes",[978,4894,4895],{},"AbsolutePath",". The response aggregates every check registered\nin the shared ",[978,4898,4899],{},"HealthRegistry"," (Mongo, Redis, and any a module adds), returning\n",[978,4902,4903],{},"200"," when all pass and ",[978,4906,4907],{},"503"," when any fails.",[1086,4910,4912],{"id":4911},"flowdsl-node-subpackages","FlowDSL node subpackages",[974,4914,4915,4916,4919],{},"Each built-in module (auth, users, groups) has a ",[978,4917,4918],{},"flowdsl\u002F"," subdirectory providing FlowDSL\nnodes as a separate companion module. Enable with a blank import:",[1091,4921,4923],{"className":1093,"code":4922,"language":1095,"meta":1096,"style":1096},"import (\n    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fauth\"\n    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fauth\u002Fflowdsl\"   \u002F\u002F 7 FlowDSL nodes\n)\n",[978,4924,4925,4929,4934,4939],{"__ignoreMap":1096},[1100,4926,4927],{"class":1102,"line":10},[1100,4928,1105],{},[1100,4930,4931],{"class":1102,"line":16},[1100,4932,4933],{},"    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fauth\"\n",[1100,4935,4936],{"class":1102,"line":22},[1100,4937,4938],{},"    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fauth\u002Fflowdsl\"   \u002F\u002F 7 FlowDSL nodes\n",[1100,4940,4941],{"class":1102,"line":51},[1100,4942,1120],{},[1389,4944,4945,4961],{},[1392,4946,4947],{},[1395,4948,4949,4952,4955,4958],{},[1398,4950,4951],{},"Subpackage",[1398,4953,4954],{},"Factory",[1398,4956,4957],{},"Nodes",[1398,4959,4960],{},"Kinds",[1408,4962,4963,4981,4999],{},[1395,4964,4965,4970,4975,4978],{},[1413,4966,4967],{},[978,4968,4969],{},"modules\u002Fauth\u002Fflowdsl",[1413,4971,4972],{},[978,4973,4974],{},"auth-flowdsl",[1413,4976,4977],{},"7",[1413,4979,4980],{},"1 action, 1 transform, 1 router, 4 source",[1395,4982,4983,4988,4993,4996],{},[1413,4984,4985],{},[978,4986,4987],{},"modules\u002Fusers\u002Fflowdsl",[1413,4989,4990],{},[978,4991,4992],{},"users-flowdsl",[1413,4994,4995],{},"8",[1413,4997,4998],{},"3 action, 2 transform, 3 source",[1395,5000,5001,5006,5011,5013],{},[1413,5002,5003],{},[978,5004,5005],{},"modules\u002Fgroups\u002Fflowdsl",[1413,5007,5008],{},[978,5009,5010],{},"groups-flowdsl",[1413,5012,4977],{},[1413,5014,5015],{},"3 action, 1 transform, 3 source",[974,5017,5018,5019,1004],{},"For the full node catalog and subpackage structure, see the\n",[1014,5020,5022],{"href":5021},"\u002Fdocs\u002Freference\u002Fgo-flowdsl#flowdsl-node-subpackages","FlowDSL node subpackage reference",[1030,5024,5026],{"id":5025},"generic-crud","Generic CRUD",[1086,5028,5030],{"id":5029},"mongodb-crud","MongoDB CRUD",[1091,5032,5034],{"className":1093,"code":5033,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcrud\"\n\ntype MyModel struct {\n    crud.BaseModel `bson:\",inline\"`\n    Name string    `bson:\"name\" json:\"name\"`\n}\n\nfunc (m *MyModel) GetID() primitive.ObjectID { return m.ID }\n\nc := crud.NewMongoCRUD[*MyModel](collection)\n\nitem, _ := c.Create(ctx, &MyModel{Name: \"test\"})\nitem, _ = c.FindByID(ctx, id)\nitem, _ = c.FindByField(ctx, \"name\", \"test\")\nresult, _ := c.List(ctx, crud.ListParams{\n    Filter: bson.M{\"name\": \"test\"},\n    Skip: 0, Limit: 20,\n    Sort: bson.D{{Key: \"created_at\", Value: -1}},\n})\n\u002F\u002F result.Items, result.Total, result.Skip, result.Limit\n",[978,5035,5036,5041,5045,5050,5055,5060,5064,5068,5073,5077,5082,5086,5091,5096,5101,5106,5111,5116,5121,5125],{"__ignoreMap":1096},[1100,5037,5038],{"class":1102,"line":10},[1100,5039,5040],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcrud\"\n",[1100,5042,5043],{"class":1102,"line":16},[1100,5044,1126],{"emptyLinePlaceholder":1125},[1100,5046,5047],{"class":1102,"line":22},[1100,5048,5049],{},"type MyModel struct {\n",[1100,5051,5052],{"class":1102,"line":51},[1100,5053,5054],{},"    crud.BaseModel `bson:\",inline\"`\n",[1100,5056,5057],{"class":1102,"line":57},[1100,5058,5059],{},"    Name string    `bson:\"name\" json:\"name\"`\n",[1100,5061,5062],{"class":1102,"line":102},[1100,5063,1226],{},[1100,5065,5066],{"class":1102,"line":108},[1100,5067,1126],{"emptyLinePlaceholder":1125},[1100,5069,5070],{"class":1102,"line":114},[1100,5071,5072],{},"func (m *MyModel) GetID() primitive.ObjectID { return m.ID }\n",[1100,5074,5075],{"class":1102,"line":120},[1100,5076,1126],{"emptyLinePlaceholder":1125},[1100,5078,5079],{"class":1102,"line":181},[1100,5080,5081],{},"c := crud.NewMongoCRUD[*MyModel](collection)\n",[1100,5083,5084],{"class":1102,"line":186},[1100,5085,1126],{"emptyLinePlaceholder":1125},[1100,5087,5088],{"class":1102,"line":191},[1100,5089,5090],{},"item, _ := c.Create(ctx, &MyModel{Name: \"test\"})\n",[1100,5092,5093],{"class":1102,"line":197},[1100,5094,5095],{},"item, _ = c.FindByID(ctx, id)\n",[1100,5097,5098],{"class":1102,"line":203},[1100,5099,5100],{},"item, _ = c.FindByField(ctx, \"name\", \"test\")\n",[1100,5102,5103],{"class":1102,"line":214},[1100,5104,5105],{},"result, _ := c.List(ctx, crud.ListParams{\n",[1100,5107,5108],{"class":1102,"line":223},[1100,5109,5110],{},"    Filter: bson.M{\"name\": \"test\"},\n",[1100,5112,5113],{"class":1102,"line":229},[1100,5114,5115],{},"    Skip: 0, Limit: 20,\n",[1100,5117,5118],{"class":1102,"line":234},[1100,5119,5120],{},"    Sort: bson.D{{Key: \"created_at\", Value: -1}},\n",[1100,5122,5123],{"class":1102,"line":240},[1100,5124,2936],{},[1100,5126,5127],{"class":1102,"line":246},[1100,5128,5129],{},"\u002F\u002F result.Items, result.Total, result.Skip, result.Limit\n",[5131,5132,5134,5135],"h4",{"id":5133},"projections-listparamsprojection","Projections — ",[978,5136,5137],{},"ListParams.Projection",[974,5139,5140,5143,5144,4385,5147,5150],{},[978,5141,5142],{},"Projection"," takes the driver's usual ",[978,5145,5146],{},"bson.M{\"field\": 1}",[978,5148,5149],{},"{\"field\": 0}","\nform and is nil by default, which reads whole documents.",[974,5152,5153,5154,1068,5157,5160],{},"Reach for it whenever the documents are much larger than the response. The\nmotivating case is localized content: a catalogue whose ",[978,5155,5156],{},"name",[978,5158,5159],{},"description","\nand step lists hold every locale is roughly 39 KB per document, so a 50-item\npage pulls ~1.9 MB off the server to render a grid of names and thumbnails.",[974,5162,5163,5164,5167,5168,5170],{},"Prefer an ",[988,5165,5166],{},"exclusion"," projection (",[978,5169,5149],{},"):",[3483,5172,5173,5176],{},[1049,5174,5175],{},"An inclusion list has to enumerate every other field, so a field added to the\nmodel later comes back empty in list views with nothing to say why.",[1049,5177,5178,5179,5182,5183,5186,5187,1004],{},"Exclusion cannot damage a legacy document shape. If ",[978,5180,5181],{},"instructions"," is still a\nplain array on old rows rather than a per-locale map, naming it in an\nexclusion projection still just removes it, whereas including\n",[978,5184,5185],{},"instructions.en"," silently yields ",[978,5188,5189],{},"[]",[974,5191,5192,5195,5196,5199,5200,5203,5204,5206],{},[988,5193,5194],{},"Move the DTO with the projection."," A projected-away field decodes as its zero\nvalue, which is indistinguishable from \"this record genuinely has none\" — so a\nlist DTO should nil those fields and mark them ",[978,5197,5198],{},"omitempty",", leaving the key\nabsent. ",[978,5201,5202],{},"undefined"," then means \"not in this view\" and ",[978,5205,5189],{}," means empty. Test the\npair together; each half is silent on its own.",[5131,5208,5210],{"id":5209},"read-preference-per-query-not-per-deployment","Read preference — per query, not per deployment",[974,5212,5213,5214,5216],{},"The client default is ",[978,5215,1511],{}," and should stay there. A global secondary\npreference is the tempting setting and the wrong one: replication is\nasynchronous, so it breaks read-your-writes on every endpoint at once — a user\nwrites, the next poll hits a lagging member, and the write appears to vanish.",[974,5218,5219],{},"Whether a read tolerates staleness is a property of the read, so it is opted\ninto at the call site:",[1091,5221,5223],{"className":1093,"code":5222,"language":1095,"meta":1096,"style":1096},"import rtmongo \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\"\n\n\u002F\u002F Curated content, an analytics rollup — anything the caller did not just write.\nctx = rtmongo.Nearest(ctx)          \u002F\u002F closest member, including the primary\nctx = rtmongo.Secondary(ctx)        \u002F\u002F keep heavy reporting off the primary\nitems, err := store.List(ctx, params)\n",[978,5224,5225,5230,5234,5239,5244,5249],{"__ignoreMap":1096},[1100,5226,5227],{"class":1102,"line":10},[1100,5228,5229],{},"import rtmongo \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\"\n",[1100,5231,5232],{"class":1102,"line":16},[1100,5233,1126],{"emptyLinePlaceholder":1125},[1100,5235,5236],{"class":1102,"line":22},[1100,5237,5238],{},"\u002F\u002F Curated content, an analytics rollup — anything the caller did not just write.\n",[1100,5240,5241],{"class":1102,"line":51},[1100,5242,5243],{},"ctx = rtmongo.Nearest(ctx)          \u002F\u002F closest member, including the primary\n",[1100,5245,5246],{"class":1102,"line":57},[1100,5247,5248],{},"ctx = rtmongo.Secondary(ctx)        \u002F\u002F keep heavy reporting off the primary\n",[1100,5250,5251],{"class":1102,"line":102},[1100,5252,5253],{},"items, err := store.List(ctx, params)\n",[974,5255,5256,5259,5260,5263],{},[978,5257,5258],{},"crud.MongoCRUD"," honours it on ",[988,5261,5262],{},"reads only"," — a read preference on a write is\nmeaningless, and routing writes through the same seam would imply otherwise. A\ncontext with no preference uses the client default, so existing code is\nunaffected.",[974,5265,5266,5269,5270,5273,5274,5276],{},[978,5267,5268],{},"Secondary"," maps to ",[978,5271,5272],{},"SecondaryPreferred"," deliberately: on a single-node\ndeployment (every dev machine) a hard ",[978,5275,5268],{}," has nowhere to go and fails\nthe query, where preferring one degrades to the primary.",[5131,5278,5280,5281],{"id":5279},"lifecycle-events-withevents","Lifecycle events — ",[978,5282,5283],{},"WithEvents",[974,5285,5286,5287,4385,5290,4385,5293,5296],{},"Any CRUD can publish ",[978,5288,5289],{},"\u003Centity>.created",[978,5291,5292],{},".updated",[978,5294,5295],{},".deleted"," after\nsuccessful writes, so other modules react to data changes without importing the\nowner:",[1091,5298,5300],{"className":1093,"code":5299,"language":1095,"meta":1096,"style":1096},"c := crud.NewMongoCRUD[*WorkoutProgram](col).WithEvents(deps.EventBus, \"program\")\n",[978,5301,5302],{"__ignoreMap":1096},[1100,5303,5304],{"class":1102,"line":10},[1100,5305,5299],{},[974,5307,5308,5309,1068,5311,3502,5313,5315,5316,3108,5319,5322,5323,5326,5327,5329],{},"That is the whole opt-in. Every ",[978,5310,4585],{},[978,5312,4625],{},[978,5314,4635],{}," then publishes an\n",[978,5317,5318],{},"EventMessage",[978,5320,5321],{},"EntityType: \"program\"",", the entity id, and\n",[978,5324,5325],{},"Headers[\"topic\"] = \"program.updated\""," — the same routing convention the\nbuilt-in domain modules use, so flows and ",[978,5328,2408],{}," consumers subscribe\nby topic name.",[3483,5331,5332,5345,5351],{},[1049,5333,5334,1012,5337,5340,5341,5344],{},[988,5335,5336],{},"No transport configured?",[978,5338,5339],{},"WithEvents(nil, …)"," is a no-op — wire\n",[978,5342,5343],{},"deps.EventBus"," unconditionally.",[1049,5346,5347,5350],{},[988,5348,5349],{},"Publish failures never fail the write."," The write already succeeded; the\nevent is best-effort.",[1049,5352,5353,5359],{},[988,5354,5355,5356,1004],{},"The payload is ",[978,5357,5358],{},"{entity_type, entity_id}"," Consumers that need the full\ndocument re-read it by id — by the time they run it may have changed again, so\nthe id is the only durable handle.",[974,5361,5362,5363,2229],{},"Typical consumer: a search module re-embedding exactly the document that changed\n(see ",[1014,5364,5365],{"href":249},"Search — keeping an index fresh",[1086,5367,5369,5370],{"id":5368},"per-user-singleton-usersingletont","Per-user singleton — ",[978,5371,5372],{},"UserSingleton[T]",[974,5374,5375,5376,5379,5380,5383],{},"\"Preferences\", \"profile\", \"settings for me\" and their relatives are the same\nresource shape everywhere: at most one document per user, every field defaulted,\nand a reset that returns to those defaults. Written as ordinary CRUD each time,\nthey pick up the same three bugs — a ",[978,5377,5378],{},"404"," for a user who has simply never saved,\na partial update that clobbers unset fields, and a reset that leaves a stale row\nbehind. ",[978,5381,5382],{},"crud.UserSingleton[T]"," is that shape, done once.",[1091,5385,5387],{"className":1093,"code":5386,"language":1095,"meta":1096,"style":1096},"type Prefs struct {\n    crud.BaseModel `bson:\",inline\"`\n    UserID      string `bson:\"user_id\"`\n    RestSeconds int    `bson:\"rest_seconds\"`\n    WeightUnit  string `bson:\"weight_unit\"`\n}\n\nfunc (p *Prefs) SetUserID(id string) { p.UserID = id }   \u002F\u002F satisfies crud.UserOwned\n\nfunc defaults(userID string) *Prefs {\n    return &Prefs{UserID: userID, RestSeconds: 90, WeightUnit: \"kg\"}\n}\n\nstore := crud.NewUserSingleton[*Prefs](db.Collection(\"prefs\"), defaults)\n_ = store.EnsureIndexes(ctx, \"prefs_user_unique\")   \u002F\u002F one row per user\n",[978,5388,5389,5394,5398,5403,5408,5413,5417,5421,5426,5430,5435,5440,5444,5448,5453],{"__ignoreMap":1096},[1100,5390,5391],{"class":1102,"line":10},[1100,5392,5393],{},"type Prefs struct {\n",[1100,5395,5396],{"class":1102,"line":16},[1100,5397,5054],{},[1100,5399,5400],{"class":1102,"line":22},[1100,5401,5402],{},"    UserID      string `bson:\"user_id\"`\n",[1100,5404,5405],{"class":1102,"line":51},[1100,5406,5407],{},"    RestSeconds int    `bson:\"rest_seconds\"`\n",[1100,5409,5410],{"class":1102,"line":57},[1100,5411,5412],{},"    WeightUnit  string `bson:\"weight_unit\"`\n",[1100,5414,5415],{"class":1102,"line":102},[1100,5416,1226],{},[1100,5418,5419],{"class":1102,"line":108},[1100,5420,1126],{"emptyLinePlaceholder":1125},[1100,5422,5423],{"class":1102,"line":114},[1100,5424,5425],{},"func (p *Prefs) SetUserID(id string) { p.UserID = id }   \u002F\u002F satisfies crud.UserOwned\n",[1100,5427,5428],{"class":1102,"line":120},[1100,5429,1126],{"emptyLinePlaceholder":1125},[1100,5431,5432],{"class":1102,"line":181},[1100,5433,5434],{},"func defaults(userID string) *Prefs {\n",[1100,5436,5437],{"class":1102,"line":186},[1100,5438,5439],{},"    return &Prefs{UserID: userID, RestSeconds: 90, WeightUnit: \"kg\"}\n",[1100,5441,5442],{"class":1102,"line":191},[1100,5443,1226],{},[1100,5445,5446],{"class":1102,"line":197},[1100,5447,1126],{"emptyLinePlaceholder":1125},[1100,5449,5450],{"class":1102,"line":203},[1100,5451,5452],{},"store := crud.NewUserSingleton[*Prefs](db.Collection(\"prefs\"), defaults)\n",[1100,5454,5455],{"class":1102,"line":214},[1100,5456,5457],{},"_ = store.EnsureIndexes(ctx, \"prefs_user_unique\")   \u002F\u002F one row per user\n",[974,5459,5460,5461,5467],{},"The defining behaviour is that ",[988,5462,5463,5466],{},[978,5464,5465],{},"Get"," never 404s",": a user who has never opened\nthe settings screen gets defaults, not an error, and the caller need not know the\ndifference.",[1091,5469,5471],{"className":1093,"code":5470,"language":1095,"meta":1096,"style":1096},"p, stored, _ := store.Get(ctx, userID)          \u002F\u002F stored=false → these are defaults\np, _         = store.Upsert(ctx, userID, bson.M{\"rest_seconds\": 45})  \u002F\u002F partial $set\np, _         = store.Reset(ctx, userID)         \u002F\u002F deletes the row, returns defaults\nok, _       := store.Delete(ctx, userID)        \u002F\u002F reports whether one existed\n",[978,5472,5473,5478,5483,5488],{"__ignoreMap":1096},[1100,5474,5475],{"class":1102,"line":10},[1100,5476,5477],{},"p, stored, _ := store.Get(ctx, userID)          \u002F\u002F stored=false → these are defaults\n",[1100,5479,5480],{"class":1102,"line":16},[1100,5481,5482],{},"p, _         = store.Upsert(ctx, userID, bson.M{\"rest_seconds\": 45})  \u002F\u002F partial $set\n",[1100,5484,5485],{"class":1102,"line":22},[1100,5486,5487],{},"p, _         = store.Reset(ctx, userID)         \u002F\u002F deletes the row, returns defaults\n",[1100,5489,5490],{"class":1102,"line":51},[1100,5491,5492],{},"ok, _       := store.Delete(ctx, userID)        \u002F\u002F reports whether one existed\n",[974,5494,5495,5498,5499,1012,5502,5504,5505,5508],{},[978,5496,5497],{},"Upsert"," seeds the document from ",[978,5500,5501],{},"defaults",[988,5503,2123],{}," applying the patch, so a\none-field save does not write zero values across every other field. ",[978,5506,5507],{},"Reset","\ndeletes rather than overwriting with defaults — a row full of default values is\nindistinguishable from a user who deliberately chose them, so a reset should\nleave no trace.",[1086,5510,5512],{"id":5511},"clickhouse-crud","ClickHouse CRUD",[974,5514,5515,5516,5519,5520,5523],{},"Generic CRUD for ClickHouse analytics tables. Uses ",[978,5517,5518],{},"ch"," struct tags for column mapping\n(falls back to lowercase field name). Requires the ",[978,5521,5522],{},"ClickHouseModel"," interface:",[1091,5525,5527],{"className":1093,"code":5526,"language":1095,"meta":1096,"style":1096},"import (\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fcrud\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fclickhouse\"\n)\n\ntype PageView struct {\n    Timestamp time.Time `ch:\"timestamp\"`\n    Domain    string    `ch:\"domain\"`\n    Path      string    `ch:\"path\"`\n    UserAgent string    `ch:\"user_agent\"`\n    Count     uint64    `ch:\"count\"`\n}\n\nfunc (p *PageView) TableName() string { return \"page_views\" }\n\nconn, _ := clickhouse.NewConn(cfg, \"analytics\", nil)\nc := crud.NewClickHouseCRUD(conn, func() *PageView { return &PageView{} })\n\n\u002F\u002F Single insert\nc.Create(ctx, &PageView{Domain: \"example.com\", Path: \"\u002F\", Count: 1})\n\n\u002F\u002F Bulk insert (preferred for ClickHouse)\nc.BulkCreate(ctx, views)  \u002F\u002F uses PrepareBatch + AppendStruct\n\n\u002F\u002F Read\nview, _ := c.Get(ctx, \"domain\", \"example.com\")\nviews, _ := c.List(ctx, crud.CHListParams{\n    Filters:  map[string]any{\"domain\": \"example.com\"},\n    OrderBy:  \"timestamp DESC\",\n    Limit:    100,\n})\ncount, _ := c.Count(ctx, map[string]any{\"domain\": \"example.com\"})\n\n\u002F\u002F Raw query\nrows, _ := c.Query(ctx, \"SELECT * FROM page_views WHERE domain = ?\", \"example.com\")\n",[978,5528,5529,5533,5538,5543,5547,5551,5556,5561,5566,5571,5576,5581,5585,5589,5594,5598,5603,5608,5612,5617,5622,5626,5631,5636,5641,5646,5651,5656,5662,5668,5674,5679,5685,5690,5696],{"__ignoreMap":1096},[1100,5530,5531],{"class":1102,"line":10},[1100,5532,1105],{},[1100,5534,5535],{"class":1102,"line":16},[1100,5536,5537],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fcrud\"\n",[1100,5539,5540],{"class":1102,"line":22},[1100,5541,5542],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fclickhouse\"\n",[1100,5544,5545],{"class":1102,"line":51},[1100,5546,1120],{},[1100,5548,5549],{"class":1102,"line":57},[1100,5550,1126],{"emptyLinePlaceholder":1125},[1100,5552,5553],{"class":1102,"line":102},[1100,5554,5555],{},"type PageView struct {\n",[1100,5557,5558],{"class":1102,"line":108},[1100,5559,5560],{},"    Timestamp time.Time `ch:\"timestamp\"`\n",[1100,5562,5563],{"class":1102,"line":114},[1100,5564,5565],{},"    Domain    string    `ch:\"domain\"`\n",[1100,5567,5568],{"class":1102,"line":120},[1100,5569,5570],{},"    Path      string    `ch:\"path\"`\n",[1100,5572,5573],{"class":1102,"line":181},[1100,5574,5575],{},"    UserAgent string    `ch:\"user_agent\"`\n",[1100,5577,5578],{"class":1102,"line":186},[1100,5579,5580],{},"    Count     uint64    `ch:\"count\"`\n",[1100,5582,5583],{"class":1102,"line":191},[1100,5584,1226],{},[1100,5586,5587],{"class":1102,"line":197},[1100,5588,1126],{"emptyLinePlaceholder":1125},[1100,5590,5591],{"class":1102,"line":203},[1100,5592,5593],{},"func (p *PageView) TableName() string { return \"page_views\" }\n",[1100,5595,5596],{"class":1102,"line":214},[1100,5597,1126],{"emptyLinePlaceholder":1125},[1100,5599,5600],{"class":1102,"line":223},[1100,5601,5602],{},"conn, _ := clickhouse.NewConn(cfg, \"analytics\", nil)\n",[1100,5604,5605],{"class":1102,"line":229},[1100,5606,5607],{},"c := crud.NewClickHouseCRUD(conn, func() *PageView { return &PageView{} })\n",[1100,5609,5610],{"class":1102,"line":234},[1100,5611,1126],{"emptyLinePlaceholder":1125},[1100,5613,5614],{"class":1102,"line":240},[1100,5615,5616],{},"\u002F\u002F Single insert\n",[1100,5618,5619],{"class":1102,"line":246},[1100,5620,5621],{},"c.Create(ctx, &PageView{Domain: \"example.com\", Path: \"\u002F\", Count: 1})\n",[1100,5623,5624],{"class":1102,"line":251},[1100,5625,1126],{"emptyLinePlaceholder":1125},[1100,5627,5628],{"class":1102,"line":257},[1100,5629,5630],{},"\u002F\u002F Bulk insert (preferred for ClickHouse)\n",[1100,5632,5633],{"class":1102,"line":1361},[1100,5634,5635],{},"c.BulkCreate(ctx, views)  \u002F\u002F uses PrepareBatch + AppendStruct\n",[1100,5637,5639],{"class":1102,"line":5638},24,[1100,5640,1126],{"emptyLinePlaceholder":1125},[1100,5642,5643],{"class":1102,"line":271},[1100,5644,5645],{},"\u002F\u002F Read\n",[1100,5647,5648],{"class":1102,"line":277},[1100,5649,5650],{},"view, _ := c.Get(ctx, \"domain\", \"example.com\")\n",[1100,5652,5653],{"class":1102,"line":282},[1100,5654,5655],{},"views, _ := c.List(ctx, crud.CHListParams{\n",[1100,5657,5659],{"class":1102,"line":5658},28,[1100,5660,5661],{},"    Filters:  map[string]any{\"domain\": \"example.com\"},\n",[1100,5663,5665],{"class":1102,"line":5664},29,[1100,5666,5667],{},"    OrderBy:  \"timestamp DESC\",\n",[1100,5669,5671],{"class":1102,"line":5670},30,[1100,5672,5673],{},"    Limit:    100,\n",[1100,5675,5677],{"class":1102,"line":5676},31,[1100,5678,2936],{},[1100,5680,5682],{"class":1102,"line":5681},32,[1100,5683,5684],{},"count, _ := c.Count(ctx, map[string]any{\"domain\": \"example.com\"})\n",[1100,5686,5688],{"class":1102,"line":5687},33,[1100,5689,1126],{"emptyLinePlaceholder":1125},[1100,5691,5693],{"class":1102,"line":5692},34,[1100,5694,5695],{},"\u002F\u002F Raw query\n",[1100,5697,5699],{"class":1102,"line":5698},35,[1100,5700,5701],{},"rows, _ := c.Query(ctx, \"SELECT * FROM page_views WHERE domain = ?\", \"example.com\")\n",[974,5703,5704,5705,1068,5707,1068,5710,1068,5712,1068,5715,1068,5717,1068,5720,1004],{},"Available methods: ",[978,5706,4585],{},[978,5708,5709],{},"BulkCreate",[978,5711,5465],{},[978,5713,5714],{},"GetByField",[978,5716,4615],{},[978,5718,5719],{},"Count",[978,5721,5722],{},"Query",[1086,5724,5726],{"id":5725},"clickhouse-batch-writer","ClickHouse batch writer",[974,5728,5729,5730,5733,5734,5737],{},"For high-throughput scenarios (e.g. Kafka consumers writing analytics), the batch writer\nqueues items in memory and flushes in bulk — either when ",[978,5731,5732],{},"batchSize"," rows accumulate\nor ",[978,5735,5736],{},"flushInterval"," elapses, whichever comes first. Thread-safe.",[1091,5739,5741],{"className":1093,"code":5740,"language":1095,"meta":1096,"style":1096},"\u002F\u002F Backed by a ClickHouseCRUD\nwriter := crud.BatchWriterFor(c,\n    crud.WithBatchSize(500),\n    crud.WithFlushInterval(5 * time.Second),\n    crud.WithBatchName(\"pageviews\"),\n)\n\n\u002F\u002F From any goroutine — non-blocking\nwriter.Enqueue(&PageView{Domain: \"example.com\", Path: \"\u002F\"})\n\n\u002F\u002F On shutdown — drains remaining items\nwriter.Stop()\n",[978,5742,5743,5748,5753,5758,5763,5768,5772,5776,5781,5786,5790,5795],{"__ignoreMap":1096},[1100,5744,5745],{"class":1102,"line":10},[1100,5746,5747],{},"\u002F\u002F Backed by a ClickHouseCRUD\n",[1100,5749,5750],{"class":1102,"line":16},[1100,5751,5752],{},"writer := crud.BatchWriterFor(c,\n",[1100,5754,5755],{"class":1102,"line":22},[1100,5756,5757],{},"    crud.WithBatchSize(500),\n",[1100,5759,5760],{"class":1102,"line":51},[1100,5761,5762],{},"    crud.WithFlushInterval(5 * time.Second),\n",[1100,5764,5765],{"class":1102,"line":57},[1100,5766,5767],{},"    crud.WithBatchName(\"pageviews\"),\n",[1100,5769,5770],{"class":1102,"line":102},[1100,5771,1120],{},[1100,5773,5774],{"class":1102,"line":108},[1100,5775,1126],{"emptyLinePlaceholder":1125},[1100,5777,5778],{"class":1102,"line":114},[1100,5779,5780],{},"\u002F\u002F From any goroutine — non-blocking\n",[1100,5782,5783],{"class":1102,"line":120},[1100,5784,5785],{},"writer.Enqueue(&PageView{Domain: \"example.com\", Path: \"\u002F\"})\n",[1100,5787,5788],{"class":1102,"line":181},[1100,5789,1126],{"emptyLinePlaceholder":1125},[1100,5791,5792],{"class":1102,"line":186},[1100,5793,5794],{},"\u002F\u002F On shutdown — drains remaining items\n",[1100,5796,5797],{"class":1102,"line":191},[1100,5798,5799],{},"writer.Stop()\n",[974,5801,5802],{},"You can also supply a custom flush function for non-CRUD use cases:",[1091,5804,5806],{"className":1093,"code":5805,"language":1095,"meta":1096,"style":1096},"writer := crud.NewClickHouseBatchWriter(func(ctx context.Context, items []*PageView) (int, error) {\n    \u002F\u002F custom bulk insert logic\n    return len(items), nil\n}, crud.WithBatchSize(1000))\n",[978,5807,5808,5813,5818,5823],{"__ignoreMap":1096},[1100,5809,5810],{"class":1102,"line":10},[1100,5811,5812],{},"writer := crud.NewClickHouseBatchWriter(func(ctx context.Context, items []*PageView) (int, error) {\n",[1100,5814,5815],{"class":1102,"line":16},[1100,5816,5817],{},"    \u002F\u002F custom bulk insert logic\n",[1100,5819,5820],{"class":1102,"line":22},[1100,5821,5822],{},"    return len(items), nil\n",[1100,5824,5825],{"class":1102,"line":51},[1100,5826,5827],{},"}, crud.WithBatchSize(1000))\n",[1030,5829,5831],{"id":5830},"openapi-30-spec-generation","OpenAPI 3.0 spec generation",[974,5833,1581,5834,5837,5838,5841],{},[978,5835,5836],{},"openapi"," package generates a full OpenAPI 3.0.3 specification from module routes,\nincluding typed parameters, request\u002Fresponse schemas via ",[978,5839,5840],{},"$ref",", validation constraints,\nand configurable security schemes.",[1086,5843,5845],{"id":5844},"automatic-spec-ui-via-openapi-module","Automatic spec + UI (via openapi module)",[974,5847,1581,5848,5850],{},[978,5849,5836],{}," built-in module serves the spec and an interactive API reference UI.\nNo code required — blank-import it:",[1091,5852,5854],{"className":1093,"code":5853,"language":1095,"meta":1096,"style":1096},"import _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fopenapi\"\n",[978,5855,5856],{"__ignoreMap":1096},[1100,5857,5858],{"class":1102,"line":10},[1100,5859,5853],{},[974,5861,5862],{},"This adds:",[1389,5864,5865,5873],{},[1392,5866,5867],{},[1395,5868,5869,5871],{},[1398,5870,3168],{},[1398,5872,1406],{},[1408,5874,5875,5884],{},[1395,5876,5877,5881],{},[1413,5878,5879],{},[978,5880,1821],{},[1413,5882,5883],{},"Full OpenAPI 3.0.3 spec (JSON)",[1395,5885,5886,5890],{},[1413,5887,5888],{},[978,5889,1825],{},[1413,5891,5892],{},"Interactive API docs UI",[1086,5894,5896],{"id":5895},"ui-provider-selection","UI provider selection",[1389,5898,5899,5911],{},[1392,5900,5901],{},[1395,5902,5903,5908],{},[1398,5904,5905],{},[978,5906,5907],{},"OPENAPI_UI",[1398,5909,5910],{},"UI",[1408,5912,5913,5928,5938,5948],{},[1395,5914,5915,5921],{},[1413,5916,5917,5920],{},[978,5918,5919],{},"scalar"," (default)",[1413,5922,5923],{},[1014,5924,5927],{"href":5925,"rel":5926},"https:\u002F\u002Fscalar.com",[3096],"Scalar",[1395,5929,5930,5935],{},[1413,5931,5932],{},[978,5933,5934],{},"swagger",[1413,5936,5937],{},"Swagger UI with OAuth2 Authorize popup",[1395,5939,5940,5945],{},[1413,5941,5942],{},[978,5943,5944],{},"redoc",[1413,5946,5947],{},"ReDoc",[1395,5949,5950,5955],{},[1413,5951,5952],{},[978,5953,5954],{},"disabled",[1413,5956,5957],{},"No UI",[1086,5959,5961],{"id":5960},"openapi-environment-variables","OpenAPI environment variables",[1389,5963,5964,5974],{},[1392,5965,5966],{},[1395,5967,5968,5970,5972],{},[1398,5969,1400],{},[1398,5971,1403],{},[1398,5973,1406],{},[1408,5975,5976,5989,6003],{},[1395,5977,5978,5982,5986],{},[1413,5979,5980],{},[978,5981,5907],{},[1413,5983,5984],{},[978,5985,5919],{},[1413,5987,5988],{},"UI provider",[1395,5990,5991,5996,6000],{},[1413,5992,5993],{},[978,5994,5995],{},"OPENAPI_SPEC_PATH",[1413,5997,5998],{},[978,5999,1821],{},[1413,6001,6002],{},"Spec endpoint path",[1395,6004,6005,6010,6014],{},[1413,6006,6007],{},[978,6008,6009],{},"OPENAPI_UI_PATH",[1413,6011,6012],{},[978,6013,1825],{},[1413,6015,6016],{},"UI endpoint path",[1086,6018,6020],{"id":6019},"endpoint-metadata-functional-options","Endpoint metadata (functional options)",[974,6022,6023,6024,1044],{},"Routes declare OpenAPI metadata via functional options on ",[978,6025,6026],{},"Router.Handle()",[1091,6028,6030],{"className":1093,"code":6029,"language":1095,"meta":1096,"style":1096},"func (m *Module) Routes(router modules.Router) {\n    router.Group(\"\u002Fusers\", func(r modules.Router) {\n        r.Handle(\"POST\", \"\u002F\", http.HandlerFunc(m.handleCreate),\n            modules.Summary(\"Create user\"),\n            modules.Description(\"Create a new user. Requires superuser access.\"),\n            modules.Body(UserCreateInput{}),\n            modules.Response(201, \"User created\", UserResponse{}),\n            modules.Response(400, \"Invalid request body\", modules.RedelayErrorResponse{}),\n            modules.Response(409, \"User already exists\", modules.RedelayErrorResponse{}),\n            modules.Security(\"BearerAuth\"),\n        )\n    })\n}\n",[978,6031,6032,6037,6042,6047,6052,6057,6062,6067,6072,6077,6082,6087,6091],{"__ignoreMap":1096},[1100,6033,6034],{"class":1102,"line":10},[1100,6035,6036],{},"func (m *Module) Routes(router modules.Router) {\n",[1100,6038,6039],{"class":1102,"line":16},[1100,6040,6041],{},"    router.Group(\"\u002Fusers\", func(r modules.Router) {\n",[1100,6043,6044],{"class":1102,"line":22},[1100,6045,6046],{},"        r.Handle(\"POST\", \"\u002F\", http.HandlerFunc(m.handleCreate),\n",[1100,6048,6049],{"class":1102,"line":51},[1100,6050,6051],{},"            modules.Summary(\"Create user\"),\n",[1100,6053,6054],{"class":1102,"line":57},[1100,6055,6056],{},"            modules.Description(\"Create a new user. Requires superuser access.\"),\n",[1100,6058,6059],{"class":1102,"line":102},[1100,6060,6061],{},"            modules.Body(UserCreateInput{}),\n",[1100,6063,6064],{"class":1102,"line":108},[1100,6065,6066],{},"            modules.Response(201, \"User created\", UserResponse{}),\n",[1100,6068,6069],{"class":1102,"line":114},[1100,6070,6071],{},"            modules.Response(400, \"Invalid request body\", modules.RedelayErrorResponse{}),\n",[1100,6073,6074],{"class":1102,"line":120},[1100,6075,6076],{},"            modules.Response(409, \"User already exists\", modules.RedelayErrorResponse{}),\n",[1100,6078,6079],{"class":1102,"line":181},[1100,6080,6081],{},"            modules.Security(\"BearerAuth\"),\n",[1100,6083,6084],{"class":1102,"line":186},[1100,6085,6086],{},"        )\n",[1100,6088,6089],{"class":1102,"line":191},[1100,6090,2710],{},[1100,6092,6093],{"class":1102,"line":197},[1100,6094,1226],{},[974,6096,6097],{},[988,6098,6099],{},"Available options:",[1389,6101,6102,6111],{},[1392,6103,6104],{},[1395,6105,6106,6109],{},[1398,6107,6108],{},"Option",[1398,6110,1406],{},[1408,6112,6113,6123,6133,6143,6153,6163,6173,6183,6193],{},[1395,6114,6115,6120],{},[1413,6116,6117],{},[978,6118,6119],{},"Summary(s)",[1413,6121,6122],{},"Operation summary",[1395,6124,6125,6130],{},[1413,6126,6127],{},[978,6128,6129],{},"Description(s)",[1413,6131,6132],{},"Operation description",[1395,6134,6135,6140],{},[1413,6136,6137],{},[978,6138,6139],{},"Tags(t...)",[1413,6141,6142],{},"Additional tags",[1395,6144,6145,6150],{},[1413,6146,6147],{},[978,6148,6149],{},"PathParam(name, desc, type?, format?)",[1413,6151,6152],{},"Path parameter (auto-required)",[1395,6154,6155,6160],{},[1413,6156,6157],{},[978,6158,6159],{},"QueryParam(name, type, desc, default?)",[1413,6161,6162],{},"Query parameter",[1395,6164,6165,6170],{},[1413,6166,6167],{},[978,6168,6169],{},"Body(model, desc?)",[1413,6171,6172],{},"Request body from Go struct",[1395,6174,6175,6180],{},[1413,6176,6177],{},[978,6178,6179],{},"Response(status, desc, model?)",[1413,6181,6182],{},"Response with optional schema",[1395,6184,6185,6190],{},[1413,6186,6187],{},[978,6188,6189],{},"Security(scheme, scopes...)",[1413,6191,6192],{},"Security requirement",[1395,6194,6195,6200],{},[1413,6196,6197],{},[978,6198,6199],{},"EndpointDeprecated()",[1413,6201,6202],{},"Mark as deprecated",[1086,6204,6206],{"id":6205},"schema-generation","Schema generation",[974,6208,6209],{},"Go structs are reflected into JSON Schema automatically:",[3483,6211,6212,6219,6229,6237,6250,6259,6267,6275,6284],{},[1049,6213,6214,6215,6218],{},"Struct field names from ",[978,6216,6217],{},"json:\"...\""," tags",[1049,6220,6221,6224,6225,6228],{},[978,6222,6223],{},"validate:\"required\""," → JSON Schema ",[978,6226,6227],{},"required"," array",[1049,6230,6231,3178,6234],{},[978,6232,6233],{},"validate:\"email\"",[978,6235,6236],{},"format: \"email\"",[1049,6238,6239,3178,6242,6245,6246,6249],{},[978,6240,6241],{},"validate:\"min=8\"",[978,6243,6244],{},"minLength: 8"," (strings) or ",[978,6247,6248],{},"minimum: 8"," (numbers)",[1049,6251,6252,6255,6256],{},[978,6253,6254],{},"*T"," pointer fields → ",[978,6257,6258],{},"nullable: true",[1049,6260,6261,3178,6264],{},[978,6262,6263],{},"time.Time",[978,6265,6266],{},"format: \"date-time\"",[1049,6268,6269,3178,6272],{},[978,6270,6271],{},"primitive.ObjectID",[978,6273,6274],{},"format: \"objectid\"",[1049,6276,6277,3178,6280,6283],{},[978,6278,6279],{},"[]T",[978,6281,6282],{},"type: \"array\""," with typed items",[1049,6285,6286],{},"Embedded structs merge properties",[974,6288,6289,6290,6293,6294,1004],{},"Named structs are registered in ",[978,6291,6292],{},"components\u002Fschemas"," and referenced via ",[978,6295,5840],{},[1086,6297,6299],{"id":6298},"security-schemes","Security schemes",[1091,6301,6303],{"className":1093,"code":6302,"language":1095,"meta":1096,"style":1096},"openapi.GenerateSpec(title, version, registry,\n    openapi.WithBearerAuth(),                                    \u002F\u002F HTTP Bearer\u002FJWT\n    openapi.WithOAuth2PasswordFlow(\"\u002Fapi\u002Fv1\u002Fauth\u002Flogin\", nil),   \u002F\u002F OAuth2 password flow\n)\n",[978,6304,6305,6310,6315,6320],{"__ignoreMap":1096},[1100,6306,6307],{"class":1102,"line":10},[1100,6308,6309],{},"openapi.GenerateSpec(title, version, registry,\n",[1100,6311,6312],{"class":1102,"line":16},[1100,6313,6314],{},"    openapi.WithBearerAuth(),                                    \u002F\u002F HTTP Bearer\u002FJWT\n",[1100,6316,6317],{"class":1102,"line":22},[1100,6318,6319],{},"    openapi.WithOAuth2PasswordFlow(\"\u002Fapi\u002Fv1\u002Fauth\u002Flogin\", nil),   \u002F\u002F OAuth2 password flow\n",[1100,6321,6322],{"class":1102,"line":51},[1100,6323,1120],{},[974,6325,6326],{},"The OAuth2 password flow enables Swagger UI's \"Authorize\" popup with username\u002Fpassword fields.",[1086,6328,6330],{"id":6329},"schema-naming-convention","Schema naming convention",[974,6332,6333],{},"All API schemas use module-prefixed names to avoid collisions across modules:",[1389,6335,6336,6346],{},[1392,6337,6338],{},[1395,6339,6340,6342,6344],{},[1398,6341,3886],{},[1398,6343,2813],{},[1398,6345,3357],{},[1408,6347,6348,6365,6377,6387,6397,6407,6418,6429,6439,6450,6461],{},[1395,6349,6350,6355,6358],{},[1413,6351,6352],{},[978,6353,6354],{},"RedelayErrorResponse",[1413,6356,6357],{},"core",[1413,6359,6360,6361,6364],{},"Standard ",[978,6362,6363],{},"{\"detail\": \"...\"}"," error body",[1395,6366,6367,6372,6374],{},[1413,6368,6369],{},[978,6370,6371],{},"HealthCheckResponse",[1413,6373,4801],{},[1413,6375,6376],{},"Health check response",[1395,6378,6379,6383,6385],{},[1413,6380,6381],{},[978,6382,3897],{},[1413,6384,3539],{},[1413,6386,3900],{},[1395,6388,6389,6393,6395],{},[1413,6390,6391],{},[978,6392,3907],{},[1413,6394,3539],{},[1413,6396,3910],{},[1395,6398,6399,6403,6405],{},[1413,6400,6401],{},[978,6402,3917],{},[1413,6404,3539],{},[1413,6406,3920],{},[1395,6408,6409,6413,6415],{},[1413,6410,6411],{},[978,6412,3927],{},[1413,6414,3539],{},[1413,6416,6417],{},"JWT token pair response",[1395,6419,6420,6424,6426],{},[1413,6421,6422],{},[978,6423,3937],{},[1413,6425,3539],{},[1413,6427,6428],{},"Revoke confirmation response",[1395,6430,6431,6435,6437],{},[1413,6432,6433],{},[978,6434,4673],{},[1413,6436,3303],{},[1413,6438,4676],{},[1395,6440,6441,6445,6447],{},[1413,6442,6443],{},[978,6444,4683],{},[1413,6446,3303],{},[1413,6448,6449],{},"Partial update request body",[1395,6451,6452,6456,6458],{},[1413,6453,6454],{},[978,6455,4693],{},[1413,6457,3303],{},[1413,6459,6460],{},"User API response (no sensitive fields)",[1395,6462,6463,6467,6469],{},[1413,6464,6465],{},[978,6466,4703],{},[1413,6468,3303],{},[1413,6470,6471],{},"Paginated user list response",[1030,6473,6475],{"id":6474},"mcp-server","MCP server",[974,6477,6478,6479,6481,6482,6484,6485,6488,6489,6492],{},"Automatically mounted at ",[978,6480,1831],{}," by the ",[978,6483,1584],{}," package. Implements the ",[988,6486,6487],{},"MCP Streamable HTTP transport"," (protocol version ",[978,6490,6491],{},"2025-03-26",") — compatible with VS Code, Claude Desktop, Cursor, and other MCP clients.",[974,6494,6495],{},"For manual use:",[1091,6497,6499],{"className":1093,"code":6498,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\u002Fmcp\"\n\nserver := mcp.NewServer()\nserver.DiscoverFrom(registry) \u002F\u002F auto-discovers MCPProvider modules + routes\nrouter.Mount(\"\u002Fmcp\", server.Handler())\n",[978,6500,6501,6506,6510,6515,6520],{"__ignoreMap":1096},[1100,6502,6503],{"class":1102,"line":10},[1100,6504,6505],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\u002Fmcp\"\n",[1100,6507,6508],{"class":1102,"line":16},[1100,6509,1126],{"emptyLinePlaceholder":1125},[1100,6511,6512],{"class":1102,"line":22},[1100,6513,6514],{},"server := mcp.NewServer()\n",[1100,6516,6517],{"class":1102,"line":51},[1100,6518,6519],{},"server.DiscoverFrom(registry) \u002F\u002F auto-discovers MCPProvider modules + routes\n",[1100,6521,6522],{"class":1102,"line":57},[1100,6523,6524],{},"router.Mount(\"\u002Fmcp\", server.Handler())\n",[1086,6526,6528],{"id":6527},"protocol-support","Protocol support",[974,6530,6531],{},"The MCP server handles the full MCP lifecycle:",[1389,6533,6534,6542],{},[1392,6535,6536],{},[1395,6537,6538,6540],{},[1398,6539,1598],{},[1398,6541,1406],{},[1408,6543,6544,6554,6564,6576,6586,6596,6606],{},[1395,6545,6546,6551],{},[1413,6547,6548],{},[978,6549,6550],{},"initialize",[1413,6552,6553],{},"Returns protocol version, server capabilities (tools, resources), and server info",[1395,6555,6556,6561],{},[1413,6557,6558],{},[978,6559,6560],{},"notifications\u002Finitialized",[1413,6562,6563],{},"Accepted silently (202)",[1395,6565,6566,6571],{},[1413,6567,6568],{},[978,6569,6570],{},"ping",[1413,6572,1612,6573],{},[978,6574,6575],{},"{}",[1395,6577,6578,6583],{},[1413,6579,6580],{},[978,6581,6582],{},"tools\u002Flist",[1413,6584,6585],{},"Lists all discovered tools",[1395,6587,6588,6593],{},[1413,6589,6590],{},[978,6591,6592],{},"tools\u002Fcall",[1413,6594,6595],{},"Invokes a tool; returns MCP content blocks",[1395,6597,6598,6603],{},[1413,6599,6600],{},[978,6601,6602],{},"resources\u002Flist",[1413,6604,6605],{},"Lists all discovered resources",[1395,6607,6608,6613],{},[1413,6609,6610],{},[978,6611,6612],{},"resources\u002Fread",[1413,6614,6615],{},"Reads a resource; returns MCP contents array",[1086,6617,6619],{"id":6618},"ide-setup","IDE setup",[974,6621,6622,6623,1044],{},"Add to your ",[978,6624,6625],{},".vscode\u002Fmcp.json",[1091,6627,6631],{"className":6628,"code":6629,"language":6630,"meta":1096,"style":1096},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"servers\": {\n    \"my-app\": {\n      \"type\": \"http\",\n      \"url\": \"http:\u002F\u002Flocalhost:8000\u002Fmcp\"\n    }\n  }\n}\n","json",[978,6632,6633,6639,6656,6671,6695,6714,6718,6723],{"__ignoreMap":1096},[1100,6634,6635],{"class":1102,"line":10},[1100,6636,6638],{"class":6637},"sMK4o","{\n",[1100,6640,6641,6644,6648,6651,6653],{"class":1102,"line":16},[1100,6642,6643],{"class":6637},"  \"",[1100,6645,6647],{"class":6646},"spNyl","servers",[1100,6649,6650],{"class":6637},"\"",[1100,6652,1044],{"class":6637},[1100,6654,6655],{"class":6637}," {\n",[1100,6657,6658,6661,6665,6667,6669],{"class":1102,"line":22},[1100,6659,6660],{"class":6637},"    \"",[1100,6662,6664],{"class":6663},"sBMFI","my-app",[1100,6666,6650],{"class":6637},[1100,6668,1044],{"class":6637},[1100,6670,6655],{"class":6637},[1100,6672,6673,6676,6680,6682,6684,6687,6691,6693],{"class":1102,"line":51},[1100,6674,6675],{"class":6637},"      \"",[1100,6677,6679],{"class":6678},"sbssI","type",[1100,6681,6650],{"class":6637},[1100,6683,1044],{"class":6637},[1100,6685,6686],{"class":6637}," \"",[1100,6688,6690],{"class":6689},"sfazB","http",[1100,6692,6650],{"class":6637},[1100,6694,2190],{"class":6637},[1100,6696,6697,6699,6702,6704,6706,6708,6711],{"class":1102,"line":57},[1100,6698,6675],{"class":6637},[1100,6700,6701],{"class":6678},"url",[1100,6703,6650],{"class":6637},[1100,6705,1044],{"class":6637},[1100,6707,6686],{"class":6637},[1100,6709,6710],{"class":6689},"http:\u002F\u002Flocalhost:8000\u002Fmcp",[1100,6712,6713],{"class":6637},"\"\n",[1100,6715,6716],{"class":1102,"line":102},[1100,6717,2467],{"class":6637},[1100,6719,6720],{"class":1102,"line":108},[1100,6721,6722],{"class":6637},"  }\n",[1100,6724,6725],{"class":1102,"line":114},[1100,6726,1226],{"class":6637},[974,6728,6729],{},"Once connected, AI assistants can discover and invoke all registered tools and resources.",[1086,6731,6733],{"id":6732},"built-in-introspection-tools","Built-in introspection tools",[974,6735,6736,6739],{},[978,6737,6738],{},"DiscoverFrom"," installs a family of introspection tools for AI assistants\n(Claude, Cursor, Copilot, etc.) to reason about the running application.\nAll tools read live from the module registry — no caching.",[1389,6741,6742,6751],{},[1392,6743,6744],{},[1395,6745,6746,6749],{},[1398,6747,6748],{},"Tool",[1398,6750,3357],{},[1408,6752,6753,6763,6773,6793,6810,6832,6845,6855,6865],{},[1395,6754,6755,6760],{},[1413,6756,6757],{},[978,6758,6759],{},"list_modules",[1413,6761,6762],{},"Summary of every registered module with counts (events, routes, FlowDSL nodes, entities, packets).",[1395,6764,6765,6770],{},[1413,6766,6767],{},[978,6768,6769],{},"describe_module",[1413,6771,6772],{},"Full IR for a single module by name: events, packets, entities, routes, FlowDSL nodes, settings schema, external docs.",[1395,6774,6775,6780],{},[1413,6776,6777],{},[978,6778,6779],{},"list_flowdsl_nodes",[1413,6781,6782,6783,6786,6787,4257,6790,1004],{},"All FlowDSL nodes from parent and ",[978,6784,6785],{},"-flowdsl"," companion modules; optional filter by ",[978,6788,6789],{},"kind",[978,6791,6792],{},"module",[1395,6794,6795,6800],{},[1413,6796,6797],{},[978,6798,6799],{},"describe_flowdsl_node",[1413,6801,6802,6803,6806,6807,6809],{},"Full node spec: settings schema (with ",[978,6804,6805],{},"x-ui"," hints), named ports with ",[978,6808,5840],{}," schemas, handler reference, tags.",[1395,6811,6812,6817],{},[1413,6813,6814],{},[978,6815,6816],{},"list_events",[1413,6818,6819,6820,1068,6823,1068,6826,1068,6829,6831],{},"Every event with ",[978,6821,6822],{},"entity_type",[978,6824,6825],{},"action",[978,6827,6828],{},"topic",[978,6830,6792],{},", payload type.",[1395,6833,6834,6839],{},[1413,6835,6836],{},[978,6837,6838],{},"describe_event",[1413,6840,6841,6842,1004],{},"Full event definition including inline payload schema fields or ",[978,6843,6844],{},"payload_ref",[1395,6846,6847,6852],{},[1413,6848,6849],{},[978,6850,6851],{},"list_packets",[1413,6853,6854],{},"Standalone packet \u002F message-body definitions.",[1395,6856,6857,6862],{},[1413,6858,6859],{},[978,6860,6861],{},"describe_packet",[1413,6863,6864],{},"Packet fields with types and required flags.",[1395,6866,6867,6872],{},[1413,6868,6869],{},[978,6870,6871],{},"search_redelay",[1413,6873,6874],{},"Case-insensitive substring search across modules, nodes, events, and packets.",[1086,6876,6878],{"id":6877},"developer-tools","Developer tools",[974,6880,6881],{},"Additional tools for building and validating modules and flows:",[1389,6883,6884,6892],{},[1392,6885,6886],{},[1395,6887,6888,6890],{},[1398,6889,6748],{},[1398,6891,3357],{},[1408,6893,6894,6904,6914,6924,6934],{},[1395,6895,6896,6901],{},[1413,6897,6898],{},[978,6899,6900],{},"validate_flowdsl",[1413,6902,6903],{},"Parse and validate FlowDSL YAML; returns diagnostics with error codes (FDL001–FDL062).",[1395,6905,6906,6911],{},[1413,6907,6908],{},[978,6909,6910],{},"validate_module",[1413,6912,6913],{},"Validate module YAML against naming conventions and best practices (MV001–MV072).",[1395,6915,6916,6921],{},[1413,6917,6918],{},[978,6919,6920],{},"scaffold_module",[1413,6922,6923],{},"Generate Go module code (module.go, routes.go, handlers.go) from a module YAML definition. Returns file contents — does not write to disk.",[1395,6925,6926,6931],{},[1413,6927,6928],{},[978,6929,6930],{},"list_api_routes",[1413,6932,6933],{},"Detailed HTTP route listing with request body types, security schemes, and operation IDs. Optional module filter.",[1395,6935,6936,6941],{},[1413,6937,6938],{},[978,6939,6940],{},"explain_flowdsl_node",[1413,6942,6943],{},"Human-friendly node explanation: settings with types\u002Fdefaults\u002Fenums, port wiring, and a usage YAML snippet.",[1086,6945,6947],{"id":6946},"module-contributed-tools","Module-contributed tools",[974,6949,6950,6951,6954,6955,1004],{},"Any module can contribute additional MCP tools by implementing the ",[978,6952,6953],{},"MCPProvider"," interface. These are discovered automatically by ",[978,6956,6738],{},[974,6958,6959,6960,6962],{},"For example, the ",[978,6961,3310],{}," addon module contributes:",[1389,6964,6965,6973],{},[1392,6966,6967],{},[1395,6968,6969,6971],{},[1398,6970,6748],{},[1398,6972,3357],{},[1408,6974,6975,6985,6995],{},[1395,6976,6977,6982],{},[1413,6978,6979],{},[978,6980,6981],{},"list_flows",[1413,6983,6984],{},"List all stored FlowDSL flows with metadata and published version.",[1395,6986,6987,6992],{},[1413,6988,6989],{},[978,6990,6991],{},"get_flow",[1413,6993,6994],{},"Get a flow with its published version's FlowDSL document.",[1395,6996,6997,7002],{},[1413,6998,6999],{},[978,7000,7001],{},"list_node_handlers",[1413,7003,7004],{},"List all registered runtime node handlers — validates that flow nodes have matching handlers.",[974,7006,7007,7008,7011],{},"See ",[1014,7009,6953],{"href":7010},"#mcpprovider"," for how to add tools from your own modules.",[1086,7013,7015],{"id":7014},"built-in-resources","Built-in resources",[1389,7017,7018,7027],{},[1392,7019,7020],{},[1395,7021,7022,7025],{},[1398,7023,7024],{},"URI",[1398,7026,1406],{},[1408,7028,7029,7047],{},[1395,7030,7031,7036],{},[1413,7032,7033],{},[978,7034,7035],{},"redelay:\u002F\u002Fmodules.json",[1413,7037,7038,7039,7042,7043,7046],{},"Live module manifest — same shape as the ",[978,7040,7041],{},"\u002Fmodules.json"," HTTP endpoint consumed by the ",[978,7044,7045],{},"\u002Fmodules"," UI.",[1395,7048,7049,7054],{},[1413,7050,7051],{},[978,7052,7053],{},"openapi:\u002F\u002Fspec",[1413,7055,7056],{},"OpenAPI 3.0 spec for all registered HTTP routes.",[1086,7058,6953],{"id":7059},"mcpprovider",[974,7061,7062,7063,1044],{},"Modules expose tools and resources to the MCP server by implementing ",[978,7064,6953],{},[1091,7066,7068],{"className":1093,"code":7067,"language":1095,"meta":1096,"style":1096},"type MCPProvider interface {\n    MCPTools() []MCPTool\n    MCPResources() []MCPResource\n}\n",[978,7069,7070,7075,7080,7085],{"__ignoreMap":1096},[1100,7071,7072],{"class":1102,"line":10},[1100,7073,7074],{},"type MCPProvider interface {\n",[1100,7076,7077],{"class":1102,"line":16},[1100,7078,7079],{},"    MCPTools() []MCPTool\n",[1100,7081,7082],{"class":1102,"line":22},[1100,7083,7084],{},"    MCPResources() []MCPResource\n",[1100,7086,7087],{"class":1102,"line":51},[1100,7088,1226],{},[974,7090,7091],{},"Example — exposing a search tool from a custom module:",[1091,7093,7095],{"className":1093,"code":7094,"language":1095,"meta":1096,"style":1096},"func (m *Module) MCPTools() []modules.MCPTool {\n    return []modules.MCPTool{\n        {\n            Name:        \"search_products\",\n            Description: \"Search the product catalog by name or category\",\n            InputSchema: map[string]any{\n                \"type\":     \"object\",\n                \"required\": []string{\"query\"},\n                \"properties\": map[string]any{\n                    \"query\":    map[string]any{\"type\": \"string\", \"description\": \"Search term\"},\n                    \"category\": map[string]any{\"type\": \"string\", \"description\": \"Filter by category\"},\n                },\n            },\n            Handler: func(ctx context.Context, args map[string]any) (any, error) {\n                query, _ := args[\"query\"].(string)\n                return m.service.Search(ctx, query)\n            },\n        },\n    }\n}\n\nfunc (m *Module) MCPResources() []modules.MCPResource { return nil }\n",[978,7096,7097,7102,7107,7111,7116,7121,7126,7131,7136,7141,7146,7151,7156,7161,7166,7171,7176,7180,7184,7188,7192,7196],{"__ignoreMap":1096},[1100,7098,7099],{"class":1102,"line":10},[1100,7100,7101],{},"func (m *Module) MCPTools() []modules.MCPTool {\n",[1100,7103,7104],{"class":1102,"line":16},[1100,7105,7106],{},"    return []modules.MCPTool{\n",[1100,7108,7109],{"class":1102,"line":22},[1100,7110,2490],{},[1100,7112,7113],{"class":1102,"line":51},[1100,7114,7115],{},"            Name:        \"search_products\",\n",[1100,7117,7118],{"class":1102,"line":57},[1100,7119,7120],{},"            Description: \"Search the product catalog by name or category\",\n",[1100,7122,7123],{"class":1102,"line":102},[1100,7124,7125],{},"            InputSchema: map[string]any{\n",[1100,7127,7128],{"class":1102,"line":108},[1100,7129,7130],{},"                \"type\":     \"object\",\n",[1100,7132,7133],{"class":1102,"line":114},[1100,7134,7135],{},"                \"required\": []string{\"query\"},\n",[1100,7137,7138],{"class":1102,"line":120},[1100,7139,7140],{},"                \"properties\": map[string]any{\n",[1100,7142,7143],{"class":1102,"line":181},[1100,7144,7145],{},"                    \"query\":    map[string]any{\"type\": \"string\", \"description\": \"Search term\"},\n",[1100,7147,7148],{"class":1102,"line":186},[1100,7149,7150],{},"                    \"category\": map[string]any{\"type\": \"string\", \"description\": \"Filter by category\"},\n",[1100,7152,7153],{"class":1102,"line":191},[1100,7154,7155],{},"                },\n",[1100,7157,7158],{"class":1102,"line":197},[1100,7159,7160],{},"            },\n",[1100,7162,7163],{"class":1102,"line":203},[1100,7164,7165],{},"            Handler: func(ctx context.Context, args map[string]any) (any, error) {\n",[1100,7167,7168],{"class":1102,"line":214},[1100,7169,7170],{},"                query, _ := args[\"query\"].(string)\n",[1100,7172,7173],{"class":1102,"line":223},[1100,7174,7175],{},"                return m.service.Search(ctx, query)\n",[1100,7177,7178],{"class":1102,"line":229},[1100,7179,7160],{},[1100,7181,7182],{"class":1102,"line":234},[1100,7183,2515],{},[1100,7185,7186],{"class":1102,"line":240},[1100,7187,2467],{},[1100,7189,7190],{"class":1102,"line":246},[1100,7191,1226],{},[1100,7193,7194],{"class":1102,"line":251},[1100,7195,1126],{"emptyLinePlaceholder":1125},[1100,7197,7198],{"class":1102,"line":257},[1100,7199,7200],{},"func (m *Module) MCPResources() []modules.MCPResource { return nil }\n",[974,7202,7203,7204,7206],{},"Once the module is registered, its tools appear automatically in ",[978,7205,6582],{}," responses.",[1030,7208,7210],{"id":7209},"config","Config",[1086,7212,7214],{"id":7213},"helper-functions","Helper functions",[1389,7216,7217,7225],{},[1392,7218,7219],{},[1395,7220,7221,7223],{},[1398,7222,4081],{},[1398,7224,1406],{},[1408,7226,7227,7237,7247,7257,7270,7280],{},[1395,7228,7229,7234],{},[1413,7230,7231],{},[978,7232,7233],{},"GetEnv(key, default)",[1413,7235,7236],{},"String env var with fallback",[1395,7238,7239,7244],{},[1413,7240,7241],{},[978,7242,7243],{},"GetBool(key, default)",[1413,7245,7246],{},"Boolean env var",[1395,7248,7249,7254],{},[1413,7250,7251],{},[978,7252,7253],{},"GetInt(key, default)",[1413,7255,7256],{},"Integer env var",[1395,7258,7259,7264],{},[1413,7260,7261],{},[978,7262,7263],{},"GetDuration(key, default)",[1413,7265,7266,7269],{},[978,7267,7268],{},"time.Duration"," env var",[1395,7271,7272,7277],{},[1413,7273,7274],{},[978,7275,7276],{},"GetFloat64(key, default)",[1413,7278,7279],{},"Float env var",[1395,7281,7282,7287],{},[1413,7283,7284],{},[978,7285,7286],{},"GetStringSlice(key, default)",[1413,7288,7289],{},"Comma-separated slice",[1086,7291,7293],{"id":7292},"pre-built-config-loaders","Pre-built config loaders",[1389,7295,7296,7304],{},[1392,7297,7298],{},[1395,7299,7300,7302],{},[1398,7301,4081],{},[1398,7303,1406],{},[1408,7305,7306,7324,7337,7352,7362,7372,7382],{},[1395,7307,7308,7313],{},[1413,7309,7310],{},[978,7311,7312],{},"LoadAppConfig()",[1413,7314,7315,1068,7317,1068,7320,7323],{},[978,7316,1417],{},[978,7318,7319],{},"APP_HOST",[978,7321,7322],{},"APP_PORT",", etc.",[1395,7325,7326,7331],{},[1413,7327,7328],{},[978,7329,7330],{},"LoadMongoConfig()",[1413,7332,7333,1068,7335],{},[978,7334,1462],{},[978,7336,1477],{},[1395,7338,7339,7344],{},[1413,7340,7341],{},[978,7342,7343],{},"LoadRedisConfig()",[1413,7345,7346,1068,7349],{},[978,7347,7348],{},"REDIS_URL",[978,7350,7351],{},"REDIS_PREFIX",[1395,7353,7354,7359],{},[1413,7355,7356],{},[978,7357,7358],{},"LoadClickHouseConfig()",[1413,7360,7361],{},"ClickHouse connection settings",[1395,7363,7364,7369],{},[1413,7365,7366],{},[978,7367,7368],{},"LoadKafkaConfig(defaultGroupID)",[1413,7370,7371],{},"Kafka broker + SASL\u002FTLS settings",[1395,7373,7374,7379],{},[1413,7375,7376],{},[978,7377,7378],{},"LoadNATSConfig()",[1413,7380,7381],{},"NATS URL, JetStream, credentials, TLS",[1395,7383,7384,7389],{},[1413,7385,7386],{},[978,7387,7388],{},"LoadRedisStreamsConfig()",[1413,7390,7391],{},"Redis Streams transport tuning",[5131,7393,7395],{"id":7394},"kafkaconfig","KafkaConfig",[1389,7397,7398,7409],{},[1392,7399,7400],{},[1395,7401,7402,7405,7407],{},[1398,7403,7404],{},"Env var",[1398,7406,1406],{},[1398,7408,1403],{},[1408,7410,7411,7426,7440,7466,7489,7503],{},[1395,7412,7413,7418,7421],{},[1413,7414,7415],{},[978,7416,7417],{},"KAFKA_BOOTSTRAP_SERVERS",[1413,7419,7420],{},"Comma-separated broker list",[1413,7422,7423],{},[978,7424,7425],{},"localhost:9092",[1395,7427,7428,7433,7435],{},[1413,7429,7430],{},[978,7431,7432],{},"KAFKA_GROUP_ID",[1413,7434,2587],{},[1413,7436,7437],{},[1539,7438,7439],{},"(arg)",[1395,7441,7442,7447,7462],{},[1413,7443,7444],{},[978,7445,7446],{},"KAFKA_SECURITY_PROTOCOL",[1413,7448,7449,7452,7453,7452,7456,7452,7459],{},[978,7450,7451],{},"plaintext"," | ",[978,7454,7455],{},"sasl_plaintext",[978,7457,7458],{},"sasl_ssl",[978,7460,7461],{},"ssl",[1413,7463,7464],{},[978,7465,7451],{},[1395,7467,7468,7473,7484],{},[1413,7469,7470],{},[978,7471,7472],{},"KAFKA_SASL_MECHANISM",[1413,7474,7475,7452,7478,7452,7481],{},[978,7476,7477],{},"plain",[978,7479,7480],{},"scram-sha-256",[978,7482,7483],{},"scram-sha-512",[1413,7485,7486],{},[978,7487,7488],{},"\"\"",[1395,7490,7491,7496,7499],{},[1413,7492,7493],{},[978,7494,7495],{},"KAFKA_SASL_USERNAME",[1413,7497,7498],{},"SASL username",[1413,7500,7501],{},[978,7502,7488],{},[1395,7504,7505,7510,7513],{},[1413,7506,7507],{},[978,7508,7509],{},"KAFKA_SASL_PASSWORD",[1413,7511,7512],{},"SASL password",[1413,7514,7515],{},[978,7516,7488],{},[974,7518,7519,7522,7523,7526,7527,7530],{},[978,7520,7521],{},"cfg.BrokerAddrs()"," returns ",[978,7524,7525],{},"[]string"," from the comma-separated ",[978,7528,7529],{},"Brokers"," field — ready for sarama.",[5131,7532,7534],{"id":7533},"natsconfig","NATSConfig",[1389,7536,7537,7547],{},[1392,7538,7539],{},[1395,7540,7541,7543,7545],{},[1398,7542,7404],{},[1398,7544,1406],{},[1398,7546,1403],{},[1408,7548,7549,7567,7584,7602,7616,7630,7645],{},[1395,7550,7551,7556,7562],{},[1413,7552,7553],{},[978,7554,7555],{},"NATS_URL",[1413,7557,7558,7559,2218],{},"Server URL or cluster list (",[978,7560,7561],{},"nats:\u002F\u002Fa:4222,nats:\u002F\u002Fb:4222",[1413,7563,7564],{},[978,7565,7566],{},"nats:\u002F\u002Flocalhost:4222",[1395,7568,7569,7574,7580],{},[1413,7570,7571],{},[978,7572,7573],{},"NATS_JETSTREAM",[1413,7575,7576,7579],{},[978,7577,7578],{},"true"," for durable JetStream delivery",[1413,7581,7582],{},[978,7583,1452],{},[1395,7585,7586,7591,7598],{},[1413,7587,7588],{},[978,7589,7590],{},"NATS_CREDENTIALS_FILE",[1413,7592,7593,7594,7597],{},"Path to ",[978,7595,7596],{},".creds"," file",[1413,7599,7600],{},[978,7601,7488],{},[1395,7603,7604,7609,7612],{},[1413,7605,7606],{},[978,7607,7608],{},"NATS_NKEY_FILE",[1413,7610,7611],{},"Path to NKey seed file",[1413,7613,7614],{},[978,7615,7488],{},[1395,7617,7618,7623,7626],{},[1413,7619,7620],{},[978,7621,7622],{},"NATS_TLS",[1413,7624,7625],{},"Require TLS",[1413,7627,7628],{},[978,7629,1452],{},[1395,7631,7632,7637,7640],{},[1413,7633,7634],{},[978,7635,7636],{},"NATS_ACK_WAIT_SECONDS",[1413,7638,7639],{},"JetStream ack-wait seconds",[1413,7641,7642],{},[978,7643,7644],{},"30",[1395,7646,7647,7652,7659],{},[1413,7648,7649],{},[978,7650,7651],{},"NATS_MAX_DELIVER",[1413,7653,7654,7655,7658],{},"JetStream max redeliveries (",[978,7656,7657],{},"-1"," = unlimited)",[1413,7660,7661],{},[978,7662,7663],{},"3",[974,7665,7666,7669,7670,1004],{},[978,7667,7668],{},"cfg.URLs()"," splits the cluster list into ",[978,7671,7525],{},[5131,7673,7675],{"id":7674},"redisstreamsconfig","RedisStreamsConfig",[1389,7677,7678,7688],{},[1392,7679,7680],{},[1395,7681,7682,7684,7686],{},[1398,7683,7404],{},[1398,7685,1406],{},[1398,7687,1403],{},[1408,7689,7690,7707,7721,7736],{},[1395,7691,7692,7697,7702],{},[1413,7693,7694],{},[978,7695,7696],{},"REDIS_STREAMS_URL",[1413,7698,7699,7700],{},"Redis URL; falls back to ",[978,7701,7348],{},[1413,7703,7704],{},[978,7705,7706],{},"redis:\u002F\u002Flocalhost:6379",[1395,7708,7709,7714,7717],{},[1413,7710,7711],{},[978,7712,7713],{},"REDIS_STREAMS_BLOCK_MS",[1413,7715,7716],{},"XREADGROUP block in ms",[1413,7718,7719],{},[978,7720,4903],{},[1395,7722,7723,7728,7731],{},[1413,7724,7725],{},[978,7726,7727],{},"REDIS_STREAMS_BATCH_SIZE",[1413,7729,7730],{},"Messages per fetch",[1413,7732,7733],{},[978,7734,7735],{},"10",[1395,7737,7738,7743,7746],{},[1413,7739,7740],{},[978,7741,7742],{},"REDIS_STREAMS_MAX_LEN",[1413,7744,7745],{},"Approximate max stream length",[1413,7747,7748],{},[978,7749,7750],{},"10000",[974,7752,7753,7756,7757,7760],{},[978,7754,7755],{},"cfg.Addr()"," extracts ",[978,7758,7759],{},"host:port"," from the URL for go-redis.",[1030,7762,7764],{"id":7763},"runtime","Runtime",[1086,7766,7768],{"id":7767},"redis","Redis",[1091,7770,7772],{"className":1093,"code":7771,"language":1095,"meta":1096,"style":1096},"client, err := redis.NewClient(cfg)\nkey := redis.Key(cfg, \"inflight\", domain)  \u002F\u002F → \"prefix:inflight:example.com\"\n",[978,7773,7774,7779],{"__ignoreMap":1096},[1100,7775,7776],{"class":1102,"line":10},[1100,7777,7778],{},"client, err := redis.NewClient(cfg)\n",[1100,7780,7781],{"class":1102,"line":16},[1100,7782,7783],{},"key := redis.Key(cfg, \"inflight\", domain)  \u002F\u002F → \"prefix:inflight:example.com\"\n",[1086,7785,7787],{"id":7786},"clickhouse","ClickHouse",[1091,7789,7791],{"className":1093,"code":7790,"language":1095,"meta":1096,"style":1096},"conn, err := clickhouse.NewConn(cfg, \"service-name\", nil)\n",[978,7792,7793],{"__ignoreMap":1096},[1100,7794,7795],{"class":1102,"line":10},[1100,7796,7790],{},[1086,7798,7800,7801,2218],{"id":7799},"cross-container-coordination-runtimecoordination","Cross-container coordination (",[978,7802,7803],{},"runtime\u002Fcoordination",[974,7805,7806,7807,7810,7811,7814,7815,7818,7819,1004],{},"Transport-agnostic coordination primitives for multi-container deployments — pub\u002Fsub\ninvalidation, append-only streams for fan-out, and watchable KV storage. Blank-import\n",[978,7808,7809],{},"go-modules\u002Fcoordination\u002Fmodule"," and set ",[978,7812,7813],{},"COORD=nats|redis|noop"," to auto-wire into\n",[978,7816,7817],{},"ModuleDeps.Coordinator",". Zero external deps in the interface package; backends live\nin ",[978,7820,7821],{},"go-modules\u002Fcoordination\u002F{nats,redis}",[1091,7823,7825],{"className":1093,"code":7824,"language":1095,"meta":1096,"style":1096},"if c := m.deps.Coordinator; c != nil {\n    _ = c.Publish(ctx, \"flow.published\", []byte(flowID))   \u002F\u002F invalidation\n    _, _ = c.AppendStream(ctx, \"live:\"+flowID, frameJSON)  \u002F\u002F fan-out\n    _ = c.KVSet(ctx, \"routes\", flowID, routeJSON)          \u002F\u002F shared config\n    w, _ := c.KVWatch(ctx, \"routes\", \"*\")                  \u002F\u002F watch for updates\n}\n",[978,7826,7827,7832,7837,7842,7847,7852],{"__ignoreMap":1096},[1100,7828,7829],{"class":1102,"line":10},[1100,7830,7831],{},"if c := m.deps.Coordinator; c != nil {\n",[1100,7833,7834],{"class":1102,"line":16},[1100,7835,7836],{},"    _ = c.Publish(ctx, \"flow.published\", []byte(flowID))   \u002F\u002F invalidation\n",[1100,7838,7839],{"class":1102,"line":22},[1100,7840,7841],{},"    _, _ = c.AppendStream(ctx, \"live:\"+flowID, frameJSON)  \u002F\u002F fan-out\n",[1100,7843,7844],{"class":1102,"line":51},[1100,7845,7846],{},"    _ = c.KVSet(ctx, \"routes\", flowID, routeJSON)          \u002F\u002F shared config\n",[1100,7848,7849],{"class":1102,"line":57},[1100,7850,7851],{},"    w, _ := c.KVWatch(ctx, \"routes\", \"*\")                  \u002F\u002F watch for updates\n",[1100,7853,7854],{"class":1102,"line":102},[1100,7855,1226],{},[974,7857,7858,7859,7862],{},"See the full ",[1014,7860,7861],{"href":244},"Coordination reference"," for the interface,\nbackends, and conformance test suite.",[1086,7864,7866],{"id":7865},"distributed-primitives","Distributed primitives",[974,7868,7869,7870,7873],{},"Redis-backed coordination primitives in ",[978,7871,7872],{},"runtime\u002Fdistributed",". Both degrade gracefully\nwhen Redis is nil — the lock always reports leader, the semaphore always grants.",[5131,7875,7877],{"id":7876},"distributed-lock-leader-election","Distributed lock (leader election)",[974,7879,7880,7881,7884],{},"Binary mutex backed by ",[978,7882,7883],{},"SET NX EX"," with Lua CAS release. Runs a background heartbeat\nto renew the lock automatically.",[1091,7886,7888],{"className":1093,"code":7887,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fdistributed\"\n\nlock := distributed.NewDistributedLock(redisClient, \"worker-leader\",\n    distributed.WithLockTTL(120 * time.Second),\n    distributed.WithHeartbeatInterval(30 * time.Second),\n    distributed.WithLockPrefix(\"myapp:dlock:\"),\n)\n\nlock.Start(ctx)          \u002F\u002F tries to acquire; runs heartbeat in background\ndefer lock.Stop(ctx)     \u002F\u002F releases lock\n\nif lock.IsLeader() {\n    \u002F\u002F only one instance runs this\n}\n\ninfo, _ := lock.GetCurrentLeader(ctx) \u002F\u002F {\"token\": \"...\", \"is_self\": true\u002Ffalse}\n",[978,7889,7890,7895,7899,7904,7909,7914,7919,7923,7927,7932,7937,7941,7946,7951,7955,7959],{"__ignoreMap":1096},[1100,7891,7892],{"class":1102,"line":10},[1100,7893,7894],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fdistributed\"\n",[1100,7896,7897],{"class":1102,"line":16},[1100,7898,1126],{"emptyLinePlaceholder":1125},[1100,7900,7901],{"class":1102,"line":22},[1100,7902,7903],{},"lock := distributed.NewDistributedLock(redisClient, \"worker-leader\",\n",[1100,7905,7906],{"class":1102,"line":51},[1100,7907,7908],{},"    distributed.WithLockTTL(120 * time.Second),\n",[1100,7910,7911],{"class":1102,"line":57},[1100,7912,7913],{},"    distributed.WithHeartbeatInterval(30 * time.Second),\n",[1100,7915,7916],{"class":1102,"line":102},[1100,7917,7918],{},"    distributed.WithLockPrefix(\"myapp:dlock:\"),\n",[1100,7920,7921],{"class":1102,"line":108},[1100,7922,1120],{},[1100,7924,7925],{"class":1102,"line":114},[1100,7926,1126],{"emptyLinePlaceholder":1125},[1100,7928,7929],{"class":1102,"line":120},[1100,7930,7931],{},"lock.Start(ctx)          \u002F\u002F tries to acquire; runs heartbeat in background\n",[1100,7933,7934],{"class":1102,"line":181},[1100,7935,7936],{},"defer lock.Stop(ctx)     \u002F\u002F releases lock\n",[1100,7938,7939],{"class":1102,"line":186},[1100,7940,1126],{"emptyLinePlaceholder":1125},[1100,7942,7943],{"class":1102,"line":191},[1100,7944,7945],{},"if lock.IsLeader() {\n",[1100,7947,7948],{"class":1102,"line":197},[1100,7949,7950],{},"    \u002F\u002F only one instance runs this\n",[1100,7952,7953],{"class":1102,"line":203},[1100,7954,1226],{},[1100,7956,7957],{"class":1102,"line":214},[1100,7958,1126],{"emptyLinePlaceholder":1125},[1100,7960,7961],{"class":1102,"line":223},[1100,7962,7963],{},"info, _ := lock.GetCurrentLeader(ctx) \u002F\u002F {\"token\": \"...\", \"is_self\": true\u002Ffalse}\n",[5131,7965,7967],{"id":7966},"distributed-semaphore","Distributed semaphore",[974,7969,7970],{},"Counting semaphore backed by a Redis sorted set with expiry scores. Supports\nconfigurable max workers and heartbeat renewal.",[1091,7972,7974],{"className":1093,"code":7973,"language":1095,"meta":1096,"style":1096},"sem := distributed.NewDistributedSemaphore(redisClient, \"import-workers\", 3,\n    distributed.WithSemTTL(300 * time.Second),\n    distributed.WithSemHeartbeat(60 * time.Second),\n    distributed.WithAcquireTimeout(30 * time.Second),\n    distributed.WithSemPrefix(\"myapp:dsem:\"),\n)\n\n\u002F\u002F Manual acquire\u002Frelease\ntoken, _ := sem.Acquire(ctx)\ndefer sem.Release(ctx, token)\n\n\u002F\u002F Or use Hold — acquires, runs fn with background heartbeat, releases\nsem.Hold(ctx, func(ctx context.Context) error {\n    \u002F\u002F do work within semaphore slot\n    return nil\n})\n\ncount, _ := sem.CurrentCount(ctx) \u002F\u002F active holders\n",[978,7975,7976,7981,7986,7991,7996,8001,8005,8009,8014,8019,8024,8028,8033,8038,8043,8047,8051,8055],{"__ignoreMap":1096},[1100,7977,7978],{"class":1102,"line":10},[1100,7979,7980],{},"sem := distributed.NewDistributedSemaphore(redisClient, \"import-workers\", 3,\n",[1100,7982,7983],{"class":1102,"line":16},[1100,7984,7985],{},"    distributed.WithSemTTL(300 * time.Second),\n",[1100,7987,7988],{"class":1102,"line":22},[1100,7989,7990],{},"    distributed.WithSemHeartbeat(60 * time.Second),\n",[1100,7992,7993],{"class":1102,"line":51},[1100,7994,7995],{},"    distributed.WithAcquireTimeout(30 * time.Second),\n",[1100,7997,7998],{"class":1102,"line":57},[1100,7999,8000],{},"    distributed.WithSemPrefix(\"myapp:dsem:\"),\n",[1100,8002,8003],{"class":1102,"line":102},[1100,8004,1120],{},[1100,8006,8007],{"class":1102,"line":108},[1100,8008,1126],{"emptyLinePlaceholder":1125},[1100,8010,8011],{"class":1102,"line":114},[1100,8012,8013],{},"\u002F\u002F Manual acquire\u002Frelease\n",[1100,8015,8016],{"class":1102,"line":120},[1100,8017,8018],{},"token, _ := sem.Acquire(ctx)\n",[1100,8020,8021],{"class":1102,"line":181},[1100,8022,8023],{},"defer sem.Release(ctx, token)\n",[1100,8025,8026],{"class":1102,"line":186},[1100,8027,1126],{"emptyLinePlaceholder":1125},[1100,8029,8030],{"class":1102,"line":191},[1100,8031,8032],{},"\u002F\u002F Or use Hold — acquires, runs fn with background heartbeat, releases\n",[1100,8034,8035],{"class":1102,"line":197},[1100,8036,8037],{},"sem.Hold(ctx, func(ctx context.Context) error {\n",[1100,8039,8040],{"class":1102,"line":203},[1100,8041,8042],{},"    \u002F\u002F do work within semaphore slot\n",[1100,8044,8045],{"class":1102,"line":214},[1100,8046,2795],{},[1100,8048,8049],{"class":1102,"line":223},[1100,8050,2936],{},[1100,8052,8053],{"class":1102,"line":229},[1100,8054,1126],{"emptyLinePlaceholder":1125},[1100,8056,8057],{"class":1102,"line":234},[1100,8058,8059],{},"count, _ := sem.CurrentCount(ctx) \u002F\u002F active holders\n",[1030,8061,8063],{"id":8062},"core-ir-intermediate-representation","Core IR (Intermediate Representation)",[1006,8065,8066,8071,8072,8075,8076,8079],{"icon":945},[988,8067,8068,8069],{},"Moved to ",[978,8070,992],{}," — import ",[978,8073,8074],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fir",". See the ",[1014,8077,8078],{"href":184},"go-flowdsl Reference"," for the full API.",[974,8081,1581,8082,8085],{},[978,8083,8084],{},"ir"," package defines the canonical data model that all formats import into\nand export from. Every FlowDSL, OpenAPI, AsyncAPI, or business model document is first\nconverted to this IR before any processing.",[1086,8087,8089],{"id":8088},"document","Document",[1091,8091,8093],{"className":1093,"code":8092,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fir\"\n\ndoc := ir.NewDocument(\"My App\", \"1.0.0\")\n\u002F\u002F doc.ID       — auto-generated UUID\n\u002F\u002F doc.Modules  — business\u002Fdomain modules\n\u002F\u002F doc.Entities — domain aggregates with typed fields\n\u002F\u002F doc.Events   — business events (entity_type + action)\n\u002F\u002F doc.Commands — state-changing intents\n\u002F\u002F doc.Queries  — read operations\n\u002F\u002F doc.Actions  — top-level operations (command\u002Fquery\u002Fmutation\u002Fsubscription)\n\u002F\u002F doc.Workflows — multi-step processes with nodes + edges\n\u002F\u002F doc.Channels — message\u002Fevent channels with pub\u002Fsub\n\u002F\u002F doc.Schemas  — reusable data structure definitions\n\u002F\u002F doc.Packets  — data transfer payloads\n\u002F\u002F doc.Schedules — time-based triggers (cron\u002Finterval)\n\u002F\u002F doc.Migrations — database migration definitions\n\u002F\u002F doc.Metadata — arbitrary key-value metadata\n\u002F\u002F doc.Provenance — origin and mapping history\n",[978,8094,8095,8100,8104,8109,8114,8119,8124,8129,8134,8139,8144,8149,8154,8159,8164,8169,8174,8179],{"__ignoreMap":1096},[1100,8096,8097],{"class":1102,"line":10},[1100,8098,8099],{},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fir\"\n",[1100,8101,8102],{"class":1102,"line":16},[1100,8103,1126],{"emptyLinePlaceholder":1125},[1100,8105,8106],{"class":1102,"line":22},[1100,8107,8108],{},"doc := ir.NewDocument(\"My App\", \"1.0.0\")\n",[1100,8110,8111],{"class":1102,"line":51},[1100,8112,8113],{},"\u002F\u002F doc.ID       — auto-generated UUID\n",[1100,8115,8116],{"class":1102,"line":57},[1100,8117,8118],{},"\u002F\u002F doc.Modules  — business\u002Fdomain modules\n",[1100,8120,8121],{"class":1102,"line":102},[1100,8122,8123],{},"\u002F\u002F doc.Entities — domain aggregates with typed fields\n",[1100,8125,8126],{"class":1102,"line":108},[1100,8127,8128],{},"\u002F\u002F doc.Events   — business events (entity_type + action)\n",[1100,8130,8131],{"class":1102,"line":114},[1100,8132,8133],{},"\u002F\u002F doc.Commands — state-changing intents\n",[1100,8135,8136],{"class":1102,"line":120},[1100,8137,8138],{},"\u002F\u002F doc.Queries  — read operations\n",[1100,8140,8141],{"class":1102,"line":181},[1100,8142,8143],{},"\u002F\u002F doc.Actions  — top-level operations (command\u002Fquery\u002Fmutation\u002Fsubscription)\n",[1100,8145,8146],{"class":1102,"line":186},[1100,8147,8148],{},"\u002F\u002F doc.Workflows — multi-step processes with nodes + edges\n",[1100,8150,8151],{"class":1102,"line":191},[1100,8152,8153],{},"\u002F\u002F doc.Channels — message\u002Fevent channels with pub\u002Fsub\n",[1100,8155,8156],{"class":1102,"line":197},[1100,8157,8158],{},"\u002F\u002F doc.Schemas  — reusable data structure definitions\n",[1100,8160,8161],{"class":1102,"line":203},[1100,8162,8163],{},"\u002F\u002F doc.Packets  — data transfer payloads\n",[1100,8165,8166],{"class":1102,"line":214},[1100,8167,8168],{},"\u002F\u002F doc.Schedules — time-based triggers (cron\u002Finterval)\n",[1100,8170,8171],{"class":1102,"line":223},[1100,8172,8173],{},"\u002F\u002F doc.Migrations — database migration definitions\n",[1100,8175,8176],{"class":1102,"line":229},[1100,8177,8178],{},"\u002F\u002F doc.Metadata — arbitrary key-value metadata\n",[1100,8180,8181],{"class":1102,"line":234},[1100,8182,8183],{},"\u002F\u002F doc.Provenance — origin and mapping history\n",[1086,8185,8187],{"id":8186},"key-types","Key types",[1389,8189,8190,8198],{},[1392,8191,8192],{},[1395,8193,8194,8196],{},[1398,8195,2541],{},[1398,8197,1406],{},[1408,8199,8200,8209,8218,8228,8239,8287,8297,8307,8316,8326,8344,8353,8362,8372,8381,8391,8401,8411,8421,8431,8441,8451],{},[1395,8201,8202,8206],{},[1413,8203,8204],{},[978,8205,8089],{},[1413,8207,8208],{},"Root container for an entire IR",[1395,8210,8211,8215],{},[1413,8212,8213],{},[978,8214,2813],{},[1413,8216,8217],{},"Business\u002Fdomain unit with entities, events, actions, workflows, config, settings, CRUD, consumers",[1395,8219,8220,8225],{},[1413,8221,8222],{},[978,8223,8224],{},"Entity",[1413,8226,8227],{},"Business entity or domain aggregate with typed fields",[1395,8229,8230,8234],{},[1413,8231,8232],{},[978,8233,2538],{},[1413,8235,8236,8237],{},"Single field — type, constraints, nested properties, ",[978,8238,5840],{},[1395,8240,8241,8246],{},[1413,8242,8243],{},[978,8244,8245],{},"FieldType",[1413,8247,8248,8249,1068,8251,1068,8254,1068,8257,1068,8260,1068,8263,1068,8266,1068,8269,1068,8272,1068,8275,1068,8278,1068,8281,1068,8284],{},"Enum: ",[978,8250,2557],{},[978,8252,8253],{},"integer",[978,8255,8256],{},"float",[978,8258,8259],{},"boolean",[978,8261,8262],{},"array",[978,8264,8265],{},"object",[978,8267,8268],{},"date",[978,8270,8271],{},"datetime",[978,8273,8274],{},"uuid",[978,8276,8277],{},"binary",[978,8279,8280],{},"ref",[978,8282,8283],{},"enum",[978,8285,8286],{},"any",[1395,8288,8289,8294],{},[1413,8290,8291],{},[978,8292,8293],{},"Event",[1413,8295,8296],{},"Business event — entity_type + action, optional topic and payload",[1395,8298,8299,8304],{},[1413,8300,8301],{},[978,8302,8303],{},"Command",[1413,8305,8306],{},"State-changing intent with input\u002Foutput schemas and emitted events",[1395,8308,8309,8313],{},[1413,8310,8311],{},[978,8312,5722],{},[1413,8314,8315],{},"Read operation with input\u002Foutput schemas",[1395,8317,8318,8323],{},[1413,8319,8320],{},[978,8321,8322],{},"Action",[1413,8324,8325],{},"Top-level operation — kind (command\u002Fquery\u002Fmutation\u002Fsubscription), method, path, I\u002FO, emits",[1395,8327,8328,8333],{},[1413,8329,8330],{},[978,8331,8332],{},"Workflow",[1413,8334,8335,8336,8339,8340,8343],{},"Multi-step process with ",[978,8337,8338],{},"Node"," graph and ",[978,8341,8342],{},"Edge"," connections",[1395,8345,8346,8350],{},[1413,8347,8348],{},[978,8349,8338],{},[1413,8351,8352],{},"Workflow step — kind (action\u002Fevent\u002Fgateway\u002Fstart\u002Fend\u002Fwait\u002Fcondition\u002Fparallel)",[1395,8354,8355,8359],{},[1413,8356,8357],{},[978,8358,8342],{},[1413,8360,8361],{},"Directed connection between nodes with optional condition",[1395,8363,8364,8369],{},[1413,8365,8366],{},[978,8367,8368],{},"Channel",[1413,8370,8371],{},"Message channel with publish\u002Fsubscribe events, protocol, bindings",[1395,8373,8374,8378],{},[1413,8375,8376],{},[978,8377,3886],{},[1413,8379,8380],{},"Reusable data structure definition",[1395,8382,8383,8388],{},[1413,8384,8385],{},[978,8386,8387],{},"Packet",[1413,8389,8390],{},"Data transfer structure (event payload, message body)",[1395,8392,8393,8398],{},[1413,8394,8395],{},[978,8396,8397],{},"Schedule",[1413,8399,8400],{},"Time-based trigger — cron expression or interval",[1395,8402,8403,8408],{},[1413,8404,8405],{},[978,8406,8407],{},"Migration",[1413,8409,8410],{},"Database migration — backend, version, up\u002Fdown SQL",[1395,8412,8413,8418],{},[1413,8414,8415],{},[978,8416,8417],{},"Consumer",[1413,8419,8420],{},"Event consumer — topic, group ID, handler reference, batch size",[1395,8422,8423,8428],{},[1413,8424,8425],{},[978,8426,8427],{},"Provenance",[1413,8429,8430],{},"Origin tracking — source, URI, source ref, mappings, confidence",[1395,8432,8433,8438],{},[1413,8434,8435],{},[978,8436,8437],{},"ConfigDefinition",[1413,8439,8440],{},"Module runtime configuration (entries with env vars, types, defaults)",[1395,8442,8443,8448],{},[1413,8444,8445],{},[978,8446,8447],{},"SettingsDefinition",[1413,8449,8450],{},"Admin-UI-editable settings (groups of fields with components)",[1395,8452,8453,8458],{},[1413,8454,8455],{},[978,8456,8457],{},"CRUDDefinition",[1413,8459,8460],{},"CRUD capabilities for an entity (create\u002Fread\u002Fupdate\u002Fdelete\u002Flist)",[1030,8462,293],{"id":8463},"flowdsl",[1006,8465,8466,8071,8470,8075,8473,8079],{"icon":945},[988,8467,8068,8468],{},[978,8469,992],{},[978,8471,8472],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fflowdsl",[1014,8474,8078],{"href":184},[974,8476,1581,8477,8479],{},[978,8478,8463],{}," package parses, validates, normalizes, and exports FlowDSL —\nthe primary DSL for defining Redelay applications.",[1086,8481,8483],{"id":8482},"parse-validate","Parse & Validate",[1091,8485,8487],{"className":1093,"code":8486,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fflowdsl\"\n\nparsed, err := flowdsl.Parse(reader)       \u002F\u002F YAML → *FlowDSL\ndiag := flowdsl.Validate(parsed)           \u002F\u002F structural checks → *diagnostics.Collector\ndoc := flowdsl.Normalize(parsed)           \u002F\u002F *FlowDSL → *ir.Document\nerr = flowdsl.Export(doc, writer)           \u002F\u002F *ir.Document → FlowDSL YAML\n",[978,8488,8489,8494,8498,8503,8508,8513],{"__ignoreMap":1096},[1100,8490,8491],{"class":1102,"line":10},[1100,8492,8493],{},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fflowdsl\"\n",[1100,8495,8496],{"class":1102,"line":16},[1100,8497,1126],{"emptyLinePlaceholder":1125},[1100,8499,8500],{"class":1102,"line":22},[1100,8501,8502],{},"parsed, err := flowdsl.Parse(reader)       \u002F\u002F YAML → *FlowDSL\n",[1100,8504,8505],{"class":1102,"line":51},[1100,8506,8507],{},"diag := flowdsl.Validate(parsed)           \u002F\u002F structural checks → *diagnostics.Collector\n",[1100,8509,8510],{"class":1102,"line":57},[1100,8511,8512],{},"doc := flowdsl.Normalize(parsed)           \u002F\u002F *FlowDSL → *ir.Document\n",[1100,8514,8515],{"class":1102,"line":102},[1100,8516,8517],{},"err = flowdsl.Export(doc, writer)           \u002F\u002F *ir.Document → FlowDSL YAML\n",[1086,8519,8521],{"id":8520},"flowdsl-yaml-format","FlowDSL YAML format",[1091,8523,8527],{"className":8524,"code":8525,"language":8526,"meta":1096,"style":1096},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","version: \"1.0\"\ntitle: My Application\nmodules:\n  billing:\n    name: billing\n    description: Billing domain\n    depends_on: [module.users]\n    provides: [invoices]\nentities:\n  invoice:\n    name: invoice\n    fields:\n      amount: { type: float, required: true }\n      currency: { type: string, default: USD }\nevents:\n  invoice_created:\n    name: invoice_created\n    entity_type: invoice\n    action: created\n    topic: billing.invoices\nactions:\n  create_invoice:\n    name: create_invoice\n    kind: command\n    method: POST\n    path: \u002Finvoices\nworkflows:\n  payment_flow:\n    name: payment_flow\n    triggers: [invoice_created]\n    steps:\n      - id: validate\n        kind: action\n        action: validate_payment\n        next: charge\n      - id: charge\n        kind: action\n        action: charge_card\nchannels:\n  notifications:\n    name: notifications\n    protocol: kafka\nschedules:\n  daily_report:\n    name: daily_report\n    cron: \"0 9 * * *\"\n    action: generate_report\n","yaml",[978,8528,8529,8544,8554,8562,8569,8579,8589,8605,8619,8626,8633,8642,8649,8682,8710,8717,8724,8733,8742,8752,8762,8769,8776,8785,8795,8805,8815,8822,8829,8838,8852,8859,8872,8882,8892,8902,8913,8922,8932,8940,8948,8958,8969,8977,8985,8995,9010],{"__ignoreMap":1096},[1100,8530,8531,8535,8537,8539,8542],{"class":1102,"line":10},[1100,8532,8534],{"class":8533},"swJcz","version",[1100,8536,1044],{"class":6637},[1100,8538,6686],{"class":6637},[1100,8540,8541],{"class":6689},"1.0",[1100,8543,6713],{"class":6637},[1100,8545,8546,8549,8551],{"class":1102,"line":16},[1100,8547,8548],{"class":8533},"title",[1100,8550,1044],{"class":6637},[1100,8552,8553],{"class":6689}," My Application\n",[1100,8555,8556,8559],{"class":1102,"line":22},[1100,8557,8558],{"class":8533},"modules",[1100,8560,8561],{"class":6637},":\n",[1100,8563,8564,8567],{"class":1102,"line":51},[1100,8565,8566],{"class":8533},"  billing",[1100,8568,8561],{"class":6637},[1100,8570,8571,8574,8576],{"class":1102,"line":57},[1100,8572,8573],{"class":8533},"    name",[1100,8575,1044],{"class":6637},[1100,8577,8578],{"class":6689}," billing\n",[1100,8580,8581,8584,8586],{"class":1102,"line":102},[1100,8582,8583],{"class":8533},"    description",[1100,8585,1044],{"class":6637},[1100,8587,8588],{"class":6689}," Billing domain\n",[1100,8590,8591,8594,8596,8599,8602],{"class":1102,"line":108},[1100,8592,8593],{"class":8533},"    depends_on",[1100,8595,1044],{"class":6637},[1100,8597,8598],{"class":6637}," [",[1100,8600,8601],{"class":6689},"module.users",[1100,8603,8604],{"class":6637},"]\n",[1100,8606,8607,8610,8612,8614,8617],{"class":1102,"line":114},[1100,8608,8609],{"class":8533},"    provides",[1100,8611,1044],{"class":6637},[1100,8613,8598],{"class":6637},[1100,8615,8616],{"class":6689},"invoices",[1100,8618,8604],{"class":6637},[1100,8620,8621,8624],{"class":1102,"line":120},[1100,8622,8623],{"class":8533},"entities",[1100,8625,8561],{"class":6637},[1100,8627,8628,8631],{"class":1102,"line":181},[1100,8629,8630],{"class":8533},"  invoice",[1100,8632,8561],{"class":6637},[1100,8634,8635,8637,8639],{"class":1102,"line":186},[1100,8636,8573],{"class":8533},[1100,8638,1044],{"class":6637},[1100,8640,8641],{"class":6689}," invoice\n",[1100,8643,8644,8647],{"class":1102,"line":191},[1100,8645,8646],{"class":8533},"    fields",[1100,8648,8561],{"class":6637},[1100,8650,8651,8654,8656,8659,8662,8664,8667,8670,8673,8675,8679],{"class":1102,"line":197},[1100,8652,8653],{"class":8533},"      amount",[1100,8655,1044],{"class":6637},[1100,8657,8658],{"class":6637}," {",[1100,8660,8661],{"class":8533}," type",[1100,8663,1044],{"class":6637},[1100,8665,8666],{"class":6689}," float",[1100,8668,8669],{"class":6637},",",[1100,8671,8672],{"class":8533}," required",[1100,8674,1044],{"class":6637},[1100,8676,8678],{"class":8677},"sfNiH"," true",[1100,8680,8681],{"class":6637}," }\n",[1100,8683,8684,8687,8689,8691,8693,8695,8698,8700,8703,8705,8708],{"class":1102,"line":203},[1100,8685,8686],{"class":8533},"      currency",[1100,8688,1044],{"class":6637},[1100,8690,8658],{"class":6637},[1100,8692,8661],{"class":8533},[1100,8694,1044],{"class":6637},[1100,8696,8697],{"class":6689}," string",[1100,8699,8669],{"class":6637},[1100,8701,8702],{"class":8533}," default",[1100,8704,1044],{"class":6637},[1100,8706,8707],{"class":6689}," USD",[1100,8709,8681],{"class":6637},[1100,8711,8712,8715],{"class":1102,"line":214},[1100,8713,8714],{"class":8533},"events",[1100,8716,8561],{"class":6637},[1100,8718,8719,8722],{"class":1102,"line":223},[1100,8720,8721],{"class":8533},"  invoice_created",[1100,8723,8561],{"class":6637},[1100,8725,8726,8728,8730],{"class":1102,"line":229},[1100,8727,8573],{"class":8533},[1100,8729,1044],{"class":6637},[1100,8731,8732],{"class":6689}," invoice_created\n",[1100,8734,8735,8738,8740],{"class":1102,"line":234},[1100,8736,8737],{"class":8533},"    entity_type",[1100,8739,1044],{"class":6637},[1100,8741,8641],{"class":6689},[1100,8743,8744,8747,8749],{"class":1102,"line":240},[1100,8745,8746],{"class":8533},"    action",[1100,8748,1044],{"class":6637},[1100,8750,8751],{"class":6689}," created\n",[1100,8753,8754,8757,8759],{"class":1102,"line":246},[1100,8755,8756],{"class":8533},"    topic",[1100,8758,1044],{"class":6637},[1100,8760,8761],{"class":6689}," billing.invoices\n",[1100,8763,8764,8767],{"class":1102,"line":251},[1100,8765,8766],{"class":8533},"actions",[1100,8768,8561],{"class":6637},[1100,8770,8771,8774],{"class":1102,"line":257},[1100,8772,8773],{"class":8533},"  create_invoice",[1100,8775,8561],{"class":6637},[1100,8777,8778,8780,8782],{"class":1102,"line":1361},[1100,8779,8573],{"class":8533},[1100,8781,1044],{"class":6637},[1100,8783,8784],{"class":6689}," create_invoice\n",[1100,8786,8787,8790,8792],{"class":1102,"line":5638},[1100,8788,8789],{"class":8533},"    kind",[1100,8791,1044],{"class":6637},[1100,8793,8794],{"class":6689}," command\n",[1100,8796,8797,8800,8802],{"class":1102,"line":271},[1100,8798,8799],{"class":8533},"    method",[1100,8801,1044],{"class":6637},[1100,8803,8804],{"class":6689}," POST\n",[1100,8806,8807,8810,8812],{"class":1102,"line":277},[1100,8808,8809],{"class":8533},"    path",[1100,8811,1044],{"class":6637},[1100,8813,8814],{"class":6689}," \u002Finvoices\n",[1100,8816,8817,8820],{"class":1102,"line":282},[1100,8818,8819],{"class":8533},"workflows",[1100,8821,8561],{"class":6637},[1100,8823,8824,8827],{"class":1102,"line":5658},[1100,8825,8826],{"class":8533},"  payment_flow",[1100,8828,8561],{"class":6637},[1100,8830,8831,8833,8835],{"class":1102,"line":5664},[1100,8832,8573],{"class":8533},[1100,8834,1044],{"class":6637},[1100,8836,8837],{"class":6689}," payment_flow\n",[1100,8839,8840,8843,8845,8847,8850],{"class":1102,"line":5670},[1100,8841,8842],{"class":8533},"    triggers",[1100,8844,1044],{"class":6637},[1100,8846,8598],{"class":6637},[1100,8848,8849],{"class":6689},"invoice_created",[1100,8851,8604],{"class":6637},[1100,8853,8854,8857],{"class":1102,"line":5676},[1100,8855,8856],{"class":8533},"    steps",[1100,8858,8561],{"class":6637},[1100,8860,8861,8864,8867,8869],{"class":1102,"line":5681},[1100,8862,8863],{"class":6637},"      -",[1100,8865,8866],{"class":8533}," id",[1100,8868,1044],{"class":6637},[1100,8870,8871],{"class":6689}," validate\n",[1100,8873,8874,8877,8879],{"class":1102,"line":5687},[1100,8875,8876],{"class":8533},"        kind",[1100,8878,1044],{"class":6637},[1100,8880,8881],{"class":6689}," action\n",[1100,8883,8884,8887,8889],{"class":1102,"line":5692},[1100,8885,8886],{"class":8533},"        action",[1100,8888,1044],{"class":6637},[1100,8890,8891],{"class":6689}," validate_payment\n",[1100,8893,8894,8897,8899],{"class":1102,"line":5698},[1100,8895,8896],{"class":8533},"        next",[1100,8898,1044],{"class":6637},[1100,8900,8901],{"class":6689}," charge\n",[1100,8903,8905,8907,8909,8911],{"class":1102,"line":8904},36,[1100,8906,8863],{"class":6637},[1100,8908,8866],{"class":8533},[1100,8910,1044],{"class":6637},[1100,8912,8901],{"class":6689},[1100,8914,8916,8918,8920],{"class":1102,"line":8915},37,[1100,8917,8876],{"class":8533},[1100,8919,1044],{"class":6637},[1100,8921,8881],{"class":6689},[1100,8923,8925,8927,8929],{"class":1102,"line":8924},38,[1100,8926,8886],{"class":8533},[1100,8928,1044],{"class":6637},[1100,8930,8931],{"class":6689}," charge_card\n",[1100,8933,8935,8938],{"class":1102,"line":8934},39,[1100,8936,8937],{"class":8533},"channels",[1100,8939,8561],{"class":6637},[1100,8941,8943,8946],{"class":1102,"line":8942},40,[1100,8944,8945],{"class":8533},"  notifications",[1100,8947,8561],{"class":6637},[1100,8949,8951,8953,8955],{"class":1102,"line":8950},41,[1100,8952,8573],{"class":8533},[1100,8954,1044],{"class":6637},[1100,8956,8957],{"class":6689}," notifications\n",[1100,8959,8961,8964,8966],{"class":1102,"line":8960},42,[1100,8962,8963],{"class":8533},"    protocol",[1100,8965,1044],{"class":6637},[1100,8967,8968],{"class":6689}," kafka\n",[1100,8970,8972,8975],{"class":1102,"line":8971},43,[1100,8973,8974],{"class":8533},"schedules",[1100,8976,8561],{"class":6637},[1100,8978,8980,8983],{"class":1102,"line":8979},44,[1100,8981,8982],{"class":8533},"  daily_report",[1100,8984,8561],{"class":6637},[1100,8986,8988,8990,8992],{"class":1102,"line":8987},45,[1100,8989,8573],{"class":8533},[1100,8991,1044],{"class":6637},[1100,8993,8994],{"class":6689}," daily_report\n",[1100,8996,8998,9001,9003,9005,9008],{"class":1102,"line":8997},46,[1100,8999,9000],{"class":8533},"    cron",[1100,9002,1044],{"class":6637},[1100,9004,6686],{"class":6637},[1100,9006,9007],{"class":6689},"0 9 * * *",[1100,9009,6713],{"class":6637},[1100,9011,9013,9015,9017],{"class":1102,"line":9012},47,[1100,9014,8746],{"class":8533},[1100,9016,1044],{"class":6637},[1100,9018,9019],{"class":6689}," generate_report\n",[1086,9021,9023],{"id":9022},"validation-rules","Validation rules",[1389,9025,9026,9039],{},[1392,9027,9028],{},[1395,9029,9030,9033,9036],{},[1398,9031,9032],{},"Code",[1398,9034,9035],{},"Severity",[1398,9037,9038],{},"Rule",[1408,9040,9041,9054,9067,9079,9091,9103],{},[1395,9042,9043,9048,9051],{},[1413,9044,9045],{},[978,9046,9047],{},"FDL001",[1413,9049,9050],{},"error",[1413,9052,9053],{},"Document is nil",[1395,9055,9056,9061,9064],{},[1413,9057,9058],{},[978,9059,9060],{},"FDL002",[1413,9062,9063],{},"warning",[1413,9065,9066],{},"Version is empty",[1395,9068,9069,9074,9076],{},[1413,9070,9071],{},[978,9072,9073],{},"FDL010",[1413,9075,9050],{},[1413,9077,9078],{},"Event has no entity_type",[1395,9080,9081,9086,9088],{},[1413,9082,9083],{},[978,9084,9085],{},"FDL011",[1413,9087,9050],{},[1413,9089,9090],{},"Event has no action",[1395,9092,9093,9098,9100],{},[1413,9094,9095],{},[978,9096,9097],{},"FDL020",[1413,9099,9050],{},[1413,9101,9102],{},"Workflow step without ID",[1395,9104,9105,9110,9112],{},[1413,9106,9107],{},[978,9108,9109],{},"FDL021",[1413,9111,9063],{},[1413,9113,9114,9115],{},"Step references unknown ",[978,9116,9117],{},"next",[1030,9119,9121],{"id":9120},"compile-pipeline","Compile pipeline",[1006,9123,9124,2422,9129,9132,9133,9136,9137,1004],{"icon":945},[988,9125,9126,9127],{},"Base pipeline moved to ",[978,9128,992],{},[978,9130,9131],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fcompile",". The full pipeline with AsyncAPI + OpenAPI formats remains at ",[978,9134,9135],{},"github.com\u002Fredelay\u002Fgo-framework\u002Fcore\u002Fcompile",". See ",[1014,9138,1022],{"href":160},[974,9140,1581,9141,9144],{},[978,9142,9143],{},"core\u002Fcompile"," package orchestrates the full import → validate → resolve → enrich → export pipeline, including AsyncAPI and OpenAPI format adapters.",[1086,9146,9148],{"id":9147},"formats","Formats",[1389,9150,9151,9167],{},[1392,9152,9153],{},[1395,9154,9155,9158,9161,9164],{},[1398,9156,9157],{},"Constant",[1398,9159,9160],{},"Value",[1398,9162,9163],{},"Import",[1398,9165,9166],{},"Export",[1408,9168,9169,9186,9203,9219],{},[1395,9170,9171,9176,9181,9184],{},[1413,9172,9173],{},[978,9174,9175],{},"FormatFlowDSL",[1413,9177,9178],{},[978,9179,9180],{},"\"flowdsl\"",[1413,9182,9183],{},"yes",[1413,9185,9183],{},[1395,9187,9188,9193,9198,9200],{},[1413,9189,9190],{},[978,9191,9192],{},"FormatOpenAPI",[1413,9194,9195],{},[978,9196,9197],{},"\"openapi\"",[1413,9199,9183],{},[1413,9201,9202],{},"—",[1395,9204,9205,9210,9215,9217],{},[1413,9206,9207],{},[978,9208,9209],{},"FormatAsyncAPI",[1413,9211,9212],{},[978,9213,9214],{},"\"asyncapi\"",[1413,9216,9183],{},[1413,9218,9202],{},[1395,9220,9221,9226,9231,9233],{},[1413,9222,9223],{},[978,9224,9225],{},"FormatBusinessModel",[1413,9227,9228],{},[978,9229,9230],{},"\"businessmodel\"",[1413,9232,9183],{},[1413,9234,9202],{},[1086,9236,9238],{"id":9237},"functions","Functions",[1091,9240,9242],{"className":1093,"code":9241,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcore\u002Fcompile\"\n\n\u002F\u002F Import a source format into IR\ndoc, err := compile.Import(compile.FormatFlowDSL, reader)\n\n\u002F\u002F Run validation + resolution + enrichment\nresult := compile.Compile(doc)\n\u002F\u002F result.Document    — enriched IR document\n\u002F\u002F result.Diagnostics — *diagnostics.Collector with errors\u002Fwarnings\n\u002F\u002F result.Modules     — dependency-ordered modules\n\u002F\u002F result.HasErrors() — true if compilation produced errors\n\n\u002F\u002F One-step: import + compile\nresult, err := compile.ImportAndCompile(compile.FormatFlowDSL, reader)\n\n\u002F\u002F Export IR back to a format\nerr = compile.Export(doc, compile.FormatFlowDSL, writer)\n",[978,9243,9244,9249,9253,9258,9263,9267,9272,9277,9282,9287,9292,9297,9301,9306,9311,9315,9320],{"__ignoreMap":1096},[1100,9245,9246],{"class":1102,"line":10},[1100,9247,9248],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcore\u002Fcompile\"\n",[1100,9250,9251],{"class":1102,"line":16},[1100,9252,1126],{"emptyLinePlaceholder":1125},[1100,9254,9255],{"class":1102,"line":22},[1100,9256,9257],{},"\u002F\u002F Import a source format into IR\n",[1100,9259,9260],{"class":1102,"line":51},[1100,9261,9262],{},"doc, err := compile.Import(compile.FormatFlowDSL, reader)\n",[1100,9264,9265],{"class":1102,"line":57},[1100,9266,1126],{"emptyLinePlaceholder":1125},[1100,9268,9269],{"class":1102,"line":102},[1100,9270,9271],{},"\u002F\u002F Run validation + resolution + enrichment\n",[1100,9273,9274],{"class":1102,"line":108},[1100,9275,9276],{},"result := compile.Compile(doc)\n",[1100,9278,9279],{"class":1102,"line":114},[1100,9280,9281],{},"\u002F\u002F result.Document    — enriched IR document\n",[1100,9283,9284],{"class":1102,"line":120},[1100,9285,9286],{},"\u002F\u002F result.Diagnostics — *diagnostics.Collector with errors\u002Fwarnings\n",[1100,9288,9289],{"class":1102,"line":181},[1100,9290,9291],{},"\u002F\u002F result.Modules     — dependency-ordered modules\n",[1100,9293,9294],{"class":1102,"line":186},[1100,9295,9296],{},"\u002F\u002F result.HasErrors() — true if compilation produced errors\n",[1100,9298,9299],{"class":1102,"line":191},[1100,9300,1126],{"emptyLinePlaceholder":1125},[1100,9302,9303],{"class":1102,"line":197},[1100,9304,9305],{},"\u002F\u002F One-step: import + compile\n",[1100,9307,9308],{"class":1102,"line":203},[1100,9309,9310],{},"result, err := compile.ImportAndCompile(compile.FormatFlowDSL, reader)\n",[1100,9312,9313],{"class":1102,"line":214},[1100,9314,1126],{"emptyLinePlaceholder":1125},[1100,9316,9317],{"class":1102,"line":223},[1100,9318,9319],{},"\u002F\u002F Export IR back to a format\n",[1100,9321,9322],{"class":1102,"line":229},[1100,9323,9324],{},"err = compile.Export(doc, compile.FormatFlowDSL, writer)\n",[1086,9326,9328],{"id":9327},"compilation-stages","Compilation stages",[1046,9330,9331,9336,9342],{},[1049,9332,9333,9335],{},[988,9334,3993],{}," — structural correctness (IDs, refs, module naming)",[1049,9337,9338,9341],{},[988,9339,9340],{},"Resolve"," — topological sort of modules by dependencies",[1049,9343,9344,9347,9348],{},[988,9345,9346],{},"Enrich"," — promote module-level entities\u002Fevents to document level, warn on unresolved ",[978,9349,9350],{},"provides",[1030,9352,9354],{"id":9353},"diagnostics","Diagnostics",[1006,9356,9357,8071,9361,1004],{"icon":945},[988,9358,8068,9359],{},[978,9360,992],{},[978,9362,9363],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fdiagnostics",[974,9365,1581,9366,9368],{},[978,9367,9353],{}," package provides structured error\u002Fwarning collection used throughout\nthe compilation pipeline.",[1091,9370,9372],{"className":1093,"code":9371,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fdiagnostics\"\n\nc := diagnostics.NewCollector()\nc.Error(\"source\", \"CODE\", \"message\")\nc.Warning(\"source\", \"CODE\", \"message\")\nc.Info(\"source\", \"CODE\", \"message\")\n\nc.HasErrors()  \u002F\u002F bool\nc.Errors()     \u002F\u002F []*Diagnostic (error-level only)\nc.Warnings()   \u002F\u002F []*Diagnostic (warning-level only)\nc.All()        \u002F\u002F []*Diagnostic (all levels)\n",[978,9373,9374,9379,9383,9388,9393,9398,9403,9407,9412,9417,9422],{"__ignoreMap":1096},[1100,9375,9376],{"class":1102,"line":10},[1100,9377,9378],{},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fdiagnostics\"\n",[1100,9380,9381],{"class":1102,"line":16},[1100,9382,1126],{"emptyLinePlaceholder":1125},[1100,9384,9385],{"class":1102,"line":22},[1100,9386,9387],{},"c := diagnostics.NewCollector()\n",[1100,9389,9390],{"class":1102,"line":51},[1100,9391,9392],{},"c.Error(\"source\", \"CODE\", \"message\")\n",[1100,9394,9395],{"class":1102,"line":57},[1100,9396,9397],{},"c.Warning(\"source\", \"CODE\", \"message\")\n",[1100,9399,9400],{"class":1102,"line":102},[1100,9401,9402],{},"c.Info(\"source\", \"CODE\", \"message\")\n",[1100,9404,9405],{"class":1102,"line":108},[1100,9406,1126],{"emptyLinePlaceholder":1125},[1100,9408,9409],{"class":1102,"line":114},[1100,9410,9411],{},"c.HasErrors()  \u002F\u002F bool\n",[1100,9413,9414],{"class":1102,"line":120},[1100,9415,9416],{},"c.Errors()     \u002F\u002F []*Diagnostic (error-level only)\n",[1100,9418,9419],{"class":1102,"line":181},[1100,9420,9421],{},"c.Warnings()   \u002F\u002F []*Diagnostic (warning-level only)\n",[1100,9423,9424],{"class":1102,"line":186},[1100,9425,9426],{},"c.All()        \u002F\u002F []*Diagnostic (all levels)\n",[974,9428,9429,9430,9433,9434,9436,9437,1068,9439,1068,9442,9445,9446,1068,9449,1068,9452,1068,9455,1068,9458,1004],{},"Each ",[978,9431,9432],{},"Diagnostic"," has: ",[978,9435,9035],{}," (error\u002Fwarning\u002Finfo\u002Fhint), ",[978,9438,9032],{},[978,9440,9441],{},"Message",[978,9443,9444],{},"Source",",\nand optional ",[978,9447,9448],{},"File",[978,9450,9451],{},"Line",[978,9453,9454],{},"Column",[978,9456,9457],{},"Pointer",[978,9459,9460],{},"Related",[1030,9462,3993],{"id":9463},"validate",[1006,9465,9466,8071,9470,1004],{"icon":945},[988,9467,8068,9468],{},[978,9469,992],{},[978,9471,9472],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fvalidate",[974,9474,1581,9475,9477],{},[978,9476,9463],{}," package checks an IR Document for structural correctness and consistency.",[1091,9479,9481],{"className":1093,"code":9480,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fvalidate\"\n\ndiag := validate.Validate(doc) \u002F\u002F → *diagnostics.Collector\n",[978,9482,9483,9488,9492],{"__ignoreMap":1096},[1100,9484,9485],{"class":1102,"line":10},[1100,9486,9487],{},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fvalidate\"\n",[1100,9489,9490],{"class":1102,"line":16},[1100,9491,1126],{"emptyLinePlaceholder":1125},[1100,9493,9494],{"class":1102,"line":22},[1100,9495,9496],{},"diag := validate.Validate(doc) \u002F\u002F → *diagnostics.Collector\n",[1086,9498,9023],{"id":9499},"validation-rules-1",[1389,9501,9502,9512],{},[1392,9503,9504],{},[1395,9505,9506,9508,9510],{},[1398,9507,9032],{},[1398,9509,9035],{},[1398,9511,9038],{},[1408,9513,9514,9525,9537,9549,9561,9573,9585,9597,9609,9621,9633,9645],{},[1395,9515,9516,9521,9523],{},[1413,9517,9518],{},[978,9519,9520],{},"VAL001",[1413,9522,9050],{},[1413,9524,9053],{},[1395,9526,9527,9532,9534],{},[1413,9528,9529],{},[978,9530,9531],{},"VAL002",[1413,9533,9050],{},[1413,9535,9536],{},"Document ID is required",[1395,9538,9539,9544,9546],{},[1413,9540,9541],{},[978,9542,9543],{},"VAL003",[1413,9545,9063],{},[1413,9547,9548],{},"Document version is empty",[1395,9550,9551,9556,9558],{},[1413,9552,9553],{},[978,9554,9555],{},"VAL010",[1413,9557,9050],{},[1413,9559,9560],{},"Duplicate ID",[1395,9562,9563,9568,9570],{},[1413,9564,9565],{},[978,9566,9567],{},"VAL020",[1413,9569,9050],{},[1413,9571,9572],{},"Module depends on unknown module",[1395,9574,9575,9580,9582],{},[1413,9576,9577],{},[978,9578,9579],{},"VAL021",[1413,9581,9063],{},[1413,9583,9584],{},"Event references unknown payload",[1395,9586,9587,9592,9594],{},[1413,9588,9589],{},[978,9590,9591],{},"VAL022",[1413,9593,9063],{},[1413,9595,9596],{},"Action references unknown input",[1395,9598,9599,9604,9606],{},[1413,9600,9601],{},[978,9602,9603],{},"VAL023",[1413,9605,9063],{},[1413,9607,9608],{},"Action references unknown output",[1395,9610,9611,9616,9618],{},[1413,9612,9613],{},[978,9614,9615],{},"VAL030",[1413,9617,9050],{},[1413,9619,9620],{},"Workflow edge references unknown source node",[1395,9622,9623,9628,9630],{},[1413,9624,9625],{},[978,9626,9627],{},"VAL031",[1413,9629,9050],{},[1413,9631,9632],{},"Workflow edge references unknown target node",[1395,9634,9635,9640,9642],{},[1413,9636,9637],{},[978,9638,9639],{},"VAL040",[1413,9641,9050],{},[1413,9643,9644],{},"Module has no name",[1395,9646,9647,9652,9654],{},[1413,9648,9649],{},[978,9650,9651],{},"VAL041",[1413,9653,9063],{},[1413,9655,9656,9657],{},"Module ID should start with ",[978,9658,9659],{},"module.",[1030,9661,9663],{"id":9662},"resolver","Resolver",[1006,9665,9666,8071,9670,1004],{"icon":945},[988,9667,8068,9668],{},[978,9669,992],{},[978,9671,9672],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fresolver",[974,9674,1581,9675,9677],{},[978,9676,9662],{}," package performs topological sorting of modules based on dependencies\nusing Kahn's algorithm with cycle detection.",[1091,9679,9681],{"className":1093,"code":9680,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fresolver\"\n\nsorted, diag := resolver.Resolve(doc.Modules)\n\u002F\u002F sorted — modules in dependency order (dependencies first)\n\u002F\u002F diag — errors for duplicates, unknown deps, cycles\n",[978,9682,9683,9688,9692,9697,9702],{"__ignoreMap":1096},[1100,9684,9685],{"class":1102,"line":10},[1100,9686,9687],{},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fresolver\"\n",[1100,9689,9690],{"class":1102,"line":16},[1100,9691,1126],{"emptyLinePlaceholder":1125},[1100,9693,9694],{"class":1102,"line":22},[1100,9695,9696],{},"sorted, diag := resolver.Resolve(doc.Modules)\n",[1100,9698,9699],{"class":1102,"line":51},[1100,9700,9701],{},"\u002F\u002F sorted — modules in dependency order (dependencies first)\n",[1100,9703,9704],{"class":1102,"line":57},[1100,9705,9706],{},"\u002F\u002F diag — errors for duplicates, unknown deps, cycles\n",[1389,9708,9709,9719],{},[1392,9710,9711],{},[1395,9712,9713,9715,9717],{},[1398,9714,9032],{},[1398,9716,9035],{},[1398,9718,9038],{},[1408,9720,9721,9733,9745,9757],{},[1395,9722,9723,9728,9730],{},[1413,9724,9725],{},[978,9726,9727],{},"RES001",[1413,9729,9050],{},[1413,9731,9732],{},"Duplicate module ID",[1395,9734,9735,9740,9742],{},[1413,9736,9737],{},[978,9738,9739],{},"RES002",[1413,9741,9050],{},[1413,9743,9744],{},"Depends on unknown module",[1395,9746,9747,9752,9754],{},[1413,9748,9749],{},[978,9750,9751],{},"RES003",[1413,9753,9050],{},[1413,9755,9756],{},"Circular dependency detected",[1395,9758,9759,9764,9766],{},[1413,9760,9761],{},[978,9762,9763],{},"RES004",[1413,9765,9050],{},[1413,9767,9768],{},"Module is part of a dependency cycle",[1030,9770,9772],{"id":9771},"metadata","Metadata",[1006,9774,9775,8071,9779,1004],{"icon":945},[988,9776,8068,9777],{},[978,9778,992],{},[978,9780,9781],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fmetadata",[974,9783,1581,9784,9786],{},[978,9785,9771],{}," package extracts and stamps document metadata.",[1091,9788,9790],{"className":1093,"code":9789,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fmetadata\"\n\ninfo := metadata.Extract(doc)\n\u002F\u002F info.Title, info.Version, info.Compiler\n\u002F\u002F info.ModuleCount, info.EntityCount, info.EventCount\n\u002F\u002F info.ActionCount, info.WorkflowCount, info.ChannelCount, info.ScheduleCount\n\nmetadata.Stamp(doc) \u002F\u002F adds compiler version + compiled_at to doc.Metadata\n\nnames := metadata.ModuleNames(doc)  \u002F\u002F []string\nnames = metadata.ActionNames(doc)   \u002F\u002F []string\nnames = metadata.EventNames(doc)    \u002F\u002F []string\n",[978,9791,9792,9797,9801,9806,9811,9816,9821,9825,9830,9834,9839,9844],{"__ignoreMap":1096},[1100,9793,9794],{"class":1102,"line":10},[1100,9795,9796],{},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fmetadata\"\n",[1100,9798,9799],{"class":1102,"line":16},[1100,9800,1126],{"emptyLinePlaceholder":1125},[1100,9802,9803],{"class":1102,"line":22},[1100,9804,9805],{},"info := metadata.Extract(doc)\n",[1100,9807,9808],{"class":1102,"line":51},[1100,9809,9810],{},"\u002F\u002F info.Title, info.Version, info.Compiler\n",[1100,9812,9813],{"class":1102,"line":57},[1100,9814,9815],{},"\u002F\u002F info.ModuleCount, info.EntityCount, info.EventCount\n",[1100,9817,9818],{"class":1102,"line":102},[1100,9819,9820],{},"\u002F\u002F info.ActionCount, info.WorkflowCount, info.ChannelCount, info.ScheduleCount\n",[1100,9822,9823],{"class":1102,"line":108},[1100,9824,1126],{"emptyLinePlaceholder":1125},[1100,9826,9827],{"class":1102,"line":114},[1100,9828,9829],{},"metadata.Stamp(doc) \u002F\u002F adds compiler version + compiled_at to doc.Metadata\n",[1100,9831,9832],{"class":1102,"line":120},[1100,9833,1126],{"emptyLinePlaceholder":1125},[1100,9835,9836],{"class":1102,"line":181},[1100,9837,9838],{},"names := metadata.ModuleNames(doc)  \u002F\u002F []string\n",[1100,9840,9841],{"class":1102,"line":186},[1100,9842,9843],{},"names = metadata.ActionNames(doc)   \u002F\u002F []string\n",[1100,9845,9846],{"class":1102,"line":191},[1100,9847,9848],{},"names = metadata.EventNames(doc)    \u002F\u002F []string\n",[1030,9850,9852],{"id":9851},"asyncapi-importexport","AsyncAPI import\u002Fexport",[974,9854,1581,9855,9858],{},[978,9856,9857],{},"core\u002Fasyncapi"," package imports AsyncAPI 2.x YAML documents and exports IR Documents\nto AsyncAPI 2.6 YAML.",[1086,9860,9163],{"id":9861},"import",[1091,9863,9865],{"className":1093,"code":9864,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcore\u002Fasyncapi\"\n\ndoc, err := asyncapi.ImportYAML(reader) \u002F\u002F AsyncAPI YAML → *ir.Document\n",[978,9866,9867,9872,9876],{"__ignoreMap":1096},[1100,9868,9869],{"class":1102,"line":10},[1100,9870,9871],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcore\u002Fasyncapi\"\n",[1100,9873,9874],{"class":1102,"line":16},[1100,9875,1126],{"emptyLinePlaceholder":1125},[1100,9877,9878],{"class":1102,"line":22},[1100,9879,9880],{},"doc, err := asyncapi.ImportYAML(reader) \u002F\u002F AsyncAPI YAML → *ir.Document\n",[974,9882,9883,9884,9887],{},"Converts channels to ",[978,9885,9886],{},"ir.Channel"," with publish\u002Fsubscribe events, Kafka group ID bindings.",[1086,9889,9166],{"id":9890},"export",[1091,9892,9894],{"className":1093,"code":9893,"language":1095,"meta":1096,"style":1096},"err := asyncapi.Export(doc, writer) \u002F\u002F *ir.Document → AsyncAPI 2.6 YAML\n",[978,9895,9896],{"__ignoreMap":1096},[1100,9897,9898],{"class":1102,"line":10},[1100,9899,9893],{},[974,9901,9902],{},"Export behavior:",[3483,9904,9905,9908,9915],{},[1049,9906,9907],{},"IR channels with publish\u002Fsubscribe events → AsyncAPI channels",[1049,9909,9910,9911,9914],{},"If no channels exist but events are present, creates synthetic channels using ",[978,9912,9913],{},"entity_type.action"," as topic name",[1049,9916,9917],{},"Kafka bindings for group IDs",[1030,9919,9921],{"id":9920},"business-model-importexport","Business model import\u002Fexport",[1006,9923,9924,8071,9928,1004],{"icon":945},[988,9925,8068,9926],{},[978,9927,992],{},[978,9929,9930],{},"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fbusinessmodel",[974,9932,1581,9933,9936],{},[978,9934,9935],{},"businessmodel"," package parses business model YAML and converts to\u002Ffrom IR.",[1086,9938,9163],{"id":9939},"import-1",[1091,9941,9943],{"className":1093,"code":9942,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fbusinessmodel\"\n\nspec, err := businessmodel.ParseYAML(reader) \u002F\u002F YAML → *Spec\ndoc := businessmodel.Normalize(spec)          \u002F\u002F *Spec → *ir.Document\n",[978,9944,9945,9950,9954,9959],{"__ignoreMap":1096},[1100,9946,9947],{"class":1102,"line":10},[1100,9948,9949],{},"import \"github.com\u002Fredelay\u002Fgo-flowdsl\u002Fbusinessmodel\"\n",[1100,9951,9952],{"class":1102,"line":16},[1100,9953,1126],{"emptyLinePlaceholder":1125},[1100,9955,9956],{"class":1102,"line":22},[1100,9957,9958],{},"spec, err := businessmodel.ParseYAML(reader) \u002F\u002F YAML → *Spec\n",[1100,9960,9961],{"class":1102,"line":51},[1100,9962,9963],{},"doc := businessmodel.Normalize(spec)          \u002F\u002F *Spec → *ir.Document\n",[1086,9965,9166],{"id":9966},"export-1",[1091,9968,9970],{"className":1093,"code":9969,"language":1095,"meta":1096,"style":1096},"err := businessmodel.Export(doc, writer) \u002F\u002F *ir.Document → business model YAML\n",[978,9971,9972],{"__ignoreMap":1096},[1100,9973,9974],{"class":1102,"line":10},[1100,9975,9969],{},[974,9977,9902],{},[3483,9979,9980,9983],{},[1049,9981,9982],{},"If modules exist, each module becomes a domain with entities, events, and processes (workflows)",[1049,9984,9985],{},"If no modules exist, creates a single domain from top-level items",[1086,9987,9989],{"id":9988},"business-model-yaml-format","Business model YAML format",[1091,9991,9993],{"className":8524,"code":9992,"language":8526,"meta":1096,"style":1096},"title: E-Commerce Platform\nversion: \"1.0\"\ndomains:\n  - name: billing\n    description: Billing domain\n    entities:\n      - name: invoice\n        fields: [amount, currency, status]\n    events:\n      - name: invoice_created\n        entity_type: invoice\n        action: created\n    processes:\n      - name: payment_flow\n        triggers: [invoice_created]\n        steps: [validate, charge, confirm]\n",[978,9994,9995,10004,10016,10023,10035,10043,10050,10060,10084,10091,10101,10110,10118,10125,10135,10148],{"__ignoreMap":1096},[1100,9996,9997,9999,10001],{"class":1102,"line":10},[1100,9998,8548],{"class":8533},[1100,10000,1044],{"class":6637},[1100,10002,10003],{"class":6689}," E-Commerce Platform\n",[1100,10005,10006,10008,10010,10012,10014],{"class":1102,"line":16},[1100,10007,8534],{"class":8533},[1100,10009,1044],{"class":6637},[1100,10011,6686],{"class":6637},[1100,10013,8541],{"class":6689},[1100,10015,6713],{"class":6637},[1100,10017,10018,10021],{"class":1102,"line":22},[1100,10019,10020],{"class":8533},"domains",[1100,10022,8561],{"class":6637},[1100,10024,10025,10028,10031,10033],{"class":1102,"line":51},[1100,10026,10027],{"class":6637},"  -",[1100,10029,10030],{"class":8533}," name",[1100,10032,1044],{"class":6637},[1100,10034,8578],{"class":6689},[1100,10036,10037,10039,10041],{"class":1102,"line":57},[1100,10038,8583],{"class":8533},[1100,10040,1044],{"class":6637},[1100,10042,8588],{"class":6689},[1100,10044,10045,10048],{"class":1102,"line":102},[1100,10046,10047],{"class":8533},"    entities",[1100,10049,8561],{"class":6637},[1100,10051,10052,10054,10056,10058],{"class":1102,"line":108},[1100,10053,8863],{"class":6637},[1100,10055,10030],{"class":8533},[1100,10057,1044],{"class":6637},[1100,10059,8641],{"class":6689},[1100,10061,10062,10065,10067,10069,10072,10074,10077,10079,10082],{"class":1102,"line":114},[1100,10063,10064],{"class":8533},"        fields",[1100,10066,1044],{"class":6637},[1100,10068,8598],{"class":6637},[1100,10070,10071],{"class":6689},"amount",[1100,10073,8669],{"class":6637},[1100,10075,10076],{"class":6689}," currency",[1100,10078,8669],{"class":6637},[1100,10080,10081],{"class":6689}," status",[1100,10083,8604],{"class":6637},[1100,10085,10086,10089],{"class":1102,"line":120},[1100,10087,10088],{"class":8533},"    events",[1100,10090,8561],{"class":6637},[1100,10092,10093,10095,10097,10099],{"class":1102,"line":181},[1100,10094,8863],{"class":6637},[1100,10096,10030],{"class":8533},[1100,10098,1044],{"class":6637},[1100,10100,8732],{"class":6689},[1100,10102,10103,10106,10108],{"class":1102,"line":186},[1100,10104,10105],{"class":8533},"        entity_type",[1100,10107,1044],{"class":6637},[1100,10109,8641],{"class":6689},[1100,10111,10112,10114,10116],{"class":1102,"line":191},[1100,10113,8886],{"class":8533},[1100,10115,1044],{"class":6637},[1100,10117,8751],{"class":6689},[1100,10119,10120,10123],{"class":1102,"line":197},[1100,10121,10122],{"class":8533},"    processes",[1100,10124,8561],{"class":6637},[1100,10126,10127,10129,10131,10133],{"class":1102,"line":203},[1100,10128,8863],{"class":6637},[1100,10130,10030],{"class":8533},[1100,10132,1044],{"class":6637},[1100,10134,8837],{"class":6689},[1100,10136,10137,10140,10142,10144,10146],{"class":1102,"line":214},[1100,10138,10139],{"class":8533},"        triggers",[1100,10141,1044],{"class":6637},[1100,10143,8598],{"class":6637},[1100,10145,8849],{"class":6689},[1100,10147,8604],{"class":6637},[1100,10149,10150,10153,10155,10157,10159,10161,10164,10166,10169],{"class":1102,"line":223},[1100,10151,10152],{"class":8533},"        steps",[1100,10154,1044],{"class":6637},[1100,10156,8598],{"class":6637},[1100,10158,9463],{"class":6689},[1100,10160,8669],{"class":6637},[1100,10162,10163],{"class":6689}," charge",[1100,10165,8669],{"class":6637},[1100,10167,10168],{"class":6689}," confirm",[1100,10170,8604],{"class":6637},[1030,10172,10174],{"id":10173},"openapi-tool-schema-export","OpenAPI tool schema export",[974,10176,1581,10177,10180],{},[978,10178,10179],{},"core\u002Fopenapi"," package also provides functions for exporting IR Documents as\nfunction-calling tool definitions (JSON Schema format compatible with LLM tool-use APIs).",[1086,10182,10184],{"id":10183},"export-functions","Export functions",[1091,10186,10188],{"className":1093,"code":10187,"language":1095,"meta":1096,"style":1096},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcore\u002Fopenapi\"\n\ntools := openapi.ExportActions(doc)   \u002F\u002F IR actions → tool definitions\ntools = openapi.ExportEntities(doc)   \u002F\u002F IR entities → get_\u003Cname> lookup tools\ntools = openapi.ExportCommands(doc)   \u002F\u002F IR commands → tool definitions\ntools = openapi.ExportAll(doc)        \u002F\u002F all of the above combined\nerr := openapi.WriteTools(writer, tools) \u002F\u002F serialize as JSON\n",[978,10189,10190,10195,10199,10204,10209,10214,10219],{"__ignoreMap":1096},[1100,10191,10192],{"class":1102,"line":10},[1100,10193,10194],{},"import \"github.com\u002Fredelay\u002Fgo-framework\u002Fcore\u002Fopenapi\"\n",[1100,10196,10197],{"class":1102,"line":16},[1100,10198,1126],{"emptyLinePlaceholder":1125},[1100,10200,10201],{"class":1102,"line":22},[1100,10202,10203],{},"tools := openapi.ExportActions(doc)   \u002F\u002F IR actions → tool definitions\n",[1100,10205,10206],{"class":1102,"line":51},[1100,10207,10208],{},"tools = openapi.ExportEntities(doc)   \u002F\u002F IR entities → get_\u003Cname> lookup tools\n",[1100,10210,10211],{"class":1102,"line":57},[1100,10212,10213],{},"tools = openapi.ExportCommands(doc)   \u002F\u002F IR commands → tool definitions\n",[1100,10215,10216],{"class":1102,"line":102},[1100,10217,10218],{},"tools = openapi.ExportAll(doc)        \u002F\u002F all of the above combined\n",[1100,10220,10221],{"class":1102,"line":108},[1100,10222,10223],{},"err := openapi.WriteTools(writer, tools) \u002F\u002F serialize as JSON\n",[1086,10225,10227],{"id":10226},"output-format","Output format",[1091,10229,10231],{"className":6628,"code":10230,"language":6630,"meta":1096,"style":1096},"[\n  {\n    \"type\": \"function\",\n    \"function\": {\n      \"name\": \"create_invoice\",\n      \"description\": \"Create a new invoice\",\n      \"parameters\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"amount\": { \"type\": \"number\", \"description\": \"Invoice amount\" },\n          \"currency\": { \"type\": \"string\" }\n        },\n        \"required\": [\"amount\"]\n      }\n    }\n  }\n]\n",[978,10232,10233,10238,10243,10262,10274,10293,10312,10325,10344,10357,10406,10435,10439,10459,10464,10468,10472],{"__ignoreMap":1096},[1100,10234,10235],{"class":1102,"line":10},[1100,10236,10237],{"class":6637},"[\n",[1100,10239,10240],{"class":1102,"line":16},[1100,10241,10242],{"class":6637},"  {\n",[1100,10244,10245,10247,10249,10251,10253,10255,10258,10260],{"class":1102,"line":22},[1100,10246,6660],{"class":6637},[1100,10248,6679],{"class":6646},[1100,10250,6650],{"class":6637},[1100,10252,1044],{"class":6637},[1100,10254,6686],{"class":6637},[1100,10256,10257],{"class":6689},"function",[1100,10259,6650],{"class":6637},[1100,10261,2190],{"class":6637},[1100,10263,10264,10266,10268,10270,10272],{"class":1102,"line":51},[1100,10265,6660],{"class":6637},[1100,10267,10257],{"class":6646},[1100,10269,6650],{"class":6637},[1100,10271,1044],{"class":6637},[1100,10273,6655],{"class":6637},[1100,10275,10276,10278,10280,10282,10284,10286,10289,10291],{"class":1102,"line":57},[1100,10277,6675],{"class":6637},[1100,10279,5156],{"class":6663},[1100,10281,6650],{"class":6637},[1100,10283,1044],{"class":6637},[1100,10285,6686],{"class":6637},[1100,10287,10288],{"class":6689},"create_invoice",[1100,10290,6650],{"class":6637},[1100,10292,2190],{"class":6637},[1100,10294,10295,10297,10299,10301,10303,10305,10308,10310],{"class":1102,"line":102},[1100,10296,6675],{"class":6637},[1100,10298,5159],{"class":6663},[1100,10300,6650],{"class":6637},[1100,10302,1044],{"class":6637},[1100,10304,6686],{"class":6637},[1100,10306,10307],{"class":6689},"Create a new invoice",[1100,10309,6650],{"class":6637},[1100,10311,2190],{"class":6637},[1100,10313,10314,10316,10319,10321,10323],{"class":1102,"line":108},[1100,10315,6675],{"class":6637},[1100,10317,10318],{"class":6663},"parameters",[1100,10320,6650],{"class":6637},[1100,10322,1044],{"class":6637},[1100,10324,6655],{"class":6637},[1100,10326,10327,10330,10332,10334,10336,10338,10340,10342],{"class":1102,"line":114},[1100,10328,10329],{"class":6637},"        \"",[1100,10331,6679],{"class":6678},[1100,10333,6650],{"class":6637},[1100,10335,1044],{"class":6637},[1100,10337,6686],{"class":6637},[1100,10339,8265],{"class":6689},[1100,10341,6650],{"class":6637},[1100,10343,2190],{"class":6637},[1100,10345,10346,10348,10351,10353,10355],{"class":1102,"line":120},[1100,10347,10329],{"class":6637},[1100,10349,10350],{"class":6678},"properties",[1100,10352,6650],{"class":6637},[1100,10354,1044],{"class":6637},[1100,10356,6655],{"class":6637},[1100,10358,10359,10362,10364,10366,10368,10370,10372,10375,10377,10379,10381,10384,10386,10388,10390,10392,10394,10396,10398,10401,10403],{"class":1102,"line":181},[1100,10360,10361],{"class":6637},"          \"",[1100,10363,10071],{"class":8533},[1100,10365,6650],{"class":6637},[1100,10367,1044],{"class":6637},[1100,10369,8658],{"class":6637},[1100,10371,6686],{"class":6637},[1100,10373,6679],{"class":10374},"su27w",[1100,10376,6650],{"class":6637},[1100,10378,1044],{"class":6637},[1100,10380,6686],{"class":6637},[1100,10382,10383],{"class":6689},"number",[1100,10385,6650],{"class":6637},[1100,10387,8669],{"class":6637},[1100,10389,6686],{"class":6637},[1100,10391,5159],{"class":10374},[1100,10393,6650],{"class":6637},[1100,10395,1044],{"class":6637},[1100,10397,6686],{"class":6637},[1100,10399,10400],{"class":6689},"Invoice amount",[1100,10402,6650],{"class":6637},[1100,10404,10405],{"class":6637}," },\n",[1100,10407,10408,10410,10413,10415,10417,10419,10421,10423,10425,10427,10429,10431,10433],{"class":1102,"line":186},[1100,10409,10361],{"class":6637},[1100,10411,10412],{"class":8533},"currency",[1100,10414,6650],{"class":6637},[1100,10416,1044],{"class":6637},[1100,10418,8658],{"class":6637},[1100,10420,6686],{"class":6637},[1100,10422,6679],{"class":10374},[1100,10424,6650],{"class":6637},[1100,10426,1044],{"class":6637},[1100,10428,6686],{"class":6637},[1100,10430,2557],{"class":6689},[1100,10432,6650],{"class":6637},[1100,10434,8681],{"class":6637},[1100,10436,10437],{"class":1102,"line":191},[1100,10438,2515],{"class":6637},[1100,10440,10441,10443,10445,10447,10449,10451,10453,10455,10457],{"class":1102,"line":197},[1100,10442,10329],{"class":6637},[1100,10444,6227],{"class":6678},[1100,10446,6650],{"class":6637},[1100,10448,1044],{"class":6637},[1100,10450,8598],{"class":6637},[1100,10452,6650],{"class":6637},[1100,10454,10071],{"class":6689},[1100,10456,6650],{"class":6637},[1100,10458,8604],{"class":6637},[1100,10460,10461],{"class":1102,"line":203},[1100,10462,10463],{"class":6637},"      }\n",[1100,10465,10466],{"class":1102,"line":214},[1100,10467,2467],{"class":6637},[1100,10469,10470],{"class":1102,"line":223},[1100,10471,6722],{"class":6637},[1100,10473,10474],{"class":1102,"line":229},[1100,10475,8604],{"class":6637},[1086,10477,10479],{"id":10478},"type-mapping-ir-json-schema","Type mapping (IR → JSON Schema)",[1389,10481,10482,10492],{},[1392,10483,10484],{},[1395,10485,10486,10489],{},[1398,10487,10488],{},"IR FieldType",[1398,10490,10491],{},"JSON Schema type",[1408,10493,10494,10511,10522,10533,10544,10555],{},[1395,10495,10496,10506],{},[1413,10497,10498,1068,10500,1068,10502,1068,10504],{},[978,10499,2557],{},[978,10501,8268],{},[978,10503,8271],{},[978,10505,8274],{},[1413,10507,10508],{},[978,10509,10510],{},"\"string\"",[1395,10512,10513,10517],{},[1413,10514,10515],{},[978,10516,8253],{},[1413,10518,10519],{},[978,10520,10521],{},"\"integer\"",[1395,10523,10524,10528],{},[1413,10525,10526],{},[978,10527,8256],{},[1413,10529,10530],{},[978,10531,10532],{},"\"number\"",[1395,10534,10535,10539],{},[1413,10536,10537],{},[978,10538,8259],{},[1413,10540,10541],{},[978,10542,10543],{},"\"boolean\"",[1395,10545,10546,10550],{},[1413,10547,10548],{},[978,10549,8262],{},[1413,10551,10552],{},[978,10553,10554],{},"\"array\"",[1395,10556,10557,10561],{},[1413,10558,10559],{},[978,10560,8265],{},[1413,10562,10563],{},[978,10564,10565],{},"\"object\"",[974,10567,10568,10569,1068,10571,10574,10575,1068,10577,2194,10580,10582],{},"Supports nested ",[978,10570,10350],{},[978,10572,10573],{},"items"," for arrays, ",[978,10576,8283],{},[978,10578,10579],{},"format",[978,10581,6227],{}," fields.",[1030,10584,10586],{"id":10585},"cli","CLI",[974,10588,10589,10590,10596],{},"There are ",[988,10591,10592,10593],{},"two binaries named ",[978,10594,10595],{},"redelayctl"," in the Redelay source tree, with non-overlapping responsibilities. Which one you invoke depends on whether you're shaping a spec or operating a live app:",[1389,10598,10599,10614],{},[1392,10600,10601],{},[1395,10602,10603,10606,10609,10611],{},[1398,10604,10605],{},"Binary",[1398,10607,10608],{},"Source path",[1398,10610,3357],{},[1398,10612,10613],{},"Imports",[1408,10615,10616,10639],{},[1395,10617,10618,10625,10630,10636],{},[1413,10619,10620],{},[988,10621,10622,10623],{},"Framework ",[978,10624,10595],{},[1413,10626,10627],{},[978,10628,10629],{},"go-framework\u002Fcmd\u002Fredelayctl",[1413,10631,10632,10633,10635],{},"Build-time YAML\u002FIR tooling — validate, import, export, convert, ",[978,10634,8084],{}," dump. Works on files.",[1413,10637,10638],{},"Zero module factories.",[1395,10640,10641,10648,10653,10666],{},[1413,10642,10643],{},[988,10644,10645,10646],{},"Project ",[978,10647,10595],{},[1413,10649,10650],{},[978,10651,10652],{},"backend\u002Fcmd\u002Fredelayctl",[1413,10654,10655,10656,1068,10659,10662,10663,10665],{},"Runtime operations — ",[978,10657,10658],{},"create-user",[978,10660,10661],{},"assistant-ingest-docs",", anything a module contributes via ",[978,10664,3053],{},". Works against a live app.",[1413,10667,10668,10669,1004],{},"CLI module bundle at ",[978,10670,3042],{},[974,10672,10673],{},"Both names are intentional: one works on source files, the other works against a running process. They never share an import graph.",[1086,10675,10645,10677,10679],{"id":10676},"project-redelayctl-contributing-commands-from-a-module",[978,10678,10595],{}," — contributing commands from a module",[974,10681,10682,10683,10685,10686,1044],{},"Any module that satisfies the ",[978,10684,3053],{}," interface gets its commands auto-discovered at bootstrap and exposed through ",[978,10687,10688],{},".\u002Fcmd\u002Fredelayctl",[1091,10690,10692],{"className":1093,"code":10691,"language":1095,"meta":1096,"style":1096},"type CLIProvider interface {\n    CLICommands() []CLICommand\n}\n\ntype CLICommand struct {\n    Name, Description string\n    Args    []CLIArg\n    Handler func(ctx context.Context, args map[string]string) error\n}\ntype CLIArg struct {\n    Name, Description, Default string\n    Required, Flag             bool\n}\n",[978,10693,10694,10699,10704,10708,10712,10717,10722,10727,10732,10736,10741,10746,10751],{"__ignoreMap":1096},[1100,10695,10696],{"class":1102,"line":10},[1100,10697,10698],{},"type CLIProvider interface {\n",[1100,10700,10701],{"class":1102,"line":16},[1100,10702,10703],{},"    CLICommands() []CLICommand\n",[1100,10705,10706],{"class":1102,"line":22},[1100,10707,1226],{},[1100,10709,10710],{"class":1102,"line":51},[1100,10711,1126],{"emptyLinePlaceholder":1125},[1100,10713,10714],{"class":1102,"line":57},[1100,10715,10716],{},"type CLICommand struct {\n",[1100,10718,10719],{"class":1102,"line":102},[1100,10720,10721],{},"    Name, Description string\n",[1100,10723,10724],{"class":1102,"line":108},[1100,10725,10726],{},"    Args    []CLIArg\n",[1100,10728,10729],{"class":1102,"line":114},[1100,10730,10731],{},"    Handler func(ctx context.Context, args map[string]string) error\n",[1100,10733,10734],{"class":1102,"line":120},[1100,10735,1226],{},[1100,10737,10738],{"class":1102,"line":181},[1100,10739,10740],{},"type CLIArg struct {\n",[1100,10742,10743],{"class":1102,"line":186},[1100,10744,10745],{},"    Name, Description, Default string\n",[1100,10747,10748],{"class":1102,"line":191},[1100,10749,10750],{},"    Required, Flag             bool\n",[1100,10752,10753],{"class":1102,"line":197},[1100,10754,1226],{},[974,10756,2411,10757,10760,10761,10766,10767,10770,10771,10774],{},[978,10758,10759],{},"CLICommands()"," on the module struct, blank-import the module in ",[1014,10762,10764],{"href":3127,"rel":10763},[3096],[978,10765,3131],{},", and the command shows up in ",[978,10768,10769],{},"redelayctl help",". The framework also auto-exposes every command as an MCP tool (prefixed ",[978,10772,10773],{},"cli_",") so AI assistants can invoke the same operations.",[974,10776,10777,10778,10781],{},"Invoke via the Makefile helper (loads ",[978,10779,10780],{},".env",", resolves module bundle):",[1091,10783,10787],{"className":10784,"code":10785,"language":10786,"meta":1096,"style":1096},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","make redelayctl ARGS=\"help\"\nmake redelayctl ARGS=\"create-user --email=admin@test.com --password=secret --superuser\"\nmake redelayctl ARGS=\"assistant-ingest-docs --docs-dir ..\u002Fspec\u002Fdocs --url-base https:\u002F\u002Fredelay.com\u002Fdocs\"\n","bash",[978,10788,10789,10807,10822],{"__ignoreMap":1096},[1100,10790,10791,10794,10797,10800,10802,10805],{"class":1102,"line":10},[1100,10792,10793],{"class":6663},"make",[1100,10795,10796],{"class":6689}," redelayctl",[1100,10798,10799],{"class":6689}," ARGS=",[1100,10801,6650],{"class":6637},[1100,10803,10804],{"class":6689},"help",[1100,10806,6713],{"class":6637},[1100,10808,10809,10811,10813,10815,10817,10820],{"class":1102,"line":16},[1100,10810,10793],{"class":6663},[1100,10812,10796],{"class":6689},[1100,10814,10799],{"class":6689},[1100,10816,6650],{"class":6637},[1100,10818,10819],{"class":6689},"create-user --email=admin@test.com --password=secret --superuser",[1100,10821,6713],{"class":6637},[1100,10823,10824,10826,10828,10830,10832,10835],{"class":1102,"line":22},[1100,10825,10793],{"class":6663},[1100,10827,10796],{"class":6689},[1100,10829,10799],{"class":6689},[1100,10831,6650],{"class":6637},[1100,10833,10834],{"class":6689},"assistant-ingest-docs --docs-dir ..\u002Fspec\u002Fdocs --url-base https:\u002F\u002Fredelay.com\u002Fdocs",[1100,10836,6713],{"class":6637},[974,10838,10839,10842],{},[978,10840,10841],{},"make cli"," is kept as an alias for scripts pinned to the old name.",[974,10844,10845,10848],{},[988,10846,10847],{},"Currently registered commands"," (expand as modules declare more):",[1389,10850,10851,10861],{},[1392,10852,10853],{},[1395,10854,10855,10857,10859],{},[1398,10856,8303],{},[1398,10858,2813],{},[1398,10860,3357],{},[1408,10862,10863,10878],{},[1395,10864,10865,10869,10871],{},[1413,10866,10867],{},[978,10868,10658],{},[1413,10870,3303],{},[1413,10872,10873,10874,10877],{},"Create a user with optional ",[978,10875,10876],{},"--superuser"," flag. Bootstrap the first admin.",[1395,10879,10880,10884,10886],{},[1413,10881,10882],{},[978,10883,10661],{},[1413,10885,3306],{},[1413,10887,10888],{},"Walk a markdown directory, chunk by heading, embed, and upsert into the search index so the RAG assistant flow can ground answers in project docs.",[974,10890,10891,10892,10895,10896,10899],{},"Dockerfiles ship both ",[978,10893,10894],{},"\u002Fredelayctl"," and a ",[978,10897,10898],{},"\u002Fcli"," alias; the alias will be removed in a future release once scripts have migrated.",[1030,10901,10622,10903,10905],{"id":10902},"framework-redelayctl-spec-tooling",[978,10904,10595],{}," — spec tooling",[974,10907,10908,10910],{},[978,10909,10595],{}," (the framework binary) is the command-line tool for working with Redelay specifications.",[1086,10912,10914],{"id":10913},"commands","Commands",[1091,10916,10918],{"className":10784,"code":10917,"language":10786,"meta":1096,"style":1096},"# Validate a FlowDSL file (runs FlowDSL + IR validation)\nredelayctl validate app.flow.yaml\n\n# Import a source format and output IR as JSON\nredelayctl import openapi api.yaml\nredelayctl import asyncapi events.yaml\nredelayctl import businessmodel model.yaml\n\n# Export IR to a target format\nredelayctl export json app.ir.yaml      # IR → JSON\nredelayctl export yaml app.ir.json      # IR → YAML\nredelayctl export flowdsl app.ir.json   # IR → FlowDSL YAML\nredelayctl export openapi app.ir.json   # IR → OpenAPI tool schema JSON\n\n# Convert between formats directly\nredelayctl convert flowdsl json app.flow.yaml\nredelayctl convert openapi flowdsl api.yaml\nredelayctl convert asyncapi openapi events.yaml\n\n# Parse FlowDSL and output canonical IR\nredelayctl ir app.flow.yaml\n\n# Print version\nredelayctl version\n",[978,10919,10920,10926,10936,10940,10945,10958,10970,10982,10986,10991,11007,11022,11036,11049,11053,11058,11071,11083,11095,11099,11104,11113,11117,11122],{"__ignoreMap":1096},[1100,10921,10922],{"class":1102,"line":10},[1100,10923,10925],{"class":10924},"sHwdD","# Validate a FlowDSL file (runs FlowDSL + IR validation)\n",[1100,10927,10928,10930,10933],{"class":1102,"line":16},[1100,10929,10595],{"class":6663},[1100,10931,10932],{"class":6689}," validate",[1100,10934,10935],{"class":6689}," app.flow.yaml\n",[1100,10937,10938],{"class":1102,"line":22},[1100,10939,1126],{"emptyLinePlaceholder":1125},[1100,10941,10942],{"class":1102,"line":51},[1100,10943,10944],{"class":10924},"# Import a source format and output IR as JSON\n",[1100,10946,10947,10949,10952,10955],{"class":1102,"line":57},[1100,10948,10595],{"class":6663},[1100,10950,10951],{"class":6689}," import",[1100,10953,10954],{"class":6689}," openapi",[1100,10956,10957],{"class":6689}," api.yaml\n",[1100,10959,10960,10962,10964,10967],{"class":1102,"line":102},[1100,10961,10595],{"class":6663},[1100,10963,10951],{"class":6689},[1100,10965,10966],{"class":6689}," asyncapi",[1100,10968,10969],{"class":6689}," events.yaml\n",[1100,10971,10972,10974,10976,10979],{"class":1102,"line":108},[1100,10973,10595],{"class":6663},[1100,10975,10951],{"class":6689},[1100,10977,10978],{"class":6689}," businessmodel",[1100,10980,10981],{"class":6689}," model.yaml\n",[1100,10983,10984],{"class":1102,"line":114},[1100,10985,1126],{"emptyLinePlaceholder":1125},[1100,10987,10988],{"class":1102,"line":120},[1100,10989,10990],{"class":10924},"# Export IR to a target format\n",[1100,10992,10993,10995,10998,11001,11004],{"class":1102,"line":181},[1100,10994,10595],{"class":6663},[1100,10996,10997],{"class":6689}," export",[1100,10999,11000],{"class":6689}," json",[1100,11002,11003],{"class":6689}," app.ir.yaml",[1100,11005,11006],{"class":10924},"      # IR → JSON\n",[1100,11008,11009,11011,11013,11016,11019],{"class":1102,"line":186},[1100,11010,10595],{"class":6663},[1100,11012,10997],{"class":6689},[1100,11014,11015],{"class":6689}," yaml",[1100,11017,11018],{"class":6689}," app.ir.json",[1100,11020,11021],{"class":10924},"      # IR → YAML\n",[1100,11023,11024,11026,11028,11031,11033],{"class":1102,"line":191},[1100,11025,10595],{"class":6663},[1100,11027,10997],{"class":6689},[1100,11029,11030],{"class":6689}," flowdsl",[1100,11032,11018],{"class":6689},[1100,11034,11035],{"class":10924},"   # IR → FlowDSL YAML\n",[1100,11037,11038,11040,11042,11044,11046],{"class":1102,"line":197},[1100,11039,10595],{"class":6663},[1100,11041,10997],{"class":6689},[1100,11043,10954],{"class":6689},[1100,11045,11018],{"class":6689},[1100,11047,11048],{"class":10924},"   # IR → OpenAPI tool schema JSON\n",[1100,11050,11051],{"class":1102,"line":203},[1100,11052,1126],{"emptyLinePlaceholder":1125},[1100,11054,11055],{"class":1102,"line":214},[1100,11056,11057],{"class":10924},"# Convert between formats directly\n",[1100,11059,11060,11062,11065,11067,11069],{"class":1102,"line":223},[1100,11061,10595],{"class":6663},[1100,11063,11064],{"class":6689}," convert",[1100,11066,11030],{"class":6689},[1100,11068,11000],{"class":6689},[1100,11070,10935],{"class":6689},[1100,11072,11073,11075,11077,11079,11081],{"class":1102,"line":229},[1100,11074,10595],{"class":6663},[1100,11076,11064],{"class":6689},[1100,11078,10954],{"class":6689},[1100,11080,11030],{"class":6689},[1100,11082,10957],{"class":6689},[1100,11084,11085,11087,11089,11091,11093],{"class":1102,"line":234},[1100,11086,10595],{"class":6663},[1100,11088,11064],{"class":6689},[1100,11090,10966],{"class":6689},[1100,11092,10954],{"class":6689},[1100,11094,10969],{"class":6689},[1100,11096,11097],{"class":1102,"line":240},[1100,11098,1126],{"emptyLinePlaceholder":1125},[1100,11100,11101],{"class":1102,"line":246},[1100,11102,11103],{"class":10924},"# Parse FlowDSL and output canonical IR\n",[1100,11105,11106,11108,11111],{"class":1102,"line":251},[1100,11107,10595],{"class":6663},[1100,11109,11110],{"class":6689}," ir",[1100,11112,10935],{"class":6689},[1100,11114,11115],{"class":1102,"line":257},[1100,11116,1126],{"emptyLinePlaceholder":1125},[1100,11118,11119],{"class":1102,"line":1361},[1100,11120,11121],{"class":10924},"# Print version\n",[1100,11123,11124,11126],{"class":1102,"line":5638},[1100,11125,10595],{"class":6663},[1100,11127,11128],{"class":6689}," version\n",[1086,11130,11132],{"id":11131},"supported-format-conversions","Supported format conversions",[1389,11134,11135,11145],{},[1392,11136,11137],{},[1395,11138,11139,11142],{},[1398,11140,11141],{},"From",[1398,11143,11144],{},"To",[1408,11146,11147,11163,11179,11196],{},[1395,11148,11149,11153],{},[1413,11150,11151],{},[978,11152,8463],{},[1413,11154,11155,1068,11157,1068,11159,1068,11161],{},[978,11156,6630],{},[978,11158,8526],{},[978,11160,8463],{},[978,11162,5836],{},[1395,11164,11165,11169],{},[1413,11166,11167],{},[978,11168,5836],{},[1413,11170,11171,1068,11173,1068,11175,1068,11177],{},[978,11172,6630],{},[978,11174,8526],{},[978,11176,8463],{},[978,11178,5836],{},[1395,11180,11181,11186],{},[1413,11182,11183],{},[978,11184,11185],{},"asyncapi",[1413,11187,11188,1068,11190,1068,11192,1068,11194],{},[978,11189,6630],{},[978,11191,8526],{},[978,11193,8463],{},[978,11195,5836],{},[1395,11197,11198,11202],{},[1413,11199,11200],{},[978,11201,9935],{},[1413,11203,11204,1068,11206,1068,11208,1068,11210],{},[978,11205,6630],{},[978,11207,8526],{},[978,11209,8463],{},[978,11211,5836],{},[11213,11214,11215],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .su27w, html code.shiki .su27w{--shiki-light:#916B53;--shiki-default:#916B53;--shiki-dark:#916B53}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1096,"searchDepth":16,"depth":16,"links":11217},[11218,11224,11229,11235,11236,11237,11251,11258,11265,11274,11283,11287,11294,11298,11303,11308,11309,11312,11313,11314,11318,11323,11328,11332],{"id":1032,"depth":16,"text":1033,"children":11219},[11220,11221,11222,11223],{"id":1088,"depth":22,"text":1089},{"id":1139,"depth":22,"text":1140},{"id":1176,"depth":22,"text":1177},{"id":1200,"depth":22,"text":1201},{"id":1245,"depth":16,"text":1246,"children":11225},[11226,11227,11228],{"id":1249,"depth":22,"text":1250},{"id":1367,"depth":22,"text":1368},{"id":1386,"depth":22,"text":1387},{"id":1577,"depth":16,"text":1578,"children":11230},[11231,11232,11233,11234],{"id":1588,"depth":22,"text":1589},{"id":1639,"depth":22,"text":1640},{"id":1695,"depth":22,"text":1696},{"id":1788,"depth":22,"text":1789},{"id":1834,"depth":16,"text":1835},{"id":1867,"depth":16,"text":1868},{"id":1912,"depth":16,"text":1913,"children":11238},[11239,11240,11241,11242,11244,11245,11246,11247,11248,11249,11250],{"id":1916,"depth":22,"text":1917},{"id":1997,"depth":22,"text":1998},{"id":2129,"depth":22,"text":2130},{"id":2213,"depth":22,"text":11243},"EventDefinition (go-events\u002Ftyped)",{"id":2407,"depth":22,"text":2408},{"id":2635,"depth":22,"text":2636},{"id":2678,"depth":22,"text":2679},{"id":2729,"depth":22,"text":2730},{"id":2821,"depth":22,"text":2822},{"id":2954,"depth":22,"text":2955},{"id":3314,"depth":22,"text":3315},{"id":3535,"depth":16,"text":3536,"children":11252},[11253,11254,11255,11256,11257],{"id":3539,"depth":22,"text":3539},{"id":3303,"depth":22,"text":3303},{"id":4714,"depth":22,"text":4714},{"id":4801,"depth":22,"text":4801},{"id":4911,"depth":22,"text":4912},{"id":5025,"depth":16,"text":5026,"children":11259},[11260,11261,11263,11264],{"id":5029,"depth":22,"text":5030},{"id":5368,"depth":22,"text":11262},"Per-user singleton — UserSingleton[T]",{"id":5511,"depth":22,"text":5512},{"id":5725,"depth":22,"text":5726},{"id":5830,"depth":16,"text":5831,"children":11266},[11267,11268,11269,11270,11271,11272,11273],{"id":5844,"depth":22,"text":5845},{"id":5895,"depth":22,"text":5896},{"id":5960,"depth":22,"text":5961},{"id":6019,"depth":22,"text":6020},{"id":6205,"depth":22,"text":6206},{"id":6298,"depth":22,"text":6299},{"id":6329,"depth":22,"text":6330},{"id":6474,"depth":16,"text":6475,"children":11275},[11276,11277,11278,11279,11280,11281,11282],{"id":6527,"depth":22,"text":6528},{"id":6618,"depth":22,"text":6619},{"id":6732,"depth":22,"text":6733},{"id":6877,"depth":22,"text":6878},{"id":6946,"depth":22,"text":6947},{"id":7014,"depth":22,"text":7015},{"id":7059,"depth":22,"text":6953},{"id":7209,"depth":16,"text":7210,"children":11284},[11285,11286],{"id":7213,"depth":22,"text":7214},{"id":7292,"depth":22,"text":7293},{"id":7763,"depth":16,"text":7764,"children":11288},[11289,11290,11291,11293],{"id":7767,"depth":22,"text":7768},{"id":7786,"depth":22,"text":7787},{"id":7799,"depth":22,"text":11292},"Cross-container coordination (runtime\u002Fcoordination)",{"id":7865,"depth":22,"text":7866},{"id":8062,"depth":16,"text":8063,"children":11295},[11296,11297],{"id":8088,"depth":22,"text":8089},{"id":8186,"depth":22,"text":8187},{"id":8463,"depth":16,"text":293,"children":11299},[11300,11301,11302],{"id":8482,"depth":22,"text":8483},{"id":8520,"depth":22,"text":8521},{"id":9022,"depth":22,"text":9023},{"id":9120,"depth":16,"text":9121,"children":11304},[11305,11306,11307],{"id":9147,"depth":22,"text":9148},{"id":9237,"depth":22,"text":9238},{"id":9327,"depth":22,"text":9328},{"id":9353,"depth":16,"text":9354},{"id":9463,"depth":16,"text":3993,"children":11310},[11311],{"id":9499,"depth":22,"text":9023},{"id":9662,"depth":16,"text":9663},{"id":9771,"depth":16,"text":9772},{"id":9851,"depth":16,"text":9852,"children":11315},[11316,11317],{"id":9861,"depth":22,"text":9163},{"id":9890,"depth":22,"text":9166},{"id":9920,"depth":16,"text":9921,"children":11319},[11320,11321,11322],{"id":9939,"depth":22,"text":9163},{"id":9966,"depth":22,"text":9166},{"id":9988,"depth":22,"text":9989},{"id":10173,"depth":16,"text":10174,"children":11324},[11325,11326,11327],{"id":10183,"depth":22,"text":10184},{"id":10226,"depth":22,"text":10227},{"id":10478,"depth":22,"text":10479},{"id":10585,"depth":16,"text":10586,"children":11329},[11330],{"id":10676,"depth":22,"text":11331},"Project redelayctl — contributing commands from a module",{"id":10902,"depth":16,"text":11333,"children":11334},"Framework redelayctl — spec tooling",[11335,11336],{"id":10913,"depth":22,"text":10914},{"id":11131,"depth":22,"text":11132},"Full reference for the redelay go-framework — module system, auth, users, groups, CRUD, server, and infrastructure.","md",null,{},{"order":10,"icon":138},{"title":135,"description":11337},"_0czzy39GqsTr55ER73YkZ_v6gBdFcU_dXdQDEnKrI0",[11345,11346],{"title":64,"path":127,"stem":128,"description":11339,"order":22,"children":-1},{"title":140,"path":141,"stem":142,"description":11347,"order":16,"icon":46,"children":-1},"Redelay ships with 8 production-ready modules: auth, users, groups, health, OpenAPI, AsyncAPI, MCP, and module spec browser — all wired up automatically.",1790332223783]