Skip to content
contract-ops CLI suite

Tool · TypeScript

sign-cli

sign-cli v0.9.0 243/wk on npm Latest from npm · checked Sat, 18 Jul 2026

A command-line e-signature tool for humans, ops teams, and AI agents. The built-in PAdES signer (PKCS#7 in /ByteRange, self-issued cert) produces real, cryptographically verifiable signed PDFs fully offline — no signup, no API keys, no third-party SaaS. Route through Dropbox Sign, DocuSign, or SignWell when you need an external provider. See how sign-cli compares to DocuSign →

TL;DR — npm i -g @drbaher/sign-cli — or npx @drbaher/sign-cli demo for a five-second offline walkthrough of the full consent-gated lifecycle (create with both gates on → blocked sign attempts → email verified → consent captured → approve → sign → verify → receipt). Pick the local PAdES signer or a hosted provider (Dropbox Sign, DocuSign, SignWell) and send a PDF with one command.
sign-cli — offline demo flow

Try it live

Run the offline lifecycle, browse the tokenized signer inbox, inspect a request, or verify the tamper-evident audit chain — in a sandbox, no install.

Runs the real CLI on your input in a sandbox — no setup, nothing stored. Open in a new tab ↗

What it does

  • Fully-offline PAdES signing. The built-in local provider produces real PKCS#7 signed PDFs with a self-issued X.509 cert — verifiable with any standard PDF-signature inspector. Zero network calls.
  • Multi-provider hosted sending. Dropbox Sign, DocuSign, and SignWell are wired in for when you need an external trust anchor. Same surface across all four providers.
  • Per-signer approval tokens with TTL. Every signer gets an explicit token tied to their email; agents can't impersonate humans, and tokens expire.
  • Hash-chained audit events. Every event hashes into the previous one and into append-only DB triggers. Tampering breaks audit verify.
  • Consent & attribution layer. Opt in per request: --require-consent gates signing on a recorded intent-to-sign attestation plus an electronic-records (ESIGN) disclosure; --require-email-verification gates it on proof of mailbox control; signer record-identity records how you verified a signer's identity out-of-band. No accounts, no KYC forms, no identity documents stored — by design.
  • RFC 3161 timestamping. External TSA anchors prove a signature existed by a given date — durable evidence that survives even if the provider disappears.
  • Auto-place signatures. Detect AcroForm /Sig widgets and anchor text (Signature:, Date:, Sign here, French/EU conventions) and stamp at the right rectangle. Multi-candidate selectors (first, last, all, page:N, index:N) handle the foot-gun where multiple anchors exist.
  • One-shot DOCX → sealed PDF. sign document takes a .docx (or .pdf), converts via the bundled docx2pdf-cli, auto-places, stamps, PAdES-seals, and verifies — all in one call against a scoped temp database.
  • Named profiles. Bundle provider + dbPath + credentials under a name; activate by flag, env, or project-level sign-profile.json (git/npm-style upward discovery). Secrets reference shell env via {{env:VAR}} — credentials never live in the profile file.
  • Path-traversal guards. Every input and output path runs through a validator; an agent or buggy caller can't read or write outside the working directory unless you opt in with SIGN_ALLOW_ABSOLUTE_DOCS=1.
  • Cryptographic PDF verification. Verify a signed PDF later, offline, with the receipt bundle — the embedded PKCS#7 signature value is checked against the signer certificate's public key (RSA/ECDSA), not just the /ByteRange digest, so a forged or tampered signature fails.
  • 19-tool MCP server. Every operationally-useful CLI command is also an MCP tool over stdio — plus a 20-route HTTP API at /v1/* for non-MCP clients.

Quickstart

install + offline demo
# Install
npm i -g @drbaher/sign-cli

# Five-second offline walkthrough: create → send → sign → verify → receipt
sign demo

# Or run without installing
npx @drbaher/sign-cli demo

Send a real document

local PAdES (offline) or hosted
# Fully-offline: built-in local provider produces a real PAdES-signed PDF
sign request create \
  --title "Vendor MSA" \
  --document contract.pdf \
  --signer "name:Alice Founder,email:alice@acme.com" \
  --signer "name:Bob Counsel,email:bob@beta.com" \
  --provider local

# Or via a hosted provider
sign request create \
  --title "Vendor MSA" \
  --document contract.pdf \
  --signer "name:Alice Founder,email:alice@acme.com" \
  --signer "name:Bob Counsel,email:bob@beta.com" \
  --provider signwell

Consent, verification, and identity assurance — without collecting anyone's ID

Under US ESIGN/UETA, what a court asks about an e-signature is procedural: did the signer intend to sign, did they consent to transact electronically, and can you attribute the signature to them? sign-cli records all three in its hash-chained audit log — and deliberately stops short of collecting identity documents, because self-asserted KYC data adds privacy liability without adding evidentiary weight.

  • Intent + consent, as versioned attestations. With --require-consent true, no signer can sign until they've affirmatively accepted two canonical statements — an intent-to-sign attestation and an electronic-records disclosure covering the ESIGN §7001(c) elements (right to paper, withdrawal, retention). sign consent show prints the exact texts; the audit chain records each acceptance with the statement's version id, SHA-256, full text, and timestamp. Statement versions are immutable: historical consents always re-verify against exactly what was accepted.
  • Email verification, opt-in. --require-email-verification true gates signing on a 6-digit code that proves control of the signer's mailbox — evidence the approval token alone can't give you. Codes are hashed at rest, TTL-bounded, locked after 5 wrong attempts, and never appear in the audit chain (only a masked hint does). Wire SIGN_VERIFICATION_WEBHOOK_URL to your mailer, or deliver codes out-of-band.
  • Identity assurance, recorded — not collected. When you have verified who you're dealing with (a video call, an in-person meeting, a provider IDV flow), sign signer record-identity logs the method, the verifier, and a pointer to the evidence in the audit chain. The assertion is recorded; the personal data stays wherever it already lives.
  • Enforced everywhere, gated where it counts. Both gates live in the shared signing service, so the CLI, the MCP sign tool, and POST /v1/sign all honor them — an agent holding a token still can't get past a gate (CONSENT_REQUIRED, EMAIL_VERIFICATION_REQUIRED), and --auto-approve is rejected in combination with either. Capture itself stays CLI-side, consistent with the tool's core asymmetry: agents drive the workflow, humans perform the signing gesture.
consent-gated flow
# Gate signing on consent + mailbox proof
sign request create \
  --title "Vendor MSA" \
  --document contract.pdf \
  --signer "name:Alice Founder,email:alice@acme.com" \
  --require-consent true \
  --require-email-verification true

# Read the canonical statements (version id + SHA-256 + full text)
sign consent show

# Prove mailbox control: issue a 6-digit code, then redeem it
sign signer send-verification --request-id req_abc --email alice@acme.com
sign signer verify-email --request-id req_abc --email alice@acme.com --code 123456

# The signer attests intent + accepts the ESIGN disclosure while approving
sign approve --request-id req_abc --token alice-tok-... \
  --agree true --accept-disclosure true

# Record how you verified who they are (the assertion, not the evidence)
sign signer record-identity --request-id req_abc --email alice@acme.com \
  --identity-assurance method:video-call,verifier:ops@acme.com,reference:TICKET-123

Each step lands in the audit chain as a first-class event: request.consent_captured, request.esign_consent_captured, request.signer_verification_issued, request.signer_email_verified, request.identity_assurance_recorded.

What it doesn't claim — This strengthens the ESIGN/UETA evidence story. It does not change the eIDAS tier — the local provider still produces a Simple Electronic Signature, and verified legal identity remains the domain of hosted-provider IDV or a QTSP. The full posture is documented in docs/reference/legal.md and docs/reference/consent-and-identity.md.

One-shot: DOCX → sealed PDF

sign document chains conversion, auto-placement, stamping, sealing, and chain-verify in one call. All intermediate state lives in a temp database scoped to the call — your main ./data/sign.db is untouched.

one-shot pipeline
sign document contract.docx \
  --signer "Alice Founder" --signer-email alice@acme.com \
  --name-signature true \
  --auto-place first \
  --out contract.sealed.pdf

Preview placement before committing

iterate offline
# See where the detector would place a signature — no DB writes, no PAdES envelope
sign pdf detect-signature-field --pdf contract.pdf

# Stamp a preview to inspect visually
sign preview --pdf contract.pdf \
  --name-signature "Alice Founder" \
  --auto-place first --preserve-aspect-ratio true \
  --out preview.pdf

Verify a signed PDF

Verification is offline and works on any signed PDF the tool produced — including the self-issued PAdES output. The receipt bundle contains the audit chain, the timestamp anchor, a per-signer event subset, and a detached manifest.sig + manifest.cert.pem you can hand to opposing counsel or a court without further cooperation from the signing provider.

verify offline
# Re-parse the PDF, recompute the digest, extract signer certs
sign pdf inspect --pdf contract.signed.pdf

# Verify the hash-chained audit log for a specific request
sign audit verify --request-id req_abc

# Verify a stored chain-bundle (dir or .tar.gz)
sign audit verify-chain-bundle ./bundle.tar.gz

Named profiles for credentials and DBs

Profiles bundle provider, dbPath, and credentials under a name. The credentials block uses {{env:VAR}} references that resolve from the shell at call time — so secrets never live in the profile file. Activate via --profile prod, SIGN_PROFILE=prod, or implicitly via a project-level sign-profile.json.

sign profile init --name prod \
  --provider signwell --db "~/.sign-cli/prod.db" --strict-provider true
sign profile set --name prod \
  --key credentials.SIGNWELL_API_KEY --value "{{env:SIGNWELL_API_KEY}}"

sign --profile prod request show --request-id req_abc

Preflight check

sign doctor preflight runs a structured per-check report: Node version, DB-path writability, provider env vars, provider API reachability, filesystem permissions. Exit 0 on verdict: "ok"; 1 on any failure. Branch on checks[].name for agent self-recovery.

sign doctor preflight --provider signwell
# stderr: [sign] preflight: ok (provider=signwell, 7 ok, 0 failed, 0 skipped)

MCP server for agents

Every operationally-useful CLI command is also exposed as an MCP tool over stdio — 19 tools in total, split read-only vs. mutating. Wire it into Claude Code, Cursor, or any other MCP-aware client and your agent can drive the workflow with the same primitives — including the per-signer approval-token guardrail, so a runaway agent can't sign on a human's behalf. Total tool count is live in the catalog — call sign mcp tools rather than hardcoding.

sign mcp serve                  # start the MCP server on stdio
sign mcp serve --read-only true # sandboxed: blocks mutating tools
sign mcp tools                  # print the catalog without booting the server

See the MCP guide for the full tool list, wire-up snippets, and the read-only mode walkthrough. The agent quickstart contract — output envelope, exit codes, discovery, failure → recovery — is in AGENTS.md; provider setup in docs/setup/; concept deep-dives (audit chain, profiles, security model, legal posture) in docs/reference/.

HTTP API for non-MCP clients

Twenty routes under /v1/* mirror the MCP surface — same input shape, same path-traversal guards, same read-only gating. Useful for agents and integrations that speak REST rather than MCP.

sign serve --read-only true --rate-limit 5
curl http://localhost:4000/v1/openapi.json    # discover the route catalog (default port 4000)

Where it fits in the workflow

sign-cli is the last step in the contract pipeline — after a template is pulled from template-vault-cli, draft-cli fills it, nda-review-cli produces the agreed text, compare-cli gates it for last-minute drift, and docx2pdf-cli renders the PDF. Or skip the chain entirely and hand a .docx straight to sign document. See the full workflow page for the chained commands.

It also stands alone for any document that needs signing — vendor agreements, employment documents, board consents, anything PDF.

Repo

github.com/DrBaher/sign-cli · MIT licensed · TypeScript · MCP server + HTTP API included.

Edit this page on GitHub