Skip to main content

nfse-br

nfse-br is an open-source Python toolkit for Brazil's National NFS-e ecosystem.

The current release line provides local typed primitives, a deliberately small unsigned restricted DPS builder, validation for DPS and already-recovered NFS-e XML, structural extraction, and confirmed local consistency checks. It does not implement a complete fiscal model, XML signatures, issuance, or transmission. Local XSD validation is available through an optional dependency.

Release 0.2.0 contract

The supported public surface for 0.2.0 is deliberately small:

  • CompetenceDate, DomainValidationError, FederalTaxId, FederalTaxIdKind, MunicipalityCode, and NfseEnvironment from nfse_br.domain;
  • opt-in CPF and CNPJ check-digit validation from their documented domain submodules;
  • DpsSeries, DpsNumber, and DpsIdentity from nfse_br.dps;
  • RestrictedDpsDraft and build_unsigned_dps from nfse_br.dps.builder;
  • NfseAccessKey, NfseId, NfseDocumentError, NfseDocumentInfo, extract_nfse_document_info, NfseConsistencyError, and validate_nfse_document_consistency from nfse_br.nfse;
  • RestrictedDpsXsdValidator, RecoveredNfseValidator, and XsdValidationError from nfse_br.xsd, when the xsd extra is installed; and
  • the nfse-br check-unsigned and nfse-br check-nfse commands.

Modules below nfse_br._f0 and nfse_br._xmlsig, together with freeze and schema-contract tooling, are private or experimental implementation details. They may change without being treated as public API.

Version 0.2.0 does not support issuance or transmission, HTTP/SEFIN calls, XMLDSig signing or cryptographic verification, certificate or private-key handling, production-environment operation, a complete fiscal model, or number allocation and persistence. See the changelog for the release summary and known limitations.

Quickstart from a checkout

This path uses the repository checkout directly; it does not assume a PyPI release. It requires Python 3.12 or 3.13 and uv (CI currently uses uv 0.12.13).

git clone https://github.com/cassao29/nfse-br.git
cd nfse-br
uv sync --frozen
uv run --frozen python -c "from pathlib import Path; Path('build/quickstart').mkdir(parents=True, exist_ok=True)"
uv run --frozen python examples/build_unsigned_dps.py --output build/quickstart/dps.xml

The last command prints Unsigned synthetic DPS written. and creates build/quickstart/dps.xml. It refuses to overwrite an existing file. The example uses fixed synthetic data and writes the exact bytes returned by build_unsigned_dps(); generation needs neither lxml, a schema bundle, nor network access after the checkout has been installed.

Schema validation is a separate operation. First, explicitly obtain and audit the pinned official restricted artifacts:

uv run --frozen python scripts/f0_freeze_restricted.py --work-dir .f0/restricted --manifest contracts/restricted/manifest.json

That preparation step accesses gov.br, validates the discovered artifacts against the frozen evidence, and writes the ignored bundle as .f0/restricted/restricted-xsd.zip. It is never called by the example or the validator. With the bundle present, install the optional XSD support and check the generated document:

uv sync --frozen --extra xsd
uv run --frozen --extra xsd nfse-br check-unsigned build/quickstart/dps.xml --bundle .f0/restricted/restricted-xsd.zip

Success emits exactly:

{"status":"ok","stage":"complete","code":null,"transmission_ready":false}

Exit code 0 means XSD validation and the unsigned-DPS structural preflight passed. Exit code 1 means the document was rejected; 2 means usage, dependency, file, or bundle preparation failed. Passing these checks is not fiscal authorization, signature validation, or permission to transmit.

Current scope

The library currently provides immutable primitives for NFS-e environments, lexical CPF/CNPJ identifiers, IBGE municipality codes, competence dates, and a working local DPS identity value. It also provides an exact lexical primitive for NFS-e identifiers and access keys from the frozen restricted XSD contract.

Explicit CPF and CNPJ check-digit validation is available separately from lexical construction. Issuance, transmission, complete DPS XML generation, and XMLDSig remain outside the current scope.

NFS-e identifier

NfseId is the immutable lexical primitive for the 53-position NFSe/infNFSe/@Id value defined by TSIdNFSe. It preserves the input exactly and accepts only the uppercase ASCII format declared by the frozen restricted XSD contract:

from nfse_br.nfse import NfseId

nfse_id = NfseId("NFS" + "0" * 9 + "SYNTHETIC00000" + "0" * 27)
print(nfse_id)

Construction proves only lexical conformance. It does not establish that an identifier exists, is authorized or authentic, and it does not query or transmit anything. NfseId deliberately provides no conversion to NfseAccessKey: the current official evidence confirms their semantic relationship but their frozen XSD lexical languages conflict. See contracts/restricted/NFSE_IDENTITY.md for the evidence and fail-closed decision.

NFS-e document information

extract_nfse_document_info performs safe local structural extraction from NFS-e XML bytes using only the standard library:

from nfse_br.nfse import extract_nfse_document_info

info = extract_nfse_document_info(xml_bytes)
print(info.nfse_id)
print(info.nfse_number)
print(info.embedded_dps_id)

It requires the exact NFS-e root and unique direct paths for infNFSe/@Id, nNFSe, and the embedded DPS/infDPS/@Id. It performs no network access and does not require lxml. Structural extraction does not imply XSD validity; when schema assurance is needed, validate the same bytes first with RecoveredNfseValidator from the optional xsd extra.

The extractor does not derive an access key, verify XML signatures, establish transport origin or fiscal authorization, or transmit anything. See contracts/restricted/NFSE_DOCUMENT_INFO.md for the exact frozen paths and limitations.

NFS-e and embedded DPS consistency

validate_nfse_document_consistency performs three fail-closed local checks confirmed by the frozen restricted evidence: it recomposes the embedded DPS identifier, compares its municipality with the NFS-e identity, and compares the federal registration selected by tpEmit with the NFS-e identity.

from nfse_br.nfse import validate_nfse_document_consistency

validate_nfse_document_consistency(xml_bytes)

The check uses only the standard library and performs no network access. It is not XSD validation, signature verification, fiscal authorization, or proof of transport origin. For stronger local assurance, first validate the same bytes with RecoveredNfseValidator, then extract their document information and run the consistency check. Unconfirmed environment and number relationships are not enforced, and no access key is derived. See contracts/restricted/NFSE_DPS_CONSISTENCY.md for the exact evidence matrix.

NFS-e access key

NfseAccessKey is the immutable lexical primitive for the 50-position TSChaveNFSe value defined by the frozen restricted XSD. It preserves the input exactly and accepts only the uppercase ASCII format declared by that schema:

from nfse_br.nfse import NfseAccessKey

key = NfseAccessKey("000000SYNTHETIC00000000000000000000000000000000000")
print(key)

Construction proves only lexical conformance. It does not check that a key exists, reconstruct or decompose one, establish fiscal authorization or authenticity, query a service, or transmit anything. See contracts/restricted/NFSE_ACCESS_KEY.md for the exact frozen facets and source pins.

Explicit CPF and CNPJ check digits

FederalTaxId.cpf() deliberately remains lexical. Call the separate validator when mathematical CPF check digits are required:

from nfse_br.domain import FederalTaxId
from nfse_br.domain.cpf import validate_cpf_check_digits

identifier = FederalTaxId.cpf("11144477735")
validate_cpf_check_digits(identifier)

The CPF calculation follows the two-stage modulo-11 algorithm described by the UFSC technical teaching material. That reference is technical rather than an official Receita Federal standard. The validator additionally rejects all ten repeated-digit sequences as local policy; it deliberately accepts 12345678909 under the mathematical rule.

FederalTaxId.cnpj() deliberately remains lexical and normalizes ASCII letters to uppercase. Call the separate validator when mathematical CNPJ check digits are required:

from nfse_br.domain import FederalTaxId
from nfse_br.domain.cnpj import validate_cnpj_check_digits

identifier = FederalTaxId.cnpj("12ABC34501DE35")
validate_cnpj_check_digits(identifier)

The implementation follows the Receita Federal CNPJ check-digit manual, pages 3–4: ASCII value minus 48, the documented modulo-11 weights, and two numeric check digits. It also explicitly rejects the all-zero identifier. A correct CPF or CNPJ checksum does not establish registration, cadastral status, ownership, fiscal authorization, or permission to transmit. check-unsigned does not call either validator.

DPS identity

The local working contract composes a 45-position identity from the DPS prefix, a seven-digit municipality code, the derived CPF/CNPJ inscription type, a fourteen-position federal document, a five-position padded series, and a fifteen-position padded DPS number. CPF values are left-padded for composition, and alphanumeric CNPJ values remain strings.

Series validation reflects only the current working lexical evidence; it does not define a software/API series allocation convention. Lexically distinct series values that produce the same padded component map to the same logical DpsIdentity value and therefore compare as equal identities.

The DPS identity contract is backed by frozen official restricted-environment XSD and layout bytes and is suitable for local deterministic work and testing. Transmission remains unavailable: the supported DPS subset is deliberately limited and the current XMLDSig algorithm profile is not confirmed.

Contract evidence

The working identity can be audited against exact official restricted- environment bytes with scripts/f0_freeze_restricted.py. The resulting SHA-256 provenance and audited facets are recorded in contracts/restricted/manifest.json; downloaded artifacts remain under the ignored .f0/ directory and are not redistributed.

The identity manifest covers the DPS identity and series facets. The separate structural contract covers the reviewed builder/validator subset. Neither artifact confirms the current XMLDSig algorithms, fiscal authorization, or transmission; transmission_ready remains false.

The derived restricted DPS structural subset is recorded separately in contracts/restricted/dps-schema-contract.json. It is bound to the identity manifest and official XSD ZIP by SHA-256 and remains repository evidence; the runtime does not load that JSON as configuration.

Local XSD validation

Install the optional xsd extra to validate XML bytes locally against the exact frozen Produção Restrita bundle:

uv sync --frozen --extra xsd

The constructor accepts only the pinned official ZIP bytes and never downloads schemas. validate() accepts at most 1 MiB of UTF-8 XML bytes, requires the exact {http://www.sped.fazenda.gov.br/nfse}DPS root, returns None on success, and otherwise raises a privacy-safe XsdValidationError. Each instance is intended for sequential use.

This proves only safe parsing and conformance to the compiled restricted XSD. It does not verify signatures, certificates, fiscal semantics, authorization, or SEFIN acceptance. See contracts/restricted/DPS_XSD_VALIDATOR.md for the compilation profile and explicit local integration command.

Already-recovered NFS-e XML bytes can be checked separately against the NFS-e entrypoint from the same pinned bundle:

from nfse_br.xsd import RecoveredNfseValidator

validator = RecoveredNfseValidator(bundle_bytes)
validator.validate(nfse_xml_bytes)

This validates XML structure against the pinned restricted XSD bundle. It does not verify XML signatures, fiscal authorization, or transport origin. The library still has no POST-response parser, Base64/GZip recovery, HTTP client, or transmission support. See contracts/restricted/NFSE_RECOVERED_VALIDATOR.md for the exact NFS-e closure and local integration command.

Unsigned restricted DPS builder

nfse_br.dps.builder constructs deterministic unsigned XML for one explicit restricted-profile subset: CNPJ provider, national service location, service code and description, service amount, and caller-supplied minimal tax codes. It derives infDPS@Id from the same municipality, CNPJ, series, and number written to the XML.

Building uses only the standard library and does not implicitly validate, sign, transmit, read files, or access the network. The optional validator is a separate explicit call. Monetary values use exact Decimal input with no silent rounding, and the timestamp must already contain an allowed whole-hour UTC offset. The executable example in the quickstart supplies every field. See contracts/restricted/DPS_UNSIGNED_BUILDER.md for the supported mapping and limits.

XML signature preflight

The private nfse_br._xmlsig package performs a fail-closed structural check of unsigned builder output before any future signature operation. It requires the unique direct infDPS, rejects pre-existing signatures and alternative identifiers, and recomputes the target Id from cLocEmi, provider CNPJ, series, and DPS number. It does not sign, verify cryptography, or replace XSD validation.

The frozen XMLDSig schema defines grammar but leaves algorithm attributes as open URIs. The detailed RSA-SHA1/SHA-1/C14N profile found in official material belongs to the historical v1.00.02 manual and is not treated as confirmation for the current restricted v1.01 bundle. See contracts/restricted/DPS_XMLDSIG_PROFILE.md.

Unsigned DPS check CLI

After following the checkout quickstart, check one local unsigned DPS against the pinned restricted schema and structural preflight:

uv run --frozen --extra xsd nfse-br check-unsigned build/quickstart/dps.xml --bundle .f0/restricted/restricted-xsd.zip

The command reads only explicitly named regular files, classifying the opened descriptor rather than relying on a prior path check. Symbolic links are rejected where the platform provides O_NOFOLLOW; otherwise the opened target must itself be a regular file. XML input is limited to 1 MiB and the bundle read to the pinned profile size. The command writes one JSON object to stdout: exit code 0 means both checks passed, 1 means the document was rejected, and 2 means usage, dependency, file, or bundle preparation failed. For example:

{"status":"ok","stage":"complete","code":null,"transmission_ready":false}

--help, --version, package import, and the unsigned builder remain usable without lxml. A successful result is not fiscal authorization, signature validation, or permission to transmit.

Recovered NFS-e check CLI

Check one already-recovered local NFS-e XML document against the pinned restricted schema, extract its required structural information, and enforce the confirmed NFS-e/embedded-DPS consistency rules:

uv run --frozen --extra xsd \
  nfse-br check-nfse recovered-nfse.xml \
  --bundle .f0/restricted/restricted-xsd.zip

The document must already contain the recovered NFS-e XML; this command does not parse a POST response or perform Base64/GZip recovery. Its pipeline is XSD validation (xsd_* stages), structural extraction (structure), then local consistency validation (consistency). Success emits exactly:

{"status":"ok","stage":"complete","code":null,"transmission_ready":false}

Exit code 0 means all three local checks passed, 1 means the document was rejected, and 2 means a usage, dependency, file, bundle, or validation-engine error occurred. No fiscal identifiers are emitted. A successful check does not verify an XML signature or certificate, fiscal authorization, SEFIN acceptance, transport origin, or an access key. It does not access the network, derive an access key, sign, or transmit anything. Use remains possible only with the explicitly supplied local XML and bundle files.

Development

The project requires Python 3.12 or 3.13 and uses uv for project management.

uv sync --frozen --extra xsd
uv run --frozen --extra xsd ruff check .
uv run --frozen --extra xsd ruff format --check .
uv run --frozen --extra xsd mypy src tests scripts examples
mkdir -p build/coverage
uv run --frozen --extra xsd pytest \
  --cov=src/nfse_br --cov-branch --cov-report=term-missing \
  --cov-report=json:build/coverage/coverage.json --cov-fail-under=80
uv run --frozen --extra xsd python scripts/check_coverage.py \
  build/coverage/coverage.json
uv build
python scripts/smoke_base_wheel.py dist/nfse_br-0.2.0-py3-none-any.whl

The executable coverage gates require at least 80% combined coverage for the library, 90% aggregate branch coverage for src/nfse_br/_f0/, 90% branch coverage for src/nfse_br/_f0/dps_schema_contract.py, 90% aggregate branch coverage for src/nfse_br/xsd/, and 90% branch coverage for src/nfse_br/dps/builder.py, and 90% branch coverage for src/nfse_br/_xmlsig/preflight.py, and 90% branch coverage for src/nfse_br/cli.py, and 90% branch coverage for src/nfse_br/domain/cnpj.py, and 90% branch coverage for src/nfse_br/domain/cpf.py, and 90% branch coverage for each of src/nfse_br/nfse/access_key.py, src/nfse_br/nfse/identifier.py, and src/nfse_br/nfse/document.py, and src/nfse_br/nfse/consistency.py. Gate decisions use exact counters from Coverage.py JSON rather than rounded display percentages.

CI also installs the built base wheel into a fresh virtual environment without dependencies or the xsd extra. The official restricted ZIP is not distributed or fetched in CI; its end-to-end compilation remains the explicit local command documented with the frozen profile.

License

MIT

See CONTRIBUTING.md for development instructions and SECURITY.md for the vulnerability-reporting policy. The maintainer release procedure is in docs/RELEASE.md.

Release files for nfse-br 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 nfse-br 0.2.0
File Size Uploaded
nfse_br-0.2.0.tar.gz 170.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nfse-br 0.2.0
File Interpreter ABI Platform
nfse_br-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 225.1 kB

Release files / nfse_br-0.2.0.tar.gz

Download URL nfse_br-0.2.0.tar.gz
Size 170.4 kB
Tags Source
SHA-256 checksum
How to use checksums
4bef34141fedb61d4dab4ab19140383bc5c118d7d0165b866cc8a964ed538e84
BLAKE2b-256 checksum
How to use checksums
08330de2701c99555576faf5cd6fe957e07418a0f1ef79caaf98fb92fa7fcb9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

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

Download URL nfse_br-0.2.0-py3-none-any.whl
Size 54.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
03f2638a7f6b22f2bdad03f4c1263653ab52442c874d6caa981dd5ff490d6eb0
BLAKE2b-256 checksum
How to use checksums
b6772d99261330bbc4b910e235999fc6f4519ea6e9fe977b059050da62c118ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.0

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