mgf-fastapi 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,64 @@
1
+ # IDE
2
+ .idea/
3
+ .vscode/
4
+ *.swp
5
+ *.swo
6
+
7
+ # Python
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+ *.so
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+
29
+ # Virtual environments
30
+ .venv/
31
+ venv/
32
+ env/
33
+ ENV/
34
+
35
+ # Testing and coverage
36
+ .pytest_cache/
37
+ .hypothesis/
38
+ .coverage
39
+ .coverage.*
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ coverage.xml
44
+ *.cover
45
+
46
+ # Type checking
47
+ .mypy_cache/
48
+ .pyre/
49
+ .pytype/
50
+
51
+ # Ruff
52
+ .ruff_cache/
53
+
54
+ # Packaging
55
+ MANIFEST
56
+
57
+ # Claude Code — keep settings.json (committed for the team), ignore
58
+ # per-machine local overrides + runtime locks/state.
59
+ .claude/settings.local.json
60
+ .claude/*.lock
61
+ .claude/scheduled_tasks*
62
+
63
+ # Maintainer planning notes (per-machine; never commit)
64
+ my_stuff/
@@ -0,0 +1,80 @@
1
+ # Changelog
2
+
3
+ All notable changes to `mgf-fastapi` are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
6
+ the project follows [SemVer](https://semver.org/spec/v2.0.0.html). Per
7
+ the 0.x window ([SemVer 0.x](https://semver.org/#spec-item-4) and rule
8
+ **AP-03** from `mgf-common/docs/standards/API_DESIGN.md`), MINOR
9
+ releases MAY break the public API. Pin tightly:
10
+
11
+ ```toml
12
+ mgf-fastapi = ">=0.X.0,<0.Y" # one minor at a time
13
+ ```
14
+
15
+ The release-engineering discipline that produces this changelog is in
16
+ [`mgf-common/docs/standards/RELEASING.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/standards/RELEASING.md).
17
+
18
+ ---
19
+
20
+ ## [Unreleased]
21
+
22
+ Nothing yet.
23
+
24
+ ---
25
+
26
+ ## [0.1.0] — 2026-05-09
27
+
28
+ PyPI: <https://pypi.org/project/mgf-fastapi/0.1.0/> · Tag: `v0.1.0`
29
+
30
+ > **Maiden voyage** — extracted from `mgf-common` v0.28.0 per the
31
+ > federation split plan ([mgf-common/docs/release/federation_roadmap.md](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/release/federation_roadmap.md)).
32
+ > Cutover guide: [`docs/cutover/v0.1.0.md`](docs/cutover/v0.1.0.md).
33
+
34
+ ### Added
35
+
36
+ - **`mgf.fastapi`** — top-level FastAPI integration. `bootstrap` ↔
37
+ FastAPI lifespan (`setup_lifespan`); `RequestIdMiddleware` +
38
+ `current_request_id()`; `ExceptionTranslationMiddleware` +
39
+ `DEFAULT_STATUS_MAP`; `Depends()` helpers (`get_app_context`,
40
+ `get_settings`, `get_request_id`); `REQUEST_ID_HEADER` constant.
41
+ - **`mgf.fastapi.webhooks`** — Svix-shape HMAC webhook verification.
42
+ `WebhookHeaderSchema` (frozen dataclass; case-folds header names);
43
+ `SVIX_SCHEMA` (Svix preset; used verbatim by Clerk);
44
+ `HmacWebhookVerifier` (transport-agnostic core; thread-safe;
45
+ `verify(headers, body) -> (event_id, ts)`); `verify_request`
46
+ (async FastAPI request adapter).
47
+ - **`mgf.fastapi.security`** — `IpAllowlist` FastAPI dependency.
48
+ CIDR allowlist with IPv4 + IPv6 dual-stack; cross-version matches
49
+ deliberately rejected. `trust_proxy=False` default concentrates
50
+ the proxy-trust footgun. Default `cidrs=LOOPBACK_CIDRS` so tests
51
+ work out-of-the-box.
52
+ - **`mgf.fastapi.testing`** — `run_test_app` async context manager.
53
+ Starts uvicorn for a FastAPI app on a free port; yields the base
54
+ URL; tears down cleanly on context exit. Surfaces server-startup
55
+ exceptions immediately instead of timing out silently. Optional
56
+ `[testing]` extra pulls in `uvicorn>=0.30` + `httpx>=0.27`.
57
+ - **`mgf.fastapi.exceptions`** — `HmacVerificationError(HttpUnauthorized)`.
58
+ Inherits the HTTP-01 status leaf from `mgf.common.exceptions` so
59
+ PAPER-24's `http_status` resolution maps to 401 in
60
+ `ExceptionTranslationMiddleware` without an explicit `status_map`
61
+ entry. (Other concrete leaves can be added here in subsequent
62
+ minors per the federation rule "siblings own their domain
63
+ concretes; mgf-common owns hierarchy roots + status leaves.")
64
+
65
+ ### Engineering
66
+
67
+ - 83 tests carried over from `mgf-common/tests/unit/fastapi/` —
68
+ every pre-extraction failure mode and integration path stays
69
+ green at parity. Coverage threshold ≥80% (matches mgf-common's
70
+ standard).
71
+ - Imports from `mgf.common.fastapi.*` rewrite to `mgf.fastapi.*`.
72
+ `HmacVerificationError` re-exported from `mgf.fastapi.exceptions`
73
+ rather than `mgf.common.exceptions` (the latter no longer ships
74
+ it as of mgf-common v0.28).
75
+ - `mgf-common>=0.28,<0.29` runtime dependency. AP-03 0.x window
76
+ pin discipline applies — bump in lock-step with mgf-common's
77
+ next minor.
78
+ - PEP 420 namespace package (no top-level `mgf/__init__.py`); the
79
+ wheel ships `src/mgf/` as-is so `mgf.fastapi` and `mgf.common`
80
+ coexist as siblings.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bassam Alsanie and mgf-common contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: mgf-fastapi
3
+ Version: 0.1.0
4
+ Summary: FastAPI integration adapters for mgf-common — request-id + exception translation + lifespan + webhooks (Svix HMAC) + IpAllowlist + run_test_app. Sibling of mgf-common under the mgf.* namespace.
5
+ Project-URL: Homepage, https://codeberg.org/magogi-admin/mgf-fastapi
6
+ Project-URL: Issues, https://codeberg.org/magogi-admin/mgf-fastapi/issues
7
+ Project-URL: Changelog, https://codeberg.org/magogi-admin/mgf-fastapi/src/branch/master/CHANGELOG.md
8
+ Author: Bassam Alsanie, mgf-fastapi contributors
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: fastapi,ip-allowlist,middleware,request-id,svix,webhooks
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Internet :: WWW/HTTP
24
+ Classifier: Topic :: Software Development :: Libraries
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.11
27
+ Requires-Dist: fastapi>=0.110
28
+ Requires-Dist: mgf-common<0.29,>=0.28
29
+ Provides-Extra: dev
30
+ Requires-Dist: httpx>=0.27; extra == 'dev'
31
+ Requires-Dist: hypothesis>=6.100; extra == 'dev'
32
+ Requires-Dist: import-linter>=2.0; extra == 'dev'
33
+ Requires-Dist: mypy>=1.10; extra == 'dev'
34
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
35
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
36
+ Requires-Dist: pytest>=8.0; extra == 'dev'
37
+ Requires-Dist: ruff>=0.4; extra == 'dev'
38
+ Requires-Dist: uvicorn>=0.30; extra == 'dev'
39
+ Provides-Extra: testing
40
+ Requires-Dist: httpx>=0.27; extra == 'testing'
41
+ Requires-Dist: uvicorn>=0.30; extra == 'testing'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # `mgf-fastapi` — FastAPI integration adapters for mgf-common
45
+
46
+ [![PyPI](https://img.shields.io/pypi/v/mgf-fastapi)](https://pypi.org/project/mgf-fastapi/)
47
+ [![Python](https://img.shields.io/pypi/pyversions/mgf-fastapi)](https://pypi.org/project/mgf-fastapi/)
48
+
49
+ > **Sibling of [`mgf-common`](https://pypi.org/project/mgf-common/)
50
+ > under the `mgf.*` namespace.** Houses every FastAPI-specific
51
+ > adapter that previously lived under `mgf.common.fastapi.*` —
52
+ > extracted at mgf-common v0.28 / mgf-fastapi v0.1 per the
53
+ > [federation split plan](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/release/federation_roadmap.md).
54
+
55
+ ## What this provides
56
+
57
+ | Submodule | What |
58
+ |---|---|
59
+ | `mgf.fastapi` | `bootstrap` ↔ FastAPI lifespan; `RequestIdMiddleware`; `ExceptionTranslationMiddleware`; `Depends()` helpers (`get_app_context`, `get_settings`, `get_request_id`); `setup_lifespan`. |
60
+ | `mgf.fastapi.webhooks` | Svix-shape HMAC webhook verification (`HmacWebhookVerifier`, `WebhookHeaderSchema`, `SVIX_SCHEMA`, `verify_request`). |
61
+ | `mgf.fastapi.security` | `IpAllowlist` FastAPI dependency with proxy-trust opt-in. |
62
+ | `mgf.fastapi.testing` | `run_test_app` async context manager — start uvicorn for a FastAPI app on a free port; yield base URL; clean shutdown. |
63
+ | `mgf.fastapi.exceptions` | `HmacVerificationError(HttpUnauthorized)` and other framework-domain concrete leaves. (HTTP-01 hierarchy roots stay in `mgf.common.exceptions`.) |
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ pip install mgf-fastapi
69
+ # Or with the test-helper extra (uvicorn + httpx for run_test_app):
70
+ pip install 'mgf-fastapi[testing]'
71
+ ```
72
+
73
+ Pulls in `mgf-common` and `fastapi` automatically.
74
+
75
+ ## Quick start
76
+
77
+ ```python
78
+ from fastapi import FastAPI, Request
79
+ from mgf.fastapi import (
80
+ ExceptionTranslationMiddleware,
81
+ RequestIdMiddleware,
82
+ setup_lifespan,
83
+ )
84
+ from mgf.fastapi.webhooks import HmacWebhookVerifier, verify_request
85
+
86
+ app = FastAPI(lifespan=setup_lifespan(app_name="my-service", app_version="0.1.0"))
87
+ app.add_middleware(ExceptionTranslationMiddleware)
88
+ app.add_middleware(RequestIdMiddleware)
89
+
90
+ webhook = HmacWebhookVerifier(secret=settings.webhook_secret)
91
+
92
+ @app.post("/webhooks/clerk")
93
+ async def clerk_webhook(request: Request) -> dict:
94
+ event_id, ts = await verify_request(request, webhook)
95
+ payload = await request.json()
96
+ # ... process the (now-trusted) payload ...
97
+ return {"ok": True}
98
+ ```
99
+
100
+ ## Documentation
101
+
102
+ - [`docs/recipes/fastapi.md`](docs/recipes/fastapi.md) — full FastAPI service walkthrough.
103
+ - [`docs/recipes/webhooks.md`](docs/recipes/webhooks.md) — HMAC webhook verification.
104
+ - [`docs/cutover/v0.1.0.md`](docs/cutover/v0.1.0.md) — maiden voyage migration story (the v0.28 split).
105
+ - [`PUBLIC_API.md`](PUBLIC_API.md) — full public surface contract.
106
+ - [`CHANGELOG.md`](CHANGELOG.md) — release history.
107
+
108
+ For the federation-wide engineering standards (DESIGN_PRINCIPLES,
109
+ ERROR_HANDLING, SECURITY, etc.) see
110
+ [`mgf-common/docs/standards/`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/standards/).
111
+ This sibling inherits them by reference; the standards source-of-truth
112
+ lives in mgf-common.
113
+
114
+ ## Status
115
+
116
+ 🚧 **Experimental** — every public name is `experimental` per AP-09.
117
+ Promotion to `stable` happens release-by-release as consumer feedback
118
+ in [`FEEDBACK.md`](FEEDBACK.md) converges. The 0.x window applies.
119
+ Pin tightly: `mgf-fastapi = ">=0.X.0,<0.Y"`.
120
+
121
+ ## Cross-references
122
+
123
+ - **Filing process for sharp edges**: open an entry on
124
+ [`mgf-common/FEEDBACK.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/FEEDBACK.md)
125
+ with `[mgf-fastapi]` prefix, OR file directly on this repo's
126
+ Issues → maintainer mirrors into the canonical FEEDBACK.md.
127
+ - **Federation pattern**:
128
+ [`mgf-common/docs/design/federation.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/design/federation.md).
129
+ - **The split that created this sibling**:
130
+ [`mgf-common/docs/release/federation_roadmap.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/release/federation_roadmap.md).
@@ -0,0 +1,138 @@
1
+ # Public API — `mgf-fastapi`
2
+
3
+ > ⚠️ **This file documents `master` HEAD.** Names listed here may
4
+ > be unreleased. To learn what your installed wheel actually
5
+ > ships, run `pip show mgf-fastapi` and compare against the
6
+ > `__version__` field at the top of `PUBLIC_API.json`. The git
7
+ > tag matching the published version is authoritative.
8
+
9
+ This document is the **contract list** for `mgf-fastapi`. Every name
10
+ listed below is a public name that consumers MAY depend on.
11
+
12
+ The contract terms are defined in
13
+ [`mgf-common/docs/standards/API_DESIGN.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/standards/API_DESIGN.md). In
14
+ short:
15
+
16
+ - **Stability tiers:**
17
+ - `experimental` — MAY change shape in any MINOR release
18
+ (the 0.x window keeps everything experimental in practice).
19
+ - `stable` — follows the deprecation cycle; removal only in MAJOR.
20
+ - `deprecated` — slated for removal; emits `DeprecationWarning`.
21
+ - **Names not listed below are private.** Anything starting with
22
+ `_` (underscore-prefixed module names like `_lifespan.py`) is
23
+ internal and MAY change without notice.
24
+ - **The 0.x window applies.** Per [SemVer](https://semver.org/) 0.x
25
+ semantics, MINOR releases MAY break the public API. Pin tightly:
26
+ `mgf-fastapi = ">=0.X.0,<0.Y"`.
27
+
28
+ ---
29
+
30
+ ## `mgf.fastapi`
31
+
32
+ Top-level FastAPI integration adapters. Re-exports the load-bearing
33
+ public names from the submodules. Closed-box: depends on
34
+ `mgf-common` + `fastapi` only.
35
+
36
+ | Name | Tier | Description |
37
+ |---|---|---|
38
+ | `setup_lifespan(*, app_name=None, app_version=None, settings=None)` | experimental | Build a FastAPI lifespan that runs `mgf.common.bootstrap()` at app startup. The resulting `AppContext` is stashed on `app.state.mgf_context` for dependency injection. LIFO unwind on shutdown. |
39
+ | `RequestIdMiddleware` | experimental | ASGI middleware. Injects/forwards `X-Request-Id` header (case-insensitive); generates UUID4 if absent. Sets `request.state.request_id` AND a contextvar shared with `mgf.common._request_id` (so cross-framework processes correlate end-to-end). Echoes in response. |
40
+ | `current_request_id() -> str` | experimental | Read the per-request id from the contextvar. Returns empty string outside a request. PEP 567 propagates across async children. |
41
+ | `REQUEST_ID_HEADER = "X-Request-Id"` | experimental | Canonical header name. Inbound case-insensitive; outbound canonical. |
42
+ | `ExceptionTranslationMiddleware` | experimental | ASGI middleware. Catches `AppError` from handlers; maps to typed JSON responses via `status_map` (default per category) AND falls back to `HttpError.http_status` (PAPER-24 resolution) for HTTP-01 leaves like `HttpUnauthorized`, `HttpForbidden`, `HttpNotFound`, etc. — including sibling-domain concretes like `HmacVerificationError` (401 via inheritance). Configurable via `status_map=` and `response_factory=` kwargs. Includes `request_id` in error body when `RequestIdMiddleware` is installed. |
43
+ | `DEFAULT_STATUS_MAP: dict[type[AppError], int]` | experimental | Default exception → HTTP status mapping. `SchemaValidationError`→400, `ResourceNotFoundError`→404, `ResourceAlreadyExistsError`→409, `HostEnvironmentError`→503, `ConfigError`/`OperationError`/`VaultError`/`AppError`→500. |
44
+ | `get_app_context(request) -> AppContext` | experimental | FastAPI dependency. Reads `request.app.state.mgf_context`; raises `AppConfigError` if unset (lifespan not wired). |
45
+ | `get_settings(request) -> MgfSettings` | experimental | FastAPI dependency. Returns the `MgfSettings` from the active context. |
46
+ | `get_request_id(request) -> str` | experimental | FastAPI dependency. Reads `request.state.request_id`; returns `""` if `RequestIdMiddleware` is not installed. |
47
+ | `HmacVerificationError` | experimental | Re-exported from `mgf.fastapi.exceptions`. Convenience top-level alias for the most-commonly-caught sibling-domain exception. |
48
+
49
+ ---
50
+
51
+ ## `mgf.fastapi.exceptions`
52
+
53
+ Sibling-domain concrete exception leaves. Per the federation rule
54
+ (decision **D3** in the [federation roadmap](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/release/federation_roadmap.md)),
55
+ siblings own their domain concretes; mgf-common owns hierarchy
56
+ roots + generic status-code leaves.
57
+
58
+ | Name | Tier | Description |
59
+ |---|---|---|
60
+ | `HmacVerificationError` | experimental | A webhook HMAC signature failed verification. Inherits `mgf.common.exceptions.HttpUnauthorized` (status 401). Raised by `mgf.fastapi.webhooks.HmacWebhookVerifier` on any failure mode (missing header, unparseable timestamp, replay-window miss, signature mismatch). PAPER-24's `http_status` resolution maps it to 401 in `ExceptionTranslationMiddleware` without an explicit `status_map` entry. |
61
+
62
+ ---
63
+
64
+ ## `mgf.fastapi.webhooks`
65
+
66
+ Svix-shape HMAC webhook verification. Pulls the per-consumer
67
+ verification dance into one tested primitive. Closes
68
+ [`mgf-common`'s PAPER-26](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/FEEDBACK.md)
69
+ (originally shipped in mgf-common v0.26; moved to this sibling at
70
+ mgf-common v0.28 / mgf-fastapi v0.1).
71
+
72
+ | Name | Tier | Description |
73
+ |---|---|---|
74
+ | `WebhookHeaderSchema(id_header, timestamp_header, signature_header)` | experimental | Frozen dataclass naming the three Svix-style headers a provider uses. Header names are case-folded to lowercase on construction. |
75
+ | `SVIX_SCHEMA: WebhookHeaderSchema` | experimental | Pre-built schema for Svix's canonical headers (`svix-id` / `svix-timestamp` / `svix-signature`). Used by Clerk verbatim. Stripe / GitHub / Slack are deferred to follow-up papers. |
76
+ | `HmacWebhookVerifier(*, secret, schema=SVIX_SCHEMA, replay_window_seconds=300, signature_prefix="v1,", now=time.time)` | experimental | Transport-agnostic verifier. `verify(headers, body) -> (event_id, ts)`. Raises `HmacVerificationError` on any failure (missing header, unparseable timestamp, replay-window miss, signature mismatch). Stateless and thread-safe. |
77
+ | `verify_request(request, verifier) -> (event_id, ts)` | experimental | Async FastAPI request adapter. Reads `await request.body()`, lifts `request.headers` into the verifier's input, returns `verifier.verify(...)`. Caller MUST NOT have consumed the body yet. |
78
+
79
+ See [`docs/recipes/webhooks.md`](docs/recipes/webhooks.md)
80
+ for the full walkthrough including key rotation, custom schemas,
81
+ and the consumer pattern for replacing per-provider verifiers.
82
+
83
+ ---
84
+
85
+ ## `mgf.fastapi.security`
86
+
87
+ Request-side security primitives for FastAPI. Closes
88
+ [`mgf-common`'s PAPER-27](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/FEEDBACK.md)
89
+ (originally shipped in mgf-common v0.27; moved to this sibling at
90
+ mgf-common v0.28 / mgf-fastapi v0.1).
91
+
92
+ | Name | Tier | Description |
93
+ |---|---|---|
94
+ | `IpAllowlist(cidrs=LOOPBACK_CIDRS, *, trust_proxy=False)` | experimental | FastAPI dependency that 403s requests whose client IP isn't in `cidrs`. Concentrates the proxy-trust footgun in one knob — `trust_proxy=False` default; honouring `X-Forwarded-For` requires explicit opt-in. IPv4 + IPv6 dual-stack with cross-version matches deliberately rejected. Misconfigured CIDRs raise at construction. |
95
+ | `LOOPBACK_CIDRS: tuple[str, ...]` | experimental | The default `cidrs=` for `IpAllowlist` — `("127.0.0.0/8", "::1/128")`. Loopback-only so tests work out-of-the-box; consumers OVERRIDE for production. |
96
+
97
+ ---
98
+
99
+ ## `mgf.fastapi.testing`
100
+
101
+ Test-server lifecycle helper. Closes
102
+ [`mgf-common`'s PAPER-28](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/FEEDBACK.md)
103
+ (originally shipped in mgf-common v0.27; moved to this sibling at
104
+ mgf-common v0.28 / mgf-fastapi v0.1).
105
+
106
+ Optional `[testing]` extra: `pip install 'mgf-fastapi[testing]'`
107
+ (pulls `uvicorn>=0.30` + `httpx>=0.27`).
108
+
109
+ | Name | Tier | Description |
110
+ |---|---|---|
111
+ | `run_test_app(app, *, port=None, host="127.0.0.1", log_level="warning", startup_timeout_seconds=5.0)` | experimental | Async context manager. Starts uvicorn for `app` on a free port; yields `f"http://{host}:{port}"`; tears down cleanly on context exit (including under exception). `port=None` picks a free ephemeral port. Surfaces server-startup exceptions immediately instead of timing out silently. Raises `TimeoutError` if uvicorn doesn't reach `started` within `startup_timeout_seconds`. `CancelledError` from the serve task is suppressed on shutdown (expected). |
112
+
113
+ ---
114
+
115
+ ## Summary
116
+
117
+ | Module | Public name count |
118
+ |---|---|
119
+ | `mgf.fastapi` | 10 |
120
+ | `mgf.fastapi.exceptions` | 1 |
121
+ | `mgf.fastapi.webhooks` | 4 |
122
+ | `mgf.fastapi.security` | 2 |
123
+ | `mgf.fastapi.testing` | 1 |
124
+ | **Total** | **18** |
125
+
126
+ (`HmacVerificationError` counted once — at `mgf.fastapi.exceptions`. The
127
+ re-export at `mgf.fastapi` is a convenience alias.)
128
+
129
+ ---
130
+
131
+ ## Cross-references
132
+
133
+ - [`CHANGELOG.md`](CHANGELOG.md) — release history.
134
+ - [`docs/recipes/`](docs/recipes/) — full how-to for each surface.
135
+ - [`docs/cutover/v0.1.0.md`](docs/cutover/v0.1.0.md) — maiden voyage migration story (the v0.28 split).
136
+ - [`mgf-common/FEEDBACK.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/FEEDBACK.md) — design conversation queue.
137
+ - [`mgf-common/docs/standards/`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/standards/) — the federation-wide engineering standards.
138
+ - [`mgf-common/docs/release/federation_roadmap.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/release/federation_roadmap.md) — the split plan that created this sibling.
@@ -0,0 +1,87 @@
1
+ # `mgf-fastapi` — FastAPI integration adapters for mgf-common
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/mgf-fastapi)](https://pypi.org/project/mgf-fastapi/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/mgf-fastapi)](https://pypi.org/project/mgf-fastapi/)
5
+
6
+ > **Sibling of [`mgf-common`](https://pypi.org/project/mgf-common/)
7
+ > under the `mgf.*` namespace.** Houses every FastAPI-specific
8
+ > adapter that previously lived under `mgf.common.fastapi.*` —
9
+ > extracted at mgf-common v0.28 / mgf-fastapi v0.1 per the
10
+ > [federation split plan](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/release/federation_roadmap.md).
11
+
12
+ ## What this provides
13
+
14
+ | Submodule | What |
15
+ |---|---|
16
+ | `mgf.fastapi` | `bootstrap` ↔ FastAPI lifespan; `RequestIdMiddleware`; `ExceptionTranslationMiddleware`; `Depends()` helpers (`get_app_context`, `get_settings`, `get_request_id`); `setup_lifespan`. |
17
+ | `mgf.fastapi.webhooks` | Svix-shape HMAC webhook verification (`HmacWebhookVerifier`, `WebhookHeaderSchema`, `SVIX_SCHEMA`, `verify_request`). |
18
+ | `mgf.fastapi.security` | `IpAllowlist` FastAPI dependency with proxy-trust opt-in. |
19
+ | `mgf.fastapi.testing` | `run_test_app` async context manager — start uvicorn for a FastAPI app on a free port; yield base URL; clean shutdown. |
20
+ | `mgf.fastapi.exceptions` | `HmacVerificationError(HttpUnauthorized)` and other framework-domain concrete leaves. (HTTP-01 hierarchy roots stay in `mgf.common.exceptions`.) |
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install mgf-fastapi
26
+ # Or with the test-helper extra (uvicorn + httpx for run_test_app):
27
+ pip install 'mgf-fastapi[testing]'
28
+ ```
29
+
30
+ Pulls in `mgf-common` and `fastapi` automatically.
31
+
32
+ ## Quick start
33
+
34
+ ```python
35
+ from fastapi import FastAPI, Request
36
+ from mgf.fastapi import (
37
+ ExceptionTranslationMiddleware,
38
+ RequestIdMiddleware,
39
+ setup_lifespan,
40
+ )
41
+ from mgf.fastapi.webhooks import HmacWebhookVerifier, verify_request
42
+
43
+ app = FastAPI(lifespan=setup_lifespan(app_name="my-service", app_version="0.1.0"))
44
+ app.add_middleware(ExceptionTranslationMiddleware)
45
+ app.add_middleware(RequestIdMiddleware)
46
+
47
+ webhook = HmacWebhookVerifier(secret=settings.webhook_secret)
48
+
49
+ @app.post("/webhooks/clerk")
50
+ async def clerk_webhook(request: Request) -> dict:
51
+ event_id, ts = await verify_request(request, webhook)
52
+ payload = await request.json()
53
+ # ... process the (now-trusted) payload ...
54
+ return {"ok": True}
55
+ ```
56
+
57
+ ## Documentation
58
+
59
+ - [`docs/recipes/fastapi.md`](docs/recipes/fastapi.md) — full FastAPI service walkthrough.
60
+ - [`docs/recipes/webhooks.md`](docs/recipes/webhooks.md) — HMAC webhook verification.
61
+ - [`docs/cutover/v0.1.0.md`](docs/cutover/v0.1.0.md) — maiden voyage migration story (the v0.28 split).
62
+ - [`PUBLIC_API.md`](PUBLIC_API.md) — full public surface contract.
63
+ - [`CHANGELOG.md`](CHANGELOG.md) — release history.
64
+
65
+ For the federation-wide engineering standards (DESIGN_PRINCIPLES,
66
+ ERROR_HANDLING, SECURITY, etc.) see
67
+ [`mgf-common/docs/standards/`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/standards/).
68
+ This sibling inherits them by reference; the standards source-of-truth
69
+ lives in mgf-common.
70
+
71
+ ## Status
72
+
73
+ 🚧 **Experimental** — every public name is `experimental` per AP-09.
74
+ Promotion to `stable` happens release-by-release as consumer feedback
75
+ in [`FEEDBACK.md`](FEEDBACK.md) converges. The 0.x window applies.
76
+ Pin tightly: `mgf-fastapi = ">=0.X.0,<0.Y"`.
77
+
78
+ ## Cross-references
79
+
80
+ - **Filing process for sharp edges**: open an entry on
81
+ [`mgf-common/FEEDBACK.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/FEEDBACK.md)
82
+ with `[mgf-fastapi]` prefix, OR file directly on this repo's
83
+ Issues → maintainer mirrors into the canonical FEEDBACK.md.
84
+ - **Federation pattern**:
85
+ [`mgf-common/docs/design/federation.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/design/federation.md).
86
+ - **The split that created this sibling**:
87
+ [`mgf-common/docs/release/federation_roadmap.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/release/federation_roadmap.md).