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.

v2.0.0发布于 2026-11-02 18:41 UTC457star +1 / 7 天32fork同步于 2026-11-10 17:59 UTC

怎么上手

由 README 提炼,执行前请确认
git clone https://github.com/sh4den/Montscan.git
cd Montscan
cargo build --release
ollama pull llava
硬件要求按 README 说明

README 未说明具体显存或 GPU 型号要求;命名依赖本地 Ollama 视觉模型(默认 qwen3-vl:8b),需自行部署 Ollama 服务。

纯 CPU 可运行有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

自托管扫描件处理工具,用本地 Ollama 视觉模型给扫描文件自动起带日期的描述性文件名

自托管的扫描件处理工具,通过 FTP、SMB 或监听文件夹接收扫描文件,用本地 Ollama 视觉模型识别内容并自动生成带日期的描述性文件名,再投递到 WebDAV 或本地目录。

核心能力

  • 通过 FTP、SFTP、SCP、SMB 轮询或监听本地文件夹接收扫描件
  • 用本地 Ollama 视觉模型读文档内容生成带日期的描述性文件名
  • 投递到 WebDAV(Nextcloud、ownCloud)、SMB 共享或本地目录
  • PDF 页在进程内用 hayro 渲染,运行时无需 Poppler 等外部程序
  • 单一 Rust 二进制,另附 Docker Compose 部署方式

容易踩的坑

需先安装 Ollama 并拉取视觉模型,否则无法生成文件名;从源码构建需要 Rust 1.92 或更高版本;2.0.0 与 1.x 不兼容,运行时文件改到 storage/ 目录,旧路径需用 FTP_UPLOAD_DIR 等变量调整;Docker 下启用 SFTP/SCP 需发布 2222/2223 端口并挂载 ./storage:/app/storage。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v2.0.0
  1. 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
  2. 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

  3. 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 size once 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 via OLLAMA_NUM_CTX (default 8192).

    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_CTX Setting: New environment variable to control the context window (in tokens) requested from Ollama per call, default 8192
    • 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.example now document OLLAMA_NUM_CTX

    Configuration

    Raise the Ollama context window if you see exceeds the available context size errors in the Ollama logs:

    OLLAMA_NUM_CTX=8192
    

    WebDAV debug logging requires no configuration, it's on by default and only logs when a request fails.

    Technical Details

    • Added an Options.NumCtx field to the /api/chat request body in agent/ollama.go
    • Added OllamaNumCtx to config/config.go, read from OLLAMA_NUM_CTX (default 8192)
    • Added a debugTransport http.RoundTripper wrapper in providers/webdav.go that logs response bodies (up to 4KB) for any WebDAV request returning a 4xx/5xx status
    • Updated docker-compose.yml, .env.example, and README.md to document OLLAMA_NUM_CTX

    Upgrade Notes

    OLLAMA_NUM_CTX defaults to 8192, doubling the context memory Ollama reserves versus its own 4096 default, a negligible increase on typical hardware. No action is required unless you're running Ollama in a memory-constrained environment, in which case set OLLAMA_NUM_CTX=4096 to keep the prior behavior.


    Full Changelog: View commits since v1.3.0

  4. 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 editing docker-compose.yml by hand.

    Changes

    • Folder Watcher Ingress: New FOLDER_ENABLED, FOLDER_INPUT_DIR, FOLDER_OUTPUT_DIR, and FOLDER_POLL_INTERVAL_SEC settings to monitor a local directory for new PDFs
    • Rename-in-Place or Move-to-Output: Omit FOLDER_OUTPUT_DIR to rename files where they sit, or set it to move renamed files to a separate output directory
    • Docker GPU Toggle: New COMPOSE_PROFILES and OLLAMA_GPU_COUNT variables 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=5
    

    Enable GPU acceleration for Ollama in Docker Compose:

    COMPOSE_PROFILES=gpu
    OLLAMA_GPU_COUNT=1
    

    Leave COMPOSE_PROFILES=cpu (or unset) for CPU-only.

    Technical Details

    • Added server/folder.go for the folder watcher loop and providers/local.go for rename/move handling
    • Updated config/config.go and main.go to wire up the new folder ingress
    • Updated agent/agent.go to support the local provider
    • Split the Ollama service in docker-compose.yml into ollama (CPU) and ollama-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 unless COMPOSE_PROFILES=gpu is set.


    Full Changelog: View commits since v1.2.0

  5. 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 providers package 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=/incoming
    

    Or 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=/incoming
    

    Technical Details

    • Added Samba provider implementation in providers/samba.go
    • Extended upload flow to support provider-based routing (providers package 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

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    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

    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 the montscan binary 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 Ingress trait and only passes finished PDFs to the document processor. Delivery (WebDAV, Samba, local move) lives in a separate egress module, 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_TIMEOUT controls how long Montscan waits for a filename before falling back to scan_<timestamp>.pdf. Raise it for slower "thinking" models.
    • The Docker image no longer installs poppler-utils and 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.0 branch and no longer receives updates.

    • Environment variable names are unchanged. The old SAMBA_INGRESS_* names still work as fallbacks for SAMBA_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, set FTP_UPLOAD_DIR=./scans or 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 ports 2222/2223 and mount ./storage:/app/storage so scans and the generated host key survive container recreation. The included docker-compose.yml already does this.
    • Logging is controlled by RUST_LOG (default info).

    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.

    ModuleRoleBuilt on
    ingressIngress / DocumentProcessor traits
    ingress::ftpFTP ingresslibunftp + unftp-sbe-fs
    ingress::sftpSFTP ingressrussh + russh-sftp
    ingress::scpSCP ingressrussh; there is no SCP server crate, so the sink side of the protocol is implemented in the module
    ingress::smbSMB share pollersmb, a pure Rust SMB2/3 client
    ingress::folderLocal folder watchertokio
    ingress::sshHost key, auth and path jailing shared by SFTP and SCPrussh
    agentPDF rendering and Ollama filename generationhayro, reqwest, image
    egressWebDAV / Samba / local deliveryreqwest, smb
    config, env, bannerSettings and startup banner

    Prerequisites

    • Rust 1.92 or newer, if you build from source
    • Ollama with a vision model such as llava or llama3.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

    VariableDescriptionDefault
    FTP_ENABLEDEnable the FTP servertrue
    FTP_HOSTBind address0.0.0.0
    FTP_PORTPort21
    FTP_USERNAMEUsernamescanner
    FTP_PASSWORDPasswordscanner123
    FTP_UPLOAD_DIRLocal directory for received files./storage/inbox
    FTP_PASSIVE_PORTSPassive data port range21000-21010
    FTP_PASSIVE_HOSTAddress 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

    VariableDescriptionDefault
    SFTP_ENABLEDEnable the SFTP serverfalse
    SFTP_HOSTBind address0.0.0.0
    SFTP_PORTPort2222
    SFTP_USERNAMEUsername (password auth only, no shell)scanner
    SFTP_PASSWORDPasswordscanner123
    SFTP_UPLOAD_DIRDirectory clients see as /./storage/inbox
    SFTP_HOST_KEYEd25519 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

    VariableDescriptionDefault
    SCP_ENABLEDEnable the SCP serverfalse
    SCP_HOSTBind address0.0.0.0
    SCP_PORTPort2223
    SCP_USERNAMEUsername (password auth only, no shell)scanner
    SCP_PASSWORDPasswordscanner123
    SCP_UPLOAD_DIRDirectory clients see as /./storage/inbox
    SCP_HOST_KEYEd25519 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)

    VariableDescriptionDefault
    SAMBA_SERVER_ENABLEDEnable the Samba pollerfalse
    SAMBA_SERVER_HOSTSMB server hostname or IPlocalhost
    SAMBA_SERVER_PORTSMB port445
    SAMBA_SERVER_USERNAMESMB username
    SAMBA_SERVER_PASSWORDSMB password
    SAMBA_SERVER_SHAREShare namescans
    SAMBA_SERVER_PATHPath within the share to watch/scans
    SAMBA_SERVER_POLL_INTERVAL_SECPolling interval in seconds10
    SAMBA_SERVER_DELETE_AFTER_READDelete the file from the share after downloading ittrue
    SAMBA_SERVER_WORK_DIRLocal staging directory(FTP_UPLOAD_DIR)

    Ingress: folder watcher

    VariableDescriptionDefault
    FOLDER_ENABLEDEnable the folder watcherfalse
    FOLDER_INPUT_DIRDirectory to watch for PDFs./input
    FOLDER_OUTPUT_DIRWhere renamed files go; leave unset to rename in place
    FOLDER_POLL_INTERVAL_SECPolling interval in seconds5

    Egress: WebDAV

    VariableDescriptionDefault
    WEBDAV_ENABLEDEnable WebDAV uploadfalse
    WEBDAV_URLServer base URL
    WEBDAV_USERNAMEUsername
    WEBDAV_PASSWORDPassword
    WEBDAV_UPLOAD_PATHRemote folder for uploads/Documents/Scanned
    WEBDAV_INSECURESkip TLS certificate verificationfalse

    Egress: Samba (uploads to an SMB share)

    VariableDescriptionDefault
    SAMBA_ENABLEDEnable Samba uploadfalse
    SAMBA_HOSTSMB server hostname or IPlocalhost
    SAMBA_PORTSMB port445
    SAMBA_USERNAMESMB username
    SAMBA_PASSWORDSMB password
    SAMBA_SHAREDestination sharescans
    SAMBA_PATHPath within the sharescans

    Naming (Ollama)

    VariableDescriptionDefault
    OLLAMA_HOSTOllama URLhttp://localhost:11434
    OLLAMA_MODELVision modelqwen3-vl:8b
    OLLAMA_NUM_CTXContext window (tokens) requested per call8192
    OLLAMA_TIMEOUTSeconds to wait before falling back to a timestamped name120
    LANGUAGELanguage of the generated filenamesenglish

    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:

    Questions and bug reports are welcome as GitHub issues.