[{"data":1,"prerenderedAt":298},["ShallowReactive",2],{"docs-\u002Fdocs\u002Fdeployment\u002Fdesktop-apps":3,"docs-navigation":227},{"id":4,"title":5,"body":6,"description":218,"extension":219,"meta":220,"navTitle":221,"navigation":222,"path":223,"seo":224,"stem":225,"__hash__":226},"docs\u002Fdocs\u002F4.deployment\u002F2.desktop-apps.md","Desktop and other non-ASP.NET Core apps",{"type":7,"value":8,"toc":213},"minimark",[9,29,40,57,85,133,139,144,162,166,202,206,209],[10,11,12,16,17,20,21,24,25,28],"p",{},[13,14,15],"code",{},"UseNafasDashboard"," needs an ",[13,18,19],{},"IApplicationBuilder"," to mount onto — a plain\nWinForms, WPF, or console\u002Fworker app has no ASP.NET Core pipeline of its\nown to provide one. ",[13,22,23],{},"Nafas.Observability.Server"," is a separate, optional\npackage for exactly that case: it runs a second, independent HTTP listener\n(on its own port) that serves the same dashboard against the same running\napp's real data — no Kestrel, no OWIN, just a minimal HTTP\u002F1.1 server\ndirectly on ",[13,26,27],{},"TcpListener",".",[10,30,31,32,35,36,39],{},"Why not Kestrel or ",[13,33,34],{},"HttpListener","? The last version of Kestrel ever\npublished as a plain NuGet package is 2.3.13 from 2019, with no HTTP\u002F2 and\nno security patches since. ",[13,37,38],{},"System.Net.HttpListener"," — the BCL's own\nlistener — turned out not to be usable for the LAN case either: on Windows\nit's backed by HTTP.sys, which refuses to bind any prefix other than\n\"localhost\" unless the process runs elevated or a URL ACL was reserved\nbeforehand (verified empirically).",[10,41,42,43,46,47,52,53,56],{},"Being plain ",[13,44,45],{},"netstandard2.0"," like the core package itself, this also works\nfrom classic .NET Framework 4.6.1+ apps, not just modern .NET — confirmed\nagainst a real net48 console host, not just the TFM compatibility rules\n(see ",[48,49,51],"a",{"href":50},"\u002Fdocs\u002Fgetting-started\u002Frequirements","Requirements"," for the one\nnet48-specific gotcha it also caught, around SQLite and ",[13,54,55],{},"PlatformTarget",").",[58,59,64],"pre",{"className":60,"code":61,"language":62,"meta":63,"style":63},"language-bash shiki shiki-themes github-light github-dark","dotnet add package Nafas.Observability.Server\n","bash","",[13,65,66],{"__ignoreMap":63},[67,68,71,75,79,82],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"sScJk","dotnet",[67,76,78],{"class":77},"sZZnC"," add",[67,80,81],{"class":77}," package",[67,83,84],{"class":77}," Nafas.Observability.Server\n",[58,86,90],{"className":87,"code":88,"language":89,"meta":63,"style":63},"language-csharp shiki shiki-themes github-light github-dark","services.AddNafasServer();               \u002F\u002F same as always -- storage + ingestion\nservices.AddNafasHttpServer(o =>\n{\n    o.Enabled = true;                    \u002F\u002F opt-in; does nothing while false\n    o.Port = 5099;\n    o.AccessMode = NafasHttpServerAccessMode.LocalhostOnly; \u002F\u002F or .Lan\n});\n","csharp",[13,91,92,97,103,109,115,121,127],{"__ignoreMap":63},[67,93,94],{"class":69,"line":70},[67,95,96],{},"services.AddNafasServer();               \u002F\u002F same as always -- storage + ingestion\n",[67,98,100],{"class":69,"line":99},2,[67,101,102],{},"services.AddNafasHttpServer(o =>\n",[67,104,106],{"class":69,"line":105},3,[67,107,108],{},"{\n",[67,110,112],{"class":69,"line":111},4,[67,113,114],{},"    o.Enabled = true;                    \u002F\u002F opt-in; does nothing while false\n",[67,116,118],{"class":69,"line":117},5,[67,119,120],{},"    o.Port = 5099;\n",[67,122,124],{"class":69,"line":123},6,[67,125,126],{},"    o.AccessMode = NafasHttpServerAccessMode.LocalhostOnly; \u002F\u002F or .Lan\n",[67,128,130],{"class":69,"line":129},7,[67,131,132],{},"});\n",[10,134,135,136,28],{},"Then open ",[13,137,138],{},"http:\u002F\u002Flocalhost:5099\u002Fnafas",[140,141,143],"h2",{"id":142},"starting-and-stopping-at-runtime","Starting and stopping at runtime",[10,145,146,149,150,153,154,157,158,161],{},[13,147,148],{},"NafasHttpServerOptions.Enabled"," is only read once at startup — to\nstart\u002Fstop\u002Freconfigure it later (e.g. a settings screen's \"enable\ndashboard\" checkbox), resolve ",[13,151,152],{},"NafasHttpServer"," from DI and call its\n",[13,155,156],{},"StartAsync","\u002F",[13,159,160],{},"StopAsync"," directly.",[140,163,165],{"id":164},"access-modes","Access modes",[10,167,168,171,172,175,176,179,180,182,183,186,187,191,192,195,196,198,199,28],{},[13,169,170],{},"AccessMode"," only controls which network interface is bound —\n",[13,173,174],{},"LocalhostOnly"," (the default) binds loopback only, ",[13,177,178],{},"Lan"," binds every\ninterface so other devices on the network can reach the port via this\nmachine's own IP, with no administrator rights required either way (unlike\n",[13,181,38],{},", verified empirically on both). It does not by\nitself relax ",[13,184,185],{},"NafasServerOptions.Authorize"," (still local-requests-only by\ndefault, see ",[48,188,190],{"href":189},"\u002Fdocs\u002Fconfiguration\u002Fsecurity","Security","): set ",[13,193,194],{},"Authorize","\nexplicitly too if ",[13,197,178],{}," should actually let those requests through,\notherwise they still get a ",[13,200,201],{},"403",[140,203,205],{"id":204},"one-request-per-connection","One request per connection",[10,207,208],{},"Not HTTP\u002F1.1 keep-alive — this listener is a local\u002FLAN admin tool, not a\npublic high-throughput API, so giving up connection reuse (an extra TCP\nhandshake per dashboard API call, imperceptible on localhost\u002FLAN) removes\nan entire dimension of correctness surface instead.",[210,211,212],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":63,"searchDepth":99,"depth":99,"links":214},[215,216,217],{"id":142,"depth":99,"text":143},{"id":164,"depth":99,"text":165},{"id":204,"depth":99,"text":205},"Nafas.Observability.Server runs a standalone HTTP listener for WinForms, WPF, and console apps with no ASP.NET Core pipeline.","md",{},"Desktop apps",true,"\u002Fdocs\u002Fdeployment\u002Fdesktop-apps",{"title":5,"description":218},"docs\u002F4.deployment\u002F2.desktop-apps","GpPJC7K5pLRe2XkoNuNuviRK3yR-d94UiptUk6C9Uo4",[228],{"title":229,"path":230,"stem":231,"children":232,"page":-1},"Docs","\u002Fdocs","docs",[233,236,252,265,276,286,294],{"title":234,"path":230,"stem":235},"Documentation","docs\u002Findex",{"title":237,"path":238,"stem":239,"children":240,"page":251},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",[241,245,249],{"title":242,"path":243,"stem":244},"Why Nafas","\u002Fdocs\u002Fgetting-started\u002Fwhy-nafas","docs\u002F1.getting-started\u002F1.why-nafas",{"title":246,"path":247,"stem":248},"Quick start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","docs\u002F1.getting-started\u002F2.quick-start",{"title":51,"path":50,"stem":250},"docs\u002F1.getting-started\u002F3.requirements",false,{"title":253,"path":254,"stem":255,"children":256,"page":251},"How It Works","\u002Fdocs\u002Fhow-it-works","docs\u002F2.how-it-works",[257,261],{"title":258,"path":259,"stem":260},"What gets captured, and how","\u002Fdocs\u002Fhow-it-works\u002Fwhat-gets-captured","docs\u002F2.how-it-works\u002F1.what-gets-captured",{"title":262,"path":263,"stem":264},"How it's built internally","\u002Fdocs\u002Fhow-it-works\u002Finternals","docs\u002F2.how-it-works\u002F2.internals",{"title":266,"path":267,"stem":268,"children":269,"page":251},"Configuration","\u002Fdocs\u002Fconfiguration","docs\u002F3.configuration",[270,274],{"title":271,"path":272,"stem":273},"Configuration options","\u002Fdocs\u002Fconfiguration\u002Foptions","docs\u002F3.configuration\u002F1.options",{"title":190,"path":189,"stem":275},"docs\u002F3.configuration\u002F2.security",{"title":277,"path":278,"stem":279,"children":280,"page":251},"Deployment","\u002Fdocs\u002Fdeployment","docs\u002F4.deployment",[281,285],{"title":282,"path":283,"stem":284},"Multi-instance deployments","\u002Fdocs\u002Fdeployment\u002Fmulti-instance","docs\u002F4.deployment\u002F1.multi-instance",{"title":5,"path":223,"stem":225},{"title":287,"path":288,"stem":289,"children":290,"page":251},"Alerting","\u002Fdocs\u002Falerting","docs\u002F5.alerting",[291],{"title":287,"path":292,"stem":293},"\u002Fdocs\u002Falerting\u002Falert-rules","docs\u002F5.alerting\u002F1.alert-rules",{"title":295,"path":296,"stem":297},"Contributing","\u002Fdocs\u002Fcontributing","docs\u002F6.contributing",1789838144161]