Metadata-Version: 2.4
Name: prodkit
Version: 0.2.1
Summary: The production framework for FastAPI. One line. Production ready.
Project-URL: Homepage, https://github.com/Pushkarpant/PRODKIT
Project-URL: Documentation, https://github.com/Pushkarpant/PRODKIT#readme
Project-URL: Repository, https://github.com/Pushkarpant/PRODKIT
Project-URL: Changelog, https://github.com/Pushkarpant/PRODKIT/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/Pushkarpant/PRODKIT/issues
Author-email: Pushkar Pant <pantpushkar4@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: fastapi,health-check,logging,middleware,observability,production,security
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.110
Requires-Dist: pydantic-settings>=2.1
Requires-Dist: pydantic>=2.5
Requires-Dist: tomli>=2.0; python_version < '3.11'
Provides-Extra: brotli
Requires-Dist: brotli-asgi>=1.4; extra == 'brotli'
Provides-Extra: cli
Requires-Dist: rich>=13; extra == 'cli'
Requires-Dist: typer>=0.12; extra == 'cli'
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: import-linter>=2.0; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: rich>=13; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: tomli>=2.0; extra == 'dev'
Requires-Dist: typer>=0.12; extra == 'dev'
Description-Content-Type: text/markdown

# ProdKit

> **One line. Production ready.**

The production framework for [FastAPI](https://fastapi.tiangolo.com/).

```python
from fastapi import FastAPI
from prodkit import Production

app = FastAPI()
Production(app)
```

That's it. Your app now has security headers, structured JSON logging with
request-ID correlation, RFC 9457 error responses, Kubernetes-ready health
endpoints, gzip compression, and opt-in rate limiting — configured to current
best practice, hardened for production, and pleasant in development. Then run
[`prodkit doctor`](#cli--prodkit-doctor) to score how production-ready it is.

[![PyPI](https://img.shields.io/pypi/v/prodkit.svg)](https://pypi.org/project/prodkit/)
[![Python](https://img.shields.io/pypi/pyversions/prodkit.svg)](https://pypi.org/project/prodkit/)
[![CI](https://github.com/Pushkarpant/PRODKIT/actions/workflows/ci.yml/badge.svg)](https://github.com/Pushkarpant/PRODKIT/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Pushkarpant/PRODKIT/blob/main/LICENSE)

---

**Contents:** [Why](#why) · [Install](#installation) · [Quick Start](#quick-start) ·
[What You Get](#what-you-get) · [Configuration](#configuration) ·
[Plugins](#writing-a-plugin) · [CLI / doctor](#cli--prodkit-doctor) ·
[Status & Roadmap](#project-status)

---

## Why

Every production FastAPI service re-implements the same ~500 lines of glue:
middleware ordering, security headers, structured logging, health checks,
error normalization, graceful shutdown. FastAPI deliberately doesn't ship
this — it's a micro framework. **ProdKit is the batteries.**

And unlike a project template, ProdKit is a library: when best practices
evolve, `pip install -U prodkit` updates every app you own.

## Installation

[**`pip install prodkit`**](https://pypi.org/project/prodkit/)

```bash
pip install prodkit           # the library
pip install "prodkit[cli]"    # + the `prodkit doctor` CLI (typer + rich)
```

Requires Python 3.10+ and FastAPI 0.110+. The base install depends only on
FastAPI and Pydantic — nothing else. Optional extras: `cli` (CLI),
`brotli` (Brotli compression).

## Quick Start

```python
from fastapi import FastAPI
from prodkit import Production

app = FastAPI()
Production(app)                      # production profile by default

@app.get("/hello")
def hello():
    return {"message": "hello"}
```

```bash
uvicorn main:app
```

```text
$ curl -i localhost:8000/hello
HTTP/1.1 200 OK
x-request-id: 26fdc49565614c2a9ef1a3b8d4e0f712
x-content-type-options: nosniff
x-frame-options: DENY
strict-transport-security: max-age=63072000; includeSubDomains
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
x-xss-protection: 0
content-type: application/json
```

For local development, flip the profile — pretty console logs, debug error
details, no HSTS:

```python
Production(app, environment="development")
```

## What You Get

| Feature | Details |
|---|---|
| 🆔 **Request IDs** | `X-Request-ID` on every response, propagated into every log line. Inbound IDs untrusted by default. |
| 📋 **Structured logging** | One JSON object per request in production (Datadog/Loki/CloudWatch-ready); pretty console logs in development. |
| 🛡️ **Security headers** | OWASP-aligned: `nosniff`, `X-Frame-Options`, HSTS, `Referrer-Policy`, `Permissions-Policy`. Your own headers always win. |
| 🚨 **Error normalization** | [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) `problem+json` responses. Unhandled 500s are **opaque to clients** — the traceback goes to logs, correlated by request ID. |
| ❤️ **Health endpoints** | `/health`, `/live` (liveness) and `/ready` (readiness — aggregates checks from every plugin, 503 until all pass). Kubernetes-native. |
| 🌐 **CORS** | Explicit origins only; the wildcard-with-credentials footgun is refused at boot. |
| 📦 **Compression** | Gzip for responses over 500 bytes. |
| 🚦 **Rate limiting** | Opt-in per-IP limiting (`100/minute`), `429 problem+json` with `Retry-After`. In-memory backend (Redis in v0.3). |
| 🩺 **`prodkit doctor`** | CLI production-readiness audit with a 0–100 score. `--strict` gates CI. |
| 🔌 **Plugin system** | Every feature above is a plugin. Write your own with optional hooks incl. `doctor()`. |

## Configuration

Everything is configurable through four layers (highest wins):

```
Python args  >  environment variables  >  prodkit.toml  >  profile defaults
```

**Python:**

```python
Production(
    app,
    environment="production",
    cors={"origins": ["https://app.example.com"]},   # dict = configure & enable
    compression=False,                                # bool = toggle
    security={"trusted_hosts": ["api.example.com"]},
    rate_limit={"default": "100/minute"},             # opt-in per-IP limiting
)
```

**Environment variables** (`__` descends into sections):

```bash
PRODKIT_ENVIRONMENT=production
PRODKIT_LOGGING__LEVEL=WARNING
PRODKIT_SECURITY__TRUSTED_HOSTS=api.example.com,admin.example.com
```

**`prodkit.toml`:**

```toml
[prodkit]
environment = "production"

[logging]
level = "INFO"

[cors]
enabled = true
origins = ["https://app.example.com"]

[rate_limit]
enabled = true
default = "100/minute"
```

### Fail-fast, refuse-unsafe

Misconfiguration fails **at startup with a named key**, never silently:

```text
ProdKitConfigError: Invalid ProdKit configuration:
  - logging.levle: Extra inputs are not permitted
```

And configurations that would weaken a production deployment are refused,
not warned about:

- `debug=True` in production
- error responses that would leak tracebacks in production
- CORS `origins=["*"]` combined with `allow_credentials=True`

## Writing a Plugin

```python
from prodkit import Audit, Check, Plugin, Production

class DatabasePlugin(Plugin):
    name = "database"

    async def startup(self, ctx):
        self.pool = await create_pool(...)
        ctx.registry.provide("db", self.pool)

    async def shutdown(self, ctx):
        await self.pool.close()

    def checks(self, ctx):                     # runtime readiness → /ready
        return [Check(name="database", passed=self.pool.is_alive())]

    def doctor(self, ctx):                     # static audit → prodkit doctor
        return [Audit(name="Database pool",
                      status="ok", detail="connection pool configured")]

Production(app, plugins=[DatabasePlugin()])
```

Your `checks()` result now shows up in `/ready` automatically, and your
`doctor()` findings roll into the `prodkit doctor` score. Plugins can declare
`requires = ("other-plugin",)` and the kernel activates them in dependency
order — cycles and missing dependencies fail at boot.

Middleware registered by plugins carries an explicit integer priority, so
the middleware onion is always correctly ordered no matter what order
plugins load in (request-id outermost, compression innermost).

## CLI — `prodkit doctor`

Install the CLI extra and audit any `Production`-configured app:

```bash
pip install "prodkit[cli]"
prodkit doctor --app main:app
```

```text
              Production readiness
  ┌───┬───────────────────────┬─────────────────────┬─────────────────────────┐
  │ ✔ │ Security headers      │ nosniff, X-Frame ... │                         │
  │ ✔ │ Structured logging    │ json @ INFO          │                         │
  │ ✔ │ Error normalization   │ 500s opaque          │                         │
  │ ⚠ │ Content-Security-Po.. │ not set              │ set a CSP to mitigate.. │
  │ ⚠ │ Rate limiting         │ disabled             │ enable for public APIs  │
  └───┴───────────────────────┴─────────────────────┴─────────────────────────┘
  Production score: 88/100    2 warning(s)
```

Make it a CI quality gate — fail the build below a threshold:

```bash
prodkit doctor --app main:app --strict --min-score 90
```

Other commands:

```bash
prodkit inspect --app main:app   # resolved config, plugins, middleware order
prodkit plugins --app main:app   # active plugins and the hooks each implements
prodkit init --example           # scaffold prodkit.toml (+ starter main.py)
```

Every plugin contributes findings via its `doctor(ctx)` hook, so your own
plugins score too.

## Plays Nice With Your App

- **Same app object.** Routes, dependencies, and existing middleware keep
  working. Remove `Production(app)` and you have a plain FastAPI app again.
- **Your lifespan survives.** ProdKit *composes* with an existing `lifespan`:
  plugin startup → your lifespan → plugin shutdown (LIFO).
- **Your headers win.** Security headers use set-if-absent semantics.
- **Every feature can be turned off.** `Production(app, security=False, ...)`

## Project Status

**v0.2.1 — alpha.** Core kernel, eight built-in plugins (incl. rate-limiting),
the `prodkit doctor` CLI with a production-readiness score, strict mypy, CI
across Python 3.10–3.13.

Roadmap: ✅ `prodkit doctor` CLI + rate limiting (v0.2), Prometheus metrics +
Redis backends (v0.3), Dockerfile/nginx/CI generators (v0.4), public plugin SDK
(v0.5), auth helpers (v0.6), stable API (v1.0).
Full details in [docs/ARCHITECTURE.md](https://github.com/Pushkarpant/PRODKIT/blob/main/docs/ARCHITECTURE.md).

## Contributing

Contributions welcome — see [CONTRIBUTING.md](https://github.com/Pushkarpant/PRODKIT/blob/main/CONTRIBUTING.md).
Security reports: see [SECURITY.md](https://github.com/Pushkarpant/PRODKIT/blob/main/SECURITY.md) (never open a public issue).

```bash
git clone https://github.com/Pushkarpant/PRODKIT
cd PRODKIT
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
```

## License

[MIT](https://github.com/Pushkarpant/PRODKIT/blob/main/LICENSE)

## Author

**[Pushkar Pant](https://github.com/Pushkarpant)** — [pantpushkar4@gmail.com](mailto:pantpushkar4@gmail.com)

---

*FastAPI builds APIs. ProdKit makes them production-ready.*
