Skip to main content

Zero-dependency Python CLI for sharing local directories over HTTP with authentication, file browsing, and ZIP downloads

Project description

neev

neev

Zero-dependency Python CLI for sharing local directories over HTTP — with auth, file browsing, ZIP downloads, and uploads.

PyPI version Python versions PyPI downloads License CI Publish Zero dependencies Coverage Ruff uv


Table of Contents


Why neev?

python -m http.server is great, but it has no authentication, no way to download a folder, no uploads, and exposes dotfiles by default. neev is the drop-in replacement with the things you actually need:

  • HTTP Basic Auth — constant-time credential comparison
  • ZIP folder downloads — streamed on the fly, no temp files
  • File uploads — opt-in, with size limits and path-traversal protection
  • Clean browser UI — dark/light theme, Markdown preview, syntax-aware file icons
  • HTTP Range support — resumable downloads, video/audio seeking
  • Concurrent requests — threaded server, not single-request-at-a-time
  • Secure defaults — localhost-only, no writes, no hidden files
  • Zero dependencies — pure Python stdlib, Python 3.11+

Built for developers sharing build artifacts, teams exchanging files on a LAN, and anyone who wants http.server but grown up.


Install

From PyPI (recommended)

# with uv (fastest)
uv tool install neev

# or pipx
pipx install neev

# or plain pip
pip install neev

Run without installing

uvx neev --dir ./public

From source

git clone https://github.com/prabhuakshay/neev
cd neev
uv sync
uv run neev --help

Requires Python 3.11 or newer. Works on Linux, macOS, and Windows.


Quick Start

# Serve the current directory on http://127.0.0.1:8000
neev

# Serve a specific directory
neev ./public

# With auth, on all interfaces, custom port
neev ./share --host 0.0.0.0 --port 8080 --auth alice:s3cret

# Full-featured: auth + uploads + zip downloads
neev ./share --auth alice:s3cret --enable-upload --enable-zip-download

Open the URL printed on startup. You'll see a file browser. If auth is enabled, your browser will prompt for credentials.


CLI Reference

neev [DIRECTORY] [OPTIONS]

Positional arguments

Argument Default Description
directory . Directory to serve. Must exist. Resolved to its real path (symlinks followed).

Options

Flag Default Description
--host HOST 127.0.0.1 Address to bind. Use 0.0.0.0 to expose on LAN.
--port PORT, -p PORT 8000 TCP port (1–65535).
--auth USER:PASS (none) Enable HTTP Basic Auth. Equivalent to NEEV_AUTH env var.
--show-hidden / --no-show-hidden off Show dotfiles and neev.toml in listings.
--enable-zip-download / --no-enable-zip-download off Allow folders to be downloaded as streamed ZIP.
--max-zip-size MB 100 Maximum ZIP archive size in MB. Rejected if exceeded.
--enable-upload / --no-enable-upload off Allow multipart file uploads from the browser.
--read-only / --no-read-only off Force-disable uploads (overrides --enable-upload).
--banner TEXT (none) Message displayed at the top of directory listings.
-h, --help Show help and exit.

All boolean flags use argparse.BooleanOptionalAction, so --no-<flag> works too — useful for overriding neev.toml from the CLI.

Examples

# Read-only share even if TOML enables uploads
neev ./build --read-only

# Serve on LAN with auth, show dotfiles, custom banner
neev ./code --host 0.0.0.0 --auth me:pw --show-hidden --banner "Internal only"

# Raise ZIP size cap to 500 MB
neev ./data --enable-zip-download --max-zip-size 500

Configuration File (neev.toml)

neev looks for a neev.toml file in the served directory and merges it with CLI args. CLI flags always win.

Example

# ./neev.toml
host = "0.0.0.0"
port = 9000
show-hidden = false
enable-zip-download = true
max-zip-size = 250
enable-upload = false
read-only = false
banner = "Build artifacts — ask #devops for access"

Recognized keys

TOML key Type Notes
host string Same as --host.
port integer Same as --port.
auth string "user:pass". Same as --auth.
show-hidden bool Same as --show-hidden.
enable-zip-download bool Same as --enable-zip-download.
max-zip-size integer MB. Same as --max-zip-size.
enable-upload bool Same as --enable-upload.
read-only bool Same as --read-only.
banner string Same as --banner.

Denied keys: directory is never read from TOML (the served directory is always set by CLI, to avoid surprise path changes).

Unknown keys are logged at WARN level and ignored. Malformed TOML is logged and the file is skipped — neev keeps running with CLI defaults.

The neev.toml file itself is hidden from listings unless --show-hidden is set.


Environment Variables

Variable Purpose
NEEV_AUTH Credentials as user:pass. Alternative to --auth.

--auth beats NEEV_AUTH when both are set.


Configuration Precedence

From highest to lowest:

  1. CLI flags (explicit --foo bar)
  2. NEEV_AUTH env var (auth only)
  3. neev.toml in the served directory
  4. Built-in defaults (see CLI reference table)

Features

File browser

  • Directory listing with sortable columns (name, size, modified).
  • Per-file icons by extension/type.
  • Breadcrumb navigation.
  • In-browser preview for text files, Markdown (rendered), images, and PDFs.
  • Correct MIME types and charset detection per file.

ZIP folder downloads

  • Triggered by a "Download as ZIP" button on any folder (if --enable-zip-download).
  • Streamed — no temp files, constant memory usage.
  • Capped at --max-zip-size MB; oversized archives return 413 Payload Too Large.
  • Hidden files excluded unless --show-hidden.

Uploads

  • Opt-in via --enable-upload. Disabled under --read-only.
  • Multipart upload form on each directory page.
  • Filename sanitization — path traversal, null bytes, and absolute paths rejected.
  • Writes land in the directory currently being viewed.
  • Authentication (if configured) is required.

HTTP Range requests

Partial content (206) supported for all file downloads — resumable downloads, video/audio seeking, curl -C -.

Concurrency

Threaded HTTPServer — multiple clients can browse, download, and upload simultaneously.

Authentication

  • HTTP Basic Auth.
  • Credentials compared with hmac.compare_digest (constant-time).
  • Failed auth returns 401 with a WWW-Authenticate: Basic challenge.

HTTP API

neev is primarily browser-driven, but every interaction is a plain HTTP request — scriptable with curl, wget, httpie, etc.

Listing / serving files

GET /path/to/dir/      → HTML directory listing
GET /path/to/file.txt  → file contents (with Range support)

Trailing slash → directory; no slash → file. Requests to a path without a trailing slash that resolves to a directory are redirected with 301 to the canonical slashed URL.

ZIP download

GET /path/to/dir/?zip=1

Returns application/zip stream. Filename is <dirname>.zip. Disabled unless --enable-zip-download.

File preview (rendered)

GET /path/to/file.md?preview=1

Returns HTML with Markdown rendered server-side. Works for text and Markdown files.

Upload

POST /path/to/dir/
Content-Type: multipart/form-data; boundary=...

<file field named "file">

Returns 303 See Other redirect to the directory on success, 4xx on validation failure. Disabled unless --enable-upload and not --read-only.

Example with curl:

curl -u "$USER_PASS" -F "file=@./report.pdf" http://localhost:8000/uploads/

Authentication

Every endpoint honors Basic Auth when enabled:

curl -u "$USER_PASS" http://localhost:8000/
# or (equivalent) — build the header yourself
curl -H "Authorization: Basic $(printf '%s' "$USER_PASS" | base64)" http://localhost:8000/

Status codes

Code When
200 OK Successful GET of file/listing.
206 Partial Content Successful Range request.
301 Moved Permanently Directory URL missing trailing slash.
303 See Other Successful upload.
400 Bad Request Malformed request / upload.
401 Unauthorized Auth required or invalid credentials.
403 Forbidden Path traversal, access denied, or writes on read-only.
404 Not Found Path doesn't exist or hidden without --show-hidden.
405 Method Not Allowed e.g. POST when uploads disabled.
413 Payload Too Large ZIP exceeds --max-zip-size.
416 Range Not Satisfiable Invalid Range header.
500 Internal Server Error Unexpected server fault.

Security Model

What neev protects against:

  • Path traversal — every request resolves os.path.realpath() and verifies the result is a descendant of the served directory. ../, symlink escapes, and absolute paths are rejected with 403.
  • Credential leaks in timinghmac.compare_digest for both username and password.
  • Upload filename injection — rejects traversal, null bytes, and absolute paths.
  • Accidental exposure — defaults are localhost-only, no writes, no dotfiles, no ZIPs.
  • Oversized ZIP abuse — streaming ZIPs are bounded by --max-zip-size.

What neev does NOT do:

  • No HTTPS/TLS. Run behind a reverse proxy (nginx, Caddy) for internet-facing deployments.
  • No rate limiting. Use a reverse proxy or firewall.
  • No user management — single Basic Auth pair, one shared credential.
  • No virus scanning on uploads.
  • No audit log beyond stdout request logging.

If exposing to a network or internet:

  1. Always set --auth.
  2. Use a strong password (treat as a bearer token).
  3. Put it behind a TLS-terminating proxy.
  4. Prefer --read-only unless uploads are required.
  5. Keep --show-hidden off.

Recipes

Share a build artifact with a coworker

neev ./dist --host 0.0.0.0 --auth reviewer:pleasecheck
# share: http://<your-ip>:8000/

Drop box — let someone upload files to you

mkdir -p ~/inbox
neev ~/inbox --host 0.0.0.0 --auth sender:pw --enable-upload

Static preview site with Markdown rendering

neev ./docs --enable-zip-download --banner "Project docs"

Behind Caddy with TLS

share.example.com {
    reverse_proxy localhost:8000
}
neev /srv/share --auth alice:s3cret --enable-zip-download

Systemd service

# /etc/systemd/system/neev.service
[Unit]
Description=neev file server
After=network.target

[Service]
ExecStart=/usr/local/bin/neev /srv/share --host 127.0.0.1 --port 8000 --enable-zip-download
Environment=NEEV_AUTH=alice:s3cret
Restart=on-failure
User=neev

[Install]
WantedBy=multi-user.target

Docker one-liner

docker run --rm -p 8000:8000 -v "$PWD:/srv:ro" python:3.13-slim \
  sh -c "pip install neev && neev /srv --host 0.0.0.0 --read-only"

Architecture

src/neev/
├── cli.py                # argparse, validation, entry point
├── config.py             # frozen Config dataclass
├── toml_config.py        # neev.toml loader + CLI merge
├── server.py             # HTTPServer wiring (threaded)
├── server_core.py        # main request dispatcher
├── server_auth.py        # HTTP Basic Auth
├── server_preview.py     # file/Markdown preview
├── server_upload.py      # multipart upload handler
├── server_zip.py         # streaming ZIP response
├── server_assets.py      # bundled static assets (CSS, JS)
├── server_utils.py       # Range, redirects, MIME helpers
├── fs.py                 # path-safe filesystem ops
├── auth.py               # constant-time credential check
├── upload.py             # upload validation
├── upload_multipart.py   # multipart body parsing
├── zip.py                # streaming zip generator
├── url_utils.py          # URL encoding/decoding
├── log.py                # logging helpers + ANSI styling
├── html*.py              # HTML templating (no jinja — pure stdlib)
└── static/               # compiled CSS bundled with package

Design pillars:

  1. Stdlib only. Every feature is built on http.server, zipfile, argparse, html, tomllib, base64, hmac.
  2. Defense-in-depth on paths. Every filesystem op goes through fs.py which resolves and verifies containment.
  3. Frozen config. Config is built once at startup; request handlers never mutate configuration state.
  4. No dynamic imports, no plugin system. Small, auditable surface area.
  5. Files under 300 lines. Enforced by convention — keeps modules focused.

Development

Uses uv for everything.

Setup

git clone https://github.com/prabhuakshay/neev
cd neev
uv sync

Run

uv run neev --help
uv run neev ./tests/fixtures --enable-zip-download

Test

uv run pytest               # full suite with coverage (≥95% enforced)
uv run pytest -k upload     # filter
uv run pytest --no-cov -x   # fast iteration

Lint & type check

uv run ruff check .
uv run ruff format .
uv run ty check

Pre-commit hooks (prek)

uv run prek install
uv run prek run --all-files

The pre-commit config also recompiles the Tailwind CSS bundle when any html_*.py changes.

Build & publish

uv build                    # produces sdist + wheel in dist/
# Publishing is automated: create a GitHub Release → PyPI workflow uploads.

Troubleshooting

Symptom Likely cause Fix
Address already in use Port taken Pick a different --port or kill the prior process.
Browser keeps re-prompting for password Wrong credentials or auth off on restart Verify --auth / NEEV_AUTH, clear browser cache.
403 Forbidden on a file that exists Path-traversal guard / symlink escape The file isn't inside the served directory's real path.
ZIP download returns 413 Folder exceeds --max-zip-size Raise the cap or download individual files.
Uploads return 405 --enable-upload not set, or --read-only on Enable uploads and disable read-only.
Dotfiles don't appear Hidden by default Use --show-hidden.
neev.toml takes effect unexpectedly Merged from served directory Check startup banner; override with CLI flags.

FAQ

Does neev support HTTPS? No. Use a reverse proxy (Caddy, nginx, Traefik) for TLS termination.

Can I serve multiple directories? Not in one process. Run multiple neev instances on different ports.

Is it safe to expose to the internet? Only behind HTTPS + auth + ideally --read-only. For anything mission-critical, reach for a proper server.

Why "neev"? "Neev" (नींव) means foundation in Hindi — a simple base to build file sharing on.

Does it work on Windows? Yes. Python 3.11+ required.

What about WebDAV / S3 / FTP? Out of scope. neev is HTTP + browser UI only.


Contributing

Issues and PRs welcome at github.com/prabhuakshay/neev.

Before submitting:

uv run ruff check . && uv run ruff format --check .
uv run ty check
uv run pytest

See CLAUDE.md for the full development philosophy (stability > features, stdlib only, files < 300 lines, etc.).


License

MIT © Akshay Prabhu

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

neev-0.1.0.tar.gz (77.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

neev-0.1.0-py3-none-any.whl (91.2 kB view details)

Uploaded Python 3

File details

Details for the file neev-0.1.0.tar.gz.

File metadata

  • Download URL: neev-0.1.0.tar.gz
  • Upload date:
  • Size: 77.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for neev-0.1.0.tar.gz
Algorithm Hash digest
SHA256 73a6d463a5da0cd09bcf1f7a6992517a28b44e62663dc5aa26c5e5b7b039e0f3
MD5 6cf6f8ddc7fce7b27d0ce2ed87bcfac2
BLAKE2b-256 3da7924508587052c09fe9aa7c09b8e689aa30f7246baa475208ff5dc4b239bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for neev-0.1.0.tar.gz:

Publisher: publish.yml on prabhuakshay/neev

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file neev-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: neev-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 91.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for neev-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dced5403bbb6e5264ea6d735b191cbd05cdbd55bb7125ff01ee6b52152990fe2
MD5 2f1e32beedb97edbd2eed73f597ef782
BLAKE2b-256 4a1de5f0b59d822f1617468a1acddbf8540b4169642bbd4ffd2a6e6c1ff20a86

See more details on using hashes here.

Provenance

The following attestation bundles were made for neev-0.1.0-py3-none-any.whl:

Publisher: publish.yml on prabhuakshay/neev

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page