# freeze URL: /chirp/api/freeze/ Section: api -------------------------------------------------------------------------------- freeze - Chirp window.BENGAL_THEME_DEFAULTS = { appearance: 'light', palette: 'charcoal-bengal' }; window.Bengal = window.Bengal || {}; window.Bengal.enhanceBaseUrl = '/chirp/assets/js/enhancements'; window.Bengal.watchDom = true; window.Bengal.debug = false; window.Bengal.enhanceUrls = { 'toc': '/chirp/assets/js/enhancements/toc.736b857e.js', 'docs-nav': '/chirp/assets/js/enhancements/docs-nav.57e4b129.js', 'tabs': '/chirp/assets/js/enhancements/tabs.aac9e817.js', 'lightbox': '/chirp/assets/js/enhancements/lightbox.1ca22aa1.js', 'interactive': '/chirp/assets/js/enhancements/interactive.96c8880c.js', 'mobile-nav': '/chirp/assets/js/enhancements/mobile-nav.d991657f.js', 'action-bar': '/chirp/assets/js/enhancements/action-bar.d62417f4.js', 'copy-link': '/chirp/assets/js/enhancements/copy-link.7d9a5c29.js', 'data-table': '/chirp/assets/js/enhancements/data-table.1f5bc1eb.js', 'lazy-loaders': '/chirp/assets/js/enhancements/lazy-loaders.a5c38245.js', 'holo': '/chirp/assets/js/enhancements/holo.ee13c841.js', 'link-previews': '/chirp/assets/js/enhancements/link-previews.96620bcf.js' }; (function () { try { var defaults = window.BENGAL_THEME_DEFAULTS || { appearance: 'system', palette: '' }; var defaultAppearance = defaults.appearance; if (defaultAppearance === 'system') { defaultAppearance = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; } var storedTheme = localStorage.getItem('bengal-theme'); var storedPalette = localStorage.getItem('bengal-palette'); var theme = storedTheme ? (storedTheme === 'system' ? defaultAppearance : storedTheme) : defaultAppearance; var palette = storedPalette ?? defaults.palette; document.documentElement.setAttribute('data-theme', theme); if (palette) { document.documentElement.setAttribute('data-palette', palette); } } catch (e) { document.documentElement.setAttribute('data-theme', 'light'); } })(); Skip to main content For a complete page index, fetch /chirp/llms.txt. ⌁⌁ DocumentationInfoAboutGet StartedBuild AppsQuality and OperationsFile TextReferenceCubeExamplesApplied TutorialsReleasesDevGitHubChirp API ReferenceForum Magnifying Glass Search ⌘K Palette Appearance Chevron Down Mode Monitor System Sun Light Moon Dark Palette Snow Lynx Brown Bengal Silver Bengal Charcoal Bengal Blue Bengal List Chirp API Reference Caret Right Internal asgi invoke invoke_plan kwargs_resolve multimap types Caret Right Ai _providers _structured errors llm streaming Caret Right App compiler diagnostics lifecycle mount registry runtime server state url_for Caret Right Cache Caret Right Backends memory null redis deferred key middleware protocol Caret Right Cli Caret Right Templates minimal scaffold shell sse v2 _check _freeze _makemigrations _new _resolve _routes _run _security_check _templates Caret Right Contracts checker declarations patterns routes rules_accessibility rules_alpine_cdn rules_boundary rules_chirpui_runtime rules_commands rules_composition rules_context_cascade rules_csrf_forms rules_debug_wiring rules_defer_falsy rules_form_routes rules_forms rules_fragment_scope rules_fragment_targets rules_htmx rules_inline rules_islands rules_kida_analysis rules_layout rules_live_blocks rules_mount_app rules_oob_registry rules_oob_targets rules_page_handlers rules_page_shell rules_reactive rules_route_contract rules_route_names rules_safety rules_sse rules_swap rules_unreachable_blocks rules_vary template_scan types utils Caret Right Data Caret Right Drivers postgres sqlite Caret Right Schema diff generate introspect operations parse types _mapping _sqlite database errors migrate pagination query types Caret Right Docs autodoc checks collection frontmatter models plugin search tools Caret Right Ext chirp_ui Caret Right Http cookies forms headers query request response sync_request Caret Right I18N catalog detection formatting middleware Caret Right Markdown errors filters renderer Caret Right Middleware _redis_rate_limit allowed_hosts auth auth_rate_limit builtin csp_nonce csrf debug_fragment_validator inject layout_debug protocol security_headers sessions static streaming_html Caret Right Pages actions context debug discovery Caret Right index bus events stream renderer resolve sections shell_actions shell_context types Caret Right Realtime events sse Caret Right Routing params route router Caret Right Security audit decorators lockout passwords urls Caret Right Server Caret Right Debug editor frames render_plan_snapshot renderers request_context styles template_context alpine debug_page debug_runtime delegation dev dev_browser_reload devtools errors fragment_dispatch fragment_targets_debug handler handler_kwargs htmx_debug htmx_safe_target islands negotiation negotiation_oob production route_explorer sender speculation_rules sse_lifecycle sync_handler terminal_checks terminal_errors view_transitions Caret Right Templating adapter composition filters fragment_target_registry integration kida_adapter macros navigation_swap oob_registry render_plan returns streaming suspense trace Caret Right Testing assertions client route_smoke sse Caret Right Tools events handler registry schema Caret Right Validation result rules chirp config context domains errors extraction freeze health live_blocks logging plugin resilience shell_actions shell_regions sources Chirp API Reference ᗢ Caret Down Link Copy URL External Open LLM text Copy Copy LLM text Share with AI Ask Claude Ask ChatGPT Ask Gemini Ask Copilot Module freeze Freeze — render routes to static HTML files. Walks the app's route table after freeze, renders each freezable URL through the full ASGI middleware stack via TestClient, and writes the output to disk. URLs in the frozen HTML are rewritten from absolute (/about) to relative (../about/) so the output works on any static host — S3, GitHub Pages, Cloudflare, or plain file:// — without a server to resolve clean URLs. 3Classes18Functions Classes FreezeResult 5 ▼ Summary of a freeze run. Summary of a freeze run. Attributes Name Type Description pages_written int — pages_skipped int — errors list[str] — urls tuple[str, ...] — elapsed float — BlockEntry 5 ▼ One addressable section inside a ``SearchEntry``. Block-grained entries enable deep-linking search… One addressable section inside a SearchEntry. Block-grained entries enable deep-linking search results to #anchor targets rather than only page URLs. body should be plain text (tags stripped) for direct match scoring. Attributes Name Type Description block_id str — heading str — body str — anchor str — depth int — SearchEntry 9 ▼ Structured search data captured at render time. Route handlers call `search_contribute`() during f… Structured search data captured at render time. Route handlers call search_contribute() during freeze to register structured metadata for the current page. This bypasses HTML scraping entirely — category, tags, TOC, and description survive from the template context to the search index. Attributes Name Type Description url str — title str — description str — category str — tags frozenset[str] — toc tuple[dict[str, Any], ...] — template_name str — body str — blocks tuple[BlockEntry, ...] — Functions search_contribute 1 None ▼ Register a search contribution for the current freeze render. No-op outside of… def search_contribute(entry: SearchEntry) -> None Register a search contribution for the current freeze render. No-op outside of freeze (the ContextVar defaults to None). Parameters Name Type Description entry SearchEntry _url_to_file_path 2 Path ▼ Map a URL path to an output file path. ``/docs/get-started/`` → ``output_dir/d… def _url_to_file_path(url: str, output_dir: Path) -> Path Map a URL path to an output file path. /docs/get-started/ → output_dir/docs/get-started/index.html / → output_dir/index.html Parameters Name Type Description url str output_dir Path Returns Path _expand_params 2 str ▼ Replace ``{name}`` or ``{name:type}`` placeholders with values. def _expand_params(route_path: str, params: dict[str, str]) -> str Parameters Name Type Description route_path str params dict[str, str] Returns str _make_relative 2 str ▼ Convert an absolute URL to a relative path to ``index.html``. Both arguments a… def _make_relative(from_url: str, to_url: str) -> str Convert an absolute URL to a relative path to index.html. Both arguments are absolute paths (e.g. /articles/foo). The result is relative from the directory containing from_url/index.html and points to to_url/index.html so that file:// browsing works (no server-side index resolution needed). Examples:: _make_relative("/articles/foo", "/about") # "../../about/index.html" _make_relative("/articles/foo", "/") # "../../index.html" _make_relative("/", "/about") # "about/index.html" Parameters Name Type Description from_url str to_url str Returns str _relativize_html 3 str ▼ Rewrite absolute URL paths in *html* to relative paths. Only rewrites paths th… def _relativize_html(html: str, page_url: str, known_urls: frozenset[str]) -> str Rewrite absolute URL paths in html to relative paths. Only rewrites paths that match known_urls (the set of frozen pages). External URLs, anchors, and unknown paths are left untouched. Parameters Name Type Description html str page_url str known_urls frozenset[str] Returns str _page_depth 1 int ▼ Return the directory depth of a URL path (for relative path computation). def _page_depth(url: str) -> int Parameters Name Type Description url str Returns int _extract_title 1 str ▼ Extract the page title from or first . def _extract_title(html: str) -> str Parameters Name Type Description html str Returns str _extract_snippet 2 str ▼ Extract a text snippet from the element for search matching. def _extract_snippet(html: str, max_len: int = 200) -> str Parameters Name Type Description html str max_len int Default: 200 Returns str _build_search_index 1 list[dict[str, str]] ▼ Build a search index from rendered (url, html) pairs. Each entry has ``u`` (re… def _build_search_index(rendered: list[tuple[str, str]]) -> list[dict[str, str]] Build a search index from rendered (url, html) pairs. Each entry has u (relative file path), t (title), and optionally d (text snippet for full-text matching). Parameters Name Type Description rendered list[tuple[str, str]] Returns list[dict[str, str]] _build_rich_search_index 2 dict[str, Any] ▼ Build a structured search manifest from render-time contributions. Pages that … def _build_rich_search_index(contributions: list[SearchEntry], rendered: list[tuple[str, str]]) -> dict[str, Any] Build a structured search manifest from render-time contributions. Pages that contributed structured data via search_contribute() get full metadata (category, tags, TOC, description). Pages without contributions fall back to HTML scraping for title + snippet. The manifest format (v2):: { "version": 2, "facets": {"category": [...], "tags": [...]}, "entries": [{"u", "t", "d", "c", "tags", "toc", "body", "blocks"}, ...] } Each block entry has {"id", "h", "b", "a", "d"} for block id, heading, plain-text body, anchor (href fragment), and heading depth. Version 2 is a superset of v1: readers that ignore blocks behave identically to v1 readers. Parameters Name Type Description contributions list[SearchEntry] rendered list[tuple[str, str]] Returns dict[str, Any] _extract_internal_hrefs 2 list[str] ▼ Return the set of internal URLs referenced by ```` in *html*. Fragments (``#se… def _extract_internal_hrefs(html: str, known_urls: frozenset[str]) -> list[str] Return the set of internal URLs referenced by <a href> in html. Fragments (#section) and query strings (?x=1) are stripped before matching against known_urls. External links (https:, mailto:) are not captured because the regex anchors on href="/ . Parameters Name Type Description html str known_urls frozenset[str] Returns list[str] _build_xref_graph 2 dict[str, Any] ▼ Build the cross-reference graph from rendered pages. Manifest format:: { … def _build_xref_graph(rendered: list[tuple[str, str]], known_urls: frozenset[str]) -> dict[str, Any] Build the cross-reference graph from rendered pages. Manifest format:: { "version": 1, "pages": { "/docs/intro/": { "references": ["/docs/a/", "/docs/b/"], "referenced_by": ["/docs/c/"] } } } Lists are sorted alphabetically so the output is deterministic. Self- references are excluded. Parameters Name Type Description rendered list[tuple[str, str]] known_urls frozenset[str] Returns dict[str, Any] _inject_static_search 3 str ▼ Inject the client-side search script into a frozen HTML page. Only injects if … def _inject_static_search(html: str, depth: int, index_path: str) -> str Inject the client-side search script into a frozen HTML page. Only injects if the page contains a .chirp-docs-search input. Uses a <script src> for the index (works on file://) and an inline script for the search logic. Parameters Name Type Description html str depth int index_path str Returns str _enumerate_urls 1 tuple[list[str], list[st… ▼ Classify routes and expand parameterized ones. Returns (urls, skipped_reasons,… def _enumerate_urls(app: App) -> tuple[list[str], list[str], list[str]] Classify routes and expand parameterized ones. Returns (urls, skipped_reasons, warnings). Parameters Name Type Description app App Returns tuple[list[str], list[str], list[str]] _live_blocks_for_url 2 list[tuple[str, LiveBloc… ▼ Return ``(route_path, spec)`` for live blocks matching *url*. An empty list me… def _live_blocks_for_url(app: App, url: str) -> list[tuple[str, LiveBlockSpec]] Return (route_path, spec) for live blocks matching url. An empty list means this URL has no live-block rewrites to apply. Parameters Name Type Description app App url str Returns list[tuple[str, LiveBlockSpec]] _live_placeholder_html 2 str ▼ Render the htmx placeholder that replaces a live block in frozen HTML. def _live_placeholder_html(url: str, spec: LiveBlockSpec) -> str Parameters Name Type Description url str spec LiveBlockSpec Returns str _apply_live_blocks 5 str ▼ Rewrite declared live blocks in *html* as htmx placeholders. Matches *url* to … def _apply_live_blocks(html: str, url: str, app: App, captures: list[tuple[str, dict[str, Any]]], errors: list[str]) -> str Rewrite declared live blocks in html as htmx placeholders. Matches url to a registered route, then for each live block declared on that route renders it in isolation with the captured template context and replaces the exact string match in html. No-op when nothing matches. Parameters Name Type Description html str url str app App captures list[tuple[str, dict[str, Any]]] errors list[str] Returns str freeze 3 FreezeResult ▼ Freeze the app to static HTML files. Renders every freezable GET route through… async async def freeze(app: App, output_dir: Path, *, exclude: list[str] | None = None) -> FreezeResult Freeze the app to static HTML files. Renders every freezable GET route through the full ASGI stack and writes the output to output_dir. Parameters Name Type Description app App output_dir Path exclude list[str] | None Default: None Returns FreezeResult ← Previous extraction Next → health List Magnifying Glass ESC Recent Clear Magnifying Glass No results for "" Start typing to search... ↑↓ Navigate ↵ Open ESC Close Powered by Lunr ⌁⌁ Magnifying Glass Search X Close Documentation Caret Down Info About Get Started Build Apps Quality and Operations File Text Reference Cube Examples Applied Tutorials Releases Dev Caret Down GitHub Chirp API Reference Forum Palette Appearance Chevron Down Mode Monitor System Sun Light Moon Dark Palette Snow Lynx Brown Bengal Silver Bengal Charcoal Bengal Blue Bengal © 2026 Chirp built in ᓚᘏᗢ { "linkPreviews": { "enabled": true, "hoverDelay": 200, "hideDelay": 150, "showSection": true, "showReadingTime": true, "showWordCount": true, "showDate": true, "showTags": true, "maxTags": 3, "includeSelectors": [".prose"], "excludeSelectors": ["nav", ".toc", ".breadcrumb", ".pagination", ".card", "[class*='-card']", ".tab-nav", "[class*='-widget']", ".child-items", ".content-tiles"], "allowedHosts": [], "allowedSchemes": ["https"], "hostFailureThreshold": 3, "showDeadLinks": true } } window.BENGAL_LAZY_ASSETS = { tabulator: '/chirp/assets/js/tabulator.min.js', dataTable: '/chirp/assets/js/data-table.js', mermaidToolbar: '/chirp/assets/js/mermaid-toolbar.9de5abba.js', mermaidTheme: '/chirp/assets/js/mermaid-theme.344822c5.js', graphMinimap: '/chirp/assets/js/graph-minimap.ff04e939.js', graphContextual: '/chirp/assets/js/graph-contextual.355458ba.js' }; window.BENGAL_ICONS = { close: '/chirp/assets/icons/close.911d4fe1.svg', enlarge: '/chirp/assets/icons/enlarge.652035e5.svg', copy: '/chirp/assets/icons/copy.3d56e945.svg', 'download-svg': '/chirp/assets/icons/download.04f07e1b.svg', 'download-png': '/chirp/assets/icons/image.c34dfd40.svg', 'zoom-in': '/chirp/assets/icons/zoom-in.237b4a83.svg', 'zoom-out': '/chirp/assets/icons/zoom-out.38857c77.svg', reset: '/chirp/assets/icons/reset.d26dba29.svg' }; { "prerender": [ { "where": { "and": [ { "href_matches": "/docs/*" }, { "not": { "selector_matches": "[data-external], [target=_blank], .external" } } ] }, "eagerness": "conservative" } ], "prefetch": [ { "where": { "and": [ { "href_matches": "/*" }, { "not": { "selector_matches": "[data-external], [target=_blank], .external" } } ] }, "eagerness": "conservative" } ] } Arrow Up -------------------------------------------------------------------------------- Metadata: - Word Count: 2096 - Reading Time: 10 minutes