Third-party parser for Paperless-ngx, which adds support for EU electronically signed documents.
| Format | Extensions |
|---|---|
| ASiC-E containers | .edoc, .asice, .bdoc, .adoc |
| CAdES signatures | .p7m |
| PAdES-signed PDFs | .pdf |
Paperless-ngx does not consume these files by default. The parser is required for them.
Why Paperless-ngx cannot handle these files
ASiC-E containers are ZIP archives. libmagic reports them as application/zip, and Paperless-ngx rejects the MIME type. CAdES files are reported as application/octet-stream and are also rejected. PAdES-signed PDFs are consumed, but the built-in parser does not expose their signature metadata.
Signature formats in detail
Used inside ETSI ASiC-E containers (.edoc, .asice, .bdoc, .adoc). The parser extracts the signing time, signer certificate, certificate chain, RFC 3161 timestamp, and OCSP values. It verifies the document digest, the SignedProperties digest, and the signature value offline.
CMS SignedData (ETSI EN 319 122) in .p7m files. The embedded PDF becomes the rendition. The CMS signing time, signer, and verification results are exposed as metadata. Detached .p7s signatures are detected but rejected during parsing with a clear error, because they carry no document.
PDFs signed with the ETSI.CAdES.detached or adbe.pkcs7.detached subfilter. The signed PDF is the rendition. The document date prefers the CMS signing time and falls back to the signature's /M field. The covered byte range, signer, and verification results are exposed as metadata.
Both DER and BER (indefinite-length) CMS encodings are accepted. Some signers, such as the adbe.pkcs7.detached flavour, emit BER. Verification is offline only. It proves that the document digest and the signature value are consistent with the signer certificate. It does not validate trust chains, revocation status, or timestamps.
-
Stores the original file unchanged. This is required for legal compliance.
How to download the original file
The default download option returns the parsed PDF. The parsed PDF is not a signed document. To download the original file (
.edoc,.asice,.p7m, and so on), use the "Download original" option instead.PAdES-signed PDFs are not affected. For these files, the original file and the parsed rendition are the same document.
Web UI:
- Open the document detail view.
- Click the arrow next to "Download".
- Select "Download original".
Mobile apps:
- Use the share or download action.
- Select the original document option.
The original document option is available in mobile apps such as Swift Paperless for iOS.
-
Extracts the signed PDF as the display and archive rendition. Browsers cannot render ZIP containers. A PAdES PDF is already a rendition and keeps its signature.
-
Extracts the text of the PDF for search.
-
Sets the document date from the signature signing time, with fallbacks to the PAdES
/Mfield and the PDF creation date. -
Displays signature metadata in the metadata tab: signer name, organisation, country, signing time, certificate chain and issuer, RFC 3161 timestamp authority, and OCSP presence.
-
Verifies the document digest and the signature value offline.
-
Handles nested containers (an EDOC inside an EDOC, as produced by the Latvian e-archive) and multi-document containers (multiple PDFs and office documents merged into a single rendition).
-
Assigns the signer as the document's correspondent. This happens after consumption, only when no correspondent was determined by content matching or workflow rules. The signer (organisation preferred over common name) is looked up case-insensitively and created if it does not exist, and the document is re-indexed so the correspondent is searchable immediately. This is enabled by default. Disable it with
PAPERLESS_ESIG_ASSIGN_SIGNER_AS_CORRESPONDENT=false. Known limitations: the assignment is not recorded in the audit log, and the UI may show the new correspondent as "Private" until the page is reloaded.
The parser runs inside Paperless-ngx. Use one of two methods.
-
Clone this repository and build the image:
docker build -t paperless-ngx-esig . -
In
docker-compose.yml, replace thewebserverimage with your build:services: webserver: image: paperless-ngx-esig # ...everything else stays the same
-
Restart the stack:
docker compose up -d
Install the package into the same virtual environment that runs Paperless-ngx:
uv pip install paperless-esig-
Start Paperless-ngx and check the logs. Look for a line like this:
[paperless.parsers.registry] [third-party] Paperless-ngx ESig Parser v0.3.0 — https://github.com/Exerra/paperless-esigIf the logs say
No third-party parsers discovered.instead, see Troubleshooting. -
Upload an
.edoc,.asice,.bdoc,.adoc, or.p7mfile, or a PAdES-signed PDF. The document is consumed, displays the inner PDF, and shows signature metadata in the metadata tab.
- Documents are stored with the MIME type that libmagic reports. ASiC-E containers are stored as
application/zipand CAdES files asapplication/octet-stream. The original file extension (.edoc,.asice,.p7m, and so on) is preserved in the stored filename. - Plain ZIP files pass API and mail upload validation but are rejected during consumption with an "Unsupported mime type" error. The parser cannot inspect a file at validation time. ZIP files placed in the consume directory are attempted instead of silently skipped. The same applies to
application/octet-streamfiles: CAdES signatures are the only octet-stream files that are consumed. - Detached
.p7ssignatures are detected but rejected during parsing. They carry no document, so there is nothing to display or search. - Office documents (DOCX, ODT, and similar) inside a container are converted to PDF by Gotenberg, and their text is extracted by Tika, when those services are configured with
PAPERLESS_TIKA_ENDPOINT. Without them, the DOCX text is still extracted locally and the affected pages are omitted from the rendition.
- Paperless-ngx 2.x. The parser uses the
paperless_ngx.parsersentrypoint registry. - The container must contain a PDF. Containers without a PDF cannot be ingested.
No third-party parsers discovered.in the logs. The package is not installed in the environment that Paperless-ngx runs in. For Docker, check that your compose file points at the image you built (docker images).uv: command not found. Install uv first. Alternatively,python3 -m pip install .works.- Plain ZIP files are rejected with "Unsupported mime type". This is expected. See Limitations.
GPL-3.0-or-later. Derived from the Paperless-ngx project, which is GPL-3.0.
Building the Docker image
The build defaults to the latest release from PyPI:
docker build -t paperless-ngx-esig .Build a specific release:
docker build --build-arg ESIG_VERSION=0.3.0 -t paperless-ngx-esig .Build from your local checkout, for development or unreleased changes:
docker build --build-arg ESIG_SOURCE=local -t paperless-ngx-esig .Build against a specific Paperless-ngx base version:
docker build --build-arg PAPERLESS_VERSION=2.14.7 -t paperless-ngx-esig .make docker and make docker-local are shortcuts for the first and third commands.
Development
- uv
- Python 3.11 or newer (uv installs it for you)
- Docker, only for the Docker image targets
- A checkout of Paperless-ngx, for the test suite
git clone https://github.com/Exerra/paperless-esig.git
cd paperless-esig
uv sync
git clone --depth 1 https://github.com/paperless-ngx/paperless-ngx.git ../paperless-ngxuv sync creates a virtual environment and installs the package plus dev dependencies (django, pytest, ruff, and others). The tests import Paperless-ngx's own code, so the checkout must be on PYTHONPATH.
PYTHONPATH=../paperless-ngx/src uv run pytestmake test does the same. It uses PAPERLESS_NGX_SRC, which defaults to ../paperless-ngx/src relative to this repo. Override it with make test PAPERLESS_NGX_SRC=/path/to/paperless-ngx/src.
The tests build synthetic signed containers. They do not contain real personal data. They need only the Paperless-ngx source importable. No database or running instance is required.
If the tests fail with ModuleNotFoundError: No module named 'documents', the Paperless-ngx checkout is missing or not on PYTHONPATH.
uv run ruff check src testsuv buildArtifacts land in dist/ as paperless_esig-<version>-py3-none-any.whl and paperless_esig-<version>.tar.gz.
| Command | What it does |
|---|---|
make venv |
Create venv and install dev dependencies (uv sync) |
make test |
Run the test suite |
make lint |
Run ruff |
make build |
Build wheel and sdist into dist/ |
make docker |
Build the image from the latest PyPI release |
make docker-local |
Build the image from your local checkout |
make clean |
Remove build artifacts and caches |