Digital certificates (A1)
Some sites ask for a digital certificate instead of, or on top of, a password. Brazilian government portals are the common case: issuing an NFS-e, eCAC, state tax portals. A Burrowbox browser can hold a certificate (.pfx / .p12, such as an ICP-Brasil A1) and use it in two ways:
- Certificate login (TLS). When a site asks the browser for a client certificate, the browser presents yours automatically on the sites you list.
- Signing in the page. Portals that sign documents in the browser use the Lacuna Web PKI component (
pki.signXmlElement,pki.signData, …). Burrowbox installs it for you. Every signature still needs an explicit approval, which you or your agent give through an MCP call after seeing what will be signed.
Everything is done with MCP tools. There's nothing to install and no desktop to click.
#How it works
browser_certificate_importstores the certificate encrypted with the machine's vault key, the same way as the vault. It then installs the certificate in the browser's certificate stores and switches the browser to the full engine (Chromium). The first time, it also installs Lacuna Web PKI, which takes about a minute.- Your agent uses the portal with the normal
browser_*tools: log in, fill the form, click the button that issues or signs. - Web PKI then asks for consent in its own dialog:
- Access: "A website wants to access your digital certificates" (once per site, unless remembered).
- Signature: "A website is trying to perform a digital signature", showing the website and the certificate (every signature, unless remembered).
browser_certificate_promptshows the dialog as an image (status) and answers it (approveordeny). Nothing is approved without that call.- The portal receives the signature and finishes, for example by issuing the invoice.
The private key and the password never come back through any tool. Every import, deletion and dialog answer is written to an audit log on the machine.
#Step by step
#1. Put the certificate password in the vault (recommended)
The password can go straight into the import call. If you'd rather keep it out of the agent's conversation, store it in the vault first, from the dashboard (Live view → Vault) or with the REST API:
curl -X PUT https://burrowbox.dev/api/machines/2f6aeedcd3/vault/cert-password \
-H "Authorization: Bearer $BURROWBOX_KEY" \
-H "Content-Type: application/json" \
-d '{"password": "…"}'
#2. Import the certificate
{
"name": "browser_certificate_import",
"arguments": {
"name": "acme-a1",
"pfx_base64": "MIIKYQIBAzCCCi…",
"password_credential": "cert-password",
"auto_select_urls": ["nfse.example.gov.br"]
}
}
pfx_base64is the file itself. If the file is already on the machine, passpath(for example~/Downloads/acme.pfx) instead.- Use either
passwordorpassword_credential(the name of a vault entry whose password is the certificate's). auto_select_urlslists the sites that get this certificate when they ask for one at login. A bare host covers its subdomains. Full Chromium patterns such ashttps://[*.]gov.bralso work. Leave it out if the site only signs with Web PKI.
The result shows the holder, CNPJ/CPF, issuer and validity, plus "webPkiExtension": "ready". The browser has restarted on Chromium with your open tabs.
#3. Use the portal
Drive the site as usual: browser_navigate, browser_snapshot, browser_login, browser_fill, browser_click. Stop right after the action that signs, such as Gravar, Emitir or Assinar.
Many portals ask first ("Deseja gravar a nota?") with a JavaScript dialog. That blocks the page until it's answered, so the browser tool reports it instead ("waiting": "JavaScript confirm dialog", with the message). Answer it with browser_dialog (accept or dismiss), then go on to the Web PKI dialogs.
#4. Review and approve
While a Web PKI dialog is waiting, the portal waits too, and the page can look frozen. This is expected: nothing has crashed. If a browser tool is still running after about 3 seconds and a dialog is open, it returns right away with "waiting": "Web PKI signature dialog" (or access) instead of hanging. browser_certificate_prompt reads the machine's desktop, not the page, so it works even while the page waits.
Look at the dialog first:
{ "name": "browser_certificate_prompt", "arguments": { "action": "status" } }
It returns the dialog type (access or signature) and an image of it. Check the website and the certificate, then answer:
{ "name": "browser_certificate_prompt", "arguments": { "action": "approve" } }
The first time on a site you get the access dialog and then the signature dialog, so you approve twice. deny cancels, and the page receives "The user cancelled the operation". The tool waits up to wait_seconds (default 10) for a dialog to appear.
To stop being asked for a site, add "remember": true when approving. That answers Always Allow on the access dialog and ticks Don't ask again for this website and certificate on the signature dialog. Only do this for a site and certificate you trust the agent to sign with unattended.
#5. Confirm the result
Read the page (browser_snapshot) to confirm the portal finished: an invoice number, a protocol, a PDF.
#Keeping a person in the loop
The approval is a separate tool call on purpose. Your application decides who makes it:
In the dashboard. The machine shows Waiting for you with the request and Approve / Deny buttons. See Waiting for you.
Your agent asks the user. It calls
status, shows the image ("Sign NFS-e for R$ 65,000.00 with ACME LTDA's certificate?") and only callsapproveonce the user says yes.Your backend approves. Your own code calls
browser_certificate_promptthrough the machine's MCP endpoint after its checks pass.Unattended. Approve with
remember: trueonce per site, and later signatures there go through without a dialog.
#Managing certificates
| Tool | |
|---|---|
browser_certificates |
Installed certificates (holder, CNPJ/CPF, issuer, validity, auto-select sites) and whether Web PKI is installed |
browser_certificate_import |
Without a file: updates auto_select_urls of an existing certificate. With a file and an existing name: replaces it, for example on renewal |
browser_certificate_delete |
name: removes it from the browser and the machine |
Certificates survive restarts. While a machine has any certificate, its browser always runs on the full engine (Chromium). Each change restarts the browser once, and your tabs reopen.
#Good to know
- x86-64 machines only. Lacuna Web PKI has no ARM build.
- The first import takes about a minute. Web PKI is downloaded from Lacuna, and its browser extension from the Chrome Web Store.
- Expired certificates still import, but portals will refuse them. Check
expiredandnotAfterinbrowser_certificates. - Audit log:
~/.tiny/certificates-audit.logon the machine, one JSON line per import, deletion and dialog answer. - Which sites: Web PKI only works for sites licensed by Lacuna (portals that use it already are) and for
localhost, which is handy for testing.
#Portals that don't use Lacuna
Certificate login (TLS) works on any site. For signing, some portals use other components, such as Softplan Web Signer or BRy. Install those with browser_extension_install and the vendor's native app. See Browser extensions & signers.
#Troubleshooting
| Symptom | What to do |
|---|---|
| The page says Web PKI is not installed | The extension was still downloading when the page loaded. Check browser_certificates, then reload the page |
| The browser freezes completely after clicking a button, and even screenshots fail | The page opened a JavaScript dialog, such as confirm("Deseja gravar?"). Browser tools now report it ("waiting": "JavaScript confirm dialog" with its message). Answer it with browser_dialog (accept or dismiss) |
| The browser "freezes" after clicking the button that signs (Gravar, Emitir…) | A Web PKI dialog is waiting. Call browser_certificate_prompt with action: "status", then approve or deny. Restarting the machine doesn't help, because the dialog comes back on the next signature |
| The access dialog appears on every page load | Approve it once with "remember": true for that site |
status says no dialog is open |
The page hasn't asked yet. Check that the action that signs actually ran (browser_snapshot), or raise wait_seconds |
| "wrong certificate password" on import | Check the password, or the vault entry named in password_credential |
| The site still shows a certificate picker at login | Add the site to auto_select_urls. Patterns must match the exact scheme and host |