Changelog¶
Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
O formato é baseado em Keep a Changelog, e este projeto adere ao Versionamento Semântico.
Sobre a numeração: a
1.0.0no fim deste arquivo é a API REST anterior, que nunca chegou a ser marcada com uma tag. A0.5.0é a primeira versão publicada deste repositório. O emissor em linha de comando começou do zero em0.xporque a primeira emissão real contra a Sefin ainda não foi confirmada — a1.0.0fica reservada para quando for.
Não lançado¶
Adicionado¶
- Especificação OpenAPI oficial da API de Parâmetros Municipais do ADN em
docs/api/adn-parametrizacao-swagger.json(#5). docs/convenio-municipal.mddocumenta o que o swagger não diz, conferido no serviço real: o código completo do serviço (01.07.01.000), a competência emAAAA-MM-DD, a alíquota em percentual e os dois formatos de erro (#5).- Site de documentação no GitHub Pages, com a fonte em
site/, publicado pelo workflowpages.yml(ADR 0016). - Página com os 426 códigos de rejeição que a Sefin pode devolver na recepção de uma DPS, gerada do ANEXO I oficial a cada publicação, com a mensagem, a regra, o campo do XML e o nível de cada um (ADR 0016).
- Quando a Sefin rejeita uma DPS, cada código vem acompanhado do que o ANEXO I diz sobre ele: o campo da DPS a corrigir, a regra que falhou, um aviso quando a regra depende do município e o link para o código no site (ADR 0019).
SECURITY.mdcom a política de segurança: como relatar uma vulnerabilidade em privado, prazos de resposta e de divulgação, o modelo de ameaças, o que o código faz contra cada risco, as regras para quem contribui e o que ainda falta, cada lacuna com a sua issue (#43 a #48).
Removido¶
- O catálogo de 38 códigos de rejeição em
internal/domain/emission/errors.go(E001,E042,E200...). Nenhum deles existe: os códigos da Sefin têm quatro dígitos e estão no ANEXO I. Era código morto da época da API REST, sem chamadas nem testes (#37).
Corrigido¶
docs/convenio-municipal.mdensinava os caminhos do manual em PDF (/parametros_municipais/...), que não existem na API. Agora segue o swagger (#5).- O README ilustrava uma rejeição com códigos que não existem (
E001,E042). Agora mostra oE0714real da primeira emissão (ADR 0016).
Segurança¶
- A resposta da Sefin não escolhe mais onde a NFS-e é gravada. O nome do arquivo vinha da
chaveAcessodevolvida, sem validação: uma resposta adulterada ou com defeito, com../na chave, gravava a nota fora do diretório de saída. Agora só uma chave de acesso válida (50 dígitos) ou o Id da DPS dão nome a um arquivo; noconsultar, o nome vem da chave pedida. Encontrado pelogosec(#47). - Os XMLs, os PDFs, o
nfse.yamle o arquivo de numeração passam a ser gravados com permissão0600, e os diretórios criados pelonfse, com0700. Antes eram legíveis por todos os usuários da máquina, e os documentos trazem CPF, nome e endereço de tomadores. Um arquivo sobrescrito também é restringido; um diretório que já existe não é alterado (#46). - O
go.modpede o Go1.26.8(toolchain): a CI e quem instala comgo installcompilam com as correções de segurança mais recentes da biblioteca padrão. - A CI ganha o
govulncheck(vulnerabilidades conhecidas no Go e nas dependências, também toda semana), ogosece o CodeQL (análise estática) e o OpenSSF Scorecard (#43, #47). - O Dependabot passa a atualizar os módulos Go, as ações do GitHub e o construtor do site (#44).
- Binários prontos para Linux, macOS e Windows, publicados a cada versão pela CI com
SHA256SUMS, SBOM e proveniência SLSA assinada pelo Sigstore, conferível comgh attestation verify(#48, ADR 0020). - Todas as ações do GitHub ficam fixadas pelo hash do commit, e os checkouts não deixam a credencial do Git gravada (#45).
0.9.0 - 2026-09-30¶
O onboard completa o nfse.yaml no terminal e aceita o município pelo nome, o CNPJ do certificado passa a ser lido de onde a Sefin o lê, e a DPS passa a ser validada contra os XSDs oficiais.
Ver ADR 0013, ADR 0014 e ADR 0015.
Adicionado¶
- No terminal, o
nfse onboardpergunta só o que o certificado e o cadastro público não responderam (#14). - O regime tributário é explicado e nunca sugerido; se o cadastro diz que a empresa está fora do Simples, ele nem é perguntado (#14).
--nao-interativomantém oonboardsem perguntas, que continua sendo o padrão quando não há terminal (#14).- O
onboardpreenchepadroes.servico.descricao(#14). nfse onboard --municipioaceita o código IBGE, conferido sem rede, ou "Cidade/UF", resolvido pela lista do IBGE e guardado em cache (#11).- Opções
--fonte-municipiose--cache-municipiosnoonboard(#11). pkg/codmunconfere um código de município pelo prefixo da UF e pelo dígito verificador, sem embutir a tabela (#12).- Testes de contrato contra a BrasilAPI e o IBGE (
NFSE_TESTE_CONTRATO=1), rodados por um workflow semanal (#12). - O
emitiravisa quando a competência é de 2027 em diante, quando o grupo IBS/CBS passa a ser obrigatório no Simples (#21).
Modificado¶
- O CNPJ do certificado passa a ser lido do
subjectAltName(OID 2.16.76.1.3.3), onde a Sefin o lê; o common name vira reserva (#13). - Um certificado com CNPJs divergentes ou ilegíveis na extensão é recusado como ambíguo, sem escolher um lado (#13).
- A DPS passa a ser validada contra os XSDs oficiais do pacote v1.01, por um validador em Go puro, com todos os problemas de uma vez (#4).
Removido¶
- O validador estrutural escrito à mão (
validation.StructuralValidator), substituído pela validação pelo XSD (#4). - Os montadores de endereço sem uso de
pkg/xmlbuilder(BuildAddressXML,BuildNationalAddressXML,BuildForeignAddressXML,AddressFromDomain) (#4).
Corrigido¶
--deducoesescreviavDRepDRjuntos, e a Sefin recusaria a DPS (#4).- O endereço do tomador em
pkg/xmlbuildersaía fora do leiaute doTCEndereco(#4). - O
onboardgravava o código de município do cadastro sem conferir o dígito verificador e a UF (#12). - O
config checke oemitirconferiam só o tamanho deprestador.municipioeservico.municipio_prestacao, e não o dígito verificador (#11). - O cache de municípios, também usado pelo
nfse danfse, aceitava entradas adulteradas sem conferir código e UF (#11). - A conferência do
emitire doenviarcomparava o CNPJ do common name, e não o da extensão, que é o que a Sefin compara (#13). - O
nfse.yamlgerado peloonboardpodia sair ilegível com um caractere que o YAML não aceita, vindo do cadastro ou do certificado (#14).
0.8.0 - 2026-09-29¶
O DANFSe volta, agora gerado aqui: a NT 008 suspendeu a API do governo em 03/08/2026 e passou a geração para quem emite. Ver ADR 0011 e ADR 0012.
Adicionado¶
nfse danfse <arquivo-da-nfse.xml>desenha o PDF do DANFSe a partir do XML autorizado, conforme a NT 008 v1.02, em uma página A4 (#22).- QR Code da consulta pública desenhado como vetor (#22).
- Tarja "NFS-e SEM VALIDADE JURÍDICA" quando
tpAmb = 2, como a NT exige (#22). - Informações complementares na ordem da NT, sempre com os totais aproximados de tributos da Lei 12.741/2012 (#22).
- Canhoto de recebimento, que
--sem-canhotoomite (#22). - Marcas d'água
CANCELADAeSUBSTITUÍDA, por--canceladae--substituida(#22). - Blocos de prestador, tomador, destinatário e intermediário, com a frase da NT para quem não foi identificado (#22).
- Dados de quem emitiu vêm do grupo
emitda NFS-e quando a DPS não os traz (#22). - Local da prestação e município de incidência no formato "Município / UF / País" (#22).
- Nome do município por consulta ao IBGE, com cache em disco,
--sem-redee sem nunca impedir o documento (#22) (ADR 0012). - O
danfseconfere a assinatura do governo e avisa quando ela falta ou não confere (#22). - Aviso quando um texto tem caracteres fora da fonte do PDF, em vez de sumirem em silêncio (#22).
- Dependências
go-pdf/fpdfeboombuler/barcode, ambas semcgo(ADR 0011). - Consultas à Sefin tentam de novo, até 3 vezes, quando a conexão nem chega a abrir; cada tentativa é anunciada (ADR 0005).
Modificado¶
- Abrir a conexão com a Sefin tem prazo próprio de 10 s, em vez do padrão do sistema operacional (ADR 0005).
- Uma falha de conexão diz se a requisição chegou ao governo; no
emitir --enviar, mostra onfse enviarque manda a mesma DPS (ADR 0005). - A consulta de existência (
consultar --dps --existe) mandaAccept: application/json, como as outras (ADR 0005).
Corrigido¶
- O
danfsesó recusava um PDF já existente depois de consultar o IBGE e desenhar o documento (#22). - A consulta ao IBGE esperava o prazo de novo a cada bloco quando o serviço estava fora (#22).
- O cache de municípios podia ficar pela metade numa interrupção; agora é gravado de forma atômica (#22).
- A descrição do serviço sumia pela direita da página; agora quebra em linhas dentro do quadro (#22).
- PIS/COFINS retidos com
tpRetPisCofins3 a 9 saíam como débito próprio e fora do total retido (#22). - Quebras de linha e tabulações do XML caíam em campos de uma linha só (#22).
- Um valor maior que o schema permite estourava o inteiro e imprimia um total errado (#22).
- XML maior que 5 MB passa a ser recusado (#22).
0.7.0 - 2026-09-21¶
Fecha a substituição de NFS-e; o nfse danfse pela API do governo foi removido antes do lançamento, quando a NT 008 a suspendeu.
Ver ADR 0010.
Adicionado¶
nfse emitir --substitui <chave> --motivo <nome>emite uma nota que substitui outra;--motivo-textoacrescenta a descrição (ADR 0010).- Motivos de substituição com nomes próprios (
saiu-do-simples,recusada-pelo-tomadoretc.), para não se confundirem com os docancelar(ADR 0010). - Pacote de schemas v1.01 em
docs/schemas/: a 1.01 só acrescenta o grupo opcionalIBSCBS, e a DPS 1.00 continua válida (#21). - NT 008 v1.02 em
docs/notas-tecnicas/, com o leiaute do DANFSe (#22).
Modificado¶
- A lista nacional de serviços embutida passou para a v1.01: 338 códigos, com
141403,141404e200102novos (#10). - A validação do
substconfere o tipo da chave e o motivo pela enumeração do XSD, não só a presença (ADR 0010). - O ANEXO I de regras em
docs/anexos/passou de 322 para 441 códigos de rejeição (#21).
Removido¶
nfse danfsepela API do Ambiente de Dados Nacional, suspensa pela NT 008 antes do lançamento (#22) (ADR 0010).
0.6.0 - 2026-09-21¶
O emissor monta a própria configuração a partir do certificado A1 e do cadastro público de CNPJ, e ajuda a achar o código de serviço (cTribNac).
Ver ADR 0007 e ADR 0009.
Adicionado¶
nfse onboardgrava umnfse.yamlpreenchido com o que o certificado e o cadastro público informam, e lista o que falta (ADR 0007).--cnpj,--sem-redee--fontenoonboard; o arquivo registra no cabeçalho de onde veio cada valor (ADR 0007).- Uma falha na consulta ao cadastro não interrompe o
onboard: o que falta vai para a lista de pendências (ADR 0007). - Lista nacional de serviços (LC 116/2003) embutida no binário, gerada do
ANEXO_B(#10) (ADR 0009). nfse servico buscar <termo>procura o código pela descrição, sem acento e com peso por raridade das palavras (#10).nfse servico ver <codigo>enfse servico listar [item](#10).nfse onboard --servico <codigo>, conferido contra a lista, com a descrição oficial como comentário (#10).- Sugestões de
cTribNaca partir do CNAE do cadastro, gravadas comentadas nonfse.yaml: são palpites, e o campo fica vazio (#10) (ADR 0009).
Modificado¶
- O erro de configuração ausente sugere
nfse onboardantes denfse config init(ADR 0007). nfse config checkdiz o que o código de serviço significa e avisa, sem recusar, quando ele não está na lista embutida (#10).- A lista de pendências do
onboardnão cita o código de serviço quando ele foi informado, e quebra linha no terminal (#10). - O README ficou com o que o emissor faz; o histórico de versões e defeitos está neste arquivo e nas ADRs.
0.5.2 - 2026-09-18¶
A primeira versão que emitiu uma NFS-e de verdade: emitir, enviar, consultar e cancelar, contra a Sefin Nacional em produção restrita. Ver ADR 0008.
Adicionado¶
- Testes de canonicalização ancorados numa implementação externa (libxml2), e não no próprio pacote (ADR 0008).
Modificado¶
nfse consultarsobrescreve a nota já gravada, porque a consulta é idempotente;--sobrescreversai dele.
Corrigido¶
- O
totTribera escolhido pelo percentual configurado, e não pelo regime; a Sefin recusava com E0710 e E0712. - A razão social do prestador ia na DPS quando o próprio prestador emite; a Sefin recusava com E0121.
- O tipo de inscrição federal estava invertido no identificador da DPS; a Sefin recusava com E0004.
- O digest da assinatura era calculado sem a declaração de namespace; toda DPS assinada antes desta versão é inválida (E0714) (ADR 0008).
nfse consultar --dpsimprimia o identificador em branco, porque o serviço real não o devolve como o swagger diz.nfse consultarrecusava consultar a mesma nota duas vezes, com uma mensagem que falava em--numero.
0.5.1 - 2026-09-18¶
emitir e enviar passam a recusar, antes de assinar e antes de abrir a conexão, um certificado que não é do prestador.
Atenção: se a emissão parar em E0120, esvazie prestador.inscricao_municipal, que é opcional.
Adicionado¶
xmlsigner.CertificateInfo.SubjectCNPJeSubjectHolderName, que leem o titularRAZÃO SOCIAL:CNPJdo certificado ICP-Brasil, com dígitos verificadores conferidos.
Modificado¶
- Um
403sem envelope da Sefin cita primeiro a hipótese do certificado, e proxy e firewall em segundo lugar.
Corrigido¶
- Um certificado de outro CNPJ só falhava na Sefin, com um
403que mandava procurar proxy e firewall; oemitirainda gravava uma DPS assinada que nunca seria aceita.
0.5.0 - 2026-09-18¶
Primeira versão publicada do emissor em linha de comando: emitir, enviar, consultar e cancelar, em um binário único, sem cgo e sem serviço externo.
Ver ADR 0001.
Adicionado¶
- CLI
nfse, instalável comgo install github.com/edusouza/nfse-emissor-go/cmd/nfse@latest. nfse versao, com versão, plataforma e versão do Go.nfse cert infoinspeciona um certificado A1 e avisa quando faltam menos de 30 dias para o vencimento.- Senha do certificado por
--senha,NFSE_CERT_SENHAou prompt interativo, nessa ordem. nfse config initgera umnfse.yamlcomentado;nfse config checkconfere se está completo e coerente.nfse emitirmonta, valida e assina a DPS a partir depadroes, de--yamle das flags;--sem-assinargera o XML sem assinatura.nfse emitir --enviartransmite a DPS à Sefin e grava a NFS-e autorizada; rejeições trazem todos os motivos de uma vez (#3).nfse enviar <arquivo.xml>transmite, byte a byte, uma DPS já assinada (#3).nfse consultar <chave-de-acesso>enfse consultar --dps <id>, com--existe(#3).nfse cancelar <chave-de-acesso>, com os motivoserro-emissao,nao-prestadoeoutros.- Emitir e cancelar em produção pedem confirmação no terminal;
--confirmardispensa a pergunta em scripts. - Numeração automática da DPS em
.nfse-estado.json;--numerovirou opcional (#8). nfse numero verenfse numero definir(#8).- Validação da alíquota do ISS antes de assinar, pelas regras E0595, E0600, E0621 e E0625 (#5) (ADR 0006).
--retencaoepadroes.valores.retencao_issqn, que alimentamtpRetISSQN(#5) (ADR 0006).prestador.regime_apuracao, que alimentaregApTribSN.- Validação de CNPJ e CPF na configuração e nos dados da nota.
nfse emitirrecusa sobrescrever um arquivo existente;--sobrescreverpermite a substituição deliberada.- Campos desconhecidos no YAML são recusados.
- O ambiente reportado pelo governo na resposta é exibido.
- Uma negativa por sigilo fiscal (403) é explicada, e um 403 sem corpo cita a hipótese de proxy ou firewall.
- Consultar exige só o ambiente e o certificado, não uma configuração completa de emissão.
SignDocumentassina qualquer documento do sistema nacional, não só a DPS.- Exemplo executável em
exemplos/, em shell e PowerShell, com certificado descartável. docs/convenio-municipal.md, sobre como consultar o convênio de um município (E0635 e E0640).- Especificações OpenAPI oficiais em
docs/api/(#3). - Registro de decisões de arquitetura em
docs/decisoes/. - Teste que confere as chaves de acesso citadas na documentação.
- CI com formatação,
go mod tidy,go vet, build, testes com detector de corrida e cobertura.
Modificado¶
- O projeto deixou de ser uma API REST e passou a ser um CLI (ADR 0001).
- Módulo Go renomeado para
github.com/edusouza/nfse-emissor-go, e o código saiu desrc/para a raiz. - Dependências diretas reduzidas de 12 para 5, e a versão mínima do Go passou a 1.26.
- O validador foi renomeado para
StructuralValidatore perdeu o parâmetroschemaDir, que era ignorado (#4). - Testes dependentes de relógio são pulados com
-short.
Removido¶
- Cliente SOAP da Sefin, que seguia um contrato inventado, e seus testes (#3).
- API REST, worker assíncrono, MongoDB, Redis, webhooks, chave de API, rate limiting, métricas e
docker-compose.yml(ADR 0001). - O
internal/configde variáveis de ambiente de servidor e as entidades de persistência. - O que não era validação da chave de acesso em
internal/domain/query.
Corrigido¶
- O cliente da Sefin falava SOAP; a API é REST/JSON, com
basePath/SefinNacional(#3) (ADR 0005). - A conexão com a Sefin falhava com
tls: no renegotiation, porque o governo pede o certificado numa renegociação (#3). - A assinatura digital nunca verificava: reindentação depois de assinar, namespace perdido e verificação exigindo a chave privada (ADR 0004).
- O XML da DPS tinha sete divergências em relação ao
DPS_v1.00.xsd(ADR 0003). - O validador estrutural procurava
cTribNac,cLocPrestevServPrestem caminhos inexistentes e exigiasubst(#4). - O
verAplicestourava os 20 caracteres deTSVerAplicem toda DPS. - A validação da chave de acesso exigia um prefixo
NFSeinexistente. - O README documentava uma chave de acesso com 47 dígitos.
- Certificados A1 no formato do OpenSSL 3 (AES-256, MAC SHA-256) não eram lidos (ADR 0002).
- A cadeia de certificados intermediários do PFX era descartada.
[1.0.0] - 2026-01-08¶
Versão da API REST, anterior à mudança para CLI, mantida como registro histórico; o código está no histórico do git.
Adicionado¶
- API REST para emissão de NFS-e com processamento assíncrono, chave de API, rate limiting, webhooks e métricas Prometheus.
- Assinatura XMLDSig (RSA-SHA256, exc-c14n) e leitura de certificados A1.
- Montagem do XML da DPS, cálculo de valores e tradução de códigos de rejeição.