pigeondrop 0.0.0-stage → 1.6.1

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ # What's new
2
+
3
+ ## v1.6.1 — 2 November 2026
4
+
5
+ - Changed: **The page heading reads Pigeonhole.** It is no longer shown in capitals.
6
+
7
+ ## v1.6.0 — 2 November 2026
8
+
9
+ - New: **Filter the file list by name.** Type in the box above the list to show only the files and folders whose names contain what you typed.
10
+
11
+ ## v1.5.1 — 16 October 2026
12
+
13
+ - Changed: **Pigeonhole's code now lives at github.com/cventour/pigeondrop.** Old links and install commands still work.
14
+
15
+ ## v1.5.0 — 16 October 2026
16
+
17
+ - New: **Run it with npx.** `npx pigeondrop` starts Pigeonhole in the folder you are in, with nothing to install. Its files and settings stay in that folder.
18
+
19
+ ## v1.4.0 — 16 October 2026
20
+
21
+ - New: **Pigeonhole has an icon.** It shows in the browser tab and beside the page heading.
22
+
23
+ ## v1.3.0 — 14 October 2026
24
+
25
+ - New: **Verify the certificate can be turned off.** For a scanner with a self-signed certificate on a network you trust, the connection is still encrypted.
26
+ - Changed: **TLS failures say why.** Test connection, uploads and Save explain what is wrong with the scanner's certificate or connection and what to change.
27
+
28
+ ## v1.2.0 — 14 October 2026
29
+
30
+ - New: **Scan over ICAPS.** Settings has a TLS option for ICAP servers that only accept encrypted connections. The server's certificate is checked, and the test explains when it is not trusted.
31
+
32
+ ## v1.1.1 — 2 October 2026
33
+
34
+ - Fixed: **Settings could not be saved when Pigeonhole runs as a hardened systemd service.** They are now kept in the service's own state folder, and if a folder cannot be written the message says what to change.
35
+
36
+ ## v1.1.0 — 2 October 2026
37
+
38
+ - New: **Scan uploads with an ICAP server.** Open Settings, enter the server, and every upload is checked before it appears in the list. Files the scanner blocks are discarded and the threat is named.
39
+ - New: **Test connection.** Settings can check a server before you save it, including whether it is able to scan.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christos Ventouris
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.
package/README.md CHANGED
@@ -1,3 +1,525 @@
1
- # Temporary Holding Version
1
+ <img src="icon.svg" width="88" alt="Pigeonhole icon: a wall of arched pigeonholes, one holding a letter">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # Pigeonhole
4
+
5
+ A file pigeonhole you can stand up in a minute. Put files in, let people take
6
+ them out, and take back whatever they leave you.
7
+
8
+ Useful when a group needs a shared drop: distributing builds, installers or
9
+ documents to a handful of machines, and collecting files back from them
10
+ without email, chat attachments or a cloud account.
11
+
12
+ Drag files in or browse for them, with a progress bar on each. Browse folders,
13
+ rename, delete, download, make folders. Tick several items to download them
14
+ together; a folder, or any selection of more than one file, arrives as a
15
+ single zip, streamed as it is built. Optionally, every upload is checked by
16
+ an ICAP virus scanner before it appears. That is the whole feature list and it
17
+ is meant to stay short.
18
+
19
+ **No dependencies.** Node's standard library and nothing else. No install
20
+ step, no lockfile, nothing to update when someone else's package goes wrong.
21
+
22
+ ## Requirements
23
+
24
+ Node 20 or newer, on any platform it runs on. That is the entire list.
25
+
26
+ ```
27
+ node --version
28
+ ```
29
+
30
+ ## Quickest: npx
31
+
32
+ ```bash
33
+ npx pigeondrop
34
+ ```
35
+
36
+ Runs it from the folder you are in and serves `./files`, created if missing.
37
+ Open http://localhost:3001. Changes made on the Settings page are kept in
38
+ `./settings.json`. The variables under [Configuration](#configuration) work
39
+ the same way:
40
+
41
+ ```bash
42
+ REPO_ROOT=/srv/files PORT=8080 npx pigeondrop
43
+ ```
44
+
45
+ On Windows, set them first, as in
46
+ [Serve a folder you already have](#serve-a-folder-you-already-have-1), then
47
+ run `npx pigeondrop`.
48
+
49
+ To keep it installed rather than fetched each time, `npm install -g pigeondrop`
50
+ and then run `pigeondrop`.
51
+
52
+ For anything longer-lived, follow whichever section below matches your
53
+ machine. Each one is self-contained: install, run, share, keep running.
54
+
55
+ ---
56
+
57
+ # macOS and Linux
58
+
59
+ Also WSL, and anything else with bash.
60
+
61
+ ## Install it
62
+
63
+ ```bash
64
+ curl -fsSL https://raw.githubusercontent.com/cventour/pigeondrop/main/install.sh -o install.sh
65
+ ```
66
+
67
+ Read it — it is about a hundred lines and does nothing surprising. Then:
68
+
69
+ ```bash
70
+ bash install.sh
71
+ ```
72
+
73
+ It installs to `~/pigeonhole`, checks your Node version, and starts the
74
+ server. Open http://localhost:3001.
75
+
76
+ Options:
77
+
78
+ | Option | Meaning |
79
+ |---|---|
80
+ | `--dir` | Where to install. Default `~/pigeonhole`. |
81
+ | `--root` | The directory to serve. Default `<install dir>/files`. |
82
+ | `--port` | Default `3001`. |
83
+ | `--host` | Bind address. Default `127.0.0.1`. |
84
+ | `--title` | Page heading. |
85
+ | `--no-start` | Install without starting. |
86
+
87
+ So to serve a folder you already have, to the whole network:
88
+
89
+ ```bash
90
+ bash install.sh --root /srv/files --host 0.0.0.0
91
+ ```
92
+
93
+ Run the script again later and it updates the copy it installed.
94
+
95
+ ## Run it by hand
96
+
97
+ The script saves typing, not much else. The manual version is three lines:
98
+
99
+ ```bash
100
+ git clone https://github.com/cventour/pigeondrop.git
101
+ cd pigeonhole
102
+ node server.js
103
+ ```
104
+
105
+ Files land in `./files`, created on first run.
106
+
107
+ ## Serve a folder you already have
108
+
109
+ Configuration is environment variables. Set them inline for one run:
110
+
111
+ ```bash
112
+ REPO_ROOT=/srv/files node server.js
113
+ ```
114
+
115
+ Or for the whole shell session:
116
+
117
+ ```bash
118
+ export REPO_ROOT=/srv/files REPO_TITLE="Team Drop"
119
+ ```
120
+
121
+ The path can be anywhere the user running the process can read and write — an
122
+ external drive, a home directory, a network mount.
123
+
124
+ ## Let other machines reach it
125
+
126
+ It binds `127.0.0.1`, so by default only this machine can reach it. To open it
127
+ to the network, bind all interfaces:
128
+
129
+ ```bash
130
+ HOST=0.0.0.0 PORT=3001 REPO_ROOT=/srv/files node server.js
131
+ ```
132
+
133
+ Find the address to hand out — macOS:
134
+
135
+ ```bash
136
+ ipconfig getifaddr en0
137
+ ```
138
+
139
+ Linux:
140
+
141
+ ```bash
142
+ hostname -I
143
+ ```
144
+
145
+ Others open `http://<that-address>:3001`. Read
146
+ [There is no authentication](#there-is-no-authentication) before you do this.
147
+
148
+ ## Keep it running
149
+
150
+ The shell that started it owns it. To survive a closed terminal:
151
+
152
+ ```bash
153
+ nohup env REPO_ROOT=/srv/files node server.js > pigeonhole.log 2>&1 &
154
+ ```
155
+
156
+ **Linux, with systemd.** Save as `/etc/systemd/system/pigeonhole.service`:
157
+
158
+ ```ini
159
+ [Unit]
160
+ Description=Pigeonhole
161
+ After=network.target
162
+
163
+ [Service]
164
+ Type=simple
165
+ User=pigeonhole
166
+ WorkingDirectory=/opt/pigeonhole
167
+ ExecStart=/usr/bin/node /opt/pigeonhole/server.js
168
+ Environment=REPO_ROOT=/srv/files
169
+ # A place the service may write its settings, which ProtectSystem=strict
170
+ # would otherwise refuse. Pigeonhole finds it by itself: /var/lib/pigeonhole.
171
+ StateDirectory=pigeonhole
172
+ # Quote any value containing a space: systemd splits unquoted ones.
173
+ Environment="REPO_TITLE=Team Drop"
174
+ Restart=on-failure
175
+
176
+ NoNewPrivileges=true
177
+ ProtectSystem=strict
178
+ ProtectHome=true
179
+ ReadWritePaths=/srv/files
180
+
181
+ [Install]
182
+ WantedBy=multi-user.target
183
+ ```
184
+
185
+ ```bash
186
+ sudo systemctl enable --now pigeonhole
187
+ ```
188
+
189
+ ```bash
190
+ systemctl status pigeonhole
191
+ ```
192
+
193
+ **macOS, with launchd.** Save as
194
+ `~/Library/LaunchAgents/com.pigeonhole.plist`, with your own paths — launchd
195
+ does not expand `~`, and needs the full path to node:
196
+
197
+ ```xml
198
+ <?xml version="1.0" encoding="UTF-8"?>
199
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
200
+ <plist version="1.0">
201
+ <dict>
202
+ <key>Label</key> <string>com.pigeonhole</string>
203
+ <key>ProgramArguments</key>
204
+ <array>
205
+ <string>/usr/local/bin/node</string>
206
+ <string>/Users/you/pigeonhole/server.js</string>
207
+ </array>
208
+ <key>EnvironmentVariables</key>
209
+ <dict>
210
+ <key>REPO_ROOT</key> <string>/Users/you/Shared</string>
211
+ <key>REPO_TITLE</key> <string>Team Drop</string>
212
+ </dict>
213
+ <key>RunAtLoad</key> <true/>
214
+ <key>KeepAlive</key> <true/>
215
+ <key>StandardErrorPath</key> <string>/tmp/pigeonhole.log</string>
216
+ </dict>
217
+ </plist>
218
+ ```
219
+
220
+ `which node` gives the path to use. Then load it:
221
+
222
+ ```bash
223
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.pigeonhole.plist
224
+ ```
225
+
226
+ ```bash
227
+ launchctl print gui/$(id -u)/com.pigeonhole
228
+ ```
229
+
230
+ To stop it for good:
231
+
232
+ ```bash
233
+ launchctl bootout gui/$(id -u)/com.pigeonhole
234
+ ```
235
+
236
+ ---
237
+
238
+ # Windows
239
+
240
+ PowerShell throughout. Every command works in Windows PowerShell 5.1 and in
241
+ PowerShell 7.
242
+
243
+ ## Install it
244
+
245
+ ```powershell
246
+ irm https://raw.githubusercontent.com/cventour/pigeondrop/main/install.ps1 -OutFile install.ps1
247
+ ```
248
+
249
+ Read it — it is about a hundred lines and does nothing surprising. Then:
250
+
251
+ ```powershell
252
+ powershell -ExecutionPolicy Bypass -File .\install.ps1
253
+ ```
254
+
255
+ Windows blocks downloaded scripts by default, which is why that is longer than
256
+ `.\install.ps1`. It applies to this one run and changes nothing on the machine.
257
+
258
+ It installs to `$HOME\pigeonhole`, checks your Node version, and starts the
259
+ server. Open http://localhost:3001.
260
+
261
+ Options:
262
+
263
+ | Option | Meaning |
264
+ |---|---|
265
+ | `-Dir` | Where to install. Default `$HOME\pigeonhole`. |
266
+ | `-Root` | The directory to serve. Default `<install dir>\files`. |
267
+ | `-Port` | Default `3001`. |
268
+ | `-Bind` | Bind address. Default `127.0.0.1`. |
269
+ | `-Title` | Page heading. |
270
+ | `-NoStart` | Install without starting. |
271
+
272
+ So to serve a folder you already have, to the whole network:
273
+
274
+ ```powershell
275
+ powershell -ExecutionPolicy Bypass -File .\install.ps1 -Root D:\files -Bind 0.0.0.0
276
+ ```
277
+
278
+ The switch is `-Bind` rather than `-Host` because `$Host` is a variable
279
+ PowerShell reserves for itself.
280
+
281
+ Run the script again later and it updates the copy it installed.
282
+
283
+ ## Run it by hand
284
+
285
+ The script saves typing, not much else. The manual version is three lines:
286
+
287
+ ```powershell
288
+ git clone https://github.com/cventour/pigeondrop.git
289
+ cd pigeonhole
290
+ node server.js
291
+ ```
292
+
293
+ Windows does not ship with git, so on a fresh machine that first line fails.
294
+ Take the zip instead — this needs nothing that is not already there:
295
+
296
+ ```powershell
297
+ irm https://github.com/cventour/pigeondrop/archive/refs/heads/main.zip -OutFile pigeondrop.zip
298
+ Expand-Archive pigeondrop.zip -DestinationPath .
299
+ cd pigeondrop-main
300
+ node server.js
301
+ ```
302
+
303
+ Files land in `.\files`, created on first run.
304
+
305
+ ## Serve a folder you already have
306
+
307
+ Configuration is environment variables. PowerShell sets them on their own line —
308
+ the `REPO_ROOT=... node server.js` form you see in Unix instructions is not
309
+ valid here:
310
+
311
+ ```powershell
312
+ $env:REPO_ROOT = "D:\files"
313
+ node server.js
314
+ ```
315
+
316
+ They last as long as the window. To set one for good, for your account:
317
+
318
+ ```powershell
319
+ [Environment]::SetEnvironmentVariable("REPO_ROOT", "D:\files", "User")
320
+ ```
321
+
322
+ Open a new PowerShell window for that to take effect.
323
+
324
+ ## Let other machines reach it
325
+
326
+ It binds `127.0.0.1`, so by default only this machine can reach it. To open it
327
+ to the network, bind all interfaces:
328
+
329
+ ```powershell
330
+ $env:HOST = "0.0.0.0"; $env:REPO_ROOT = "D:\files"; node server.js
331
+ ```
332
+
333
+ Find the address to hand out:
334
+
335
+ ```powershell
336
+ Get-NetIPAddress -AddressFamily IPv4 | Where-Object IPAddress -notlike '127.*' | Select-Object InterfaceAlias, IPAddress
337
+ ```
338
+
339
+ Windows Firewall will block the port even once it is bound, and the prompt is
340
+ easy to miss. Allow it from an **administrator** PowerShell:
341
+
342
+ ```powershell
343
+ New-NetFirewallRule -DisplayName "Pigeonhole" -Direction Inbound -Protocol TCP -LocalPort 3001 -Action Allow
344
+ ```
345
+
346
+ Others open `http://<that-address>:3001`. Read
347
+ [There is no authentication](#there-is-no-authentication) before you do this.
348
+
349
+ To close it again afterwards:
350
+
351
+ ```powershell
352
+ Remove-NetFirewallRule -DisplayName "Pigeonhole"
353
+ ```
354
+
355
+ ## Keep it running
356
+
357
+ The window that started it owns it. To put it in the background for this
358
+ session:
359
+
360
+ ```powershell
361
+ $p = Start-Process node -ArgumentList "server.js" -WorkingDirectory "$HOME\pigeonhole" -WindowStyle Hidden -PassThru
362
+ ```
363
+
364
+ `-PassThru` hands back the process, so you can stop it again:
365
+
366
+ ```powershell
367
+ Stop-Process -Id $p.Id
368
+ ```
369
+
370
+ To survive a reboot, register a scheduled task from an **administrator**
371
+ PowerShell. Install it somewhere outside your profile first, since the task
372
+ runs as SYSTEM:
373
+
374
+ ```powershell
375
+ powershell -ExecutionPolicy Bypass -File .\install.ps1 -Dir C:\pigeonhole -NoStart
376
+ ```
377
+
378
+ Set every setting you need machine-wide, because SYSTEM does not see your
379
+ account's variables:
380
+
381
+ ```powershell
382
+ [Environment]::SetEnvironmentVariable("REPO_ROOT", "D:\files", "Machine")
383
+ [Environment]::SetEnvironmentVariable("HOST", "0.0.0.0", "Machine")
384
+ ```
385
+
386
+ ```powershell
387
+ $action = New-ScheduledTaskAction -Execute (Get-Command node).Source -Argument "server.js" -WorkingDirectory "C:\pigeonhole"
388
+ $trigger = New-ScheduledTaskTrigger -AtStartup
389
+ Register-ScheduledTask -TaskName Pigeonhole -Action $action -Trigger $trigger -User SYSTEM -RunLevel Highest
390
+ ```
391
+
392
+ `(Get-Command node).Source` resolves the full path to node.exe, because SYSTEM
393
+ does not necessarily share your `PATH`.
394
+
395
+ Start it now rather than waiting for a reboot:
396
+
397
+ ```powershell
398
+ Start-ScheduledTask -TaskName Pigeonhole
399
+ ```
400
+
401
+ ```powershell
402
+ Get-ScheduledTask -TaskName Pigeonhole | Get-ScheduledTaskInfo
403
+ ```
404
+
405
+ A `LastTaskResult` of `267009` is not an error — it is `0x41301`, "the task is
406
+ currently running", which is what you want to see.
407
+
408
+ Stop it, or remove it entirely:
409
+
410
+ ```powershell
411
+ Stop-ScheduledTask -TaskName Pigeonhole
412
+ ```
413
+
414
+ ```powershell
415
+ Unregister-ScheduledTask -TaskName Pigeonhole -Confirm:$false
416
+ ```
417
+
418
+ ---
419
+
420
+ ## Configuration
421
+
422
+ All optional, all environment variables, identical on every platform.
423
+
424
+ | Variable | Default | Purpose |
425
+ |---|---|---|
426
+ | `REPO_ROOT` | `./files`, in the working directory | The directory it manages. Created if missing. |
427
+ | `HOST` | `127.0.0.1` | Bind address. Loopback by default, on purpose. |
428
+ | `PORT` | `3001` | |
429
+ | `REPO_TITLE` | `Pigeonhole` | Page title and heading |
430
+ | `REPO_SUBTITLE` | `Drop files in. Take files out.` | Line under the heading |
431
+ | `SETTINGS_FILE` | `$STATE_DIRECTORY/settings.json` under systemd, else `settings.json` beside `server.js` (in the working directory when run through npx or a global install) | Where changes made on the Settings page are kept. Mode 600; not in the managed directory. It must be somewhere the service can write. |
432
+
433
+ Branding is configuration rather than code, so one copy serves any deployment.
434
+
435
+ How you set them is the only thing that differs:
436
+
437
+ | | macOS and Linux | Windows |
438
+ |---|---|---|
439
+ | One run | `REPO_ROOT=/srv/files node server.js` | `$env:REPO_ROOT = "D:\files"` then `node server.js` |
440
+ | This session | `export REPO_ROOT=/srv/files` | `$env:REPO_ROOT = "D:\files"` |
441
+ | Permanently | put the `export` in `~/.zshrc` or `~/.bashrc` | `[Environment]::SetEnvironmentVariable("REPO_ROOT", "D:\files", "User")` |
442
+
443
+ ## Scan uploads with ICAP
444
+
445
+ **Settings** in the toolbar points Pigeonhole at an ICAP server (MetaDefender
446
+ ICAP Server, c-icap with ClamAV, anything that speaks ICAP `REQMOD`). Each
447
+ upload is sent there once it has arrived, and appears in the listing only if
448
+ the scanner passes it. A blocked file is deleted and the upload card shows the
449
+ threat name.
450
+
451
+ | Field | Meaning |
452
+ |---|---|
453
+ | Server, Port | Where the ICAP server listens. 1344 is the standard port. |
454
+ | Service | The ICAP service name, the last part of `icap://host:1344/service`. |
455
+ | Timeout | Seconds to wait for the scanner before giving up. |
456
+ | Use TLS (ICAPS) | Connect over TLS. The port switches to 11344, the usual ICAPS port, unless you set another. |
457
+ | Verify the certificate | On (the default): the scanner's certificate must be valid and match Server. Off: the link is still encrypted, but any certificate is accepted. |
458
+ | Reject the upload if the scanner cannot be reached or cannot scan | On (the default): such an upload is refused. Off: it is kept, and the page says it was not scanned. |
459
+
460
+ **Test connection** asks the server what it is and then scans a few harmless
461
+ bytes, so it also catches a server that answers but cannot scan, such as one
462
+ with no licence. It uses the values in the form, so a server can be checked
463
+ before saving.
464
+
465
+ If the certificate is self-signed or issued by a private CA, either start
466
+ Pigeonhole with `NODE_EXTRA_CA_CERTS` set to that CA's PEM file, or turn off
467
+ **Verify the certificate**. Use the name the certificate was issued for as
468
+ Server.
469
+
470
+ When a TLS connection fails, Test connection, the upload card and the message
471
+ after Save all say why in plain words: an expired or self-signed certificate, a
472
+ certificate for another name, a CA this server does not trust, no TLS version in
473
+ common, a scanner that wants a client certificate, or a port that does not
474
+ speak TLS (or the other way round).
475
+
476
+ Only uploads are scanned. Files already in the folder, or put there by other
477
+ means, are not.
478
+
479
+ ## There is no authentication
480
+
481
+ Read that again before you expose it. Anyone who can reach it can upload,
482
+ rename and delete. Pigeonhole assumes it sits on a network you trust — a home
483
+ or office LAN, a VPN, a lab segment.
484
+
485
+ That is a scope decision, not an oversight. Authentication done badly is worse
486
+ than none, so it does none and says so plainly.
487
+
488
+ It binds `127.0.0.1` by default so that exposing it takes a decision.
489
+
490
+ ## Safety
491
+
492
+ Every client-supplied path is resolved and checked against the root before
493
+ use, so `../../etc` lands inside the managed directory rather than outside it.
494
+ Names for rename and mkdir must be a single path component — a separator is
495
+ refused rather than stripped. Filenames render as text, never as markup.
496
+
497
+ Because anyone who can reach the page can open Settings, they can also make
498
+ the server connect to any host and port, which is a way to probe the network
499
+ it sits on, and can turn scanning off. Another reason to keep it on a network
500
+ you trust.
501
+
502
+ None of that substitutes for the authentication it does not have.
503
+
504
+ ## Design notes
505
+
506
+ Three choices worth explaining, because each looks wrong at first glance.
507
+
508
+ **Uploads are a raw `PUT` body, not `multipart/form-data`.** Multipart is the
509
+ conventional answer and would need a parser — the one dependency this would
510
+ otherwise have. A raw body streams straight to disk, so a four-gigabyte file
511
+ never lands in memory, and there is nothing to audit or update.
512
+
513
+ **Uploads use `XMLHttpRequest`, not `fetch`.** `fetch` still cannot report
514
+ upload progress. A large upload with no progress bar is indistinguishable from
515
+ a hang, so XHR stays until browsers fix that.
516
+
517
+ **Files upload to `<name>.part` and are renamed on completion.** An
518
+ interrupted transfer then leaves nothing behind, rather than a truncated file
519
+ that looks complete until something tries to use it. The ICAP scan runs on
520
+ that `.part` file, before the rename, so a file being scanned is never
521
+ downloadable under its real name.
522
+
523
+ ## Licence
524
+
525
+ MIT.
package/icon.svg ADDED
@@ -0,0 +1,14 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <rect x="2" y="2" width="60" height="60" rx="13" fill="#2d3339"/>
3
+ <g fill="#0f1113">
4
+ <path d="M8 28V17a7 7 0 0 1 14 0v11z"/>
5
+ <path d="M25 28V17a7 7 0 0 1 14 0v11z"/>
6
+ <path d="M8 51V40a7 7 0 0 1 14 0v11z"/>
7
+ <path d="M25 51V40a7 7 0 0 1 14 0v11z"/>
8
+ <path d="M42 51V40a7 7 0 0 1 14 0v11z"/>
9
+ </g>
10
+ <path d="M42 28V17a7 7 0 0 1 14 0v11z" fill="#ff6a1f"/>
11
+ <path d="M44.5 19.5h9v6.5h-9z" fill="#1b1e21"/>
12
+ <path d="M44.5 19.5l4.5 3.5 4.5-3.5" fill="none" stroke="#ff6a1f" stroke-width="1.3"/>
13
+ <path d="M6 30h52M6 53h52" stroke="#c8ced4" stroke-width="2.5" stroke-linecap="round"/>
14
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,14 @@
1
1
  {
2
2
  "name": "pigeondrop",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.6.1",
4
+ "description": "A small self-hosted file manager with no dependencies. Drag-and-drop upload with progress, browse, rename, delete, download files or folders as zip.",
5
+ "type": "module",
6
+ "main": "server.js",
7
+ "bin": { "pigeondrop": "./server.js", "pigeonhole": "./server.js" },
8
+ "scripts": { "start": "node server.js" },
9
+ "engines": { "node": ">=20" },
10
+ "license": "MIT",
11
+ "keywords": ["file-manager", "upload", "self-hosted", "zero-dependency", "http"],
12
+ "repository": { "type": "git", "url": "git+https://github.com/cventour/pigeondrop.git" },
13
+ "files": ["server.js", "public", "icon.svg", "README.md", "LICENSE", "CHANGELOG.md"]
14
+ }