Metadata-Version: 2.1
Name: mdeprecation-proxy
Version: 0.1.0
Summary: 
Author: Jean-Luc Thirion
Author-email: jean-luc.thirion@appfire.com
Requires-Python: >=3.11,<3.12
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Requires-Dist: mitmproxy (>=9.0,<10.0)
Description-Content-Type: text/markdown

# DeprecationProxy

A mitmproxy-based forwarding proxy that returns **HTTP 410 Gone** for endpoints listed as deprecated in JSON config files. Anything not listed is forwarded to the real upstream.

## Install

```sh
poetry install
```

## Run

```sh
poetry run deproxy
# or with custom ports / folder:
poetry run deproxy --port 8080 --control-port 8081 --deprecations-dir ./deprecations
```

Configure your client to use `http://localhost:8080` as its HTTP(S) proxy.

## Deprecation config

Layout: one **folder per host pattern** (the folder name is the `baseUrl`), one **JSON file per deprecation notice** inside each folder.

```
deprecations/
  {instance}.atlassian.net/
    issue-v2.json
    search-v2.json
    project-v2.json
```

Each JSON file:

```json
{
  "summary": "Jira v2 issue endpoints are deprecated. Migrate to v3.",
  "deprecationDate": "2026-06-30",
  "link": "https://developer.atlassian.com/cloud/jira/platform/changelog/#CHANGE-1234",
  "deprecations": [
    {"url": "/rest/api/2/issue/{issueIdOrKey}", "verbs": ["GET", "PUT", "DELETE"]}
  ]
}
```

- `summary`, `deprecationDate`, `link` are **optional** — missing fields emit a warning at load time and are omitted from the response.
- The same `summary`/`deprecationDate`/`link` applies to every rule in `deprecations[]`.
- When a request matches, the 410 response includes a `reason` object with whichever of those fields are present.

Matching rules:
- `{name}` in `baseUrl` matches a single host label (no `.`). `{instance}.atlassian.net` matches `roberto.atlassian.net` but not `foo.bar.atlassian.net`.
- `{name}` in `url` matches a single path segment (no `/`).
- Verb match is case-insensitive. Use `"*"` for any method.
- Host/path match is case-insensitive.
- If the pattern URL includes a query string, those key/values are required on the incoming request (extra keys are tolerated). If the pattern has no query string, the incoming query string is ignored.

## Control server

A small HTTP server runs on the control port (default 8081, localhost-only):

- `GET /deprecations` — dump currently loaded rules.
- `POST /reload` — re-read all `*.json` files from the deprecations folder. Use this to refresh after edits or after a future DB seed job runs.
- `POST /stop` — gracefully shut the proxy down.

```sh
curl -s http://127.0.0.1:8081/deprecations
curl -s -X POST http://127.0.0.1:8081/reload
curl -s -X POST http://127.0.0.1:8081/stop
```

## HTTPS

mitmproxy generates a self-signed CA on first run at `~/.mitmproxy/mitmproxy-ca-cert.pem`. Clients must trust that CA to make HTTPS calls through the proxy.

If the proxy itself reaches the upstream through a TLS-intercepting hop (e.g. Netskope), point it at the corporate CA — the CLI forwards the value to mitmproxy's `ssl_verify_upstream_trusted_ca`:

```sh
poetry run deproxy --upstream-ca ~/.mitmproxy/appfire-netskope-ca.pem
# or via env (used as fallback in this order: $DEPROXY_UPSTREAM_CA, $SSL_CERT_FILE):
export DEPROXY_UPSTREAM_CA=~/.mitmproxy/appfire-netskope-ca.pem
poetry run deproxy
```

On macOS, export the corporate intermediate CA from the system keychain (Appfire example):

```sh
security find-certificate -a -c "ca.appfire.goskope.com" -p /Library/Keychains/System.keychain \
  > ~/.mitmproxy/appfire-netskope-ca.pem
```

## Test

```sh
poetry run pytest
```

