---
title: Hot reload and file watching
description: "Why Pennington ships its own file watcher and WebSocket reload channel, and how the dev-only script is kept out of published builds."
canonical_url: https://usepennington.net/explanation/dev-experience/hot-reload/
sidecar_url: https://usepennington.net/explanation/dev-experience/hot-reload.md
content_hash: sha256:481713121f1c19096d8a3cf9c39c73af702ba94b049ef570e70f3b07c1cb882d
tokens: 8385
uid: explanation.dev-experience.hot-reload
reading_time_minutes: 6
---

Under the Hood
# Hot reload and file watching

Why Pennington ships its own file watcher and WebSocket reload channel, and how the dev-only script is kept out of published builds.

 
Content files — `.md` sources, front matter, images, assets tracked under a source's `ContentPath` — are not part of the .NET compilation. Restarting the host for every markdown typo would tear down Kestrel and throw away the expensive in-memory caches that make the second request fast. Pennington instead watches content in process and reloads only the browser: an in-process file watcher, services that discard and rebuild their derived caches on change, and a debounced WebSocket channel through which the browser is told to reload. WebSocket also makes server restarts easy to detect on the client — the socket closes and reconnects, and the browser reloads — without the careful `onerror` plumbing SSE would need for the same signal. Polling, the third alternative, adds latency under load and noise under idle.

 
## How it works

 
The mechanism is a single chain: files change, cached services drop their state, a debounce window elapses, and the browser reloads.

 
.b-490b7444{--beck-surface:var(--color-base-50, #ffffff);--beck-node-bg:var(--color-base-50, #ffffff);--beck-node-border:var(--color-base-200, #e2e8f0);--beck-node-shadow:0 1px 3px rgb(0 0 0 / 0.05), 0 4px 12px rgb(0 0 0 / 0.06);--beck-text:var(--color-base-800, #1e293b);--beck-text-muted:var(--color-base-500, #64748b);--beck-text-faint:var(--color-base-400, #94a3b8);--beck-primary:var(--color-primary-600, #175ddc);--beck-success:var(--color-emerald-500, #10b981);--beck-warn:var(--color-amber-500, #f59e0b);--beck-danger:var(--color-red-500, #ef4444);--beck-info:var(--color-violet-500, #8b5cf6);--beck-neutral:var(--color-base-400, #94a3b8);--beck-group-border:color-mix(in srgb, var(--beck-neutral) 45%, transparent);--beck-group-label:var(--beck-text-muted);--beck-edge:var(--color-base-300, #cbd5e1);--beck-packet:var(--beck-primary);--beck-icon-bg:var(--color-base-100, #f1f5f9);--beck-accent:var(--beck-primary);--beck-font:'Inter', system-ui, -apple-system, sans-serif;--beck-font-mono:'IBM Plex Mono', ui-monospace, monospace;}[data-theme='dark'] .b-490b7444{--beck-surface:var(--color-base-950, #0d1117);--beck-node-bg:var(--color-base-900, #161b22);--beck-node-border:var(--color-base-700, #30363d);--beck-node-shadow:0 1px 3px rgb(0 0 0 / 0.3), 0 4px 14px rgb(0 0 0 / 0.4);--beck-text:var(--color-base-50, #f0f6fc);--beck-text-muted:var(--color-base-400, #8b949e);--beck-text-faint:var(--color-base-500, #6e7681);--beck-edge:var(--color-base-700, #30363d);--beck-icon-bg:var(--color-base-800, #21262d);--beck-font:'Inter', system-ui, -apple-system, sans-serif;--beck-font-mono:'IBM Plex Mono', ui-monospace, monospace;}@media (prefers-color-scheme: dark){:root:not([data-theme='light']) .b-490b7444{--beck-surface:var(--color-base-950, #0d1117);--beck-node-bg:var(--color-base-900, #161b22);--beck-node-border:var(--color-base-700, #30363d);--beck-node-shadow:0 1px 3px rgb(0 0 0 / 0.3), 0 4px 14px rgb(0 0 0 / 0.4);--beck-text:var(--color-base-50, #f0f6fc);--beck-text-muted:var(--color-base-400, #8b949e);--beck-text-faint:var(--color-base-500, #6e7681);--beck-edge:var(--color-base-700, #30363d);--beck-icon-bg:var(--color-base-800, #21262d);--beck-font:'Inter', system-ui, -apple-system, sans-serif;--beck-font-mono:'IBM Plex Mono', ui-monospace, monospace;}}.b-490b7444{font-family:var(--beck-font);}.b-490b7444 .beck-fx-node{transform-box:fill-box;transform-origin:center;}.b-490b7444 .beck-packet-label{font-family:var(--beck-font-mono);font-size:11px;font-weight:600;}.b-490b7444 .beck-node{fill:var(--beck-node-bg);stroke:color-mix(in srgb, var(--beck-accent) 32%, var(--beck-node-border));stroke-width:1.5;filter:drop-shadow(0 1px 3px rgb(0 0 0/.05)) drop-shadow(0 4px 12px rgb(0 0 0/.06));}[data-theme='dark'] .b-490b7444 .beck-node{filter:drop-shadow(0 1px 3px rgb(0 0 0/.3)) drop-shadow(0 4px 14px rgb(0 0 0/.4));}@media (prefers-color-scheme: dark){:root:not([data-theme='light']) .b-490b7444 .beck-node{filter:drop-shadow(0 1px 3px rgb(0 0 0/.3)) drop-shadow(0 4px 14px rgb(0 0 0/.4));}}.b-490b7444 .beck-node--external{stroke-dasharray:5 4;}.b-490b7444 .beck-node--subtle{opacity:.72;}.b-490b7444 .beck-node--ghost{fill:transparent;stroke-dasharray:5 4;filter:none;}.b-490b7444 .beck-icon-chip{fill:color-mix(in srgb, var(--beck-accent) 15%, var(--beck-icon-bg));}.b-490b7444 .beck-node--ghost .beck-icon-chip{fill:transparent;}.b-490b7444 .beck-icon{color:var(--beck-accent);}.b-490b7444 .beck-node-title{fill:var(--beck-text);}.b-490b7444 .beck-node-subtitle,.b-490b7444 .beck-ghost-label{fill:var(--beck-text-muted);}.b-490b7444 .beck-status-inline{fill:var(--beck-accent);}.b-490b7444 .beck-status-bg{fill:color-mix(in srgb, var(--beck-accent) 14%, transparent);}.b-490b7444 .beck-status-text{fill:var(--beck-accent);}.b-490b7444 .beck-group{fill:none;stroke:var(--beck-group-border);stroke-width:1.5;stroke-dasharray:6 6;}.b-490b7444 .beck-group-label-bg{fill:var(--beck-surface);}.b-490b7444 .beck-group-label{fill:var(--beck-group-label);}.b-490b7444 .beck-node--start{fill:var(--beck-text-muted);}.b-490b7444 .beck-node--end{fill:none;stroke:var(--beck-text-muted);stroke-width:2;}.b-490b7444 .beck-end-dot{fill:var(--beck-text-muted);}.b-490b7444 .beck-class-head{fill:color-mix(in srgb, var(--beck-accent) 10%, transparent);}.b-490b7444 .beck-class-head-border{stroke:color-mix(in srgb, var(--beck-accent) 28%, var(--beck-node-border));stroke-width:1;}.b-490b7444 .beck-class-divider{stroke:var(--beck-node-border);stroke-width:1;}.b-490b7444 .beck-class-stereo{fill:var(--beck-text-muted);}.b-490b7444 .beck-class-title{fill:var(--beck-text);}.b-490b7444 .beck-class-field{fill:var(--beck-text-muted);}.b-490b7444 .beck-class-method{fill:var(--beck-text);}.b-490b7444 .beck-lifeline{stroke-width:2;stroke-dasharray:6 7;}.b-490b7444 .beck-activation{filter:drop-shadow(0 0 5px color-mix(in srgb, var(--beck-accent) 45%, transparent));}.b-490b7444 .beck-msg-chip{fill:var(--beck-node-bg);stroke:color-mix(in srgb, var(--beck-accent) 40%, transparent);stroke-width:1;}.b-490b7444 .beck-msg-text{fill:color-mix(in srgb, var(--beck-accent) 34%, var(--beck-text));}.b-490b7444 .beck-msg--reply .beck-msg-chip{stroke:none;}.b-490b7444 .beck-msg--reply .beck-msg-text,.b-490b7444 .beck-msg-text--bare{fill:var(--beck-text-muted);}.b-490b7444 .beck-band-box{fill:color-mix(in srgb, var(--beck-accent) 5%, transparent);stroke:color-mix(in srgb, var(--beck-accent) 30%, transparent);stroke-width:1.5;stroke-dasharray:6 6;}.b-490b7444 .beck-band-chip{fill:var(--beck-surface);stroke:color-mix(in srgb, var(--beck-accent) 40%, transparent);stroke-width:1;}.b-490b7444 .beck-band-label{fill:color-mix(in srgb, var(--beck-accent) 70%, var(--beck-text));}.b-490b7444 .beck-edge{fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;}.b-490b7444 .beck-edge-label{fill:var(--beck-text-muted);paint-order:stroke;stroke:var(--beck-surface);stroke-width:3px;stroke-linejoin:round;}.b-490b7444 .beck-title{fill:var(--beck-text);}.b-490b7444 .beck-subtitle{fill:var(--beck-text-muted);}@media (prefers-reduced-motion:no-preference){.b-490b7444 .bp0-490b7444{animation:kp0-490b7444 38.12s linear infinite;}@keyframes kp0-490b7444{0%{offset-distance:0%;opacity:0;}12.5887%{opacity:0;}12.5987%{offset-distance:0%;opacity:1;animation-timing-function:linear;}16.0817%{offset-distance:100%;opacity:1;}16.0917%{opacity:0;}100%{offset-distance:0%;opacity:0;}}.b-490b7444 .bt0-490b7444{animation:kt0-490b7444 38.12s linear infinite;}@keyframes kt0-490b7444{0%{stroke-dashoffset:278.8;}12.5987%{stroke-dashoffset:278.8;animation-timing-function:linear;}16.0817%{stroke-dashoffset:0;}94.7531%{stroke-dashoffset:0;}94.7631%{stroke-dashoffset:278.8;}100%{stroke-dashoffset:278.8;}}.b-490b7444 .bp1-490b7444{animation:kp1-490b7444 38.12s linear infinite;}@keyframes kp1-490b7444{0%{offset-distance:0%;opacity:0;}17.6457%{opacity:0;}17.6557%{offset-distance:0%;opacity:1;animation-timing-function:linear;}22.3392%{offset-distance:100%;opacity:1;}22.3492%{opacity:0;}100%{offset-distance:0%;opacity:0;}}.b-490b7444 .bt1-490b7444{animation:kt1-490b7444 38.12s linear infinite;}@keyframes kt1-490b7444{0%{stroke-dashoffset:374.9;}17.6557%{stroke-dashoffset:374.9;animation-timing-function:linear;}22.3392%{stroke-dashoffset:0;}94.7531%{stroke-dashoffset:0;}94.7631%{stroke-dashoffset:374.9;}100%{stroke-dashoffset:374.9;}}.b-490b7444 .bp2-490b7444{animation:kp2-490b7444 38.12s linear infinite;}@keyframes kp2-490b7444{0%{offset-distance:0%;opacity:0;}39.2797%{opacity:0;}39.2897%{offset-distance:0%;opacity:1;animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}42.3633%{offset-distance:100%;opacity:1;}42.3733%{opacity:0;}100%{offset-distance:0%;opacity:0;}}.b-490b7444 .bt2-490b7444{animation:kt2-490b7444 38.12s linear infinite;}@keyframes kt2-490b7444{0%{stroke-dashoffset:374.9;}39.2897%{stroke-dashoffset:374.9;animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}42.3633%{stroke-dashoffset:0;}94.7531%{stroke-dashoffset:0;}94.7631%{stroke-dashoffset:374.9;}100%{stroke-dashoffset:374.9;}}.b-490b7444 .bp3-490b7444{animation:kp3-490b7444 38.12s linear infinite;}@keyframes kp3-490b7444{0%{offset-distance:0%;opacity:0;}43.9273%{opacity:0;}43.9373%{offset-distance:0%;opacity:1;animation-timing-function:linear;}52.3249%{offset-distance:100%;opacity:1;}52.3349%{opacity:0;}100%{offset-distance:0%;opacity:0;}}.b-490b7444 .bt3-490b7444{animation:kt3-490b7444 38.12s linear infinite;}@keyframes kt3-490b7444{0%{stroke-dashoffset:671.4;}43.9373%{stroke-dashoffset:671.4;animation-timing-function:linear;}52.3249%{stroke-dashoffset:0;}94.7531%{stroke-dashoffset:0;}94.7631%{stroke-dashoffset:671.4;}100%{stroke-dashoffset:671.4;}}.b-490b7444 .bp4-490b7444{animation:kp4-490b7444 38.12s linear infinite;}@keyframes kp4-490b7444{0%{offset-distance:0%;opacity:0;}70.1914%{opacity:0;}70.2014%{offset-distance:0%;opacity:1;animation-timing-function:linear;}71.7754%{offset-distance:100%;opacity:1;}71.7854%{opacity:0;}100%{offset-distance:0%;opacity:0;}}.b-490b7444 .bt4-490b7444{animation:kt4-490b7444 38.12s linear infinite;}@keyframes kt4-490b7444{0%{stroke-dashoffset:79.22;}70.2014%{stroke-dashoffset:79.22;animation-timing-function:linear;}71.7754%{stroke-dashoffset:0;}94.7531%{stroke-dashoffset:0;}94.7631%{stroke-dashoffset:79.22;}100%{stroke-dashoffset:79.22;}}.b-490b7444 .bp5-490b7444{animation:kp5-490b7444 38.12s linear infinite;}@keyframes kp5-490b7444{0%{offset-distance:0%;opacity:0;}82.2345%{opacity:0;}82.2445%{offset-distance:0%;opacity:1;animation-timing-function:linear(0 0%, 0.0012 6.67%, 0.0095 13.33%, 0.032 20%, 0.0759 26.67%, 0.1481 33.33%, 0.256 40%, 0.3255 43.33%, 0.4065 46.67%, 0.5935 53.33%, 0.6745 56.67%, 0.744 60%, 0.8519 66.67%, 0.9241 73.33%, 0.968 80%, 0.9905 86.67%, 0.9988 93.33%, 1 100%);}90.0309%{offset-distance:100%;opacity:1;}90.0409%{opacity:0;}100%{offset-distance:0%;opacity:0;}}.b-490b7444 .bt5-490b7444{animation:kt5-490b7444 38.12s linear infinite;}@keyframes kt5-490b7444{0%{stroke-dashoffset:445.2;}82.2445%{stroke-dashoffset:445.2;animation-timing-function:linear(0 0%, 0.0012 6.67%, 0.0095 13.33%, 0.032 20%, 0.0759 26.67%, 0.1481 33.33%, 0.256 40%, 0.3255 43.33%, 0.4065 46.67%, 0.5935 53.33%, 0.6745 56.67%, 0.744 60%, 0.8519 66.67%, 0.9241 73.33%, 0.968 80%, 0.9905 86.67%, 0.9988 93.33%, 1 100%);}90.0309%{stroke-dashoffset:0;}94.7531%{stroke-dashoffset:0;}94.7631%{stroke-dashoffset:445.2;}100%{stroke-dashoffset:445.2;}}.b-490b7444 .bn1-490b7444 .beck-fx-node{animation:kn1-490b7444 38.12s linear infinite;}@keyframes kn1-490b7444{0%{transform:none;}16.0817%{transform:none;animation-timing-function:linear(0 0%, 0.2557 3.33%, 0.4834 6.67%, 0.6845 10%, 0.8603 13.33%, 1.0121 16.67%, 1.1414 20%, 1.2495 23.33%, 1.3377 26.67%, 1.4074 30%, 1.46 33.33%, 1.4967 36.67%, 1.5191 40%, 1.5284 43.33%, 1.5259 46.67%, 1.4913 53.33%, 1.4261 60%, 1.3411 66.67%, 1.2472 73.33%, 1.1554 80%, 1.0763 86.67%, 1.0209 93.33%, 1 100%);}16.5539%{transform:translateY(-2px) scale(1.04);animation-timing-function:linear(0 0%, 0.1286 1.67%, 0.2749 3.33%, 0.5785 6.67%, 0.7194 8.33%, 0.8455 10%, 0.9534 11.67%, 1.0415 13.33%, 1.1093 15%, 1.1575 16.67%, 1.1878 18.33%, 1.2023 20%, 1.2035 21.67%, 1.1941 23.33%, 1.154 26.67%, 1.0496 33.33%, 1.0082 36.67%, 0.9807 40%, 0.9668 43.33%, 0.964 46.67%, 0.9773 53.33%, 0.9952 60%, 1.0049 66.67%, 1.0061 73.33%, 1.0032 80%, 1.0003 86.67%, 0.999 93.33%, 1 100%);}17.6557%{transform:none;}42.3633%{transform:none;animation-timing-function:linear(0 0%, 0.2557 3.33%, 0.4834 6.67%, 0.6845 10%, 0.8603 13.33%, 1.0121 16.67%, 1.1414 20%, 1.2495 23.33%, 1.3377 26.67%, 1.4074 30%, 1.46 33.33%, 1.4967 36.67%, 1.5191 40%, 1.5284 43.33%, 1.5259 46.67%, 1.4913 53.33%, 1.4261 60%, 1.3411 66.67%, 1.2472 73.33%, 1.1554 80%, 1.0763 86.67%, 1.0209 93.33%, 1 100%);}42.8355%{transform:translateY(-2px) scale(1.04);animation-timing-function:linear(0 0%, 0.1286 1.67%, 0.2749 3.33%, 0.5785 6.67%, 0.7194 8.33%, 0.8455 10%, 0.9534 11.67%, 1.0415 13.33%, 1.1093 15%, 1.1575 16.67%, 1.1878 18.33%, 1.2023 20%, 1.2035 21.67%, 1.1941 23.33%, 1.154 26.67%, 1.0496 33.33%, 1.0082 36.67%, 0.9807 40%, 0.9668 43.33%, 0.964 46.67%, 0.9773 53.33%, 0.9952 60%, 1.0049 66.67%, 1.0061 73.33%, 1.0032 80%, 1.0003 86.67%, 0.999 93.33%, 1 100%);}43.9373%{transform:none;}100%{transform:none;}}.b-490b7444 .bn2-490b7444 .beck-fx-node{animation:kn2-490b7444 38.12s linear infinite;}@keyframes kn2-490b7444{0%{transform:none;}22.3392%{transform:none;animation-timing-function:linear(0 0%, 0.2557 3.33%, 0.4834 6.67%, 0.6845 10%, 0.8603 13.33%, 1.0121 16.67%, 1.1414 20%, 1.2495 23.33%, 1.3377 26.67%, 1.4074 30%, 1.46 33.33%, 1.4967 36.67%, 1.5191 40%, 1.5284 43.33%, 1.5259 46.67%, 1.4913 53.33%, 1.4261 60%, 1.3411 66.67%, 1.2472 73.33%, 1.1554 80%, 1.0763 86.67%, 1.0209 93.33%, 1 100%);}22.8114%{transform:translateY(-2px) scale(1.04);animation-timing-function:linear(0 0%, 0.1286 1.67%, 0.2749 3.33%, 0.5785 6.67%, 0.7194 8.33%, 0.8455 10%, 0.9534 11.67%, 1.0415 13.33%, 1.1093 15%, 1.1575 16.67%, 1.1878 18.33%, 1.2023 20%, 1.2035 21.67%, 1.1941 23.33%, 1.154 26.67%, 1.0496 33.33%, 1.0082 36.67%, 0.9807 40%, 0.9668 43.33%, 0.964 46.67%, 0.9773 53.33%, 0.9952 60%, 1.0049 66.67%, 1.0061 73.33%, 1.0032 80%, 1.0003 86.67%, 0.999 93.33%, 1 100%);}23.9133%{transform:none;}100%{transform:none;}}.b-490b7444 .bn3-490b7444 .beck-fx-node{animation:kn3-490b7444 38.12s linear infinite;}@keyframes kn3-490b7444{0%{transform:none;}52.3249%{transform:none;animation-timing-function:linear(0 0%, 0.2557 3.33%, 0.4834 6.67%, 0.6845 10%, 0.8603 13.33%, 1.0121 16.67%, 1.1414 20%, 1.2495 23.33%, 1.3377 26.67%, 1.4074 30%, 1.46 33.33%, 1.4967 36.67%, 1.5191 40%, 1.5284 43.33%, 1.5259 46.67%, 1.4913 53.33%, 1.4261 60%, 1.3411 66.67%, 1.2472 73.33%, 1.1554 80%, 1.0763 86.67%, 1.0209 93.33%, 1 100%);}52.7971%{transform:translateY(-2px) scale(1.04);animation-timing-function:linear(0 0%, 0.1286 1.67%, 0.2749 3.33%, 0.5785 6.67%, 0.7194 8.33%, 0.8455 10%, 0.9534 11.67%, 1.0415 13.33%, 1.1093 15%, 1.1575 16.67%, 1.1878 18.33%, 1.2023 20%, 1.2035 21.67%, 1.1941 23.33%, 1.154 26.67%, 1.0496 33.33%, 1.0082 36.67%, 0.9807 40%, 0.9668 43.33%, 0.964 46.67%, 0.9773 53.33%, 0.9952 60%, 1.0049 66.67%, 1.0061 73.33%, 1.0032 80%, 1.0003 86.67%, 0.999 93.33%, 1 100%);}53.8989%{transform:none;}71.7754%{transform:none;animation-timing-function:linear(0 0%, 0.2557 3.33%, 0.4834 6.67%, 0.6845 10%, 0.8603 13.33%, 1.0121 16.67%, 1.1414 20%, 1.2495 23.33%, 1.3377 26.67%, 1.4074 30%, 1.46 33.33%, 1.4967 36.67%, 1.5191 40%, 1.5284 43.33%, 1.5259 46.67%, 1.4913 53.33%, 1.4261 60%, 1.3411 66.67%, 1.2472 73.33%, 1.1554 80%, 1.0763 86.67%, 1.0209 93.33%, 1 100%);}72.2476%{transform:translateY(-2px) scale(1.04);animation-timing-function:linear(0 0%, 0.1286 1.67%, 0.2749 3.33%, 0.5785 6.67%, 0.7194 8.33%, 0.8455 10%, 0.9534 11.67%, 1.0415 13.33%, 1.1093 15%, 1.1575 16.67%, 1.1878 18.33%, 1.2023 20%, 1.2035 21.67%, 1.1941 23.33%, 1.154 26.67%, 1.0496 33.33%, 1.0082 36.67%, 0.9807 40%, 0.9668 43.33%, 0.964 46.67%, 0.9773 53.33%, 0.9952 60%, 1.0049 66.67%, 1.0061 73.33%, 1.0032 80%, 1.0003 86.67%, 0.999 93.33%, 1 100%);}73.3495%{transform:none;}100%{transform:none;}}.b-490b7444 .bn4-490b7444 .beck-fx-node{animation:kn4-490b7444 38.12s linear infinite;}@keyframes kn4-490b7444{0%{transform:none;}90.0309%{transform:none;animation-timing-function:linear(0 0%, 0.2557 3.33%, 0.4834 6.67%, 0.6845 10%, 0.8603 13.33%, 1.0121 16.67%, 1.1414 20%, 1.2495 23.33%, 1.3377 26.67%, 1.4074 30%, 1.46 33.33%, 1.4967 36.67%, 1.5191 40%, 1.5284 43.33%, 1.5259 46.67%, 1.4913 53.33%, 1.4261 60%, 1.3411 66.67%, 1.2472 73.33%, 1.1554 80%, 1.0763 86.67%, 1.0209 93.33%, 1 100%);}90.5031%{transform:translateY(-2px) scale(1.04);animation-timing-function:linear(0 0%, 0.1286 1.67%, 0.2749 3.33%, 0.5785 6.67%, 0.7194 8.33%, 0.8455 10%, 0.9534 11.67%, 1.0415 13.33%, 1.1093 15%, 1.1575 16.67%, 1.1878 18.33%, 1.2023 20%, 1.2035 21.67%, 1.1941 23.33%, 1.154 26.67%, 1.0496 33.33%, 1.0082 36.67%, 0.9807 40%, 0.9668 43.33%, 0.964 46.67%, 0.9773 53.33%, 0.9952 60%, 1.0049 66.67%, 1.0061 73.33%, 1.0032 80%, 1.0003 86.67%, 0.999 93.33%, 1 100%);}91.605%{transform:none;}100%{transform:none;}}.b-490b7444 .brip0-490b7444{animation:krip0-490b7444 38.12s linear infinite;}@keyframes krip0-490b7444{0%{opacity:0;transform:scale(1);}16.0717%{opacity:0;transform:scale(1);}16.0817%{opacity:0.6;transform:scale(1);animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}17.3409%{opacity:0;transform:scale(1.15);}100%{opacity:0;transform:scale(1);}}.b-490b7444 .brip1-490b7444{animation:krip1-490b7444 38.12s linear infinite;}@keyframes krip1-490b7444{0%{opacity:0;transform:scale(1);}22.3292%{opacity:0;transform:scale(1);}22.3392%{opacity:0.6;transform:scale(1);animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}23.5985%{opacity:0;transform:scale(1.15);}100%{opacity:0;transform:scale(1);}}.b-490b7444 .brip2-490b7444{animation:krip2-490b7444 38.12s linear infinite;}@keyframes krip2-490b7444{0%{opacity:0;transform:scale(1);}42.3533%{opacity:0;transform:scale(1);}42.3633%{opacity:0.6;transform:scale(1);animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}43.6225%{opacity:0;transform:scale(1.15);}100%{opacity:0;transform:scale(1);}}.b-490b7444 .brip3-490b7444{animation:krip3-490b7444 38.12s linear infinite;}@keyframes krip3-490b7444{0%{opacity:0;transform:scale(1);}52.3149%{opacity:0;transform:scale(1);}52.3249%{opacity:0.6;transform:scale(1);animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}53.5841%{opacity:0;transform:scale(1.15);}100%{opacity:0;transform:scale(1);}}.b-490b7444 .brip4-490b7444{animation:krip4-490b7444 38.12s linear infinite;}@keyframes krip4-490b7444{0%{opacity:0;transform:scale(1);}71.7654%{opacity:0;transform:scale(1);}71.7754%{opacity:0.6;transform:scale(1);animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}73.0347%{opacity:0;transform:scale(1.15);}100%{opacity:0;transform:scale(1);}}.b-490b7444 .brip5-490b7444{animation:krip5-490b7444 38.12s linear infinite;}@keyframes krip5-490b7444{0%{opacity:0;transform:scale(1);}90.0209%{opacity:0;transform:scale(1);}90.0309%{opacity:0.6;transform:scale(1);animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}91.2902%{opacity:0;transform:scale(1.15);}100%{opacity:0;transform:scale(1);}}.b-490b7444 .bbeat0-490b7444{animation:kbe0-490b7444 38.12s linear infinite;}@keyframes kbe0-490b7444{0%{opacity:0;}0.3048%{opacity:0;}0.3148%{opacity:0;animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}1.1018%{opacity:1;}23.9133%{opacity:1;animation-timing-function:linear(0 0%, 0.0044 6.67%, 0.0178 13.33%, 0.04 20%, 0.0711 26.67%, 0.1111 33.33%, 0.16 40%, 0.2178 46.67%, 0.2844 53.33%, 0.36 60%, 0.4444 66.67%, 0.5378 73.33%, 0.64 80%, 0.7511 86.67%, 0.8711 93.33%, 1 100%);}24.2281%{opacity:0;}100%{opacity:0;}}.b-490b7444 .bbeat1-490b7444{animation:kbe1-490b7444 38.12s linear infinite;}@keyframes kbe1-490b7444{0%{opacity:0;}24.2181%{opacity:0;}24.2281%{opacity:0;animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}25.0151%{opacity:1;}53.8989%{opacity:1;animation-timing-function:linear(0 0%, 0.0044 6.67%, 0.0178 13.33%, 0.04 20%, 0.0711 26.67%, 0.1111 33.33%, 0.16 40%, 0.2178 46.67%, 0.2844 53.33%, 0.36 60%, 0.4444 66.67%, 0.5378 73.33%, 0.64 80%, 0.7511 86.67%, 0.8711 93.33%, 1 100%);}54.2138%{opacity:0;}100%{opacity:0;}}.b-490b7444 .bbeat2-490b7444{animation:kbe2-490b7444 38.12s linear infinite;}@keyframes kbe2-490b7444{0%{opacity:0;}54.2038%{opacity:0;}54.2138%{opacity:0;animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}55.0008%{opacity:1;}73.3495%{opacity:1;animation-timing-function:linear(0 0%, 0.0044 6.67%, 0.0178 13.33%, 0.04 20%, 0.0711 26.67%, 0.1111 33.33%, 0.16 40%, 0.2178 46.67%, 0.2844 53.33%, 0.36 60%, 0.4444 66.67%, 0.5378 73.33%, 0.64 80%, 0.7511 86.67%, 0.8711 93.33%, 1 100%);}73.6643%{opacity:0;}100%{opacity:0;}}.b-490b7444 .bbeat3-490b7444{animation:kbe3-490b7444 38.12s linear infinite;}@keyframes kbe3-490b7444{0%{opacity:0;}73.6543%{opacity:0;}73.6643%{opacity:0;animation-timing-function:linear(0 0%, 0.187 6.67%, 0.349 13.33%, 0.488 20%, 0.6056 26.67%, 0.7037 33.33%, 0.784 40%, 0.8483 46.67%, 0.8984 53.33%, 0.936 60%, 0.963 66.67%, 0.981 73.33%, 0.992 80%, 0.9976 86.67%, 0.9997 93.33%, 1 100%);}74.4513%{opacity:1;}94.7531%{opacity:1;animation-timing-function:linear(0 0%, 0.0044 6.67%, 0.0178 13.33%, 0.04 20%, 0.0711 26.67%, 0.1111 33.33%, 0.16 40%, 0.2178 46.67%, 0.2844 53.33%, 0.36 60%, 0.4444 66.67%, 0.5378 73.33%, 0.64 80%, 0.7511 86.67%, 0.8711 93.33%, 1 100%);}95.2531%{opacity:0;}100%{opacity:0;}}.b-490b7444 .beck-msg path{opacity:0.15;}.b-490b7444 .beck-msg-chip,.b-490b7444 .beck-msg-text{opacity:0.35;}.b-490b7444 .beck-band{opacity:0.45;}.b-490b7444 .beck-activation{opacity:0.25;}.b-490b7444 .beck-msg[data-msg="msg0"] path{animation:kchl0-490b7444 38.12s linear infinite;}@keyframes kchl0-490b7444{0%{opacity:0.15;}12.5887%{opacity:0.15;}12.5987%{opacity:0.15;}13.2546%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.15;}100%{opacity:0.15;}}.b-490b7444 .beck-msg[data-msg="msg0"] .beck-msg-chip,.b-490b7444 .beck-msg[data-msg="msg0"] .beck-msg-text{animation:kcht0-490b7444 38.12s linear infinite;}@keyframes kcht0-490b7444{0%{opacity:0.35;}12.5887%{opacity:0.35;}12.5987%{opacity:0.35;}13.2546%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.35;}100%{opacity:0.35;}}.b-490b7444 .beck-msg[data-msg="msg1"] path{animation:kchl1-490b7444 38.12s linear infinite;}@keyframes kchl1-490b7444{0%{opacity:0.15;}17.6457%{opacity:0.15;}17.6557%{opacity:0.15;}18.3116%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.15;}100%{opacity:0.15;}}.b-490b7444 .beck-msg[data-msg="msg1"] .beck-msg-chip,.b-490b7444 .beck-msg[data-msg="msg1"] .beck-msg-text{animation:kcht1-490b7444 38.12s linear infinite;}@keyframes kcht1-490b7444{0%{opacity:0.35;}17.6457%{opacity:0.35;}17.6557%{opacity:0.35;}18.3116%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.35;}100%{opacity:0.35;}}.b-490b7444 .beck-msg[data-msg="msg2"] path{animation:kchl2-490b7444 38.12s linear infinite;}@keyframes kchl2-490b7444{0%{opacity:0.15;}39.2797%{opacity:0.15;}39.2897%{opacity:0.15;}39.9456%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.15;}100%{opacity:0.15;}}.b-490b7444 .beck-msg[data-msg="msg2"] .beck-msg-chip,.b-490b7444 .beck-msg[data-msg="msg2"] .beck-msg-text{animation:kcht2-490b7444 38.12s linear infinite;}@keyframes kcht2-490b7444{0%{opacity:0.35;}39.2797%{opacity:0.35;}39.2897%{opacity:0.35;}39.9456%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.35;}100%{opacity:0.35;}}.b-490b7444 .beck-msg[data-msg="msg3"] path{animation:kchl3-490b7444 38.12s linear infinite;}@keyframes kchl3-490b7444{0%{opacity:0.15;}43.9273%{opacity:0.15;}43.9373%{opacity:0.15;}44.5932%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.15;}100%{opacity:0.15;}}.b-490b7444 .beck-msg[data-msg="msg3"] .beck-msg-chip,.b-490b7444 .beck-msg[data-msg="msg3"] .beck-msg-text{animation:kcht3-490b7444 38.12s linear infinite;}@keyframes kcht3-490b7444{0%{opacity:0.35;}43.9273%{opacity:0.35;}43.9373%{opacity:0.35;}44.5932%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.35;}100%{opacity:0.35;}}.b-490b7444 .beck-msg[data-msg="msg4"] path{animation:kchl4-490b7444 38.12s linear infinite;}@keyframes kchl4-490b7444{0%{opacity:0.15;}70.1914%{opacity:0.15;}70.2014%{opacity:0.15;}70.8572%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.15;}100%{opacity:0.15;}}.b-490b7444 .beck-msg[data-msg="msg4"] .beck-msg-chip,.b-490b7444 .beck-msg[data-msg="msg4"] .beck-msg-text{animation:kcht4-490b7444 38.12s linear infinite;}@keyframes kcht4-490b7444{0%{opacity:0.35;}70.1914%{opacity:0.35;}70.2014%{opacity:0.35;}70.8572%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.35;}100%{opacity:0.35;}}.b-490b7444 .beck-msg[data-msg="msg5"] path{animation:kchl5-490b7444 38.12s linear infinite;}@keyframes kchl5-490b7444{0%{opacity:0.15;}82.2345%{opacity:0.15;}82.2445%{opacity:0.15;}82.9004%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.15;}100%{opacity:0.15;}}.b-490b7444 .beck-msg[data-msg="msg5"] .beck-msg-chip,.b-490b7444 .beck-msg[data-msg="msg5"] .beck-msg-text{animation:kcht5-490b7444 38.12s linear infinite;}@keyframes kcht5-490b7444{0%{opacity:0.35;}82.2345%{opacity:0.35;}82.2445%{opacity:0.35;}82.9004%{opacity:1;}92.7855%{opacity:1;}94.3596%{opacity:0.35;}100%{opacity:0.35;}}.b-490b7444 .beck-activation[data-bar="0"]{animation:kcha0-490b7444 38.12s linear infinite;}@keyframes kcha0-490b7444{0%{opacity:0.25;}21.9357%{opacity:0.25;}21.9457%{opacity:0.25;}22.7327%{opacity:1;}42.3633%{opacity:1;}43.2815%{opacity:0.25;}100%{opacity:0.25;}}}changeOnFileChangedRecreatenotifyreset 300ms debouncereload · /__pennington/reloadContent filesFileWatcherIFileWatchAware servicenav builder · xref map · search indexLiveReloadServerBrowserA save, create, delete, or rename lands in a watched ContentPathThe stale instance is dropped; the next request rebuilds it through normal constructor injectionRapid saves keep resetting the timer — a burst of changes coalesces into one reloadOne WebSocket broadcast after 300ms of quiet
### Watching content directories

 
A service tells the engine which directories to watch by declaring `WatchScopes` — the public contract through which every file-reactive service registers its content roots. Creates, deletes, and renames count as changes, not just edits in place, so a new markdown file or a deleted asset reloads the same way a save does. The watcher behaves consistently across Windows and WSL, the two platforms most contributors run, so the dev loop feels the same on either.

 
### The `IFileWatchAware` contract

 
Several services build expensive lookup tables from disk on startup: link resolvers, cross-reference uid maps, search indexes, sitemaps, and blog content resolvers. They register through `AddFileWatched<T>`, which is constrained to types implementing `IFileWatchAware` — the single contract every file-reactive service shares. A service declares the directories it needs watched and an `OnFileChanged` method whose return value says how the change should be handled: `Ignore` it, report it `Refreshed` its own state, or ask to be `Recreate`d.

 
The services above return `Recreate`, so on the next request the stale instance is dropped — disposed if it implements `IDisposable` — and a fresh one is rebuilt through normal constructor injection. The approach is to discard and rebuild the whole instance rather than bust individual caches: no service needs to know when to flush itself, because the engine replaces the entire instance when the underlying content moves.

 
This is the extension point you reach for when your own service caches something derived from content files. The how-tos that lean on it: [write a custom content service](https://usepennington.net/how-to/content-services/custom-content-service.md), [publish a custom feed from a content service](https://usepennington.net/how-to/feeds/custom-feed.md), and [use a YAML or JSON data file in pages](https://usepennington.net/how-to/content-services/data-files.md).

 
### Debouncing and broadcasting over WebSocket

 
`LiveReloadServer` resets a 300ms debounce timer on every change notification, so it broadcasts a single reload only after 300ms of quiet — coalescing rapid saves (editor auto-save, multi-file renames) into one browser reload. It listens on the WebSocket endpoint `/__pennington/reload`, which is worth knowing if a reverse proxy or a Content-Security-Policy sits in front of the dev server and needs to allow the upgrade.

 
### Script injection and reconnection

 
`LiveReloadScriptProcessor` injects an inline script before the closing `</body>` that opens a WebSocket to `/__pennington/reload`, reloads when a reload message arrives, and reloads on reconnect so a host restart refreshes the browser without waiting for a file change. Guards that keep it from firing mid-navigation or before the response pipeline settles are tuning details, not knobs a consumer sets.

 
### Build-mode gating

 
Both `LiveReloadScriptProcessor` and `UseLiveReload` check `PenningtonCli.Current.IsHeadlessOneShot` — true for any headless one-shot run, which covers `build` and `diag` alike, not just the `build` verb. When it is true, the processor's `ShouldProcess` returns `false` and the middleware skips endpoint registration entirely. This means the `OutputGenerationService` crawler sees clean HTML with no script and no WebSocket endpoint: no publish-time stripping step, no build configuration to set, and no dev-only flag to forget.

 
## Relation to `dotnet watch` and .NET Hot Reload

 
This is a different layer from .NET Hot Reload and `dotnet watch`, and the two are complementary. .NET Hot Reload patches running CLR code — your `.cs` and `.razor` source — and `dotnet watch` restarts or re-applies edits when compiled code changes. Pennington's watcher covers the half they don't: content files (`.md`, front matter, images, `_meta.yml`, data files) that never enter the compilation. Run the host under `dotnet watch` and you get both — code edits patched by the runtime, content edits reloaded by Pennington — without either stepping on the other.

 
## Disabling reload or tuning the debounce

 
In serve mode live reload is always on, and the 300ms debounce and reconnection behavior are fixed; there is no option to tune them or switch reload off while still serving. The single off-switch is build mode: a headless one-shot run (`build` or `diag`) gates the whole subsystem out, which is exactly what you want for published output. If you need a dev server with no reload at all, host the engine without `UsePennington`'s dev path rather than reaching for a flag.

 
## Further reading

 
 - Reference: [DI and middleware extension methods](https://usepennington.net/reference/host/extensions.md)
 - Reference: [Response processing interfaces](https://usepennington.net/reference/api/i-response-processor.md)
 - Explanation: [The response-processing pipeline](https://usepennington.net/explanation/core/response-processing.md)
 - Explanation: [Dev mode and build mode share one code path](https://usepennington.net/explanation/core/dev-vs-build.md)
 
 
[Previous
                
                Locale-aware URLs and content fallback](https://usepennington.net/explanation/localization/urls-and-fallback.md)[Next
                    
                What the DocSite and BlogSite templates wire for you](https://usepennington.net/explanation/positioning/docsite-positioning.md)