npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mastersigner/sdk

v1.1.12

Published

Master Signer SDK — Integração de assinatura digital ICP-Brasil para sistemas web

Readme

Master Signer SDK

Assinatura digital ICP-Brasil (A1 e A3) no seu sistema — na web e dentro do seu app.

  • 🔐 A1 (.pfx/.p12) e A3 (token USB / smartcard, na web)
  • 📄 CAdES destacado (.p7s) e PAdES B-B (assinatura embutida no PDF)
  • 🛡️ A chave privada nunca sai do dispositivo do usuário — nem para o seu servidor, nem para o nosso
  • 📱 Mesmo código na web e no app: o SDK escolhe o caminho sozinho

Instalação

npm install @mastersigner/sdk

Se o seu projeto é um app Capacitor (iOS/Android), rode também:

npx cap sync

O plugin nativo vem dentro deste mesmo pacote — não há um segundo pacote para instalar. O cap sync o encontra sozinho: no iOS pelo Package.swift (SPM), no Android pelo Gradle.

Um passo obrigatório no iOS

Acrescente ao ios/App/App/Info.plist do seu app:

<key>NSFaceIDUsageDescription</key>
<string>O Face ID protege seu certificado digital: é exigido para autorizar cada assinatura.</string>

Sem essa chave o iOS encerra o app (SIGABRT) no instante em que o Face ID é acionado — o sintoma é cruel: importar funciona (não usa Face ID) e assinar fecha o aplicativo. É exigência do sistema, não nossa; o texto aparece para o usuário e pode ser reescrito.

No Android não há passo nenhum: a permissão de biometria entra pelo próprio plugin. O aparelho precisa ter digital ou reconhecimento facial cadastrado — é o cofre que exige isso para guardar a chave, e sem nenhum cadastrado a importação falha dizendo exatamente isso.

Requisitos: Capacitor 8, iOS 15+, Android 6+ (API 23).

Como cada plataforma assina

| | Quem assina | O que o usuário precisa | |---|---|---| | Web | Extensão Master Signer (+ agente nativo para A3) | Instalar a extensão | | App (iOS/Android) | Plugin nativo deste pacote — chave no Keychain / Android Keystore, biometria a cada assinatura | Importar o certificado A1 uma vez |

Uso

import { MasterSigner } from '@mastersigner/sdk';

// apiKey: só no app. Na web a autorização é pelo domínio cadastrado no painel.
const signer = new MasterSigner({ apiKey: 'msk_...' });

const result = await signer.signDocument(pdfBytes);

if (result.signatureFormat === 'pades') {
  // PDF com a assinatura embutida — abre no Adobe e no VerificAR
  salvar(result.signedPdf);
} else {
  // .p7s em base64 — guarde junto do PDF original, ele não vale sozinho
  salvar(result.signature);
}

Junto vêm os metadados para o seu registro: thumbprint, commonName, taxId, issuer, certificateDer, signedAt, contentHash (SHA-256 do documento original) e algorithm — não crave esse último no seu código, ele vem de quem assinou.

O formato vem do painel, não do seu código: o dono da conta escolhe entre CAdES e PAdES em Integrações, e vale na web e no app sem você publicar nada. Quando o formato for imposto pelo documento (co-assinar um ZIP CAdES, por exemplo), force na chamada:

await signer.signDocument(pdfBytes, { signatureFormat: 'cades' });

Certificados

const certs = await signer.listCertificates();
await signer.signDocument(pdfBytes, { certificateThumbprint: certs[0].thumbprint });

A tela de certificados é do SDK. Você não precisa construir seletor de arquivo, campo de senha nem lista: com mais de um certificado disponível (ou nenhum, no app), o signDocument abre a tela sozinho, o usuário escolhe ou importa, e a assinatura continua.

await signer.manageCertificates();              // abrir por conta própria (menu do seu app)
const cert = await signer.chooseCertificate();  // só o seletor; null se o usuário fechar

Ela vive num shadow DOM: o CSS do seu app não entra e o dela não vaza.

As cores são opcionais — sem elas, sai a paleta do Master Signer. Para combinar com o seu app, defina as duas paletas uma vez e diga na chamada qual usar:

const signer = new MasterSigner({ apiKey, appearance: {
  light: { background: '#f4f7f6', accent: '#0f766e' },
  dark:  { background: '#0b1220', accent: '#38bdf8' },
}});

await signer.signDocument(pdf, { theme: temaAtualDoApp });   // sem isto, abre claro
await signer.manageCertificates({ theme: 'dark' });

Por que a paleta fica no construtor e o tema na chamada: a paleta é constante do seu app, o tema é estado de runtime — o usuário troca claro/escuro quando quiser, e o SDK não tem como adivinhar (ele nunca olha o tema do sistema operacional, que pode divergir do seu).

São duas cores por tema, não onze: texto, borda, superfície e a cor sobre o botão saem derivadas por contraste do background. Fundo escuro → texto claro; botão claro → texto escuro. É o que impede uma combinação infeliz de produzir texto ilegível numa tela de assinatura.

A marca Master Signer aparece no rodapé em qualquer app e não é configurável — ela também segue o contraste do fundo.

Prefere a sua própria interface? new MasterSigner({ apiKey, ui: false }) — aí os mesmos casos viram erro (no_certificate, certificate_not_chosen) para você tratar.

Se precisar das operações diretas:

await signer.importCertificate();          // só no app; abre as telas NATIVAS de arquivo e senha
await signer.removeCertificate(thumbprint);

Você não recebe o arquivo nem a senha — de propósito

importCertificate() não aceita bytes nem senha: quem os coleta é o código nativo, em telas do sistema operacional. O .p12 e a senha nunca existem no JavaScript — nem no seu código, nem no nosso —, e não há método na ponte do Capacitor que os aceite. Para o JS sobem só os metadados do certificado.

É uma decisão de segurança, não de conveniência: enquanto existia um importP12(bytes, senha), bastava um log distraído ou um envio ao seu servidor para vazar o certificado de um cliente seu. Do jeito atual, esse acidente não é possível — e você não precisa auditar nada a respeito.

A chave privada entra no Keychain (iOS) ou no Android Keystore protegida por biometria, e nunca sai: assinar acontece dentro do cofre. A senha é usada só para abrir o arquivo e não é guardada.

Erros

Todo erro é um MasterSignerError, com duas mensagens e um código:

import { MasterSignerError } from '@mastersigner/sdk';

try {
  await signer.signDocument(pdfBytes);
} catch (err) {
  if (err instanceof MasterSignerError) {
    mostrarAoUsuario(err.message);   // sempre exibível: em português, sem jargão
    if (err.code === 'cancelled') return;   // o usuário fechou a tela; não é falha
  }
}
  • message é escrito para a tela do usuário final. Pode exibir sem medo.
  • devMessage é o recado técnico (vai também para o console) — não exiba na interface.
  • code é o que o seu código testa. Alguns: no_certificate, certificate_not_chosen, invalid_p12, auth_cancelled, biometrics_required, key_invalidated, backend_refused, cancelled, plugin_unavailable. Códigos novos podem aparecer, então trate o desconhecido pelo message.

Recusas do servidor (cota esgotada, versão mínima, bundle não autorizado) chegam em backend_refused com o texto do próprio backend, que já vem escrito para o usuário — e muda sem você publicar nada.

Chave de API (só no app)

A chave é criada junto com o app, no painel: em Integrações, cadastre o bundle ID (com.exemplo.app) e a chave dele aparece uma única vez — copie na hora, guardamos só um hash.

Cada chave vale exclusivamente para o app com que nasceu. Onde guardá-la é decisão sua: embutida no código ou buscada no seu backend. Embutida, ela é extraível do binário — e o que se perde nesse caso é cota da sua organização, por isso o painel permite gerar uma nova (↻) a qualquer momento, o que revoga a anterior.

Na web não existe chave: a autorização é pelo domínio cadastrado, e nada secreto vai para o frontend.

Atalhos de formato explícito

Continuam disponíveis, para quem prefere decidir no código:

await signer.sign({ data, certificateThumbprint });   // CAdES destacado
await signer.signPdf(pdfBytes, { thumbprint });       // PAdES B-B
await signer.signBatch({ items, certificateThumbprint });  // lote, um PIN só (web)

Sem build step (só web)

<script src="https://get.mastersigner.app/sdk/v1/mastersigner.min.js"></script>
<script>
  const signer = new MasterSigner();
</script>

O bundle do CDN é web-only: dentro de um app, o caminho nativo exige a instalação via npm.

Documentação

  • API completa: https://mastersigner.app/app/docs
  • CAdES × PAdES: https://mastersigner.app/app/docs
  • Verificador de assinaturas: https://mastersigner.app/verificar

Licença

Proprietária — veja LICENSE. Para uso comercial, contrate um plano.