pypproxy 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.
Files changed (104) hide show
  1. pypproxy-0.1.0/.github/dependabot.yml +19 -0
  2. pypproxy-0.1.0/.github/workflows/ci.yml +47 -0
  3. pypproxy-0.1.0/.github/workflows/docs.yml +29 -0
  4. pypproxy-0.1.0/.github/workflows/publish.yml +46 -0
  5. pypproxy-0.1.0/.gitignore +24 -0
  6. pypproxy-0.1.0/.pre-commit-config.yaml +19 -0
  7. pypproxy-0.1.0/LICENSE +21 -0
  8. pypproxy-0.1.0/Makefile +37 -0
  9. pypproxy-0.1.0/PKG-INFO +19 -0
  10. pypproxy-0.1.0/README.md +14 -0
  11. pypproxy-0.1.0/docs/api.md +152 -0
  12. pypproxy-0.1.0/docs/architecture.md +109 -0
  13. pypproxy-0.1.0/docs/configuration.md +78 -0
  14. pypproxy-0.1.0/docs/getting-started.md +111 -0
  15. pypproxy-0.1.0/docs/index.md +35 -0
  16. pypproxy-0.1.0/docs/protocols.md +115 -0
  17. pypproxy-0.1.0/docs/replay.md +89 -0
  18. pypproxy-0.1.0/docs/rule-engine.md +126 -0
  19. pypproxy-0.1.0/docs/scripting.md +96 -0
  20. pypproxy-0.1.0/docs/web-ui.md +157 -0
  21. pypproxy-0.1.0/main.py +212 -0
  22. pypproxy-0.1.0/mkdocs.yml +62 -0
  23. pypproxy-0.1.0/pypproxy/__init__.py +0 -0
  24. pypproxy-0.1.0/pypproxy/api/__init__.py +0 -0
  25. pypproxy-0.1.0/pypproxy/api/server.py +427 -0
  26. pypproxy-0.1.0/pypproxy/bulk/__init__.py +0 -0
  27. pypproxy-0.1.0/pypproxy/bulk/sender.py +97 -0
  28. pypproxy-0.1.0/pypproxy/cert/__init__.py +0 -0
  29. pypproxy-0.1.0/pypproxy/cert/ca.py +144 -0
  30. pypproxy-0.1.0/pypproxy/cert/client_cert.py +65 -0
  31. pypproxy-0.1.0/pypproxy/codec.py +176 -0
  32. pypproxy-0.1.0/pypproxy/config/__init__.py +0 -0
  33. pypproxy-0.1.0/pypproxy/config/config.py +106 -0
  34. pypproxy-0.1.0/pypproxy/dns/__init__.py +0 -0
  35. pypproxy-0.1.0/pypproxy/dns/server.py +149 -0
  36. pypproxy-0.1.0/pypproxy/exporter/__init__.py +0 -0
  37. pypproxy-0.1.0/pypproxy/exporter/exporter.py +122 -0
  38. pypproxy-0.1.0/pypproxy/exporter/importer.py +169 -0
  39. pypproxy-0.1.0/pypproxy/graphql/__init__.py +0 -0
  40. pypproxy-0.1.0/pypproxy/graphql/detector.py +76 -0
  41. pypproxy-0.1.0/pypproxy/graphql/introspection.py +217 -0
  42. pypproxy-0.1.0/pypproxy/graphql/modifier.py +98 -0
  43. pypproxy-0.1.0/pypproxy/graphql/schema_store.py +33 -0
  44. pypproxy-0.1.0/pypproxy/intercept/__init__.py +0 -0
  45. pypproxy-0.1.0/pypproxy/intercept/manager.py +142 -0
  46. pypproxy-0.1.0/pypproxy/interceptor/__init__.py +0 -0
  47. pypproxy-0.1.0/pypproxy/interceptor/interceptor.py +172 -0
  48. pypproxy-0.1.0/pypproxy/proto/__init__.py +0 -0
  49. pypproxy-0.1.0/pypproxy/proto/grpc.py +48 -0
  50. pypproxy-0.1.0/pypproxy/proto/mqtt.py +119 -0
  51. pypproxy-0.1.0/pypproxy/proto/ws.py +120 -0
  52. pypproxy-0.1.0/pypproxy/proto/ws_intercept.py +117 -0
  53. pypproxy-0.1.0/pypproxy/proxy/__init__.py +0 -0
  54. pypproxy-0.1.0/pypproxy/proxy/proxy.py +407 -0
  55. pypproxy-0.1.0/pypproxy/replay/__init__.py +0 -0
  56. pypproxy-0.1.0/pypproxy/replay/replay.py +77 -0
  57. pypproxy-0.1.0/pypproxy/rule/__init__.py +0 -0
  58. pypproxy-0.1.0/pypproxy/rule/rule.py +198 -0
  59. pypproxy-0.1.0/pypproxy/scan/__init__.py +0 -0
  60. pypproxy-0.1.0/pypproxy/scan/scanner.py +296 -0
  61. pypproxy-0.1.0/pypproxy/script/__init__.py +0 -0
  62. pypproxy-0.1.0/pypproxy/script/engine.py +49 -0
  63. pypproxy-0.1.0/pypproxy/security/__init__.py +0 -0
  64. pypproxy-0.1.0/pypproxy/security/header_checker.py +308 -0
  65. pypproxy-0.1.0/pypproxy/security/int_overflow.py +193 -0
  66. pypproxy-0.1.0/pypproxy/security/jwt_checker.py +273 -0
  67. pypproxy-0.1.0/pypproxy/security/plugin.py +152 -0
  68. pypproxy-0.1.0/pypproxy/security/randomness.py +165 -0
  69. pypproxy-0.1.0/pypproxy/store/__init__.py +0 -0
  70. pypproxy-0.1.0/pypproxy/store/db.py +189 -0
  71. pypproxy-0.1.0/pypproxy/store/filter_parser.py +181 -0
  72. pypproxy-0.1.0/pypproxy/store/fts.py +105 -0
  73. pypproxy-0.1.0/pypproxy/store/models.py +81 -0
  74. pypproxy-0.1.0/pypproxy/store/scope.py +63 -0
  75. pypproxy-0.1.0/pypproxy/store/store.py +120 -0
  76. pypproxy-0.1.0/pypproxy/ui/__init__.py +0 -0
  77. pypproxy-0.1.0/pypproxy/ui/app.py +386 -0
  78. pypproxy-0.1.0/pypproxy/ui/bulk_sender_ui.py +125 -0
  79. pypproxy-0.1.0/pypproxy/ui/cui.py +162 -0
  80. pypproxy-0.1.0/pypproxy/ui/detail.py +179 -0
  81. pypproxy-0.1.0/pypproxy/ui/diff_view.py +118 -0
  82. pypproxy-0.1.0/pypproxy/ui/graphql_tab.py +265 -0
  83. pypproxy-0.1.0/pypproxy/ui/import_tab.py +136 -0
  84. pypproxy-0.1.0/pypproxy/ui/intercept_dialog.py +74 -0
  85. pypproxy-0.1.0/pypproxy/ui/resender.py +140 -0
  86. pypproxy-0.1.0/pypproxy/ui/scan_tab.py +98 -0
  87. pypproxy-0.1.0/pypproxy/ui/security_tab.py +356 -0
  88. pypproxy-0.1.0/pypproxy/ui/settings.py +413 -0
  89. pypproxy-0.1.0/pypproxy/ui/theme.py +59 -0
  90. pypproxy-0.1.0/pyproject.toml +68 -0
  91. pypproxy-0.1.0/tests/__init__.py +0 -0
  92. pypproxy-0.1.0/tests/test_advanced.py +287 -0
  93. pypproxy-0.1.0/tests/test_bulk.py +60 -0
  94. pypproxy-0.1.0/tests/test_cert.py +71 -0
  95. pypproxy-0.1.0/tests/test_codec.py +113 -0
  96. pypproxy-0.1.0/tests/test_exporter.py +102 -0
  97. pypproxy-0.1.0/tests/test_filter_parser.py +97 -0
  98. pypproxy-0.1.0/tests/test_graphql.py +249 -0
  99. pypproxy-0.1.0/tests/test_interceptor.py +64 -0
  100. pypproxy-0.1.0/tests/test_mqtt.py +73 -0
  101. pypproxy-0.1.0/tests/test_rule.py +115 -0
  102. pypproxy-0.1.0/tests/test_security.py +240 -0
  103. pypproxy-0.1.0/tests/test_store.py +113 -0
  104. pypproxy-0.1.0/uv.lock +2239 -0
@@ -0,0 +1,19 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ day: monday
8
+ open-pull-requests-limit: 5
9
+ labels:
10
+ - dependencies
11
+
12
+ - package-ecosystem: github-actions
13
+ directory: /
14
+ schedule:
15
+ interval: weekly
16
+ day: monday
17
+ open-pull-requests-limit: 5
18
+ labels:
19
+ - dependencies
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, feat/**]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+
15
+ - uses: astral-sh/setup-uv@v7
16
+ with:
17
+ version: latest
18
+
19
+ - name: Set up Python
20
+ run: uv python install 3.11
21
+
22
+ - name: Install dependencies
23
+ run: uv sync
24
+
25
+ - name: Lint (ruff)
26
+ run: uv run ruff check pypproxy/
27
+
28
+ - name: Format check (ruff)
29
+ run: uv run ruff format --check pypproxy/
30
+
31
+ test:
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v6
35
+
36
+ - uses: astral-sh/setup-uv@v7
37
+ with:
38
+ version: latest
39
+
40
+ - name: Set up Python
41
+ run: uv python install 3.11
42
+
43
+ - name: Install dependencies
44
+ run: uv sync
45
+
46
+ - name: Run tests
47
+ run: uv run pytest tests/ -v --tb=short
@@ -0,0 +1,29 @@
1
+ name: Deploy docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - 'docs/**'
8
+ - 'mkdocs.yml'
9
+ - '.github/workflows/docs.yml'
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: write
14
+
15
+ jobs:
16
+ deploy:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v6
20
+
21
+ - uses: actions/setup-python@v6
22
+ with:
23
+ python-version: '3.x'
24
+
25
+ - name: Install MkDocs
26
+ run: pip install mkdocs-material
27
+
28
+ - name: Deploy to GitHub Pages
29
+ run: mkdocs gh-deploy --force
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write # for trusted publishing
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+
18
+ - uses: astral-sh/setup-uv@v7
19
+ with:
20
+ version: latest
21
+
22
+ - name: Set up Python
23
+ run: uv python install 3.11
24
+
25
+ - name: Build distribution
26
+ run: uv build
27
+
28
+ - uses: actions/upload-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+
33
+ publish:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment:
37
+ name: pypi
38
+ url: https://pypi.org/project/paxy/
39
+ steps:
40
+ - uses: actions/download-artifact@v4
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ - name: Publish to PyPI
46
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,24 @@
1
+ # Go
2
+ bin/
3
+ *.exe
4
+ *.test
5
+ *.out
6
+ vendor/
7
+
8
+ # Node
9
+ web/node_modules/
10
+ web/dist/
11
+
12
+ # paxy runtime
13
+ .paxy/
14
+
15
+ # Editors
16
+ .vscode/
17
+ .idea/
18
+ *.swp
19
+
20
+ # OS
21
+ .DS_Store
22
+ Thumbs.db
23
+ site/
24
+ __pycache__/
@@ -0,0 +1,19 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - id: check-merge-conflict
10
+ - id: check-added-large-files
11
+ args: [--maxkb=500]
12
+ - id: debug-statements
13
+
14
+ - repo: https://github.com/astral-sh/ruff-pre-commit
15
+ rev: v0.4.10
16
+ hooks:
17
+ - id: ruff
18
+ args: [--fix]
19
+ - id: ruff-format
pypproxy-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kuma
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,37 @@
1
+ .PHONY: install run dev web-install web-build web-dev test lint clean docs-dev docs-build
2
+
3
+ install:
4
+ uv sync
5
+
6
+ run:
7
+ uv run python main.py
8
+
9
+ gui:
10
+ uv run python main.py --mode gui
11
+
12
+ cui:
13
+ uv run python main.py --mode cui
14
+
15
+ web-install:
16
+ cd web && npm install
17
+
18
+ web-build:
19
+ cd web && npm run build
20
+
21
+ web-dev:
22
+ cd web && npm run dev
23
+
24
+ test:
25
+ uv run pytest tests/ -v
26
+
27
+ lint:
28
+ uv run ruff check paxy/
29
+
30
+ clean:
31
+ rm -rf __pycache__ paxy/**/__pycache__ .pytest_cache site/ web/dist/
32
+
33
+ docs-dev:
34
+ mkdocs serve
35
+
36
+ docs-build:
37
+ mkdocs build
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: pypproxy
3
+ Version: 0.1.0
4
+ Summary: MITM HTTP/HTTPS proxy for inspecting and modifying traffic
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: aiosqlite>=0.20.0
8
+ Requires-Dist: brotli>=1.1.0
9
+ Requires-Dist: cbor2>=5.6.0
10
+ Requires-Dist: cryptography>=48.0.0
11
+ Requires-Dist: fastapi>=0.111.0
12
+ Requires-Dist: httpx>=0.27.0
13
+ Requires-Dist: httpx[http2]>=0.27.0
14
+ Requires-Dist: msgpack>=1.1.0
15
+ Requires-Dist: nicegui>=3.12.1
16
+ Requires-Dist: pyyaml>=6.0.3
17
+ Requires-Dist: rich>=15.0.0
18
+ Requires-Dist: uvicorn[standard]>=0.30.0
19
+ Requires-Dist: websockets>=16.0
@@ -0,0 +1,14 @@
1
+ # pypproxy
2
+
3
+ MITM HTTP/HTTPS proxy for inspecting and modifying traffic.
4
+
5
+ [![CI](https://github.com/ykus4/pypproxy/actions/workflows/ci.yml/badge.svg)](https://github.com/ykus4/pypproxy/actions/workflows/ci.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/pypproxy)](https://pypi.org/project/pypproxy/)
7
+
8
+ ```bash
9
+ pip install pypproxy
10
+ pypproxy # GUI mode → http://localhost:8081
11
+ pypproxy --mode cui # terminal UI
12
+ ```
13
+
14
+ See the **[docs](https://ykus4.github.io/pypproxy/)** for setup, CA installation, and feature guides.
@@ -0,0 +1,152 @@
1
+ # API Reference
2
+
3
+ The API server runs on `http://localhost:8081` by default.
4
+
5
+ ## Traffic
6
+
7
+ ### `GET /api/traffic`
8
+
9
+ List captured entries.
10
+
11
+ **Query parameters:** `offset`, `limit`, `method`, `host`, `search`, `protocol`
12
+
13
+ ### `GET /api/traffic/{id}`
14
+
15
+ Get a single entry by ID.
16
+
17
+ ---
18
+
19
+ ## Rules
20
+
21
+ ### `GET /api/rules` · `POST /api/rules` · `PUT /api/rules/{id}` · `DELETE /api/rules/{id}`
22
+
23
+ CRUD for intercept rules.
24
+
25
+ ---
26
+
27
+ ## Replay
28
+
29
+ ### `POST /api/replay`
30
+
31
+ ```json
32
+ {"entry_id": 42, "options": {"override_host": "staging.example.com", "count": 1}}
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Bulk Sender
38
+
39
+ ### `POST /api/bulk`
40
+
41
+ ```json
42
+ {"entry_id": 42, "mode": "payloads", "payloads": [{"label": "p1", "body": "..."}], "concurrency": 10}
43
+ ```
44
+
45
+ `mode`: `"payloads"` or `"race"`
46
+
47
+ ---
48
+
49
+ ## Active Scan
50
+
51
+ ### `POST /api/scan`
52
+
53
+ ```json
54
+ {"entry_id": 42, "categories": ["xss", "sqli", "cmdi", "ssti", "path_traversal"], "concurrency": 5}
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Export / Import
60
+
61
+ | Endpoint | Description |
62
+ |----------|-------------|
63
+ | `GET /api/export/json` | Export all entries + rules as JSON |
64
+ | `GET /api/export/har` | Export all entries as HAR 1.2 |
65
+ | `POST /api/import/har` | Import entries from HAR body |
66
+ | `POST /api/import/json` | Import entries from paxy JSON body |
67
+ | `POST /api/import/rules` | Import rules from JSON body |
68
+
69
+ ---
70
+
71
+ ## Full-text Search
72
+
73
+ ### `GET /api/search?q=keyword&limit=50`
74
+
75
+ Search across host, path, request body, response body, and headers using SQLite FTS5.
76
+
77
+ ---
78
+
79
+ ## Scope
80
+
81
+ ### `GET /api/scope`
82
+
83
+ Returns `{"enabled": bool, "rules": [...]}`.
84
+
85
+ ### `POST /api/scope`
86
+
87
+ ```json
88
+ {"enabled": true, "add": {"pattern": "*.example.com", "mode": "glob"}}
89
+ {"remove": "*.example.com"}
90
+ ```
91
+
92
+ ---
93
+
94
+ ## GraphQL
95
+
96
+ ### `POST /api/graphql/introspect`
97
+
98
+ ```json
99
+ {"url": "https://api.example.com/graphql", "headers": {"Authorization": "Bearer token"}}
100
+ ```
101
+
102
+ ### `GET /api/graphql/schemas`
103
+
104
+ List cached schemas: `[{"host": "api.example.com", "query_type": "Query", ...}]`
105
+
106
+ ### `GET /api/graphql/schema/{host}`
107
+
108
+ Full schema object for a host.
109
+
110
+ ### `DELETE /api/graphql/schema/{host}`
111
+
112
+ Remove cached schema. Returns `204`.
113
+
114
+ ### `POST /api/graphql/replay`
115
+
116
+ ```json
117
+ {"entry_id": 42, "query": "query { user { id } }", "variables": {"id": "123"}}
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Misc
123
+
124
+ ### `POST /api/clear`
125
+
126
+ Delete all captured entries. Returns `204`.
127
+
128
+ ---
129
+
130
+ ## WebSocket
131
+
132
+ ### `GET /ws`
133
+
134
+ Upgrade to WebSocket. Pushes `Entry` JSON objects in real time.
135
+
136
+ ---
137
+
138
+ ## Schemas
139
+
140
+ ### Entry
141
+
142
+ ```python
143
+ {
144
+ "id": int, "created_at": str,
145
+ "method": str, "scheme": str, "host": str, "path": str, "query": str,
146
+ "req_headers": dict, "req_body": str, # base64
147
+ "status_code": int, "resp_headers": dict, "resp_body": str, # base64
148
+ "duration_ms": int, "protocol": str,
149
+ "tags": list[str], "modified": bool, "color": str,
150
+ "graphql_operation": str, "graphql_op_type": str
151
+ }
152
+ ```
@@ -0,0 +1,109 @@
1
+ # Architecture
2
+
3
+ ## Overview
4
+
5
+ ```
6
+ ┌──────────────────────────────────────────────────────────────┐
7
+ │ Client (browser / mobile app) │
8
+ └────────────────────────┬─────────────────────────────────────┘
9
+ │ HTTP / CONNECT
10
+ ┌────────────────────────▼─────────────────────────────────────┐
11
+ │ paxy/proxy/proxy.py (port :8080) │
12
+ │ asyncio TCP server │
13
+ │ ├─ HTTP → intercept → forward upstream (httpx, HTTP/2) │
14
+ │ ├─ CONNECT → TLS termination (MITM) │
15
+ │ │ ├─ HTTP/HTTPS → intercept → forward upstream │
16
+ │ │ ├─ WebSocket → paxy/proto/ws.py │
17
+ │ │ ├─ gRPC → paxy/proto/grpc.py │
18
+ │ │ └─ MQTT → paxy/proto/mqtt.py │
19
+ │ └─ ignored hosts → raw TCP tunnel (passthrough) │
20
+ └─────────────┬────────────────────────┬───────────────────────┘
21
+ │ │
22
+ ┌─────────────▼──────┐ ┌────────────▼──────────────────────┐
23
+ │ paxy/cert/ca.py │ │ paxy/interceptor/ │
24
+ │ CA + per-host TLS │ │ apply rules, record entries │
25
+ │ SSL Context cache │ └────────────┬───────────────────────┘
26
+ │ │ │
27
+ │ paxy/cert/ │ ┌────────────▼───────────────────────┐
28
+ │ client_cert.py │ │ paxy/store/store.py │
29
+ │ Mutual TLS certs │ │ in-memory store + SQLite persist │
30
+ └────────────────────┘ │ asyncio pub/sub │
31
+ └────────────┬───────────────────────┘
32
+
33
+ ┌────────────────────────────┼──────────────────────┐
34
+ │ │ │
35
+ ┌─────────▼──────────┐ ┌─────────────▼─────────┐ ┌────────▼──────────┐
36
+ │ paxy/api/ │ │ paxy/ui/app.py │ │ paxy/ui/cui.py │
37
+ │ FastAPI REST API │ │ NiceGUI 4-tab UI │ │ rich terminal UI │
38
+ │ + WebSocket /ws │ │ Traffic/Resender/ │ │ (CUI mode) │
39
+ │ Bulk/Export APIs │ │ Bulk/Diff │ └───────────────────┘
40
+ └────────────────────┘ └─────────────────────────┘
41
+ ```
42
+
43
+ ## Package overview
44
+
45
+ | Package | Responsibility |
46
+ |---------|----------------|
47
+ | `paxy/proxy` | asyncio TCP server; HTTP forwarding; TLS MITM for CONNECT; raw tunnel for ignored hosts |
48
+ | `paxy/cert/ca` | CA certificate generation; per-host SSL Context cache |
49
+ | `paxy/cert/client_cert` | Client certificate management for mutual TLS |
50
+ | `paxy/interceptor` | Apply rules to requests and responses; record entries in the store |
51
+ | `paxy/intercept` | Manual intercept manager; pause requests for user review |
52
+ | `paxy/rule` | Rule evaluation engine; condition matching; priority ordering |
53
+ | `paxy/store/store` | Thread-safe in-memory traffic store; asyncio pub/sub |
54
+ | `paxy/store/db` | SQLite persistence via aiosqlite; load/save entries |
55
+ | `paxy/store/filter_parser` | Filter expression parser (`host == x && method == POST`) |
56
+ | `paxy/api` | FastAPI REST endpoints, WebSocket streaming, bulk/export APIs |
57
+ | `paxy/ui/app` | NiceGUI 4-tab browser UI (Traffic, Resender, Bulk Sender, Diff) |
58
+ | `paxy/ui/settings` | Settings page (rules, SSL passthrough, DNS, ports, client certs) |
59
+ | `paxy/ui/detail` | Request/response detail panel with body view selector |
60
+ | `paxy/ui/resender` | Resender tab — edit and re-send requests |
61
+ | `paxy/ui/bulk_sender_ui` | Bulk Sender tab — parallel payload sending and race testing |
62
+ | `paxy/ui/diff_view` | Diff tab — unified diff between two captured entries |
63
+ | `paxy/ui/intercept_dialog` | Intercept dialog — pause, edit, forward or drop requests |
64
+ | `paxy/ui/cui` | rich terminal UI (CUI mode) |
65
+ | `paxy/proto/ws` | WebSocket frame relay and logging |
66
+ | `paxy/proto/grpc` | gRPC length-prefix frame decoding |
67
+ | `paxy/proto/mqtt` | MQTT frame decoding and detection |
68
+ | `paxy/script` | Python script engine; `on_request` / `on_response` hooks |
69
+ | `paxy/replay` | Async HTTP replay and parallel fuzzing via httpx |
70
+ | `paxy/bulk` | Bulk sender and race condition test runner |
71
+ | `paxy/dns` | Built-in DNS server with domain spoofing |
72
+ | `paxy/exporter` | JSON/HAR export and rule import/export |
73
+ | `paxy/codec` | Content-encoding decode (gzip/br/deflate); binary format decode (Protobuf/MessagePack/CBOR) |
74
+ | `paxy/config` | YAML config loading |
75
+
76
+ ## Key design decisions
77
+
78
+ ### asyncio TCP server
79
+
80
+ The proxy is a raw `asyncio.start_server` TCP server that parses HTTP manually.
81
+ This lets a single connection handle HTTP/1.1 keep-alive, CONNECT tunnels, WebSocket upgrades, and MQTT detection without switching servers mid-connection.
82
+
83
+ ### TLS termination with `loop.start_tls()`
84
+
85
+ After responding `200 Connection Established` to a CONNECT request, paxy calls `loop.start_tls()` to upgrade the existing asyncio transport to TLS server-side. Per-host certificates are cached as `ssl.SSLContext` objects.
86
+
87
+ ### SQLite persistence via aiosqlite
88
+
89
+ All captured traffic is stored in memory for fast access. Writes to SQLite are fire-and-forget via `asyncio.run_coroutine_threadsafe`. On startup, `store.load_from_db()` restores prior sessions. The DB path defaults to `~/.paxy/paxy.db`.
90
+
91
+ ### Store pub/sub
92
+
93
+ The `Store` maintains a list of `asyncio.Queue` subscribers. The proxy calls `loop.call_soon_threadsafe` to push entries into queues from the proxy coroutine. The UI polls each queue to receive live updates without blocking.
94
+
95
+ ### HTTP/2
96
+
97
+ All upstream requests use `httpx` with `http2=True`. httpx negotiates HTTP/2 via ALPN where the server supports it and falls back to HTTP/1.1 transparently.
98
+
99
+ ### Filter expression engine
100
+
101
+ The filter bar in the UI accepts a structured expression parsed by `paxy/store/filter_parser.py`. The parser tokenizes `field op value` conditions and evaluates them with AND/OR short-circuit logic against `Entry` objects in memory.
102
+
103
+ ### Binary format detection
104
+
105
+ `paxy/codec.py` implements `sniff_content_type()` which combines Content-Type inspection with a JSON parse attempt and a binary entropy heuristic to guess the best display mode. Protobuf decoding uses wire-type heuristics without requiring a `.proto` schema.
106
+
107
+ ### GUI / CUI startup
108
+
109
+ In **GUI mode**, `ui.run()` owns the event loop and the proxy is launched via `nicegui_app.on_startup`. In **CUI mode**, `asyncio.run()` owns the loop and `asyncio.gather` runs the proxy, uvicorn API server, and rich TUI concurrently.
@@ -0,0 +1,78 @@
1
+ # Configuration
2
+
3
+ paxy can be configured via CLI flags or a YAML file. CLI flags take precedence over the config file.
4
+
5
+ ## YAML config file
6
+
7
+ ```bash
8
+ uv run python main.py --config paxy.yaml
9
+ ```
10
+
11
+ ### Full example
12
+
13
+ ```yaml
14
+ proxy:
15
+ addr: "0.0.0.0"
16
+ port: 8080
17
+ # Hosts to pass through without MITM (certificate pinning, internal services, etc.)
18
+ ignore:
19
+ - pinned.example.com
20
+ - internal.corp
21
+ # Maximum body size to capture, in bytes
22
+ max_body: 1048576 # 1 MB
23
+
24
+ ca:
25
+ cert_path: /custom/ca-cert.pem
26
+ key_path: /custom/ca-key.pem
27
+
28
+ ui:
29
+ addr: "0.0.0.0"
30
+ port: 8081
31
+
32
+ script:
33
+ path: /path/to/script.py
34
+ ```
35
+
36
+ ## Field reference
37
+
38
+ ### `proxy`
39
+
40
+ | Key | Type | Default | Description |
41
+ |-----|------|---------|-------------|
42
+ | `addr` | string | `0.0.0.0` | Proxy listen address |
43
+ | `port` | int | `8080` | Proxy port |
44
+ | `ignore` | list | `[]` | Hosts to tunnel without MITM |
45
+ | `max_body` | int | `1048576` | Max body bytes to capture |
46
+
47
+ ### `ca`
48
+
49
+ | Key | Type | Default | Description |
50
+ |-----|------|---------|-------------|
51
+ | `cert_path` | string | `~/.paxy/ca-cert.pem` | CA certificate path |
52
+ | `key_path` | string | `~/.paxy/ca-key.pem` | CA private key path |
53
+
54
+ ### `ui`
55
+
56
+ | Key | Type | Default | Description |
57
+ |-----|------|---------|-------------|
58
+ | `addr` | string | `0.0.0.0` | Web UI / API listen address |
59
+ | `port` | int | `8081` | Web UI / API port |
60
+
61
+ ### `script`
62
+
63
+ | Key | Type | Default | Description |
64
+ |-----|------|---------|-------------|
65
+ | `path` | string | — | Python script path |
66
+
67
+ ## CLI flags
68
+
69
+ | Flag | Default | Description |
70
+ |------|---------|-------------|
71
+ | `--mode` | `gui` | UI mode: `gui` or `cui` |
72
+ | `--addr` | `0.0.0.0` | Proxy listen address |
73
+ | `--port` | `8080` | Proxy port |
74
+ | `--ui-addr` | `0.0.0.0` | Web UI / API address |
75
+ | `--ui-port` | `8081` | Web UI / API port |
76
+ | `--config` | — | Config file path |
77
+ | `--script` | — | Python script path |
78
+ | `--ca-dir` | `~/.paxy` | Directory to store CA cert and key |