# mgf-fastapi v0.1.0 — Maiden voyage cutover guide

> **Audience:** consumer agents (and humans relaying for them)
> migrating from `mgf-common[fastapi]` (v0.27 and earlier) to
> `mgf-fastapi` v0.1.0 + `mgf-common` v0.28.0.
> **What this is:** the canonical migration document for the
> federation extraction. The complementary mgf-common cutover
> guide is at
> [`mgf-common/docs/cutover/v0.28.0.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/cutover/v0.28.0.md).

This is the **first sibling extraction** under the federation
split plan recorded in
[`mgf-common/docs/release/federation_roadmap.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/release/federation_roadmap.md).
Approved 2026-05-08; ASPIRE-06 in mgf-common's FEEDBACK.md.

---

## At-a-glance

| Was (≤ mgf-common v0.27) | Is (mgf-common v0.28 + mgf-fastapi v0.1) |
|---|---|
| `pip install 'mgf-common[fastapi]'` | `pip install mgf-fastapi` |
| `from mgf.common.fastapi import ExceptionTranslationMiddleware` | `from mgf.fastapi import ExceptionTranslationMiddleware` |
| `from mgf.common.fastapi.webhooks import HmacWebhookVerifier` | `from mgf.fastapi.webhooks import HmacWebhookVerifier` |
| `from mgf.common.fastapi.security import IpAllowlist` | `from mgf.fastapi.security import IpAllowlist` |
| `from mgf.common.fastapi.testing import run_test_app` | `from mgf.fastapi.testing import run_test_app` |
| `from mgf.common.exceptions import HmacVerificationError` | `from mgf.fastapi.exceptions import HmacVerificationError` (or `from mgf.fastapi import HmacVerificationError`) |

**No code body changes.** The implementation is byte-identical to
what shipped in mgf-common v0.27.0; only the import paths moved.
Every public name's signature, behaviour, and test surface is
preserved.

---

## What landed in mgf-fastapi v0.1.0

The full surface previously at `mgf.common.fastapi.*` (and the
v0.26 / v0.27 additions: webhooks, security, testing):

| Submodule | What |
|---|---|
| `mgf.fastapi` | `setup_lifespan`, `RequestIdMiddleware`, `current_request_id`, `REQUEST_ID_HEADER`, `ExceptionTranslationMiddleware`, `DEFAULT_STATUS_MAP`, `get_app_context`, `get_settings`, `get_request_id`. Plus the convenience re-export `HmacVerificationError`. |
| `mgf.fastapi.exceptions` | `HmacVerificationError(HttpUnauthorized)`. |
| `mgf.fastapi.webhooks` | `WebhookHeaderSchema`, `SVIX_SCHEMA`, `HmacWebhookVerifier`, `verify_request`. |
| `mgf.fastapi.security` | `IpAllowlist`, `LOOPBACK_CIDRS`. |
| `mgf.fastapi.testing` | `run_test_app` (requires `[testing]` extra). |

---

## Migration steps

### 1. Update your `pyproject.toml`

Replace the `mgf-common[fastapi]` extra with a direct
`mgf-fastapi` dependency:

```toml
# Before
dependencies = [
    "mgf-common[fastapi]>=0.27,<0.28",
]

# After
dependencies = [
    "mgf-common>=0.28,<0.29",
    "mgf-fastapi>=0.1,<0.2",
]
```

For test-helper consumers (using `run_test_app`):

```toml
# Before
[project.optional-dependencies]
test = [
    "mgf-common[fastapi-test]>=0.27,<0.28",
]

# After
test = [
    "mgf-common>=0.28,<0.29",
    "mgf-fastapi[testing]>=0.1,<0.2",
]
```

### 2. Rewrite imports

Mechanical find-and-replace across your codebase:

| Search | Replace |
|---|---|
| `from mgf.common.fastapi.` | `from mgf.fastapi.` |
| `from mgf.common.fastapi ` | `from mgf.fastapi ` |
| `from mgf.common.exceptions import HmacVerificationError` | `from mgf.fastapi.exceptions import HmacVerificationError` |
| `mgf.common.fastapi.` (in non-import contexts: docstrings, log messages) | `mgf.fastapi.` |

`mgf-common>=0.28` no longer ships `HmacVerificationError`; the
import has moved with the verifier into `mgf-fastapi`. The HTTP-01
hierarchy roots and status leaves (`HttpError`, `HttpUnauthorized`,
`HttpForbidden`, `HttpNotFound`, etc.) **stay in
`mgf.common.exceptions`** — keep those imports as-is.

### 3. Re-run the four gates

```bash
uv sync
uv run ruff check
uv run mypy --strict src/
uv run pytest
```

If your test suite was green against mgf-common v0.27, it should
be green against mgf-common v0.28 + mgf-fastapi v0.1 immediately
after the import rewrite.

### 4. Re-run `mgf-common catch-up`

```bash
mgf-common catch-up
```

The audit's pin-checker recognizes the new `mgf-fastapi` dep and
won't flag it. The closed-box-leak checker also understands
`mgf.fastapi` as a sibling import path, not a private-name leak.

---

## Why this split happened

Every framework adapter in mgf-common is a layer that depends on a
3rd-party framework; co-locating them with the pure-Python core
created friction:

- **Surface size:** v0.26 + v0.27 added 7 names, all
  `mgf.common.fastapi.*`. Web stack growing faster than core.
- **Versioning collision:** A `HttpError` change in core requires
  a mgf-common minor; an `IpAllowlist` knob change ALSO requires a
  mgf-common minor. They don't evolve at the same rate but they
  shipped at the same cadence.
- **Mental model:** A new consumer reading `mgf.common.*` reads
  through 15+ submodules; reducing to ~10 pure-Python submodules
  with separate framework-adapter siblings shrinks cognitive load.
- **Closed-box hygiene:** The `[fastapi]` extra was the only
  barrier between a CLI consumer and FastAPI's deps. A sibling-
  package boundary is harder.

This is the **first** of four planned extractions:

| mgf-common release | Extracts |
|---|---|
| v0.28 (this one) | mgf-fastapi |
| v0.29 | mgf-http |
| v0.30 | mgf-sqlalchemy + mgf-alembic |
| v0.31 | mgf-django |
| v0.32+ | stabilization |
| v1.0 | lock the federated surface |

The full plan is in
[`mgf-common/docs/release/federation_roadmap.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/release/federation_roadmap.md).

---

## Per-consumer migration notes

### PlasmaMapper (FastAPI scientific platform)

**Highest-impact migration consumer.** Phase-3 webhook + admin-IP
+ run-test-app code all touches the renamed paths. Steps:

1. Bump pins to `mgf-common>=0.28,<0.29` + add
   `mgf-fastapi>=0.1,<0.2` (and `mgf-fastapi[testing]` in
   test-only deps).
2. Mechanical rewrite as per Step 2 above. Roughly:
   - `src/plasmamapper/api/app.py` — `ExceptionTranslationMiddleware`,
     `RequestIdMiddleware`, `setup_lifespan` imports.
   - `src/plasmamapper/api/security/admin_ip.py` — already migrated
     to `IpAllowlist` per v0.27 cutover; just rename
     `mgf.common.fastapi.security` → `mgf.fastapi.security`.
   - `src/plasmamapper/providers/auth/clerk.py` + `mock.py` —
     `HmacWebhookVerifier` import; `HmacVerificationError` import
     (from `mgf.fastapi.exceptions` now, not
     `mgf.common.exceptions`).
   - `tests/ux/fixtures/api_server.py` — `run_test_app` import.
3. Re-run Playwright golden-paths suite. The wire shape doesn't
   change; should be green.

### inthewords (Django)

**Not affected.** Django consumer doesn't import `mgf.common.fastapi`
anything. Bump pin to `mgf-common>=0.28,<0.29`. No rewrite.

### vm-vmanager-app (Qt desktop)

**Not affected.** Qt desktop; no FastAPI surface in scope. Bump
pin. No rewrite.

---

## What's deliberately NOT in v0.1.0

The first sibling extraction is **byte-identical to v0.27 mgf-common's
FastAPI surface**. New surface waits for v0.2.0+:

- **Stripe / GitHub / Slack webhook presets** (deferred from
  PAPER-26).
- **Replay-cache hook for `HmacWebhookVerifier`** (deferred from
  PAPER-26 retrospective).
- **`X-Real-IP` / `Forwarded:` (RFC 7239) for `IpAllowlist`**
  (deferred from PAPER-27 retrospective).
- **Sync-context support for `run_test_app`** (deferred from
  PAPER-28 retrospective).
- **PAPER-29 `BearerSession`** (filed in mgf-common's FEEDBACK;
  scheduled for a future mgf-fastapi release).

---

## Engineering note

mgf-fastapi v0.1.0 carries 49 tests parity-preserved from
mgf-common v0.27.0 (every webhooks / security / testing /
exceptions / dependencies / lifespan / request-id test moves over
unchanged in shape, with imports rewritten). Coverage threshold
≥80% matches mgf-common's standard.

The PEP 420 namespace package (no top-level `mgf/__init__.py` in
either repo) means `mgf.fastapi` and `mgf.common` coexist seamlessly
in the same Python process. No `__init__` conflicts; no namespace
collision; consumers can install both without surprise.
