Cipher is a local file encryption and decryption service. It solves the problem of safely creating Fernet keys and processing file encryption or decryption jobs through a small HTTP API.
Cipher is scoped to local file operations and keeps task state in memory while background workers process queued jobs. The service binds to 127.0.0.1 on port 49158, so it is intended for local-only use on the machine where it is running.
Cipher also supports processing very large files without loading them fully into memory. When encrypting or decrypting large files the service streams data in 1 MiB chunks and writes a chunked Fernet format to disk. This chunked layout begins with a small magic header (FRTN1) followed by a sequence of length-prefixed Fernet tokens; the server will still decrypt legacy single-token files produced by older versions.
-
Install the Python dependencies with
pip install -r requirements.txt. -
Review
resources/configuration.jsonto configureport,allowed_roots, andblacklisted_roots. -allowed_roots: list of root paths the API is allowed to operate inside. If this list is non-empty, ONLY these roots are permitted and the blacklist is ignored. -blacklisted_roots: list of root paths that are forbidden whenallowed_rootsis empty. Ifallowed_rootsis empty andblacklisted_rootsis non-empty, any path inside a blacklisted root is forbidden. - Behavior summary: - Ifallowed_rootsis non-empty → only those roots are permitted (blacklist ignored). - Else ifblacklisted_rootsis non-empty → all paths are permitted except any inside a blacklisted root. - Else (both lists empty) → all paths on the system are permitted.Example `resources/configuration.json`: ```json { "port": 49158, "allowed_roots": [], "blacklisted_roots": [] } ``` -
Leave the project structure intact so the service can find
resources/andsrc/.
- Windows: run
scripts\run.bat. - Unix-like systems: run
bash scripts/run.sh. - Manual: run
python src/main.pyfrom the project root.
All endpoints also support OPTIONS; GET endpoints additionally support HEAD.
Creates a new Fernet key file.
- Body (JSON object):
directory_path(string, optional): absolute path to an existing directory where the key file should be created. If omitted, defaults to the repository root. The chosen directory must be permitted by the server policy (allowed_roots/blacklisted_roots).file_name(string, required): file name only (not a path). Must not already exist indirectory_path.
- Returns:
201->{ "status": "created", "file_name": "mykey.key" }400->{ "error": "Request body must be a JSON object." }400->{ "error": "<validation-message>" }500->{ "error": "Failed to create key file" }
Queues one encryption task executed in a background thread.
- Body (JSON object):
key_path(string, required): absolute path to existing key file.file_path(string or array of strings, required unlessfile_pathsused): absolute path(s) of existing file(s) to encrypt.file_paths(array of strings, optional alias): alternative tofile_path.encrypt_file_name(boolean, required): iftrue, encrypts the file name itself and returns the resulting absolute output path in the task result.overwrite_file(boolean, optional, defaultfalse): iftrue, encrypted content is written into the source file before any optional rename.output_file_path(string, optional unless required by rule below): absolute output file path for one input file.output_file_paths(array of strings, optional alias): one absolute output file path per input file.- Requirement rule: when
encrypt_file_nameisfalseandoverwrite_fileisfalse,output_file_pathoroutput_file_pathsis required. - Output path safety: input, key and output paths must be permitted by the server policy defined by
allowed_rootsandblacklisted_rootsinresources/configuration.json. Ifallowed_rootsis non-empty, only paths inside those roots are permitted. Ifallowed_rootsis empty butblacklisted_rootscontains entries, any path inside a blacklisted root is forbidden. If both lists are empty, all paths are permitted. Ifoverwrite_fileisfalse, output paths must not already exist.
- Returns:
202->{ "task_id": "<uuid>", "status": "queued", "operation": "encrypt", "file_count": 2 }400->{ "error": "Request body must be a JSON object." }400->{ "error": "<validation-message>" }500->{ "error": "Could not start the background worker. The server may be under heavy load." }
Queues one decryption task executed in a background thread.
- Body (JSON object):
key_path(string, required): absolute path to existing key file.file_path(string or array of strings, required unlessfile_pathsused): absolute path(s) of existing file(s) to decrypt.file_paths(array of strings, optional alias): alternative tofile_path.decrypt_file_name(boolean, required): iftrue, decrypts the file name itself and returns the resulting absolute output path in the task result.overwrite_file(boolean, optional, defaultfalse): iftrue, decrypted content is written into the source file before any optional rename.output_file_path(string, optional unless required by rule below): absolute output file path for one input file.output_file_paths(array of strings, optional alias): one absolute output file path per input file.- Requirement rule: when
decrypt_file_nameisfalseandoverwrite_fileisfalse,output_file_pathoroutput_file_pathsis required. - Output path safety: input, key and output paths must be permitted by the server policy defined by
allowed_rootsandblacklisted_rootsinresources/configuration.json. Ifallowed_rootsis non-empty, only paths inside those roots are permitted. Ifallowed_rootsis empty butblacklisted_rootscontains entries, any path inside a blacklisted root is forbidden. If both lists are empty, all paths are permitted. Ifoverwrite_fileisfalse, output paths must not already exist. - Output path safety: input, key and output paths must be permitted by the server policy defined by
allowed_rootsandblacklisted_rootsinresources/configuration.json. Ifallowed_rootsis non-empty, only paths inside those roots are permitted. Ifallowed_rootsis empty butblacklisted_rootscontains entries, any path inside a blacklisted root is forbidden. If both lists are empty, all paths are permitted. Ifoverwrite_fileisfalse, output paths must not already exist. - Note: the file referenced by
key_pathmust not be included in thefile_path/file_pathsinput or inoutput_file_path/output_file_paths. The server will reject requests that attempt to process the key file itself. - Note: the file referenced by
key_pathmust not be included in thefile_path/file_pathsinput or inoutput_file_path/output_file_paths. The server will reject requests that attempt to process the key file itself.
- Returns:
202->{ "task_id": "<uuid>", "status": "queued", "operation": "decrypt", "file_count": 1 }400->{ "error": "Request body must be a JSON object." }400->{ "error": "<validation-message>" }500->{ "error": "Could not start the background worker. The server may be under heavy load." }
Returns current task state and final result/error once finished.
- Path parameters:
task_id(string, required): task identifier returned byPOST /api/encryptorPOST /api/decrypt.
- Returns:
200-> (forqueued,in_progress, orcompleted){ "task_id": "<uuid>", "operation": "encrypt", "status": "queued|in_progress|completed", "result": { ... } }500-> (forfailedtasks){ "task_id": "<uuid>", "operation": "encrypt", "status": "failed", "error": "<failure-reason>", "error_detail": "<short exception message>" }{ "task_id": "<uuid>", "operation": "encrypt", "status": "queued|in_progress|completed|failed", "result": { "operation": "encrypt", "file_count": 1, "files": [ { "input_name": "...", "output_name": "..." } ] }, "error": "<failure-reason>" }Notes: - `result` is present only for completed tasks; `error` (and `error_detail`) only for failed tasks. - When `encrypt_file_name` or `decrypt_file_name` is `true`, each file entry uses `input_path` and `output_path` with absolute paths instead of the name-only fields. - When filename transformation is disabled, each file entry returns `input_name` and `output_name`; `output_name` matches the requested output file name or the final renamed file when `overwrite_file` is used. - The `error_detail` field contains a short exception message intended to help debug local failures (for example, permission denied caused by file syncing software). It may contain non-sensitive filesystem info. Troubleshooting permission errors: - On Windows, background sync services (OneDrive) or antivirus can temporarily lock files and cause `Permission denied` when the server attempts to replace or rename files. If you see permission errors in `error_detail`: - Pause OneDrive or move the file to a non-synced folder and retry. - Alternatively, provide an explicit `output_file_path` instead of `overwrite_file`, or run the command against a copy of the file. - The server will retry atomic replaces and falls back to copy-based moves, but transient locks can still cause failures.404->{ "error": "Task not found." }
Service and queue health snapshot.
- Body: none
- Returns:
200->{ "status": "ok", "service": "Cipher", "bind_address": "127.0.0.1", "port": 49158, "task_counts": { "queued": 0, "in_progress": 0, "completed": 0, "failed": 0, "total": 0 }, "task_retention_minutes": 60, "task_cleanup_interval_seconds": 60, "cipher_algorithm": "fernet", "hostname": "...", "primary_ip": "...", "local_ips": ["..."] }