Summary
Enable users to run code written with popular web frameworks (Flask, FastAPI, Express, etc.) in fabricks without requiring WASI-specific interfaces. Instead of exposing WASI HTTP implementation details to users, fabricks would provide compile-time adapters that transform standard framework code into WASI HTTP handlers.
Problem
Currently, to run an HTTP service in fabricks, users must implement language-specific interfaces:
- Rust: Direct
wasi:http/incoming-handler implementation
- Python: Custom
handler(request) -> dict function
- Node.js: Custom handler pattern
This leaks WASI implementation details into user code and prevents running existing applications without modification.
Why Not Runtime Bridging?
We investigated running traditional HTTP servers inside WASM with an internal bridge (see exploration below), but this is not technically feasible with current WASI:
| Blocker |
Details |
| No socket binding |
WASI forbids WASM modules from calling bind()/listen(). Only the host can create listening sockets. |
| Threading withdrawn |
wasi-threads was officially withdrawn (Aug 2023). No concurrent connection handling. |
| Blocking deadlock |
A blocking accept() loop freezes the entire WASM instance. |
| Architectural mismatch |
wasi:http/incoming-handler was designed to move the accept loop to the host. |
Every production WASM platform (Fermyon Spin, Fastly, wasmCloud, Cloudflare Workers) uses the handler model for this reason.
Proposed Solution: Compile-Time Framework Adapters
Instead of runtime bridging, build compile-time adapters that transform framework code into WASI HTTP handlers.
User Experience
Flask example:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello World!"
@app.route('/users/<int:id>')
def get_user(id):
return jsonify({"id": id, "name": "Alice"})
# No app.run() needed - fabricks handles serving
Fabrickfile:
[from]
source = "python"
[python]
framework = "flask"
app = "main:app" # Like gunicorn/uvicorn syntax
User runs:
fabricks service run ./my-flask-app --network default
The user writes standard Flask code. Fabricks handles the WASI HTTP transformation automatically.
How It Works
The adapter generates a WASI HTTP handler wrapper at build time:
# Generated by fabricks (user never sees this)
from user_app import app # User's Flask app
class WasiHttpHandler:
def handle(self, wasi_request, response_out):
# 1. Convert WASI request → WSGI environ
environ = self._to_wsgi_environ(wasi_request)
# 2. Call Flask app via WSGI interface
response_body = []
def start_response(status, headers):
self.status = status
self.headers = headers
result = app.wsgi_app(environ, start_response)
# 3. Convert WSGI response → WASI HTTP response
self._send_wasi_response(response_out, self.status, self.headers, result)
The key insight: WSGI/ASGI are already abstraction layers. We adapt those interfaces, not the frameworks directly.
Implementation Plan
Phase 1: Python WSGI Adapter (Flask, Django, Bottle)
-
Create WSGI → WASI HTTP adapter
- Convert
IncomingRequest → WSGI environ dict
- Handle
start_response() callback
- Convert response iterator →
ResponseOutparam
-
Update PythonBuilder
- Add
framework and app config options
- Generate wrapper that imports user's WSGI app
- Bundle adapter + user code into component
-
Supported frameworks:
- Flask
- Django
- Bottle
- Any WSGI-compliant app
Phase 2: Python ASGI Adapter (FastAPI, Starlette)
-
Create ASGI → WASI HTTP adapter
- Handle async request/response lifecycle
- Support streaming responses
-
Supported frameworks:
- FastAPI
- Starlette
- Quart
- Any ASGI-compliant app
Phase 3: Node.js Adapters
-
Express adapter
- Transform Express middleware chain → handler
- Support req/res objects
-
Hono/Fastify adapters
- Similar approach for other popular frameworks
Phase 4: Rust Adapters (Optional)
- Axum/Actix-web adapters
- Tower service → handler transformation
- May be less needed since Rust devs often work closer to the metal
Fabrickfile Configuration
[from]
source = "python" # or "nodejs", "rust"
# Python-specific
[python]
framework = "flask" # or "fastapi", "django", "wsgi", "asgi"
app = "main:app" # Module path to app object
# Optional: requirements handling
requirements = "requirements.txt"
# Node.js-specific
[nodejs]
framework = "express" # or "hono", "fastify"
app = "app.js" # Entry point
handler = "app" # Exported app object
Benefits
- Zero WASI knowledge required - Users write standard framework code
- Run existing apps - Migrate Flask/FastAPI/Express apps with minimal changes
- Familiar patterns - Use decorators, middleware, routing they already know
- Ecosystem compatibility - Works with framework extensions and middleware
Technical Considerations
Request/Response Mapping
| WASI HTTP |
WSGI |
ASGI |
method() |
REQUEST_METHOD |
scope["method"] |
path_with_query() |
PATH_INFO + QUERY_STRING |
scope["path"] + scope["query_string"] |
headers() |
HTTP_* environ vars |
scope["headers"] |
consume() body |
wsgi.input |
receive() |
ResponseOutparam |
start_response() + iterator |
send() |
Limitations to Document
- No WebSockets (yet) - WASI HTTP is request/response only
- No background tasks - Handler must complete synchronously
- No file uploads to disk - Must use memory or granted filesystem paths
- No subprocess spawning - WASI sandbox restriction
Success Criteria
References
Related
- Supersedes investigation into runtime HTTP bridging (not feasible with current WASI)
- Builds on existing language builders (RustBuilder, GoBuilder, PythonBuilder)
Summary
Enable users to run code written with popular web frameworks (Flask, FastAPI, Express, etc.) in fabricks without requiring WASI-specific interfaces. Instead of exposing WASI HTTP implementation details to users, fabricks would provide compile-time adapters that transform standard framework code into WASI HTTP handlers.
Problem
Currently, to run an HTTP service in fabricks, users must implement language-specific interfaces:
wasi:http/incoming-handlerimplementationhandler(request) -> dictfunctionThis leaks WASI implementation details into user code and prevents running existing applications without modification.
Why Not Runtime Bridging?
We investigated running traditional HTTP servers inside WASM with an internal bridge (see exploration below), but this is not technically feasible with current WASI:
bind()/listen(). Only the host can create listening sockets.wasi-threadswas officially withdrawn (Aug 2023). No concurrent connection handling.accept()loop freezes the entire WASM instance.wasi:http/incoming-handlerwas designed to move the accept loop to the host.Every production WASM platform (Fermyon Spin, Fastly, wasmCloud, Cloudflare Workers) uses the handler model for this reason.
Proposed Solution: Compile-Time Framework Adapters
Instead of runtime bridging, build compile-time adapters that transform framework code into WASI HTTP handlers.
User Experience
Flask example:
Fabrickfile:
User runs:
The user writes standard Flask code. Fabricks handles the WASI HTTP transformation automatically.
How It Works
The adapter generates a WASI HTTP handler wrapper at build time:
The key insight: WSGI/ASGI are already abstraction layers. We adapt those interfaces, not the frameworks directly.
Implementation Plan
Phase 1: Python WSGI Adapter (Flask, Django, Bottle)
Create WSGI → WASI HTTP adapter
IncomingRequest→ WSGIenvirondictstart_response()callbackResponseOutparamUpdate PythonBuilder
frameworkandappconfig optionsSupported frameworks:
Phase 2: Python ASGI Adapter (FastAPI, Starlette)
Create ASGI → WASI HTTP adapter
Supported frameworks:
Phase 3: Node.js Adapters
Express adapter
Hono/Fastify adapters
Phase 4: Rust Adapters (Optional)
Fabrickfile Configuration
Benefits
Technical Considerations
Request/Response Mapping
method()REQUEST_METHODscope["method"]path_with_query()PATH_INFO+QUERY_STRINGscope["path"]+scope["query_string"]headers()HTTP_*environ varsscope["headers"]consume()bodywsgi.inputreceive()ResponseOutparamstart_response()+ iteratorsend()Limitations to Document
Success Criteria
References
Related