Metadata-Version: 2.4
Name: plumefile
Version: 0.1.1
Summary: A command-line file-sharing tool that scans a file before it flies: secrets, PII, a data profile and sentiment, then a self-destructing LAN or tunnel link.
Project-URL: Homepage, https://github.com/1mystic/plumefile
Project-URL: Repository, https://github.com/1mystic/plumefile
Author: Plume contributors
License: MIT
License-File: LICENSE
Keywords: cli,file-sharing,p2p,pii,qr,secret-scanning,self-destruct
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Communications :: File Sharing
Classifier: Topic :: Security
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.110
Requires-Dist: httpx>=0.27
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.1
Requires-Dist: qrcode[pil]>=7.4
Requires-Dist: rich>=13.7
Requires-Dist: typer>=0.12
Requires-Dist: uvicorn>=0.29
Requires-Dist: vadersentiment>=3.3
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

<p align="center">
  <img src="assets/readme/hero.svg" width="100%" alt="Plume: scan a file, then let it fly. An offline secret and PII scanner that shares files over a self-destructing QR link.">
</p>

<p align="center">
  <a href="https://pypi.org/project/plumefile/"><img alt="PyPI" src="https://img.shields.io/pypi/v/plumefile?color=f0a500&label=pypi"></a>
  <a href="https://pypi.org/project/plumefile/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/plumefile?color=f0a500"></a>
  <img alt="Offline" src="https://img.shields.io/badge/intelligence-100%25%20offline-2f9e44">
  <img alt="License" src="https://img.shields.io/badge/license-MIT-blue">
</p>

**Plume is a command-line file-sharing tool.** Before a file leaves your machine it runs a
pre-flight intelligence pass - an offline scan for secrets and personal data, a lightweight
data/text profile, and sentiment - then turns those signals into a risk level and a suggested
link lifetime. It serves the file over a self-destructing link (LAN out of the box, or a free
internet tunnel) with a QR code and a neat browser download page.

No accounts. No third-party storage. No API keys. The intelligence pass is fully offline.

```bash
pipx install plumefile          # or: uv tool install plumefile
plume share q3-report.pdf       # scans it, then prints a code, a link and a QR
```

---

## See it work

`plume scan` inspects a file and prints one insight card. Here it is on a config file with a
planted AWS key, a Stripe key and a private key block:

<p align="center">
  <img src="assets/readme/scan-secrets.svg" width="88%" alt="plume scan on a secrets file: risk HIGH, suggested expiry 2m, and a table of redacted findings.">
</p>

And on a clean CSV of product reviews - it profiles the table and reads the tone instead:

<p align="center">
  <img src="assets/readme/scan-reviews.svg" width="88%" alt="plume scan on a reviews CSV: risk LOW, a 4 rows by 4 cols data profile, positive sentiment.">
</p>

> Want a live recording? Install [VHS](https://github.com/charmbracelet/vhs) and run
> `vhs assets/readme/demo.tape` to generate `assets/readme/demo.gif` from the real CLI.

---

## Install

```bash
pipx install plumefile          # isolated, recommended
uv tool install plumefile       # if you use uv
pip install plumefile           # into the current environment
```

All three give you two equivalent commands: **`plume`** (short) and **`plumefile`**.

From source:

```bash
git clone https://github.com/1mystic/plumefile
cd plumefile
uv venv .venv
uv pip install --python .venv/Scripts/python.exe -e ".[dev]"   # Windows
# uv pip install --python .venv/bin/python -e ".[dev]"         # macOS / Linux
```

---

## Quickstart

```bash
# 1. Inspect a file without sharing it
plume scan customers.csv

# 2. Share a file. Plume scans it, suggests an expiry, prints a code, a link and a QR.
plume share report.pdf
plume share ./project-folder            # a folder is zipped into one archive
plume share a.txt b.txt notes/          # several paths become one archive
plume share secrets.env --tunnel        # also expose a free public URL (needs cloudflared)

# 3. Receive on another machine, by link or by code
plume get http://192.168.1.7:8737/amber-otter-42
plume get amber-otter-42 --host 192.168.1.7:8737 --out ./downloads
```

Or just open the link in a browser: you get a countdown page with a download button and a QR.

---

## Command reference

### `plume scan <path>`

Run the pre-flight intelligence pass and print the insight card. Pure local, no networking.

| Flag           | Description                        |
| -------------- | ---------------------------------- |
| `--quiet`, `-q`| Skip the banner.                   |

### `plume share <paths...>`

Scan, then seed the file(s) over a self-destructing link with a QR code. One file is served
as-is; multiple paths or a folder are zipped into a single archive first (the staged zip is
deleted when the link closes).

| Flag             | Default            | Description                                                             |
| ---------------- | ------------------ | ----------------------------------------------------------------------- |
| `--expire`, `-e` | the scan's suggestion | Link lifetime: `2m`, `5m`, `10m`, `1h`, or a value like `45s`.        |
| `--port`, `-p`   | `8737`             | Port to seed on.                                                        |
| `--tunnel`, `-t` | off                | Also expose a free public `*.trycloudflare.com` URL (needs `cloudflared`). |
| `--no-scan`      | off                | Skip the pre-flight scan.                                               |
| `--yes`, `-y`    | off                | Do not prompt to tighten the expiry when the scan wants a shorter one.  |

### `plume get <url|code>`

Download a shared file with a progress bar and an ETA.

| Flag           | Description                                                        |
| -------------- | ----------------------------------------------------------------- |
| `--host`, `-h` | `host:port` to use when you pass a bare code instead of a URL.     |
| `--out`, `-o`  | Directory to save into (default: current directory).              |

### `plume serve`

Run a persistent serving host for any shares already registered on this machine.

| Flag           | Default | Description          |
| -------------- | ------- | -------------------- |
| `--port`, `-p` | `8737`  | Port to listen on.   |

### `plume version`

Print the installed version.

---

## The pre-flight intelligence pass

Every `scan` (and every `share` of a single file) produces one insight card. The whole pass is
offline and degrades per signal: anything that does not apply to a file is skipped, never fatal.

| Signal            | What it does                                                                 |
| ----------------- | ---------------------------------------------------------------------------- |
| Secret / PII scan | Regex ruleset for private keys, cloud keys (AWS, Google, Stripe), GitHub and Slack tokens, JWTs, password assignments, emails, IPs and Luhn-checked card numbers. Findings are redacted before display. |
| Data profiling    | CSV / TSV / JSON records get row and column counts, dtypes and null counts (pandas / numpy). |
| Text profiling    | Any text file gets line and word counts and a readability score.             |
| Sentiment         | Offline VADER lexicon sentiment for prose (skipped on data and code).        |
| Risk + expiry     | A deterministic cascade maps the signals to low / medium / high and a suggested link lifetime. A live-looking credential forces high and a 2 minute default. |

---

## How sharing works (free, over the network)

- **LAN, out of the box.** `plume share` starts a small local server and prints a link on your
  LAN address plus a QR. Anyone on the same network opens it in a browser or with `plume get`.
- **Internet, optionally, for free.** `--tunnel` shells out to
  [`cloudflared`](https://developers.cloudflare.com/cloudflared/) to open a public
  `*.trycloudflare.com` URL: no account, no cost, no Plume-run servers. If `cloudflared` is not
  installed, Plume says so and stays LAN-only.
- **Self-destructing.** The link stops working at its expiry, with a 15 minute grace window so a
  slow or in-flight download still finishes. Share state is disk-backed, so a restart does not
  lose live shares.
- **Chunked.** Files stream in 256 KB chunks; a 1.5 GB size cap guards against accidents.

Nothing is uploaded to a third-party server. The file is served straight from your machine.

---

## Try it locally

Two terminals on the same machine, both pointed at localhost:

```bash
# terminal 1 - start sharing (short expiry so it self-destructs quickly)
plume share README.md --expire 2m -y

# copy the printed link, then in terminal 2 - download it
plume get http://127.0.0.1:8737/<the-printed-code> --out ./downloads
```

Open the same link in a browser to see the receiver page: a live countdown, a download button
and a QR. Let the timer run out to watch the link self-destruct.

---

## Development

```bash
uv pip install --python .venv/Scripts/python.exe -e ".[dev]"
.venv/Scripts/python.exe -m pytest -q          # the full test suite
```

House rules: every user-visible string uses plain hyphens (never em dashes); every fix ships
with a regression test; transfer changes are verified against a real running server.

---

## Releasing (maintainers)

```bash
uv build                                        # builds sdist + wheel into dist/
uv publish                                      # upload to PyPI (needs a token)
# then cut a GitHub release and attach dist/* as assets:
gh release create v0.1.0 dist/* --generate-notes
```

The receiver web page ships inside the wheel, so an installed `plume` serves the same UI.

---

## Privacy note

Plume warns you about secrets before you share, but it is a convenience scanner, not a
guarantee. It does not scan inside archives, and no regex catches everything. Review the card,
and prefer a short expiry (or keep `--tunnel` off) for anything sensitive.

---

## License

MIT licensed. See [LICENSE](LICENSE).
