Skip to main content

watchftp

Async, polling-based watcher for FTP/SFTP endpoints with optional Redis-backed persistence and change streams.

Overview

watchftp scans remote trees using protocol-native metadata (MLSD for FTP, AsyncSSH for SFTP), fingerprints directories, and emits batched change events (create/modify/delete/heuristic move). The engine is fully async, tuned for large trees (250k+ files), and can persist its state plus event stream in Redis for multi-consumer durability.

Key capabilities:

  • Async core: aioftp + AsyncSSH connectors with bounded concurrency.
  • Pattern-aware traversal: glob/regex includes/excludes compiled via wcmatch fallback.
  • Directory fingerprints: Merkle-style digests to skip cold subtrees and detect changes efficiently.
  • Redis integration: Optional persistence and Streams; tenants are fully namespaced (wf:{tenant}:{server}:...). Defaults to redis://127.0.0.1:6379/0 when Redis streaming is enabled without a URL so local testing “just works”.
  • Config via Pydantic: Load settings from environment variables using pydantic-settings, including scale presets, watch-root limits (with -1 meaning unlimited), and security toggles.

Getting Started

Requirements

  • Python 3.12+
  • FTP/SFTP endpoints to monitor
  • Optional Redis (local development uses redis://127.0.0.1:6379/0)

Install with uv (recommended) or pip:

uv pip install -e .
# or
pip install -e .

Quick start

import asyncio
from watchftp import WatchConfig, WatchPath, Watcher, tune_for_scale

cfg = WatchConfig(
    tenant_id="demo-tenant",
    protocol="sftp",
    host="sftp.internal",
    username="svc",
    private_key="~/.ssh/id_ed25519",
    watch_paths=[WatchPath(root="/incoming", include=["**/*.csv"], exclude=["**/tmp/**"])]
)
tune_for_scale(cfg, "medium")

async def main():
    async with Watcher(cfg) as watcher:
        async for batch in watcher.watch():
            for event in batch:
                print(event.type, event.path)

asyncio.run(main())

Enable Redis streams and persistence by setting cfg.event_bus = "redis_stream" (or WATCHFTP_EVENT_BUS=redis_stream). If no redis_url is provided, the watcher will connect to redis://127.0.0.1:6379/0 automatically for local development.

Documentation

We publish docs with MkDocs Material. To preview locally:

uv pip install mkdocs mkdocs-material
mkdocs serve

Changes are automatically built and deployed to GitHub Pages whenever pyproject.toml's version is bumped on main (see .github/workflows/docs.yml).

Configuration

The main knobs live on WatchConfig (Pydantic model):

  • tenant_id: required for namespacing Redis state.
  • watch_paths: list of WatchPath roots with include/exclude patterns (glob or regex).
  • max_watch_roots: cap per process (-1 for unlimited).
  • Concurrency knobs (poll_interval, max_concurrency, per-protocol limits).
  • Security toggles (ftps_verify_cert, sftp_known_hosts, allowed ciphers).
  • Persistence settings (redis_url, redis_ttl_seconds, event_bus).

Environment variables are loaded via WatcherSettings with the prefix WATCHFTP_ (e.g., WATCHFTP_TENANT, WATCHFTP_REDIS_URL). Nested values use __ (double underscore) separators.

  • configure_logging(level="INFO", structured=True): optional helper to emit JSON logs via logging. Call once in your app (from watchftp import configure_logging).
  • ensure_metrics_server(port=9464): starts the Prometheus HTTP endpoint (uses prometheus-client).

Example:

from watchftp import configure_logging, ensure_metrics_server

configure_logging("INFO")
ensure_metrics_server()

Project Roadmap

Testing

  • Unit tests (Redis + helpers): python -m unittest

  • Integration tests (FTP/SFTP against pyftpdlib + AsyncSSH) require Python 3.12+ and can be run with pytest -m integration. They start ephemeral servers locally.

  • M0 (current): Config scaffolding, scheduler skeleton, Redis index + stream sink, unit tests.

  • M1: Real FTP/SFTP traversal, diff engine polish, adaptive scheduler knobs.

  • M2: Advanced Redis-backed recovery, rename heuristics, metrics exposure, docs/examples.

See docs/project.md for the full PRD, architecture, and sizing matrix.

Metadata

Release files for watchftp 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for watchftp 0.0.1
File Size Uploaded Provenance Actions
watchftp-0.0.1.tar.gz 19.3 kB Without provenance Details for watchftp-0.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for watchftp 0.0.1
File Interpreter ABI Platform Actions
watchftp-0.0.1-py3-none-any.whl Python 3 none any Details for watchftp-0.0.1-py3-none-any.whl

Total release size: 40.9 kB

Release files / watchftp-0.0.1.tar.gz

Download URL watchftp-0.0.1.tar.gz
Size 19.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a64d5bc683431ded92f2baef6ff66823152c5e17a5a4f1855d34867e8f2f3eeb
BLAKE2b-256 checksum
How to use checksums
6db45d83f0044e5c1e4c635c8dff13bfb7bb2ca408915206c5946e06572c1d97
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.8

Release files / watchftp-0.0.1-py3-none-any.whl

Download URL watchftp-0.0.1-py3-none-any.whl
Size 21.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bcdf64499538a58f44e4c545dc7288ab040ab1457fdee3a82ea8665a11a34ea2
BLAKE2b-256 checksum
How to use checksums
346ef941846eaab597218b9dc910a66f952ee3e6d57ec39a471dc9fa6e358855
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.8

Security for watchftp 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page