Skip to content

Framework adapter contracts

Stability

Classifications for this surface are recorded in STABILITY.md. Package maturity (Beta/Alpha) is separate from API level (beta / experimental / internal / deferred).

Status: Adapters shipped (hedron-flask, hedron-django). Capability readiness: Supported for Blueprint/init_app, AppConfig, forms bridge, and bounded QuerySet DataSource. Package maturity remains Beta on PyPI — pin versions. Portable contracts live in hedron-core. Living train: 0.20.0 (Published).

Autodoc signatures: Autodoc — Framework adapters. Quickstarts: Flask · Django.

Install

pip install "hedron-flask>=0.20.0,<0.21"
pip install "hedron-django>=0.20.0,<0.21"   # Django >=5.2,<6

Portable baseline

Hedron's portable adapter surface represents only semantics that can exist without a raw framework request or response:

  • normalized HTMX request facts and page/fragment/history mode;
  • interaction content, status, OOB updates, approved headers, history, and cache policy;
  • reverse-URL requests resolved by the host router;
  • static/build-manifest asset references;
  • authenticated/session-scope signals without session contents;
  • lifecycle resource descriptions and sanitized diagnostics; and
  • declared capability metadata.

Concrete adapters translate these values to native FastAPI, Flask, or Django objects. Core contracts never retain a raw request, response, session, dependency, database handle, or application object.

Every approved HTMX header is revalidated at the adapter boundary; arbitrary header mappings cannot bypass redirect, selector, cache, or security policy.

FastAPI (flagship)

Surface Role
Hedron / HedronRouter Application + route registration
interaction_headers / InteractionResult Typed HTMX responses
CSRF cookie + X-CSRF-Token Double-submit on unsafe methods

Full constructor contract: Hedron.

Flask (hedron_flask.HedronFlask)

Construct with an import_name to own a Flask app, or construct without an app and call init_app for application-factory composition.

from flask import Flask
from hedron_flask import HedronFlask
from hedron_core import Page, Text

hf = HedronFlask()
app = Flask(__name__)
hf.init_app(app, security="standard")


@hf.page("/")
def home():
    return Page(Text("Hello"), title="Home")

Constructor

Parameter Type Default Description
import_name str \| None None When set, creates Flask(import_name, **kwargs) and calls init_app
csrf_cookie_name str "hedron_csrf" CSRF cookie name
auto_csrf_cookie bool True Seed CSRF cookie on safe responses
csrf_protect bool True Validate CSRF on unsafe methods in respond / wrapped views
csrf_cookie_secure bool \| None None True always Secure; None follows request/FLASK_ENV; False never
security profile name | SecurityPolicy "standard" Portable security profile
**kwargs Passed to Flask(...) when import_name is set

Methods

Method Returns Description
init_app(app, *, security=None) Flask Bind extension (idempotent for the same app)
page(rule, **options) decorator Register a page view; supports fragment_regions, methods
respond(value, request, *, context=None, mode=None, extra_headers=None, fragment_regions=None) Flask Response Render NodeLike / InteractionResult; CSRF on unsafe methods when enabled
auth_signal(request=None) AuthSignal Flask-Login / session-derived auth signal (no session body to core)
csrf_token(request) str Current CSRF token for forms / headers
attach_csrf_cookie(response, request, token=None) str Set CSRF cookie on a response

Also: HedronBlueprint, hedron_route, wrap_hedron_view, interaction_response, component_response, FlaskUrlReverser — see role table below and Autodoc.

Symbol Role
HedronBlueprint Blueprint with page / component / action / include_component
hedron_route Register views returning components / InteractionResult (CSRF on unsafe methods)
wrap_hedron_view Public CSRF + InteractionResult conversion wrapper
interaction_response / component_response Build Flask Response values
FlaskUrlReverser Path-only url_for with root_path / script_name

Raises / status: CSRF failure → HTTP 403. Unauthorized OOB / fragment region → 403 body. Calling page / route before init_appRuntimeError.

Django (hedron_django.HedronDjango)

Thin helper for native Django views. Install AppConfig for system checks; wrap views with hedron_view or call respond from your own views.

from hedron_django import HedronDjango
from hedron_core import Page, Text

hd = HedronDjango()


def home(request):
    return hd.respond(Page(Text("Hello"), title="Home"), request)

Constructor

Parameter Type Default Description
(none) HedronDjango() takes no constructor args; create one helper per process

Methods

Method Returns Description
render(value, request, *, context=None, mode=None) str HTML body only
respond(value, request, *, context=None, mode=None, extra_headers=None, fragment_regions=None) HttpResponse Render component or InteractionResult; seeds CSRF cookie on safe GETs
auth_signal(request) AuthSignal Django request.user / session tenant signal
csrf_token(request) str Portable CSRF token for X-CSRF-Token
Symbol Role
HedronDjangoConfig Installable AppConfig + hedron.* system checks
hedron_view Wrap sync/async views; seeds CSRF cookie on safe GETs
interaction_response / component_response Build HttpResponse values
DjangoUrlReverser reverse with mount prefixes

Settings: Prefer CSRF_HEADER_NAME = "HTTP_X_CSRF_TOKEN" for portable X-CSRF-Token. Django floor: >=5.2,<6.

Django forms bridge (hedron_django.forms)

Function Returns Description
form_to_nodes(form, *, request=None, include_csrf=True) list[NodeLike] Optional CSRF hidden input, non-field errors, then field nodes
formset_to_nodes(formset, *, request=None, include_csrf=True) list[NodeLike] Management form + each form’s fields
validation_interaction(form, *, request=None, explanation=...) InteractionResult Invalid-form fragment for HTMX / non-HTMX parity

CSRF: pass request when include_csrf=True so the portable hidden field is included. Prefer Django’s CSRF middleware + portable X-CSRF-Token header for unsafe methods.

DjangoQuerySetDataSource (hedron_data)

Bounded QuerySet DataEditorSource. Deny-by-default: omitted sort/filter allowlists mean no client refinements. The constructor never calls .objects.all() — you supply an already authorized/tenant-scoped QuerySet.

Parameter Type Default Description
base_queryset QuerySet required Application-scoped queryset (must be a *QuerySet)
key_field str "pk" Row key field
schema sequence of ColumnSchema () Column catalog
allowlisted_sort_fields frozenset[str] \| None None → empty Client sort allowlist
allowlisted_filter_fields frozenset[str] \| None None → empty Client filter allowlist
search_fields sequence of str () Search fields
max_page_size int 100 Page size ceiling
query_budget int 25 Max ORM queries per fetch (QueryBudgetExceeded)
row_mapper callable | None None Map model → JSON row
apply_changes callable | None None Persist editor changes
transaction_owner str "application" Who owns DB transactions

Raises: TypeError if base_queryset is not a QuerySet; QueryBudgetExceeded when the fetch exceeds query_budget.

Live transport (FastAPI vs adapters)

Official HTMX SSE, focused streaming, and page/session WebSocket channels are experimental (hedron.experimental) on the FastAPI flagship until Deferred ops gates close (see live interaction and STABILITY). Flask and Django adapters keep bounded polling as the Supported live-status fallback; they do not ship the FastAPI SSE/WebSocket helpers.

Errors

Situation Host Behavior
Invalid QuerySet type for DjangoQuerySetDataSource Django data TypeError
Query count over budget Django data QueryBudgetExceeded
CSRF missing/invalid on unsafe method Flask hedron_route / respond HTTP 403
CSRF missing/invalid Django (middleware + portable header) HTTP 403 (Django CSRF)
Unauthorized fragment / OOB region Flask / Django HTTP 403 body
Invalid approved HTMX header values All adapters Rejected at adapter boundary
FastAPI SSE helpers imported on Flask/Django N/A Not shipped — use polling
page/route before init_app Flask RuntimeError

Deferred (not Supported)

Claim Notes
Full adapter live browser matrix Carryover Deferred LIVE-011-BROWSER (ops evidence; not blocking Supported adapter depth on 0.20)
Load/proxy backpressure proof for SSE/WS Carryover Deferred PERF-10-001 — prefer polling; live helpers remain Experimental

CameraCapture / MicrophoneCapture ship as Supported on the FastAPI flagship (with permission/retention policy) — see What’s ready.

Capability matrix

Machine-readable records: hedron_core.adapter.capability_matrix() / COMPATIBILITY / acceptance/ADAPTERS.