Skip to main content

rclone-crypt-python

Pure-Python reader/writer for rclone crypt files and names.

The PyPI distribution is rclone-crypt-python; Python imports remain rclone_crypt.

Background

This tool was written with three goals in mind:

  1. Proof of concept: I wanted to demonstrate that the rclone crypt format is sufficiently documented to be re-implemented, originally without the help of AI but later with it. And I wanted to demonstrate that this can be a standard used intraoperatively.
  2. Real utility: I wanted to work with rclone crypt without having to subprocess rclone each time.
  3. AI Coding Assistant Playground: When I first made this (Jan 2026, posted much later), I was new to AI coding assistants and it was a fun way to learn more.

Features

  • Streamed read/write of rclone crypt files (very large files supported)
  • Filename encryption/decryption (base32, base64, base32768)
  • Decrypt encrypted rclone config and reveal obscured passwords
  • Explicit, composable APIs for secrets, names, and file IO

Start here

Common entry points (all available from top-level rclone_crypt):

  • RcloneConfig: load config and derive secrets
  • RcloneCrypt: high-level path-like interface bound to a local crypt root
  • CryptSecrets: explicit passwords (plain or obscured)
  • NameCipher: encrypt/decrypt names and paths
  • CryptFile: stream file read/write
  • crypt_ls: list entries with decrypted names

Quick distinction: RcloneCrypt always works in decrypted names (it handles name encryption for you), while CryptFile expects the encrypted on-disk path and only handles file content encryption/decryption.

Typical flow (primary interface):

from rclone_crypt import RcloneConfig, RcloneCrypt

cfg = RcloneConfig.from_path("~/.config/rclone/rclone.conf", allow_prompt=False)
crypt = RcloneCrypt.from_config(cfg, "crypt")
path = crypt / "dir" / "file.txt"

For more examples using the high-level interface, see the high-level guide.

If you need lower-level control (config parsing, name encryption without a root, or streaming file IO), the guide also includes a mapping of high-level and low-level APIs. For advanced workflows, see advanced usage.

Non-goals

  • Cloud backend implementations
  • Calling rclone at runtime (tests only)

Why use this tool

  • You want to inspect or manipulate rclone crypt data directly from Python.
  • You need streaming read/write support without shelling out to rclone.
  • You want explicit, testable crypt operations inside your own tooling.

Requirements

  • Python 3.10+
  • Dependencies: cryptography, pynacl

Install

Install the latest published release from PyPI:

python -m pip install rclone-crypt-python

From a local clone:

python -m pip install -e '.[test]'

For runtime-only installs, drop the [test] extra:

python -m pip install -e .

The PyPI distribution is named rclone-crypt-python; import it as rclone_crypt.

Quickstart

High-level interface

from rclone_crypt import RcloneConfig, RcloneCrypt

cfg = RcloneConfig.from_path("~/.config/rclone/rclone.conf", allow_prompt=False)
crypt = RcloneCrypt.from_config(cfg, "crypt")

path = crypt / "dir" / "file.txt"
path.write_text("hello", overwrite=False)
print(path.read_text())
print(crypt.ls())

Secrets

from rclone_crypt import CryptSecrets

secrets = CryptSecrets.from_passwords("password", "password2")

Or from obscured config values:

from rclone_crypt import CryptSecrets

secrets = CryptSecrets.from_obscured(
    "vENjtZL-E-6OQ77fGY6H4WwF57s",
    "DJrXvTm8658avycmzDjpASEMuiI",
)

Obscure / reveal

from rclone_crypt import obscure, reveal

obscured = obscure("password")
plaintext = reveal(obscured)

Decrypt rclone config

from rclone_crypt import RcloneConfig

cfg = RcloneConfig.from_path(
    "~/.config/rclone/rclone.conf",
    password=None,
    allow_prompt=True,
)
# Convenience aliases:
cfg = RcloneConfig("~/.config/rclone/rclone.conf", allow_prompt=True)
cfg = RcloneConfig.load("~/.config/rclone/rclone.conf", allow_prompt=True)
section = cfg.section("crypt")

To encrypt config content:

from rclone_crypt import encrypt_config

data = b"[remote]\ntype = crypt\n"
encrypted = encrypt_config(data, "config-password")

Password sourcing order:

  1. Explicit password argument
  2. password_command argument or RCLONE_PASSWORD_COMMAND
  3. RCLONE_CONFIG_PASS
  4. Prompt (if allow_prompt=True)

By default, config passwords are treated as obscured (rclone's default):

secrets = cfg.get_crypt_secrets("crypt", passwords_are_obscured=True)
secrets = cfg.get_crypt_secrets("crypt")  # defaults to obscured

Build a name cipher directly from the config section:

name_cipher = cfg.get_name_cipher("crypt", passwords_are_obscured=True)

Or get both secrets and name cipher together:

bundle = cfg.get_crypt_remote("crypt", passwords_are_obscured=True)
secrets = bundle.secrets
name_cipher = bundle.name_cipher

Password source examples:

export RCLONE_CONFIG_PASS="my-config-password"
export RCLONE_PASSWORD_COMMAND="pass show rclone/config"
cfg = RcloneConfig.from_path("~/.config/rclone/rclone.conf", password="my-config-password")
cfg = RcloneConfig.from_path("~/.config/rclone/rclone.conf", allow_prompt=True)

Filename encryption

from rclone_crypt import CryptSecrets, NameCipher

secrets = CryptSecrets.from_passwords("password", "password2")
name_cipher = NameCipher(
    secrets,
    filename_encryption="standard",
    directory_name_encryption=True,
    filename_encoding="base32",
)

encrypted = name_cipher.encrypt_path("dir/file.txt")
decrypted = name_cipher.decrypt_path(encrypted)

File encryption/decryption

from rclone_crypt import CryptFile, CryptSecrets

secrets = CryptSecrets.from_passwords("password", "password2")

with CryptFile.open_write("encrypted.bin", secrets=secrets) as writer:
    writer.write(b"hello")

with CryptFile.open_read("encrypted.bin", secrets=secrets) as reader:
    data = reader.read()

You can also use standard file-like modes:

with CryptFile.open("encrypted.bin", "rt", secrets=secrets) as reader:
    text = reader.read()

Decrypted listing helper

from rclone_crypt import crypt_ls

for entry in crypt_ls("encrypted_dir", name_cipher):
    print(entry.name, entry.path)

Tutorial

See the tutorial for step-by-step user stories, including writing new files with correct name encoding, reading by plaintext name, and listing directories.

Additional docs

Testing

Unit tests:

scripts/test_unit.sh

Interop tests (requires rclone in PATH):

scripts/test_interop.sh

Security notes

  • rclone "obscure" is not strong encryption; it only deters casual viewing.
  • Config encryption uses NaCl secretbox with a SHA-256 derived key.

Troubleshooting and common pitfalls

  • Config password confusion: rclone config encryption password is different from remote passwords. Make sure you use the config password for RcloneConfig.from_path.
  • Hidden password command: if RCLONE_PASSWORD_COMMAND is set in your shell, it will be used unless you provide an explicit password.
  • Name options mismatch: filename_encryption, directory_name_encryption, filename_encoding, and suffix must match your rclone remote settings.
  • Append mode not supported: encrypted files cannot be appended without rewriting the last block. Use write mode and rewrite the file if needed.
  • Logging: the library emits extensive debug logging in core modules (names, files, config, and high-level paths). By default nothing is shown unless you configure logging (e.g., logging.basicConfig(level=logging.DEBUG)).

References

## AI/LLM Disclosure

As noted in the motivation, a substantial portion of this code was produced with the assistance of large language models (LLMs), primarily various versions of ChatGPT 5+ used through Codex. For all intents and purposes, it was “vibe coded.” This disclosure is intentional: I am not attempting to present the implementation as primarily human-written or to obscure the extent of LLM involvement.

The resulting code has nevertheless been exercised through both LLM-generated test cases and real-world use in complex settings. While LLMs were heavily involved in its implementation, the software has been evaluated based on its observed behavior rather than assumed correctness.

Release files for rclone-crypt-python 0.2.0

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

Source distribution (sdist)

Source distribution for rclone-crypt-python 0.2.0
File Size Uploaded
rclone_crypt_python-0.2.0.tar.gz 29.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rclone-crypt-python 0.2.0
File Interpreter ABI Platform
rclone_crypt_python-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 58.0 kB

Release files / rclone_crypt_python-0.2.0.tar.gz

Download URL rclone_crypt_python-0.2.0.tar.gz
Size 29.2 kB
Tags Source
SHA-256 checksum
How to use checksums
bd9fb9c0984f6a42411525180c5dd81d2bed8d57b181df9a95526e6e00b7a7e2
BLAKE2b-256 checksum
How to use checksums
929505464b6a9510940c8131dc050300ebb3acf86531e367e11d12ad422e3d95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / rclone_crypt_python-0.2.0-py3-none-any.whl

Download URL rclone_crypt_python-0.2.0-py3-none-any.whl
Size 28.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cb3ff67bb3fe883d7e70dfa305371ccee8c73cde53c27a3afdd627ab405900d4
BLAKE2b-256 checksum
How to use checksums
8e6d3e3d9216b08561bceba15d63691b077ef81c22e72fb9291bc387b270885d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

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