qrdrop 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 (78) hide show
  1. qrdrop-0.1.0/.gitignore +73 -0
  2. qrdrop-0.1.0/LICENSE +21 -0
  3. qrdrop-0.1.0/PKG-INFO +213 -0
  4. qrdrop-0.1.0/README.md +160 -0
  5. qrdrop-0.1.0/pyproject.toml +129 -0
  6. qrdrop-0.1.0/src/qrdrop/__init__.py +3 -0
  7. qrdrop-0.1.0/src/qrdrop/__main__.py +6 -0
  8. qrdrop-0.1.0/src/qrdrop/cli.py +164 -0
  9. qrdrop-0.1.0/src/qrdrop/core/__init__.py +1 -0
  10. qrdrop-0.1.0/src/qrdrop/core/filesystem.py +331 -0
  11. qrdrop-0.1.0/src/qrdrop/core/filetypes.py +367 -0
  12. qrdrop-0.1.0/src/qrdrop/core/log_redaction.py +50 -0
  13. qrdrop-0.1.0/src/qrdrop/core/network.py +72 -0
  14. qrdrop-0.1.0/src/qrdrop/core/password.py +49 -0
  15. qrdrop-0.1.0/src/qrdrop/core/qr.py +65 -0
  16. qrdrop-0.1.0/src/qrdrop/core/session.py +110 -0
  17. qrdrop-0.1.0/src/qrdrop/core/terminal.py +158 -0
  18. qrdrop-0.1.0/src/qrdrop/core/wordlist.py +1047 -0
  19. qrdrop-0.1.0/src/qrdrop/static/app.js +1098 -0
  20. qrdrop-0.1.0/src/qrdrop/static/style.css +1504 -0
  21. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/LICENSE +29 -0
  22. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/highlight.min.js +1213 -0
  23. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/cmake.min.js +7 -0
  24. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/dockerfile.min.js +8 -0
  25. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/fsharp.min.js +47 -0
  26. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/groovy.min.js +21 -0
  27. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/latex.min.js +33 -0
  28. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/powershell.min.js +39 -0
  29. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/properties.min.js +10 -0
  30. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/scala.min.js +28 -0
  31. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/languages/vim.min.js +12 -0
  32. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/styles/github-dark.min.css +10 -0
  33. qrdrop-0.1.0/src/qrdrop/static/vendor/highlight/styles/github.min.css +10 -0
  34. qrdrop-0.1.0/src/qrdrop/templates/base.html +52 -0
  35. qrdrop-0.1.0/src/qrdrop/templates/browse.html +224 -0
  36. qrdrop-0.1.0/src/qrdrop/templates/login.html +45 -0
  37. qrdrop-0.1.0/src/qrdrop/templates/view_image.html +39 -0
  38. qrdrop-0.1.0/src/qrdrop/templates/view_text.html +60 -0
  39. qrdrop-0.1.0/src/qrdrop/web/__init__.py +1 -0
  40. qrdrop-0.1.0/src/qrdrop/web/app.py +50 -0
  41. qrdrop-0.1.0/src/qrdrop/web/handlers/__init__.py +1 -0
  42. qrdrop-0.1.0/src/qrdrop/web/handlers/_common.py +104 -0
  43. qrdrop-0.1.0/src/qrdrop/web/handlers/archive.py +293 -0
  44. qrdrop-0.1.0/src/qrdrop/web/handlers/auth.py +129 -0
  45. qrdrop-0.1.0/src/qrdrop/web/handlers/browse.py +160 -0
  46. qrdrop-0.1.0/src/qrdrop/web/handlers/files.py +266 -0
  47. qrdrop-0.1.0/src/qrdrop/web/handlers/health.py +16 -0
  48. qrdrop-0.1.0/src/qrdrop/web/handlers/mutate.py +195 -0
  49. qrdrop-0.1.0/src/qrdrop/web/handlers/upload.py +398 -0
  50. qrdrop-0.1.0/src/qrdrop/web/middleware.py +180 -0
  51. qrdrop-0.1.0/src/qrdrop/web/routes.py +53 -0
  52. qrdrop-0.1.0/tests/__init__.py +1 -0
  53. qrdrop-0.1.0/tests/conftest.py +114 -0
  54. qrdrop-0.1.0/tests/e2e/__init__.py +1 -0
  55. qrdrop-0.1.0/tests/e2e/conftest.py +133 -0
  56. qrdrop-0.1.0/tests/e2e/test_smoke.py +44 -0
  57. qrdrop-0.1.0/tests/test_archive.py +141 -0
  58. qrdrop-0.1.0/tests/test_auth_middleware.py +253 -0
  59. qrdrop-0.1.0/tests/test_browse.py +110 -0
  60. qrdrop-0.1.0/tests/test_cli.py +70 -0
  61. qrdrop-0.1.0/tests/test_cli_main.py +209 -0
  62. qrdrop-0.1.0/tests/test_delete.py +79 -0
  63. qrdrop-0.1.0/tests/test_files.py +321 -0
  64. qrdrop-0.1.0/tests/test_filesystem.py +210 -0
  65. qrdrop-0.1.0/tests/test_filetypes.py +118 -0
  66. qrdrop-0.1.0/tests/test_health.py +15 -0
  67. qrdrop-0.1.0/tests/test_log_redaction.py +108 -0
  68. qrdrop-0.1.0/tests/test_mutate.py +212 -0
  69. qrdrop-0.1.0/tests/test_network.py +52 -0
  70. qrdrop-0.1.0/tests/test_password.py +50 -0
  71. qrdrop-0.1.0/tests/test_qr.py +114 -0
  72. qrdrop-0.1.0/tests/test_security.py +252 -0
  73. qrdrop-0.1.0/tests/test_session.py +89 -0
  74. qrdrop-0.1.0/tests/test_terminal.py +221 -0
  75. qrdrop-0.1.0/tests/test_upload.py +190 -0
  76. qrdrop-0.1.0/tests/test_upload_edge.py +208 -0
  77. qrdrop-0.1.0/tests/test_view.py +124 -0
  78. qrdrop-0.1.0/tests/test_wordlist.py +38 -0
@@ -0,0 +1,73 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ *.egg
8
+ *.egg-info/
9
+ .installed.cfg
10
+ *.eggs
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ ENV/
16
+
17
+ # Pixi
18
+ .pixi/
19
+
20
+ # Build outputs
21
+ dist/
22
+ build/
23
+ wheels/
24
+
25
+ # Testing
26
+ .coverage
27
+ .coverage.*
28
+ htmlcov/
29
+ .pytest_cache/
30
+ .tox/
31
+ .nox/
32
+
33
+ # Linting/Formatting
34
+ .ruff_cache/
35
+ .mypy_cache/
36
+
37
+ # IDE
38
+ .idea/
39
+ .vscode/
40
+ *.swp
41
+ *.swo
42
+
43
+ # Environment files (never commit secrets)
44
+ .env
45
+ .env.local
46
+ .env.*.local
47
+
48
+ # Logs
49
+ *.log
50
+
51
+ # OS files
52
+ .DS_Store
53
+ Thumbs.db
54
+ # Environment files (added by millhouse)
55
+ *.env
56
+ *.env.*
57
+ *.local
58
+ *.secret
59
+ *.secrets
60
+ config.local.*
61
+ .config.local
62
+ *.key
63
+ *.pem
64
+ *.p12
65
+ .aws/credentials
66
+ .ssh/id_*
67
+
68
+ # Agent tooling (machine-specific, never tracked)
69
+ .claude/
70
+ .lab/
71
+ AGENTS.md
72
+ CLAUDE.md
73
+
qrdrop-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 itsloopyo
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 OF OR IN CONNECTION WITH
21
+ THE SOFTWARE.
qrdrop-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,213 @@
1
+ Metadata-Version: 2.4
2
+ Name: qrdrop
3
+ Version: 0.1.0
4
+ Summary: Instant file sharing from your terminal - share files over LAN with a simple command
5
+ Project-URL: Homepage, https://github.com/itsloopyo/qrdrop
6
+ Project-URL: Documentation, https://github.com/itsloopyo/qrdrop#readme
7
+ Project-URL: Repository, https://github.com/itsloopyo/qrdrop
8
+ Project-URL: Issues, https://github.com/itsloopyo/qrdrop/issues
9
+ Author: itsloopyo
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: cli,file-sharing,http,lan,server
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Environment :: Web Environment
16
+ Classifier: Framework :: AsyncIO
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: System Administrators
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
25
+ Classifier: Topic :: System :: Networking
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.11
28
+ Requires-Dist: aiofiles==25.1.0
29
+ Requires-Dist: anyio==4.13.0
30
+ Requires-Dist: click==8.3.3
31
+ Requires-Dist: colorama==0.4.6; sys_platform == 'win32' or platform_system == 'Windows'
32
+ Requires-Dist: h11==0.16.0
33
+ Requires-Dist: idna==3.15
34
+ Requires-Dist: jinja2==3.1.6
35
+ Requires-Dist: markupsafe==3.0.3
36
+ Requires-Dist: python-multipart==0.0.27
37
+ Requires-Dist: qrcode==8.2
38
+ Requires-Dist: starlette==1.0.1
39
+ Requires-Dist: typing-extensions==4.15.0; python_version < '3.13'
40
+ Requires-Dist: uvicorn==0.46.0
41
+ Provides-Extra: dev
42
+ Requires-Dist: build>=1.0.0; extra == 'dev'
43
+ Requires-Dist: hatchling>=1.25.0; extra == 'dev'
44
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
45
+ Requires-Dist: pip-audit>=2.7.0; extra == 'dev'
46
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
47
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
48
+ Requires-Dist: pytest-playwright>=0.5.0; extra == 'dev'
49
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
50
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
51
+ Requires-Dist: twine>=5.0.0; extra == 'dev'
52
+ Description-Content-Type: text/markdown
53
+
54
+ # QRDrop
55
+
56
+ **Instant file sharing from your terminal.** Serve the current directory over your local network with one command — get a URL, a memorable password, and a QR code your phone can scan in moments.
57
+
58
+ ```bash
59
+ uvx qrdrop
60
+ ```
61
+
62
+ No setup, no fuss, ctrl+c and it's gone.
63
+
64
+ ## Getting Started
65
+
66
+ Run it without installing anything:
67
+
68
+ ```bash
69
+ uvx qrdrop # Python 3.11+ via uv
70
+ docker run --rm -p 8000:8000 -v "$PWD:/data" itsloopyo/qrdrop # no Python at all
71
+ ```
72
+
73
+ Or install it (Python 3.11+):
74
+
75
+ ```bash
76
+ pip install qrdrop
77
+ pipx install qrdrop
78
+ ```
79
+
80
+ See [Docker](#docker) below for port mapping, flags, and image details.
81
+
82
+ ### 1. Share a directory
83
+
84
+ ```bash
85
+ cd ~/photos
86
+ uvx qrdrop
87
+ ```
88
+
89
+ ```text
90
+ ╭──────────────────────────────────────────────────╮
91
+ │ 📂 QRDrop v0.1.0 │
92
+ ╰──────────────────────────────────────────────────╯
93
+
94
+ Serving: /home/you/photos
95
+
96
+ Local: http://localhost:8000
97
+ Network: http://192.168.1.42:8000
98
+
99
+ Password: ember-velvet-canyon
100
+
101
+ ┌─ Scan for instant access ─┐
102
+
103
+ ▄▄▄▄▄▄▄ ▄ ▄▄▄▄ ▄▄▄▄▄▄▄
104
+ █ ▄▄▄ █ █▄ ▀█▀ ▀ █ ▄▄▄ █
105
+ █ ███ █ ▀██ ▄▄▀▄▀ █ ███ █
106
+ █▄▄▄▄▄█ █▀█ ▄▀█ █ █▄▄▄▄▄█
107
+ ▄▄ ▄▄▄ █▀ ▀▀ ▄ ▄▄▄▄
108
+ █▀▀▀ ▄▄█▄▄▄▄█▄██ ▄▄▀█▄▀
109
+ ▄▀▄▀█▄▄▄▀ ▄ █ ▀▄█ ▀▄██▄
110
+ ▀▀ ▀▄▄▀▀█▀█ ▀▄██▀ ▄█▄▄▀
111
+ ▄▄▀█▄▀▄▄▀█▀█ ▄▄▄▄██▄█▀
112
+ ▄▄▄▄▄▄▄ ▀▀█▄▄█▄ █ ▄ █
113
+ █ ▄▄▄ █ ██ █ █▀█▄▄▄████
114
+ █ ███ █ ▄▄█▄ ▀▄▀█▀ ▄▀█▀
115
+ █▄▄▄▄▄█ █▀▄ ▀▄▀▄ ▄▀▀▀██▄
116
+
117
+ └───────────────────────────┘
118
+
119
+ Press Ctrl+C to stop the server
120
+ ```
121
+
122
+ If port 8000 is busy, QRDrop automatically picks the next free one.
123
+
124
+ ### 2. Connect from another device
125
+
126
+ - **Phone**: scan the QR code. It encodes a pre-authenticated link, so you land in the file browser with no typing.
127
+ - **Anything else**: open the network URL and enter the three-word password.
128
+
129
+ ### 3. Browse, view, download
130
+
131
+ The web UI lets you:
132
+
133
+ - **Browse** directories with breadcrumb navigation
134
+ - **View** files inline — syntax-highlighted code (Python, Rust, Go, TypeScript, shell, and dozens more), images, and PDFs
135
+ - **Download** any single file, or select several and grab them as one **ZIP, TAR.GZ, or TAR.BZ2** archive
136
+
137
+ ### 4. Allow changes (optional)
138
+
139
+ QRDrop is **read-only by default**. Opt in to more:
140
+
141
+ ```bash
142
+ qrdrop --upload # downloads and uploads only
143
+ qrdrop --modify # downloads, uploads, delete, new folders + rename
144
+ ```
145
+
146
+ ## Usage Examples
147
+
148
+ ```bash
149
+ qrdrop --port 9000 # custom port
150
+ qrdrop --password correct-horse # bring your own password
151
+ qrdrop --hide-dotfiles # exclude dotfiles from listings
152
+ qrdrop --bind 192.168.1.42 # bind one interface (default: 0.0.0.0)
153
+ qrdrop --timeout 7200 # expire sessions after 2 hours (default: never)
154
+ qrdrop --quiet # no banner, warnings-only logs
155
+ ```
156
+
157
+ Note that QRDrop lists everything by default, including dotfiles. `--hide-dotfiles` is the opt-out.
158
+
159
+ ## CLI Reference
160
+
161
+ ```text
162
+ qrdrop [OPTIONS]
163
+
164
+ Options:
165
+ -p, --port PORT Port to serve on (default: 8000)
166
+ -b, --bind ADDRESS Address to bind to (default: 0.0.0.0)
167
+ --password TEXT Use specific password instead of generating one
168
+ --hide-dotfiles Exclude files starting with '.' from listings
169
+ --upload Allow file uploads
170
+ --modify Allow uploads, deletions, and directory create/rename (implies --upload)
171
+ --timeout SECONDS Expire sessions after this many seconds (default: sessions
172
+ last until the server stops)
173
+ -q, --quiet Suppress startup banner
174
+ --version Show version and exit
175
+ --help Show help and exit
176
+ ```
177
+
178
+ ## Docker
179
+
180
+ Images are published to Docker Hub as [`itsloopyo/qrdrop`](https://hub.docker.com/r/itsloopyo/qrdrop) on every release. Mount the directory you want to share at `/data` and forward a port to 8000:
181
+
182
+ ```bash
183
+ docker run --rm -p 8000:8000 -v /path/to/share:/data itsloopyo/qrdrop
184
+ ```
185
+
186
+ The startup banner — including the generated password and QR code — goes to the container logs, so run in the foreground or check `docker logs`. Any `qrdrop` flags can be appended:
187
+
188
+ ```bash
189
+ docker run --rm -p 9000:8000 -v "$PWD:/data" itsloopyo/qrdrop --password correct-horse --modify
190
+ ```
191
+
192
+ The container always listens on port 8000 internally; pick your external port with `-p <port>:8000` rather than `--port`. The QR code and network URL in the banner show the container's internal address — from another device, use your host's IP and the port you published.
193
+
194
+ The image is multi-stage (uv on Alpine), runs as a non-root user, and has a built-in healthcheck against `/health`. To share read-write (with `--upload` or `--modify`), the mounted directory must be writable by uid 1000.
195
+
196
+ ## Development
197
+
198
+ The dev loop is [pixi](https://pixi.sh):
199
+
200
+ ```bash
201
+ pixi run build # editable install with dev extras
202
+ pixi run test # pytest with coverage
203
+ pixi run test-e2e # Playwright end-to-end tests
204
+ pixi run lint # ruff check
205
+ pixi run format # ruff format
206
+ pixi run dev # run qrdrop from source
207
+ ```
208
+
209
+ QRDrop is built on Starlette, Uvicorn, Jinja2, and aiofiles.
210
+
211
+ ## License
212
+
213
+ MIT
qrdrop-0.1.0/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # QRDrop
2
+
3
+ **Instant file sharing from your terminal.** Serve the current directory over your local network with one command — get a URL, a memorable password, and a QR code your phone can scan in moments.
4
+
5
+ ```bash
6
+ uvx qrdrop
7
+ ```
8
+
9
+ No setup, no fuss, ctrl+c and it's gone.
10
+
11
+ ## Getting Started
12
+
13
+ Run it without installing anything:
14
+
15
+ ```bash
16
+ uvx qrdrop # Python 3.11+ via uv
17
+ docker run --rm -p 8000:8000 -v "$PWD:/data" itsloopyo/qrdrop # no Python at all
18
+ ```
19
+
20
+ Or install it (Python 3.11+):
21
+
22
+ ```bash
23
+ pip install qrdrop
24
+ pipx install qrdrop
25
+ ```
26
+
27
+ See [Docker](#docker) below for port mapping, flags, and image details.
28
+
29
+ ### 1. Share a directory
30
+
31
+ ```bash
32
+ cd ~/photos
33
+ uvx qrdrop
34
+ ```
35
+
36
+ ```text
37
+ ╭──────────────────────────────────────────────────╮
38
+ │ 📂 QRDrop v0.1.0 │
39
+ ╰──────────────────────────────────────────────────╯
40
+
41
+ Serving: /home/you/photos
42
+
43
+ Local: http://localhost:8000
44
+ Network: http://192.168.1.42:8000
45
+
46
+ Password: ember-velvet-canyon
47
+
48
+ ┌─ Scan for instant access ─┐
49
+
50
+ ▄▄▄▄▄▄▄ ▄ ▄▄▄▄ ▄▄▄▄▄▄▄
51
+ █ ▄▄▄ █ █▄ ▀█▀ ▀ █ ▄▄▄ █
52
+ █ ███ █ ▀██ ▄▄▀▄▀ █ ███ █
53
+ █▄▄▄▄▄█ █▀█ ▄▀█ █ █▄▄▄▄▄█
54
+ ▄▄ ▄▄▄ █▀ ▀▀ ▄ ▄▄▄▄
55
+ █▀▀▀ ▄▄█▄▄▄▄█▄██ ▄▄▀█▄▀
56
+ ▄▀▄▀█▄▄▄▀ ▄ █ ▀▄█ ▀▄██▄
57
+ ▀▀ ▀▄▄▀▀█▀█ ▀▄██▀ ▄█▄▄▀
58
+ ▄▄▀█▄▀▄▄▀█▀█ ▄▄▄▄██▄█▀
59
+ ▄▄▄▄▄▄▄ ▀▀█▄▄█▄ █ ▄ █
60
+ █ ▄▄▄ █ ██ █ █▀█▄▄▄████
61
+ █ ███ █ ▄▄█▄ ▀▄▀█▀ ▄▀█▀
62
+ █▄▄▄▄▄█ █▀▄ ▀▄▀▄ ▄▀▀▀██▄
63
+
64
+ └───────────────────────────┘
65
+
66
+ Press Ctrl+C to stop the server
67
+ ```
68
+
69
+ If port 8000 is busy, QRDrop automatically picks the next free one.
70
+
71
+ ### 2. Connect from another device
72
+
73
+ - **Phone**: scan the QR code. It encodes a pre-authenticated link, so you land in the file browser with no typing.
74
+ - **Anything else**: open the network URL and enter the three-word password.
75
+
76
+ ### 3. Browse, view, download
77
+
78
+ The web UI lets you:
79
+
80
+ - **Browse** directories with breadcrumb navigation
81
+ - **View** files inline — syntax-highlighted code (Python, Rust, Go, TypeScript, shell, and dozens more), images, and PDFs
82
+ - **Download** any single file, or select several and grab them as one **ZIP, TAR.GZ, or TAR.BZ2** archive
83
+
84
+ ### 4. Allow changes (optional)
85
+
86
+ QRDrop is **read-only by default**. Opt in to more:
87
+
88
+ ```bash
89
+ qrdrop --upload # downloads and uploads only
90
+ qrdrop --modify # downloads, uploads, delete, new folders + rename
91
+ ```
92
+
93
+ ## Usage Examples
94
+
95
+ ```bash
96
+ qrdrop --port 9000 # custom port
97
+ qrdrop --password correct-horse # bring your own password
98
+ qrdrop --hide-dotfiles # exclude dotfiles from listings
99
+ qrdrop --bind 192.168.1.42 # bind one interface (default: 0.0.0.0)
100
+ qrdrop --timeout 7200 # expire sessions after 2 hours (default: never)
101
+ qrdrop --quiet # no banner, warnings-only logs
102
+ ```
103
+
104
+ Note that QRDrop lists everything by default, including dotfiles. `--hide-dotfiles` is the opt-out.
105
+
106
+ ## CLI Reference
107
+
108
+ ```text
109
+ qrdrop [OPTIONS]
110
+
111
+ Options:
112
+ -p, --port PORT Port to serve on (default: 8000)
113
+ -b, --bind ADDRESS Address to bind to (default: 0.0.0.0)
114
+ --password TEXT Use specific password instead of generating one
115
+ --hide-dotfiles Exclude files starting with '.' from listings
116
+ --upload Allow file uploads
117
+ --modify Allow uploads, deletions, and directory create/rename (implies --upload)
118
+ --timeout SECONDS Expire sessions after this many seconds (default: sessions
119
+ last until the server stops)
120
+ -q, --quiet Suppress startup banner
121
+ --version Show version and exit
122
+ --help Show help and exit
123
+ ```
124
+
125
+ ## Docker
126
+
127
+ Images are published to Docker Hub as [`itsloopyo/qrdrop`](https://hub.docker.com/r/itsloopyo/qrdrop) on every release. Mount the directory you want to share at `/data` and forward a port to 8000:
128
+
129
+ ```bash
130
+ docker run --rm -p 8000:8000 -v /path/to/share:/data itsloopyo/qrdrop
131
+ ```
132
+
133
+ The startup banner — including the generated password and QR code — goes to the container logs, so run in the foreground or check `docker logs`. Any `qrdrop` flags can be appended:
134
+
135
+ ```bash
136
+ docker run --rm -p 9000:8000 -v "$PWD:/data" itsloopyo/qrdrop --password correct-horse --modify
137
+ ```
138
+
139
+ The container always listens on port 8000 internally; pick your external port with `-p <port>:8000` rather than `--port`. The QR code and network URL in the banner show the container's internal address — from another device, use your host's IP and the port you published.
140
+
141
+ The image is multi-stage (uv on Alpine), runs as a non-root user, and has a built-in healthcheck against `/health`. To share read-write (with `--upload` or `--modify`), the mounted directory must be writable by uid 1000.
142
+
143
+ ## Development
144
+
145
+ The dev loop is [pixi](https://pixi.sh):
146
+
147
+ ```bash
148
+ pixi run build # editable install with dev extras
149
+ pixi run test # pytest with coverage
150
+ pixi run test-e2e # Playwright end-to-end tests
151
+ pixi run lint # ruff check
152
+ pixi run format # ruff format
153
+ pixi run dev # run qrdrop from source
154
+ ```
155
+
156
+ QRDrop is built on Starlette, Uvicorn, Jinja2, and aiofiles.
157
+
158
+ ## License
159
+
160
+ MIT
@@ -0,0 +1,129 @@
1
+ [project]
2
+ name = "qrdrop"
3
+ version = "0.1.0"
4
+ description = "Instant file sharing from your terminal - share files over LAN with a simple command"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.11"
8
+ authors = [
9
+ { name = "itsloopyo" }
10
+ ]
11
+ keywords = ["file-sharing", "lan", "http", "server", "cli"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Environment :: Console",
15
+ "Environment :: Web Environment",
16
+ "Framework :: AsyncIO",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: System Administrators",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
25
+ "Topic :: System :: Networking",
26
+ "Topic :: Utilities",
27
+ ]
28
+ # The FULL transitive closure is pinned ==, deliberately: qrdrop is an
29
+ # application installed via `uvx qrdrop`/`pip install qrdrop`, so end users
30
+ # resolve from this metadata at install time. Exact pins mean they get the
31
+ # versions we tested, not whatever was published this morning - a hijacked
32
+ # dependency release cannot reach users without a new qrdrop release.
33
+ # Bump via `pixi run lock` (enforces the two-week release cooldown), retest,
34
+ # release.
35
+ dependencies = [
36
+ "starlette==1.0.1",
37
+ "uvicorn==0.46.0",
38
+ "jinja2==3.1.6",
39
+ "qrcode==8.2",
40
+ "python-multipart==0.0.27",
41
+ "aiofiles==25.1.0",
42
+ # Transitive
43
+ "anyio==4.13.0",
44
+ "click==8.3.3",
45
+ "colorama==0.4.6; sys_platform == 'win32' or platform_system == 'Windows'",
46
+ "h11==0.16.0",
47
+ "idna==3.15",
48
+ "markupsafe==3.0.3",
49
+ "typing-extensions==4.15.0; python_version < '3.13'",
50
+ ]
51
+
52
+ [project.optional-dependencies]
53
+ dev = [
54
+ "pytest>=8.0.0",
55
+ "pytest-asyncio>=0.24.0",
56
+ "pytest-cov>=5.0.0",
57
+ "ruff>=0.6.0",
58
+ "httpx>=0.27.0",
59
+ "pytest-playwright>=0.5.0",
60
+ "build>=1.0.0",
61
+ "twine>=5.0.0",
62
+ "hatchling>=1.25.0",
63
+ "pip-audit>=2.7.0",
64
+ ]
65
+
66
+ [project.scripts]
67
+ qrdrop = "qrdrop.__main__:main"
68
+
69
+ [project.urls]
70
+ Homepage = "https://github.com/itsloopyo/qrdrop"
71
+ Documentation = "https://github.com/itsloopyo/qrdrop#readme"
72
+ Repository = "https://github.com/itsloopyo/qrdrop"
73
+ Issues = "https://github.com/itsloopyo/qrdrop/issues"
74
+
75
+ [build-system]
76
+ requires = ["hatchling"]
77
+ build-backend = "hatchling.build"
78
+
79
+ [tool.hatch.build.targets.wheel]
80
+ packages = ["src/qrdrop"]
81
+
82
+ [tool.hatch.build.targets.sdist]
83
+ include = [
84
+ "/src",
85
+ "/tests",
86
+ "/README.md",
87
+ "/LICENSE",
88
+ ]
89
+
90
+ [tool.pytest.ini_options]
91
+ testpaths = ["tests"]
92
+ asyncio_mode = "auto"
93
+ asyncio_default_fixture_loop_scope = "function"
94
+
95
+ [tool.coverage.run]
96
+ source = ["src/qrdrop"]
97
+ branch = true
98
+
99
+ [tool.coverage.report]
100
+ exclude_lines = [
101
+ "pragma: no cover",
102
+ "if __name__ == .__main__.:",
103
+ "if TYPE_CHECKING:",
104
+ ]
105
+
106
+ [tool.ruff]
107
+ target-version = "py311"
108
+ line-length = 100
109
+ src = ["src", "tests"]
110
+
111
+ [tool.ruff.lint]
112
+ select = [
113
+ "E", # pycodestyle errors
114
+ "W", # pycodestyle warnings
115
+ "F", # Pyflakes
116
+ "I", # isort
117
+ "B", # flake8-bugbear
118
+ "C4", # flake8-comprehensions
119
+ "UP", # pyupgrade
120
+ "ARG", # flake8-unused-arguments
121
+ "SIM", # flake8-simplify
122
+ ]
123
+ ignore = [
124
+ "E501", # line too long (handled by formatter)
125
+ "B008", # do not perform function calls in argument defaults
126
+ ]
127
+
128
+ [tool.ruff.lint.isort]
129
+ known-first-party = ["qrdrop"]
@@ -0,0 +1,3 @@
1
+ """QRDrop - Instant file sharing from your terminal."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ """Entry point for uvx/python -m qrdrop."""
2
+
3
+ from qrdrop.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()