https://github.com/aniketc068/atick-node
Standalone PDF digital-signature library for Node.js — PAdES/CMS signing (PFX/PEM, deferred eSign/HSM/token), RFC-3161 timestamps, LTV, green-tick appearance. Prebuilt native addon, cross-platform, npm install.
https://github.com/aniketc068/atick-node
digital-signature esign javascript nodejs npm pades pdf typescript
Last synced: 18 days ago
JSON representation
Standalone PDF digital-signature library for Node.js — PAdES/CMS signing (PFX/PEM, deferred eSign/HSM/token), RFC-3161 timestamps, LTV, green-tick appearance. Prebuilt native addon, cross-platform, npm install.
- Host: GitHub
- URL: https://github.com/aniketc068/atick-node
- Owner: Aniketc068
- License: agpl-3.0
- Created: 2026-06-15T06:49:19.000Z (19 days ago)
- Default Branch: main
- Last Pushed: 2026-06-15T10:46:45.000Z (19 days ago)
- Last Synced: 2026-06-15T12:26:06.434Z (19 days ago)
- Topics: digital-signature, esign, javascript, nodejs, npm, pades, pdf, typescript
- Language: JavaScript
- Homepage: https://atick-node.readthedocs.io/
- Size: 19.2 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Notice: NOTICE
Awesome Lists containing this project
README

# ATick for Node.js
**Standalone PDF digital-signature library for Node.js — PAdES / CMS signing with no external services.**
[](https://www.npmjs.com/package/atick)
[](https://nodejs.org/)
[](#pades-levels)
[](#compatibility--one-package-everywhere)
[](LICENSE)
[](https://github.com/Aniketc068/ATick-Python)
[](https://github.com/Aniketc068/ATick-Java)
[](https://github.com/Aniketc068/ATick-DotNet)
[](https://github.com/Aniketc068/ATick-PHP)
**Also available in other languages** — the same ATick engine, the same API, native to each ecosystem:
| Language | Install | Source · Docs |
|---|---|---|
| **Python** | `pip install atick` | [ATick-Python](https://github.com/Aniketc068/ATick-Python) · [docs](https://atick.readthedocs.io/) |
| **Java** | `io.github.aniketc068:atick` (Maven) | [ATick-Java](https://github.com/Aniketc068/ATick-Java) · [docs](https://atick-java.readthedocs.io/) |
| **.NET** | `dotnet add package ATick` | [ATick-DotNet](https://github.com/Aniketc068/ATick-DotNet) · [docs](https://atick-dotnet.readthedocs.io/) |
| **PHP** | `composer require aniketc068/atick` | [ATick-PHP](https://github.com/Aniketc068/ATick-PHP) · [docs](https://atick-php.readthedocs.io/) |
---
ATick signs PDFs the way Adobe Acrobat and the EU DSS do — **PAdES baseline** signatures with
timestamps and long-term validation. It ships a **prebuilt native addon** (N-API), so there is **no
build step**, **no external service** and **nothing to compile** — `npm install atick` and you are done.
```js
const atick = require("atick");
const fs = require("fs");
const pdf = fs.readFileSync("doc.pdf");
const pfx = fs.readFileSync("my.pfx");
const signed = atick.signPfx(pdf, pfx, JSON.stringify({
password: "••••", cn: "Aniket Chaturvedi", reason: "Approved",
green_tick: true, page: 1, rect: [300, 55, 575, 175],
pades: true, timestamp: true, ltv: true, // PAdES-B-LT
}));
fs.writeFileSync("signed.pdf", signed);
```
> **Runs server-side** (Node runtime) — Next.js API routes / server actions, Express, NestJS,
> serverless functions, CLI tools. It is a native module, so it does **not** run in the browser.
---
## The green tick your readers trust
ATick draws a verified-signature appearance with a green tick. When the certificate is valid and
trusted, Adobe Reader / Acrobat shows **“Signed and all signatures are valid.”**

Valid & trusted
green tick

Validity unknown
yellow “?”

Not verified
“?” not validated

Invalid
red cross
---
## Install
```bash
npm install atick
```
The prebuilt native addon for your platform comes with the package — no `node-gyp`, no compiler,
no `postinstall` build.
---
## Quick start (TypeScript)
```ts
import * as atick from "atick";
import { readFileSync, writeFileSync } from "fs";
const signed = atick.signPfx(
readFileSync("doc.pdf"),
readFileSync("my.pfx"),
JSON.stringify({ password: "••••", cn: "Aniket", pades: true, page: 1, rect: [300, 55, 575, 175] }),
);
writeFileSync("signed.pdf", signed);
```
Full TypeScript types ship in the package (`index.d.ts`).
---
## Features (A → Z)
| Feature | How |
|---|---|
| **Sign with a `.pfx` / `.p12` / `.pem`** | `atick.signPfx(pdf, pfx, options)` — PKCS#12 or PEM (key + certs), auto-detected |
| **PAdES levels** B-B / B-T / B-LT / B-LTA | `"pades":true` + `"timestamp":true` + `"ltv":true` + `"lta":true` |
| **Hash algorithm** | `"hash_algo":"sha256" \| "sha384" \| "sha512"` |
| **Timestamp authority** | built in — or your own with `"tsa_url":"…"` (and `"tsa_auth":["user","pass"]`) |
| **Long-term validation (LTV)** | `"ltv":true` embeds the chain + revocation (CRL/OCSP) |
| **Multi-page / custom coordinates** | `"placements":[[page,[x1,y1,x2,y2]], …]` |
| **Signature layout** | `"mode":"single"` (one signature on many pages) · `"mode":"shared"` (many fields, same value) |
| **Multi-signatory** | sign an already-signed PDF again — each signature is its own revision, all stay valid |
| **Certification (DocMDP)** | `"certify":1` (no changes) · `2` (form filling) · `3` (form filling + annotations) |
| **Field locking (FieldMDP)** | `"lock_fields":["*"]` or `["FieldA", …]` |
| **Pre-sign checks** | `"verify_expiry":true`, `"verify_crl":true`, `"verify_ocsp":true` (or `"verify":true`) |
| **Document metadata** | `atick.setMetadata(pdf, options)` |
| **Password protection** | `"encrypt_password"` (+ `"owner_password"`) for output; `"open_password"` for input; `atick.decrypt(pdf, pw)` |
| **Appearance** | options `cn, org, ou, location, reason, text, date, dn, body, heading, image` — auto-fit text, transparent logo |
| **The mark** | the `?` (Adobe greens it), an always-green tick, or nothing — see [The mark](#the-mark) |
| **CN on the left** (Adobe-style) | `"image":"cn"` |
| **Distinguished name** | `"dn":"CN=…, O=…, C=IN"` |
| **Custom-text-only appearance** | `"body":"*APPROVED*\nby *Aniket*"` — `\n` = line, `*x*` = bold |
| **Invisible signature** | `"placements":[]` |
| **Sign an already-signed PDF** | sign again (incremental) — existing signatures stay valid; use a fresh `"field_name"` |
| **Container only** | `atick.prepareFields(pdf, options)` |
| **Document timestamp** | `"lta":true` while signing; `atick.addDocTimestamp(pdf, options)` afterwards (PAdES-B-LTA) |
| **Fast signing** | revocation cache (ON by default) — `atick.setFastSigning(false)` to disable |
| **Deferred / eSign (2-step)** | `atick.prepare(pdf, options)` → external CMS → `atick.embed(prepared, cms)` |
| **Detached CMS** | `atick.cmsPfx(data, pfx, options)` |
---
## The API
```js
atick.signPfx(pdf, pfx, optionsJson) // sign with a .pfx / .p12 / .pem (auto-detected)
atick.prepare(pdf, optionsJson) // deferred / eSign: returns { prepared, bytesToSign }
atick.cmsPfx(data, pfx, optionsJson) // detached CMS over data
atick.embed(prepared, cms) // embed a detached CMS into a prepared PDF
atick.prepareFields(pdf, optionsJson) // make an empty signature field (template)
atick.signField(pdf, pfx, optionsJson) // sign an existing empty field
atick.setMetadata(pdf, optionsJson) // Title / Author / Subject / Keywords / …
atick.addDocTimestamp(pdf, optionsJson) // archive DocTimeStamp (PAdES-B-LTA)
atick.setFastSigning(true | false) // revocation-cache toggle
atick.decrypt(pdf, password) // decrypt a password-protected PDF
atick.version() // engine version
```
All buffers are Node `Buffer`s; all options are a JSON **string**. Any failure throws an `Error`
whose `.message` is the reason.
### Options (JSON)
`cn, org, ou, location, reason, text, date, dn, body, heading, show_mark, green_tick, always_check,
mark_color (hex / name / [r,g,b]), mark_gradient, mark_scale, text_color, bg_color, border, font_size,
width, height, page, rect, placements ([[page,[x1,y1,x2,y2]], …]), mode (single/shared), field_name,
pades, hash_algo (sha256/384/512), timestamp, tsa_url, tsa_auth, ltv, lta, certify, lock_fields,
verify, verify_expiry, verify_crl, verify_ocsp, open_password, encrypt_password, owner_password,
contents_size`.
---
## The mark
```js
"{… ,\"green_tick\":true}" // the "?" mark — Adobe paints it GREEN for valid+trusted, RED if invalid
"{… ,\"always_check\":true}" // the green-tick graphic as the base
"{… ,\"green_tick\":false}" // no mark — a plain signature
```
Colour it: `"mark_color":"#E53935"`, `"blue"`, `[255,140,0]` — or a gradient `"mark_gradient":["red","orange","yellow"]`.
---
## Deferred signing & Indian eSign (two-step)
When the private key lives elsewhere (a token / HSM / smart-card, or an eSign ESP):
```js
const { prepared, bytesToSign } = atick.prepare(pdf, JSON.stringify({
cn: "DS TEST", reason: "eSign", placements: [[1, [300, 55, 575, 175]]], contents_size: 16384,
}));
// the eSign InputHash is the SHA-256 of bytesToSign:
const crypto = require("crypto");
const inputHash = crypto.createHash("sha256").update(bytesToSign).digest("hex");
// ... sign with your provider / eSign ESP, get back a detached CMS ...
const signed = atick.embed(prepared, cms);
```
---
## PAdES levels
```js
atick.signPfx(pdf, pfx, "{… ,\"pades\":true}") // B-B
atick.signPfx(pdf, pfx, "{… ,\"pades\":true,\"timestamp\":true}") // B-T
atick.signPfx(pdf, pfx, "{… ,\"pades\":true,\"timestamp\":true,\"ltv\":true}") // B-LT
atick.signPfx(pdf, pfx, "{… ,\"pades\":true,\"timestamp\":true,\"lta\":true}") // B-LTA
```
---
## Compatibility — one package everywhere
- **Node 10.16 → the latest** — the prebuilt addon uses N-API (ABI-stable), so one binary works
across every Node version, no rebuild.
- **Every OS/arch** — a prebuilt addon ships for each platform:
| platform-arch | Covers |
|---|---|
| `win32-x64` / `win32-ia32` | **Windows 7 → 11**, 64 / 32-bit |
| `win32-arm64` | Windows on ARM64 |
| `linux-x64` / `linux-arm64` / `linux-arm` / `linux-ia32` | Linux x64 / ARM64 / ARM / 32-bit (glibc 2.17+, every distro) |
| `darwin-x64` / `darwin-arm64` | macOS Intel / Apple Silicon |
---
## Errors
```js
try {
atick.signPfx(pdf, pfx, JSON.stringify({ password: "wrong" }));
} catch (e) {
console.error("signing failed:", e.message);
}
```
---
## License
ATick is **dual-licensed** — free for personal & open use, paid if you sell:
- **Free under [GNU AGPL-3.0](LICENSE)** — personal projects, learning, internal use, and
open-source projects (released publicly under AGPL-3.0).
- **Commercial license (paid)** — if you **build a product with ATick and sell it**, or use it in a
**closed-source / commercial** product, you must buy a commercial license first. Contact
**aniketc.pro@gmail.com** for a quote.
See [LICENSING.md](LICENSING.md) for details. © 2026 Aniket Chaturvedi.