How It Works

Current

The default mode is the current, modern httk-serve workflow.

Directory layout

A site source directory is expected to contain:

  • content/: page sources (.md, .rst, .html)

  • templates/: Jinja2 templates (for example default.html.j2 and base_default.html.j2)

  • static/: static files copied as-is in publish mode

  • functions/: optional Python modules exposing execute(...)

Runtime flow

  1. Route resolution maps a URL path to a content page or static file.

  2. Content rendering extracts metadata and body HTML.

  3. Function injection evaluates *-function metadata entries when query/post constraints are satisfied.

  4. Template rendering produces final HTML through Jinja2.

  5. ASGI serving returns responses, or static publishing writes .html output files.

Public API

The main API surface is:

  • httk.serve.web.create_asgi_app(...)

  • httk.serve.web.serve(...)

  • httk.serve.web.publish(...)

Example usage

Serve dynamically:

from httk.serve.web import serve
serve("src", port=8080)

Publish statically:

from httk.serve.web import publish
publish("src", "public", "http://127.0.0.1/")

To control link style in published output:

publish("src", "public", "http://127.0.0.1/", use_urls_without_ext=False)  # -> about.html
publish("src", "public", "http://127.0.0.1/", use_urls_without_ext=True)   # -> about

To split static and dynamic hosting in publish mode:

publish(
    "src",
    "public",
    "https://dynamic.example",          # dynamic host
    host_static="https://static.example",  # static host
)

Default classification:

  • pages with at least one *-function metadata key are treated as dynamic

  • pages without function metadata are treated as static

You can override per page in frontmatter:

---
hosting: static   # or: dynamic
---

Examples

Modern examples live under examples/modern:

  • minimal

  • rst_site

  • blog

  • search_app

For a ready-made starter repository, see Site Template Repository.

Legacy

httk-serve also supports a compatibility mode for legacy site structures and templates.

Enable compatibility mode

Use compatibility_mode=True in API calls:

from httk.serve.web import serve
serve("src", compatibility_mode=True)

Compatibility behaviors

When compatibility mode is enabled, httk-serve additionally supports:

  • .httkweb content and .httkweb.html template resolution

  • legacy formatter constructs used by old templates (for example repeat/call/if forms)

  • loading global metadata from config.* (or another name via config_name)

  • running functions/init.py at engine startup

  • _functions/ directory fallback when functions/ is not present

Site resource lifecycle

functions/init.py can open a persistent, site-local resource once and register its synchronous cleanup callback through the stable global_data["httk_serve_resources"] entry. SiteResources calls callbacks in reverse registration order when its SiteEngine closes. The registry is idempotent, so it is safe for a server wrapper and ASGI shutdown to both close the same engine.

# src/functions/init.py
from httk.serve.web import SITE_RESOURCES_KEY


def execute(global_data, **kwargs):
    store = open_store()
    global_data["store"] = store
    global_data[SITE_RESOURCES_KEY].register(store.close)

Startup failures close resources registered so far before the original error is reported. publish(), httk serve web check, and httk serve web list close their short-lived engines automatically; an ASGI app owns its engine until ASGI lifespan shutdown. Call engine.close() (or use with SiteEngine(...)) when managing an engine directly.

Examples

Migrated legacy examples are available under examples/legacy:

  • static_simple

  • hello_world_app

  • rst_templator

  • blog

  • search_app

For legacy examples that use optional old httk subsystems, availability depends on those dependencies.

See also: Migration: Legacy Templates to Jinja2.