需先安装 Ollama 并拉取视觉模型,否则无法生成文件名;从源码构建需要 Rust 1.92 或更高版本;2.0.0 与 1.x 不兼容,运行时文件改到 storage/ 目录,旧路径需用 FTP_UPLOAD_DIR 等变量调整;Docker 下启用 SFTP/SCP 需发布 2222/2223 端口并挂载 ./storage:/app/storage。
Montscan
sh4denMIT活跃维护自托管的扫描件处理工具,通过 FTP、SMB 或监听文件夹接收扫描文件,用本地 Ollama 视觉模型识别内容并自动生成带日期的描述性文件名,再投递到 WebDAV 或本地目录。
🖨️ Self-hosted scanned document processor with AI-powered naming. Receives scans via FTP, SFTP, SCP, SMB or a watched folder, reads them with a local Ollama vision model, gives each file a dated, descriptive name, and delivers it to WebDAV (Nextcloud, ownCloud), an SMB share, or a local folder.
git clone https://github.com/sh4den/Montscan.git
cd Montscan
cargo build --release
ollama pull llavaREADME 未说明具体显存或 GPU 型号要求;命名依赖本地 Ollama 视觉模型(默认 qwen3-vl:8b),需自行部署 Ollama 服务。
自托管扫描件处理工具,用本地 Ollama 视觉模型给扫描文件自动起带日期的描述性文件名
自托管的扫描件处理工具,通过 FTP、SMB 或监听文件夹接收扫描文件,用本地 Ollama 视觉模型识别内容并自动生成带日期的描述性文件名,再投递到 WebDAV 或本地目录。
核心能力
- 通过 FTP、SFTP、SCP、SMB 轮询或监听本地文件夹接收扫描件
- 用本地 Ollama 视觉模型读文档内容生成带日期的描述性文件名
- 投递到 WebDAV(Nextcloud、ownCloud)、SMB 共享或本地目录
- PDF 页在进程内用 hayro 渲染,运行时无需 Poppler 等外部程序
- 单一 Rust 二进制,另附 Docker Compose 部署方式
容易踩的坑
- v2.0.0📦 Montscan v2.0.0最新正式版2026-11-02 · 2026-11-02 18:41 UTC
What's Changed
26 commits since
v1.3.2🚀 Features
- feat(egress): add Samba upload (
84a8fc4) - feat(ingress): poll remote SMB share for new scans (
516b07b) - feat(ingress): add SCP server for scanners without SFTP support (
c2272ec) - feat(scp): implement SCP sink protocol (
59e3f00) - feat(ingress): add SFTP server (
c7acbbc) - feat(ingress): add shared SSH host key and path helpers (
32d18c1) - feat(ingress): port FTP server to libunftp (
482f9d7) - feat(egress): add WebDAV upload (
120431e) - feat(egress): move processed files to output dir or rename in place (
00f055e) - feat(ingress): port local folder watcher (
6cc2369) - feat(ingress): add Ingress trait and document processor (
4cbb236) - feat(agent): generate filenames with Ollama vision models (
5a711aa) - feat(agent): render first PDF page with hayro instead of poppler (
e66350c) - feat(config): load settings from environment variables (
2a67b0a) - feat(ollama): Make Ollama context size configurable, add WebDAV error debugging (
cee5578)
🧹 Maintenance
- chore(release): bump version to 2.0.0 (
aea095b) - docs(readme): flag 2.0.0 as a breaking release and point to legacy branch (
f0ec9c0) - refactor(storage): group runtime folders under storage/ (
1482f04) - docs(readme): update for the Rust rewrite (
354770b) - chore(compose): expose SFTP/SCP ports and document new settings (
9218893) - build(docker): build the Rust binary on rust:alpine (
c90f576) - chore: remove Go implementation (
ec18ea6) - test(smb): cover share path conversion (
91103fd) - test(webdav): cover URL encoding and config validation (
2e15c7c) - test(pdf): cover first page rendering (
fcb4da8) - chore(rust): bootstrap cargo project for the rewrite ([
f03c351](https://github.com/sh4den/Mo
- feat(egress): add Samba upload (
- v1.3.2📦 Montscan v1.3.22026-08-24 · 2026-08-24 18:00 UTC
查看更新说明
What's Changed
3 commits since
v1.3.1🚀 Features
- feat(ci): added release publish workflow (
1046fe6) - feat(ci): added docker publish workflow (
f24ebba)
🐛 Bug fixes
- fix: never overwrite existing files on name collision (
116498e)
Full Changelog: https://github.com/sh4den/Montscan/compare/v1.3.1...v1.3.2
- feat(ci): added release publish workflow (
- v1.3.1📦 Montscan v1.3.12026-07-19 · 2026-07-19 21:03 UTC
查看更新说明
What's New
Configurable Ollama Context Window
Vision requests to Ollama could fail with
exceeds the available context sizeonce a scanned image plus the naming prompt pushed past Ollama's 4096-token default. Montscan now explicitly requests a larger context window per call, configurable viaOLLAMA_NUM_CTX(default8192).WebDAV Failure Diagnostics
WebDAV upload failures previously only surfaced a bare status code (e.g.
Write foo.pdf: 502), which isn't enough to tell what actually went wrong. Montscan now captures and logs the response body of failed WebDAV requests, exposing the real error returned by the server or reverse proxy.Changes
OLLAMA_NUM_CTXSetting: New environment variable to control the context window (in tokens) requested from Ollama per call, default8192- WebDAV Debug Logging: Failed WebDAV requests (MkdirAll, Write) now log the HTTP status and response body instead of just a status code
- Documentation Updates: README and
.env.examplenow documentOLLAMA_NUM_CTX
Configuration
Raise the Ollama context window if you see
exceeds the available context sizeerrors in the Ollama logs:OLLAMA_NUM_CTX=8192WebDAV debug logging requires no configuration, it's on by default and only logs when a request fails.
Technical Details
- Added an
Options.NumCtxfield to the/api/chatrequest body inagent/ollama.go - Added
OllamaNumCtxtoconfig/config.go, read fromOLLAMA_NUM_CTX(default8192) - Added a
debugTransporthttp.RoundTripperwrapper inproviders/webdav.gothat logs response bodies (up to 4KB) for any WebDAV request returning a 4xx/5xx status - Updated
docker-compose.yml,.env.example, andREADME.mdto documentOLLAMA_NUM_CTX
Upgrade Notes
OLLAMA_NUM_CTXdefaults to8192, doubling the context memory Ollama reserves versus its own4096default, a negligible increase on typical hardware. No action is required unless you're running Ollama in a memory-constrained environment, in which case setOLLAMA_NUM_CTX=4096to keep the prior behavior.
Full Changelog: View commits since v1.3.0
- v1.3.0📦 Montscan v1.3.02026-06-15 · 2026-06-15 22:47 UTC
查看更新说明
What's New
Folder-Based Ingress 📂
Montscan can now watch a local directory for incoming PDFs instead of (or alongside) FTP and Samba. Drop a scanned document into a folder and Montscan will rename it with an AI-generated name — either in place or by moving it to a separate output folder.
Configurable Ollama GPU Acceleration ⚡
Ollama in Docker Compose can now run on CPU or NVIDIA GPU, switchable entirely from
.env— no more editingdocker-compose.ymlby hand.Changes
- Folder Watcher Ingress: New
FOLDER_ENABLED,FOLDER_INPUT_DIR,FOLDER_OUTPUT_DIR, andFOLDER_POLL_INTERVAL_SECsettings to monitor a local directory for new PDFs - Rename-in-Place or Move-to-Output: Omit
FOLDER_OUTPUT_DIRto rename files where they sit, or set it to move renamed files to a separate output directory - Docker GPU Toggle: New
COMPOSE_PROFILESandOLLAMA_GPU_COUNTvariables select between CPU-only and GPU-accelerated Ollama containers - Documentation Updates: README now covers folder watcher setups and GPU configuration
Configuration
Enable the folder watcher:
FOLDER_ENABLED=true FOLDER_INPUT_DIR=./input FOLDER_OUTPUT_DIR=./output # omit to rename in place FOLDER_POLL_INTERVAL_SEC=5Enable GPU acceleration for Ollama in Docker Compose:
COMPOSE_PROFILES=gpu OLLAMA_GPU_COUNT=1Leave
COMPOSE_PROFILES=cpu(or unset) for CPU-only.Technical Details
- Added
server/folder.gofor the folder watcher loop andproviders/local.gofor rename/move handling - Updated
config/config.goandmain.goto wire up the new folder ingress - Updated
agent/agent.goto support the local provider - Split the Ollama service in
docker-compose.ymlintoollama(CPU) andollama-gpu(NVIDIA) variants behind Compose profiles, sharing config via a YAML anchor
Upgrade Notes
The folder watcher is disabled by default (
FOLDER_ENABLED=false); existing FTP/Samba setups are unaffected. Ollama continues to run on CPU by default in Docker Compose unlessCOMPOSE_PROFILES=gpuis set.
Full Changelog: View commits since v1.2.0
- Folder Watcher Ingress: New
- v1.2.0📦 Montscan v1.2.02026-04-04 · 2026-04-04 17:00 UTC
查看更新说明
What's New
Flexible Ingress + Samba Upload Support 🚀
Montscan now supports a more complete document pipeline with configurable ingress servers and an expanded upload backend. In addition to WebDAV, you can now send processed documents to Samba shares, and run FTP and/or Samba server ingress with environment-based configuration.
Changes
- Samba Upload Provider: Added Samba as a document destination alongside existing WebDAV support
- Provider Architecture Cleanup: Refactored upload logic into the
providerspackage for cleaner backend handling - Ingress Server Controls: Added environment/config toggles for FTP and Samba server ingress
- Docker Compose Support: Added FTP and Samba server configuration options directly in
docker-compose.yml - Filename Date Standardization: Updated filename date format guidance to ISO8601
Configuration
Configure ingress and providers using environment variables:
# Ingress servers export FTP_ENABLED=true export SAMBA_SERVER_ENABLED=true # Samba upload provider export SAMBA_HOST=192.168.1.10 export SAMBA_PORT=445 export SAMBA_SHARE=documents export SAMBA_USERNAME=your_user export SAMBA_PASSWORD=your_password export SAMBA_PATH=/incomingOr in Docker Compose:
environment: - FTP_ENABLED=true - SAMBA_SERVER_ENABLED=true - SAMBA_HOST=192.168.1.10 - SAMBA_PORT=445 - SAMBA_SHARE=documents - SAMBA_USERNAME=your_user - SAMBA_PASSWORD=your_password - SAMBA_PATH=/incomingTechnical Details
- Added Samba provider implementation in
providers/samba.go - Extended upload flow to support provider-based routing (
providerspackage refactor) - Added/updated ingress configuration loading for FTP and Samba server options in
config/config.go - Updated server startup/config paths for FTP and Samba server components (
server/ftp.go,server/samba.go) - Added Docker Compose environment wiring for FTP/Samba server configuration
Upgrade Notes
Existing installations remain compatible.
If you do not enable new ingress toggles, behavior stays aligned with previous setup expectations.For Samba uploads, define Samba provider variables (
SAMBA_HOST,SAMBA_SHARE, credentials, etc.).
If you only use WebDAV, your current configuration can remain unchanged.
Full Changelog: View commits since v1.1.0
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Self-hosted naming and filing for scanned documents
Montscan receives scanned documents from your network scanner, reads them with a local Ollama vision model, gives each file a descriptive, dated name, and delivers it wherever you keep your documents. It speaks FTP, SFTP, SCP and SMB, so most scanners work with it without extra software.
Naming runs locally through Ollama, so your documents are never sent to a third-party AI service and no API key is needed.
Install | Docker image | Configuration | Issues | Contributing
[!NOTE] Montscan is still in active development and not fully production-ready, but it is usable day to day. On a test set of 1000 documents it named 97.5% of them correctly.
If it's useful to you, a star on GitHub is appreciated.
Features
- Receives scans over FTP, SFTP or legacy SCP, by polling a remote SMB share, or by watching a local folder
- Reads each document with an Ollama vision model and generates a dated, descriptive filename
- Delivers the result to WebDAV (Nextcloud, ownCloud, etc.), an SMB/CIFS share, or a local output folder (or renames it in place)
- Ships as a single binary and a Docker Compose setup
Contents
- Rust rewrite
- Architecture
- Prerequisites
- Installation
- Configuration
- Usage
- Docker
- Troubleshooting
- License
Rust rewrite
Montscan was originally written in Go. Version 2.0.0 is a full rewrite in Rust. I wanted a single self-contained binary, clearer separation between components, and an easier way to add new protocols for receiving scans.
What changed
- PDF pages are now rendered in-process with
hayro. The Go version shelled out to Poppler (pdftoppm) or ImageMagick; now themontscanbinary is all you need at runtime. - Documents can now arrive over SFTP and legacy SCP, in addition to FTP, Samba polling and the folder watcher.
- Every ingress implements a common
Ingresstrait and only passes finished PDFs to the document processor. Delivery (WebDAV, Samba, local move) lives in a separateegressmodule, so adding a protocol doesn't touch the AI or upload code. See Architecture. - The FTP passive port range and advertised address are configurable (
FTP_PASSIVE_PORTS,FTP_PASSIVE_HOST), which fixes most problems behind NAT or Docker. OLLAMA_TIMEOUTcontrols how long Montscan waits for a filename before falling back toscan_<timestamp>.pdf. Raise it for slower "thinking" models.- The Docker image no longer installs
poppler-utilsand is smaller as a result.
Upgrading from 1.x
[!WARNING] 2.0.0 is a breaking release and is not backward compatible with 1.x. The Go version is kept on the
legacy-v1.0.0branch and no longer receives updates.
- Environment variable names are unchanged. The old
SAMBA_INGRESS_*names still work as fallbacks forSAMBA_SERVER_*. - Uploads to WebDAV and Samba, and files received over FTP, now overwrite an existing file with the same name. 1.x appended
_1,_2, and so on. Local moves by the folder watcher still never overwrite. - Runtime files now live under
storage/. Received scans default to./storage/inbox(was./scans) and SSH host keys go in./storage/ssh. If you relied on the old location, setFTP_UPLOAD_DIR=./scansor move the folder. - Building from source needs Rust 1.92 or newer (
cargo build --release). - For Docker, pull the new image (
docker-compose pull) or rebuild it. If you enable SFTP or SCP, publish ports2222/2223and mount./storage:/app/storageso scans and the generated host key survive container recreation. The includeddocker-compose.ymlalready does this. - Logging is controlled by
RUST_LOG(defaultinfo).
On AI assistance
I used AI tools during the rewrite, mostly to split the work into commits and to look for bugs and edge cases. I reviewed every change before merging it.
Architecture
Montscan is a single Rust crate with all code in src/. It doesn't need any external programs or C libraries at runtime. Each ingress is a module under src/ingress/, built on an existing protocol library, and implements the Ingress trait. An ingress only hands finished PDFs to a DocumentProcessor and knows nothing about naming or delivery.
| Module | Role | Built on |
|---|---|---|
ingress | Ingress / DocumentProcessor traits | |
ingress::ftp | FTP ingress | libunftp + unftp-sbe-fs |
ingress::sftp | SFTP ingress | russh + russh-sftp |
ingress::scp | SCP ingress | russh; there is no SCP server crate, so the sink side of the protocol is implemented in the module |
ingress::smb | SMB share poller | smb, a pure Rust SMB2/3 client |
ingress::folder | Local folder watcher | tokio |
ingress::ssh | Host key, auth and path jailing shared by SFTP and SCP | russh |
agent | PDF rendering and Ollama filename generation | hayro, reqwest, image |
egress | WebDAV / Samba / local delivery | reqwest, smb |
config, env, banner | Settings and startup banner |
Prerequisites
- Rust 1.92 or newer, if you build from source
- Ollama with a vision model such as
llavaorllama3.2-vision - Optionally, a WebDAV server or SMB share to deliver files to
Installation
git clone https://github.com/sh4den/Montscan.git
cd Montscan
cargo build --release # produces target/release/montscan
Install Ollama and pull a vision-capable model:
ollama pull llava
Configuration
Montscan is configured through environment variables. You need at least one ingress enabled. You should also configure an egress, unless you use the folder watcher, which can rename or move files on its own. Egress is picked in this order: WebDAV, then Samba, then a local move if the folder watcher is enabled. With no egress, received files stay where they landed.
Ingress: FTP server
| Variable | Description | Default |
|---|---|---|
FTP_ENABLED | Enable the FTP server | true |
FTP_HOST | Bind address | 0.0.0.0 |
FTP_PORT | Port | 21 |
FTP_USERNAME | Username | scanner |
FTP_PASSWORD | Password | scanner123 |
FTP_UPLOAD_DIR | Local directory for received files | ./storage/inbox |
FTP_PASSIVE_PORTS | Passive data port range | 21000-21010 |
FTP_PASSIVE_HOST | Address advertised for passive connections (set this to the host's LAN IP behind Docker/NAT) | (control connection address) |
A file is processed once its STOR completes, or when an upload is renamed to *.pdf.
Ingress: SFTP server
| Variable | Description | Default |
|---|---|---|
SFTP_ENABLED | Enable the SFTP server | false |
SFTP_HOST | Bind address | 0.0.0.0 |
SFTP_PORT | Port | 2222 |
SFTP_USERNAME | Username (password auth only, no shell) | scanner |
SFTP_PASSWORD | Password | scanner123 |
SFTP_UPLOAD_DIR | Directory clients see as / | ./storage/inbox |
SFTP_HOST_KEY | Ed25519 host key, generated on first start | ./storage/ssh/ssh_host_ed25519_key |
A file is processed when the client closes it, or when an upload is renamed to *.pdf. Modern scp (OpenSSH 9+) actually uses SFTP, so it works against this port.
Ingress: SCP server
| Variable | Description | Default |
|---|---|---|
SCP_ENABLED | Enable the SCP server | false |
SCP_HOST | Bind address | 0.0.0.0 |
SCP_PORT | Port | 2223 |
SCP_USERNAME | Username (password auth only, no shell) | scanner |
SCP_PASSWORD | Password | scanner123 |
SCP_UPLOAD_DIR | Directory clients see as / | ./storage/inbox |
SCP_HOST_KEY | Ed25519 host key, generated on first start (can be shared with SFTP) | ./storage/ssh/ssh_host_ed25519_key |
This is the legacy SCP protocol (scp -t), which is what most scanners and scp -O use. Only uploads are supported; recursive (-r) uploads work.
Ingress: Samba server (polls a remote SMB share)
| Variable | Description | Default |
|---|---|---|
SAMBA_SERVER_ENABLED | Enable the Samba poller | false |
SAMBA_SERVER_HOST | SMB server hostname or IP | localhost |
SAMBA_SERVER_PORT | SMB port | 445 |
SAMBA_SERVER_USERNAME | SMB username | |
SAMBA_SERVER_PASSWORD | SMB password | |
SAMBA_SERVER_SHARE | Share name | scans |
SAMBA_SERVER_PATH | Path within the share to watch | /scans |
SAMBA_SERVER_POLL_INTERVAL_SEC | Polling interval in seconds | 10 |
SAMBA_SERVER_DELETE_AFTER_READ | Delete the file from the share after downloading it | true |
SAMBA_SERVER_WORK_DIR | Local staging directory | (FTP_UPLOAD_DIR) |
Ingress: folder watcher
| Variable | Description | Default |
|---|---|---|
FOLDER_ENABLED | Enable the folder watcher | false |
FOLDER_INPUT_DIR | Directory to watch for PDFs | ./input |
FOLDER_OUTPUT_DIR | Where renamed files go; leave unset to rename in place | |
FOLDER_POLL_INTERVAL_SEC | Polling interval in seconds | 5 |
Egress: WebDAV
| Variable | Description | Default |
|---|---|---|
WEBDAV_ENABLED | Enable WebDAV upload | false |
WEBDAV_URL | Server base URL | |
WEBDAV_USERNAME | Username | |
WEBDAV_PASSWORD | Password | |
WEBDAV_UPLOAD_PATH | Remote folder for uploads | /Documents/Scanned |
WEBDAV_INSECURE | Skip TLS certificate verification | false |
Egress: Samba (uploads to an SMB share)
| Variable | Description | Default |
|---|---|---|
SAMBA_ENABLED | Enable Samba upload | false |
SAMBA_HOST | SMB server hostname or IP | localhost |
SAMBA_PORT | SMB port | 445 |
SAMBA_USERNAME | SMB username | |
SAMBA_PASSWORD | SMB password | |
SAMBA_SHARE | Destination share | scans |
SAMBA_PATH | Path within the share | scans |
Naming (Ollama)
| Variable | Description | Default |
|---|---|---|
OLLAMA_HOST | Ollama URL | http://localhost:11434 |
OLLAMA_MODEL | Vision model | qwen3-vl:8b |
OLLAMA_NUM_CTX | Context window (tokens) requested per call | 8192 |
OLLAMA_TIMEOUT | Seconds to wait before falling back to a timestamped name | 120 |
LANGUAGE | Language of the generated filenames | english |
Usage
Example setups
Scanner to FTP, delivered to WebDAV:
FTP_ENABLED=true
FTP_USERNAME=scanner
FTP_PASSWORD=secret
WEBDAV_ENABLED=true
WEBDAV_URL=https://cloud.example.com/remote.php/webdav
WEBDAV_USERNAME=user
WEBDAV_PASSWORD=secret
Scanner writes to a Samba share that is mounted locally. Montscan watches the mount, names each PDF and moves it to an output folder:
FOLDER_ENABLED=true
FOLDER_INPUT_DIR=/mnt/scanner # Samba mount point
FOLDER_OUTPUT_DIR=/mnt/documents # where named files end up
FOLDER_POLL_INTERVAL_SEC=5
FTP_ENABLED=false
Same thing, but renaming files where they are:
FOLDER_ENABLED=true
FOLDER_INPUT_DIR=/mnt/scanner
# no FOLDER_OUTPUT_DIR, so files are renamed in place
FTP_ENABLED=false
Running locally
cp .env.example .env
# edit .env
export $(grep -v '^#' .env | xargs)
./target/release/montscan
Logging defaults to info. Set RUST_LOG=debug for more detail.
Docker
Docker Compose
Set your environment variables in docker-compose.yml, then start it:
docker-compose up -d
This uses the prebuilt image from GitHub Container Registry (ghcr.io/sh4den/montscan). To build from source instead, uncomment the build: . line in docker-compose.yml and run docker-compose up -d --build.
Ollama runs on CPU by default. To use an NVIDIA GPU, add this to .env:
COMPOSE_PROFILES=gpu
OLLAMA_GPU_COUNT=1
Leave COMPOSE_PROFILES=cpu (or unset) for CPU only.
Follow the logs with docker-compose logs -f and stop everything with docker-compose down.
Plain Docker
docker build -t montscan .
docker run -d \
-p 21:21 \
-p 21000-21010:21000-21010 \
-v ./input:/app/input \
-v ./output:/app/output \
-e FOLDER_ENABLED=true \
-e FOLDER_INPUT_DIR=/app/input \
-e FOLDER_OUTPUT_DIR=/app/output \
-e FTP_ENABLED=false \
-e OLLAMA_HOST=http://host.docker.internal:11434 \
--name montscan \
montscan
Troubleshooting
FTP connection refused. Make sure port 21 isn't blocked by a firewall. On Windows, allow Montscan through Windows Defender Firewall.
FTP uploads hang after login (Docker). Passive data connections have to reach the container. Publish the FTP_PASSIVE_PORTS range and set FTP_PASSIVE_HOST to the Docker host's LAN IP.
SCP fails with "subsystem request failed". OpenSSH 9+ scp uses SFTP by default. Use scp -O against the SCP port, or point the client at the SFTP port.
SSH host key changed warning. The host key is generated on first start at SFTP_HOST_KEY / SCP_HOST_KEY. In Docker, keep it on the ./storage volume so it isn't regenerated when the container is recreated.
Folder watcher doesn't pick up files. Check that FOLDER_INPUT_DIR is the path inside the container (with a volume mounted to it) and that the process can read it. If your scanner writes slowly, increase FOLDER_POLL_INTERVAL_SEC.
Files get a scan_YYYYMMDD_HHMMSS.pdf name. That's the fallback when naming fails. Check that Ollama is running (ollama list) and that the configured model supports vision. For slow models, raise OLLAMA_TIMEOUT.
WebDAV upload fails. Double-check the URL and credentials, and make sure the upload path exists on the server. For Nextcloud, the URL should end with /remote.php/webdav. With a self-signed certificate, set WEBDAV_INSECURE=true.
"failed to parse PDF". Encrypted (password-protected) PDFs aren't supported. Turn off PDF encryption on the scanner.
License
MIT. See LICENSE.
Acknowledgments
Montscan is built on these projects:
- libunftp, async FTP server
- russh and russh-sftp, SSH and SFTP servers
- smb-rs, pure Rust SMB2/3 client
- Ollama, local model runner
- reqwest, HTTP client for Ollama and WebDAV
Questions and bug reports are welcome as GitHub issues.