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_app → RuntimeError.
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.