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 +39 -0
- package/LICENSE +21 -0
- package/README.md +524 -2
- package/icon.svg +14 -0
- package/package.json +12 -4
- package/public/index.html +1005 -0
- package/server.js +800 -0
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
|
-
|
|
1
|
+
<img src="icon.svg" width="88" alt="Pigeonhole icon: a wall of arched pigeonholes, one holding a letter">
|
|
2
2
|
|
|
3
|
-
|
|
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": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|