First Use
Load an A1 certificate, inspect the identity, and sign your first bytes — then an XML and a PDF — in a single Effect program.
What you'll do: assemble an Effect program that loads the A1 certificate, reads the identity, signs and verifies bytes — then signs an XML and a PDF reusing the same boundary.
Add the core boundary and the A1 signer. Pull in effect for the Effect runtime and Redacted.
bun add @signature-kit/core @signature-kit/a1 effecta1SignaturesLayer takes the .pfx bytes and the Redacted password and provides the Signatures service. Inside one Effect.gen, signatures.inspect() returns a normalized SignerIdentity — document carries the e-CPF or e-CNPJ when present — then signatures.sign and signatures.verify run over the same boundary. verify returns a value: result.valid is a boolean, and a real failure surfaces as a SignatureKitError in the error channel, never a thrown exception.
The .pfx bytes, not the path
pfx is a Uint8Array holding the binary contents of the .pfx/.p12 — not the file path string. Keep these bytes and the password out of logs and version control.
import { a1SignaturesLayer } from "@signature-kit/a1/signer"
import { signatures } from "@signature-kit/core/signatures"
import { Effect, Redacted } from "effect"
const layer = a1SignaturesLayer({
pfx, // Uint8Array — bytes of the .pfx/.p12
password: Redacted.make(process.env.A1_PASSWORD ?? ""),
})
export const program = Effect.gen(function* () {
// normalized identity — identity.document carries e-CPF / e-CNPJ
const identity = yield* signatures.inspect()
const content = new TextEncoder().encode("content to sign")
const artifact = yield* signatures.sign({
content,
algorithm: "rsa-sha256",
})
const result = yield* signatures.verify({
content,
signature: artifact.signature,
algorithm: artifact.algorithm,
})
return { identity, artifact, result }
}).pipe(Effect.provide(layer))Run the Effect with Effect.runPromise, or call it from another Effect program.
import { Effect } from "effect"
const { result } = await Effect.runPromise(program)
console.log(result.valid) // boolean — true if the signature checks outBoth formats consume the same boundary via Effect.provide; the layer line is identical to the previous step. XML also needs xmlRuntimeLayer; verifyXml / verifyPdf read the public key from the document and do not require the boundary.
import { signXml } from "@signature-kit/xml/sign"
import { xmlRuntimeLayer } from "@signature-kit/xml/runtime"
import { signPdf } from "@signature-kit/pdf/sign"
import { a1SignaturesLayer } from "@signature-kit/a1/signer"
import { Effect, Redacted } from "effect"
const layer = a1SignaturesLayer({
pfx, // Uint8Array — bytes of the .pfx/.p12
password: Redacted.make(process.env.A1_PASSWORD ?? ""),
})
const signedXml = signXml({ xml, referenceId: "nfe-1" })
.pipe(Effect.provide(layer), Effect.provide(xmlRuntimeLayer))
const signedPdf = signPdf({ pdf, policy: "pades-icp-brasil" })
.pipe(Effect.provide(layer))Errors you might see
Incorrect certificate password
If the .pfx password is wrong, the boundary fails with signature-kit.WRONG_PASSWORD in the error channel — not an exception. Check the secret before proceeding.
signature-kit.WRONG_PASSWORD— incorrect certificate password.signature-kit.SIGN_FAILED— failed to sign the content.signature-kit.VERIFY_FAILED— failed to verify the signature.