Skip to content

Releases: DrBaher/sign-cli

v0.6.4

16 May 09:14

Choose a tag to compare

[0.6.4] — 2026-05-16

Fix-only release. The v0.6.3 npm tarball shipped mcpName as io.github.drbaher/sign-cli (lowercase), but the official MCP registry namespaces by GitHub login (DrBaher) and rejected the publish. v0.6.4 corrects the casing in package.json and server.json so the registry submission succeeds — every other surface is unchanged.

Fixed

  • package.json mcpName and server.json name now use io.github.DrBaher/sign-cli. The MCP registry validates the npm tarball's mcpName field against the GitHub login (case-sensitive), so the previous lowercase value blocked the registry publish even after the npm publish succeeded.

v0.6.3

16 May 09:14

Choose a tag to compare

[0.6.3] — 2026-05-16

Headline: sign mcp serve --http — second MCP transport that speaks JSON-RPC 2.0 over HTTP, so the same 19 tools / 4 prompts / 12 resources can be reached from remote MCP aggregators (Smithery, OpenAI Agents SDK over HTTP, custom services) without spawning a local npx process. Stdio remains the default. Listed on Smithery at drbaher/sign-cli.

Added

  • sign mcp serve --http true — boots the MCP server over node:http instead of stdio. Flags: --http-port (default $PORT or 8080), --http-bind (default 0.0.0.0), --http-path (default /mcp), --http-auth-token (also reads $SIGN_MCP_HTTP_AUTH_TOKEN). CORS open, OPTIONS preflight handled, 1 MiB max body, JSON-RPC dispatched through the same code path as stdio (so --read-only, --capability, --tool allow-lists, --emit-events all apply). GET /health returns { ok, mcp, endpoint } and is always unauthenticated. When a bearer token is set, every /mcp request must include Authorization: Bearer <token>; mismatched/missing tokens get a 401 with WWW-Authenticate: Bearer realm="sign-cli MCP".
  • deploy/Dockerfile.mcp-http + entrypoint-mcp-http.sh — hosted container variant that runs sign mcp serve --http on $PORT. Read-only + ephemeral SQLite + 4h TTL reset, same demo-safety model as the existing serve container. Powers sign-cli-mcp-production.up.railway.app/mcp.

Changed

  • deploy/Dockerfile and Dockerfile.mcp-http now copy scripts/ before npm install so the postinstall trim-pdfjs-dist.mjs step can find itself. Fixes silent build breakage introduced when the postinstall hook was added.
  • railway.toml no longer pins dockerfilePath; each service selects its Dockerfile via the RAILWAY_DOCKERFILE_PATH env variable. This lets the same repo deploy both sign-cli-demo (HTTP /v1/*) and sign-cli-mcp (MCP-over-HTTP) services from one config.

Fixed

  • signer_list's outputSchema was the only tool whose top-level type was "array". Per the MCP spec and Smithery's scanner, outputSchema.type must be "object" (it describes structuredContent, which is always an object). Dropped the schema annotation for this one tool rather than wrapping the wire payload — the actual JSON-RPC response shape is unchanged for existing clients.

v0.6.2

16 May 09:14

Choose a tag to compare

[0.6.2] — 2026-05-15

Headline: published to npm under the scoped name @drbaher/sign-cli. The unscoped sign-cli on npm was registered by someone unrelated to this project before this codebase existed. To avoid confusion (and because npm doesn't allow taking over a name owned by another account), this CLI now lives at @drbaher/sign-cli. The repo name (DrBaher/sign-cli), the bin name (sign), and the project identity are unchanged.

Changed

  • package.json name field: "sign-cli""@drbaher/sign-cli".
  • Install commands across README, AGENTS.md, DISTRIBUTION.md, docs/, and the showcase site now use the scoped name.
  • The bin name is still sign — once installed globally (npm i -g @drbaher/sign-cli), every CLI invocation works identically (sign demo, sign request create, etc.).

Migration

# If you previously installed the unrelated `sign-cli` package, uninstall it first:
npm uninstall -g sign-cli

# Install ours:
npm i -g @drbaher/sign-cli
sign --version    # → 0.6.2

v0.6.1

16 May 09:14

Choose a tag to compare

[0.6.1] — 2026-05-15

Headline: sign pdf inspect — verify ANY signed PDF (ours, Adobe's, DocuSign's, Dropbox Sign's, SignWell's). Pairs with request verify-signed-pdf for the request-bound check. Pre-sign signature visibility on signer fetch-document so signers see what they're countersigning. --recipient <email> filter on verify-signed-pdf for mid-flight multi-signer verification. Fixes a false signer_mismatch on RFC 4514-escaped certificate subjects.

Added

  • sign pdf inspect — new top-level command that inspects PADES signatures on ANY signed PDF (ours, Adobe's, DocuSign's, Dropbox Sign's, SignWell's). Returns per-signature signer CN/email, cert subject + issuer, validity window, fingerprint, trust label (self_signed_local / self_signed_other / ca_signed / unknown), message-digest match, and parse warnings. Pure read — no DB interaction, no audit events. Exit 2 when the PDF has no signatures. Pairs with request verify-signed-pdf (which adds request-level signer-match against the local DB); use pdf inspect when you don't have a request to bind against (e.g. inspecting an incoming counterparty PDF). Trust label is structural (issuer vs subject); no chain validation, no expiry check. Companion: new MCP tool pdf_inspect_signatures and HTTP route POST /v1/pdf/inspect-signatures — same validateDocumentPath guard as the other PDF surfaces.
  • Pre-sign signature visibilitysigner fetch-document (CLI + MCP signer_fetch_document + HTTP POST /v1/signer/fetch-document) now includes an existingSignatures field in every response: a compact summary of any PADES signatures already on the PDF when the signer fetches it. Fields: count, hasSignature, allDigestsOk (false if any prior signature is broken — tamper or parse failure), signers[] (subject / issuer / validity / fingerprint / trust / per-signer digestOk), warnings[]. Best-effort: if inspection throws on a malformed PDF, the field is populated with a degenerate summary plus a warning rather than failing the fetch. Same surface is added to sign document's result for PDF (non-DOCX) inputs.
  • request verify-signed-pdf --recipient <email> — narrows the signer_match check to a single recipient instead of requiring the full persisted-signer roster. Mirrors the semantics of request show --recipient. Useful mid-flight on multi-signer requests (verify Alice's signature without failing because Bob hasn't signed yet). Refuses unknown recipients with SIGNER_NOT_RECIPIENT rather than silently passing on vacuous truth. The flag was previously silent on this subcommand — that gap is now closed; --help and --catalog json list it.

Fixed

  • request verify-signed-pdf no longer falsely reports signer_mismatch against per-signer certs — Node's X509Certificate.subject returns the RFC 4514 / LDAP DN string, which backslash-escapes reserved chars (+, <, >, ,). A per-signer cert subject like CN=Baher Test \<baher\[email protected]\> was being compared with a raw subject.includes("[email protected]") and failing — even though the signature was cryptographically valid (digest_ok: true). The matcher now strips backslash escapes before the substring check. Surfaced by a real-world signing test against GBrain agreements on 2026-05-14. Regression test in verify-summary.test.ts asserts an RFC 4514-escaped subject matches.

Docs

  • Doc reorg: AGENTS.md + docs/setup/ + docs/reference/ — adopts the shared three-file shape used across the three-CLI suite. README trimmed from 703 to ~280 lines by extracting provider setup into docs/setup/{dropbox,docusign,signwell,embedded,providers}.md and concept docs into docs/reference/{architecture,audit-chain,exit-codes,profiles,security-controls,security-model,legal,comparison}.md. New AGENTS.md is the agent quickstart (output contract / exit-code table / discovery commands / failure → recovery). Removed ONBOARDING.md and CHECKLIST.md (redundant with the new structure). scripts/lint-legal-claims.mjs and the affected tests updated for the new paths.

v0.6.0 — cross-surface parity + profiles + path-traversal guards

14 May 14:19
40a0393

Choose a tag to compare

Headline: cross-surface parity (every CLI command that has any of MCP / HTTP / both has them all), the profile system (named bundles of provider + dbPath + credentials), security-grade path-traversal guards on every input/output flag, and a structured STORAGE_UNWRITABLE error code in place of raw Node EACCES stack traces.

Added

  • API parity: 7 new HTTP routes + 3 new MCP tools (#178) — closes the cross-surface gap surfaced by the second-round audit. HTTP gains POST /v1/pdf/detect-signature-field, POST /v1/pdf/detect-date-field, POST /v1/pdf/stamp-text, POST /v1/preview, POST /v1/document, POST /v1/profile/list, POST /v1/profile/show — every new MCP tool now has a REST equivalent with the same input shape, same validateDocumentPath / validateOutputPath guards, same READ_ONLY_BLOCKED_ROUTES gating for the three mutating ones. MCP gains signer_reissue_token, audit_scan, request_receipt — every HTTP-only route now has an MCP tool. signer_reissue_token and request_receipt join READ_ONLY_BLOCKED_TOOLS; audit_scan is read-only. OpenAPI spec (GET /v1/openapi.json) updated. Coverage: new api-parity.test.ts (12 tests) + updated serve-read-only.test.ts and mcp-server.test.ts exhaustive assertions.

  • Security: input-path traversal guard on PDF/document inputs (#177) — sign pdf stamp, sign pdf stamp verify, sign pdf stamp-text, sign pdf detect-signature-field, sign pdf detect-date-field, sign preview, and sign document now run validateDocumentPath on the --pdf / --input flag. Same SIGN_ALLOW_ABSOLUTE_DOCS=1 opt-in as the existing output-path validator. The corresponding MCP tools (pdf_detect_signature_field, pdf_detect_date_field, pdf_stamp_text, preview, document) inherit the guard. Closes the parity gap with output-path validation: a malicious or buggy MCP client can no longer coax the server into reading arbitrary files via pdf_path or laundering an arbitrary file through document's convert+stamp+seal pipeline. Regression coverage in mcp-new-tools.test.ts (input-path traversal across four tools).

  • Docs coverage for new MCP tools + new error codes (#177, #181) — docs/agent-guide.md §3 now lists every MCP tool name (split read-only vs mutating) with a "don't hardcode" note pointing at sign mcp tools; §1 error-codes table adds STORAGE_UNWRITABLE and PROFILE_ENV_VAR_UNSET; §7 Decision recipes adds rules for "escapes the working directory" path errors and the SIGN_ALLOW_ABSOLUTE_DOCS opt-in. docs/recipes/agent-loop-mcp.md regenerates the tool name list to match the catalog. TROUBLESHOOTING.md adds rows for Output path escapes, Config path "..." is outside both $HOME, STORAGE_UNWRITABLE, and PROFILE_ENV_VAR_UNSET. README also refreshed: MCP tool count and HTTP routes list now reflect the full 18+20 surface, with a profile + traversal-guard mention in the agent TL;DR.

  • MCP surface: seven new tools (#177) — pdf_detect_signature_field, pdf_detect_date_field, profile_list, profile_show (all read-only), and pdf_stamp_text, preview, document (mutating; gated by the existing READ_ONLY_BLOCKED_TOOLS set when mcp serve --read-only true). Wraps the same CLI handlers byte-for-byte, including validateOutputPath for every write — so an agent calling document over MCP gets the same path-traversal protection that sign document --out already has. profile_show redacts credentials by default; pass show_secrets: true to reveal. The existing sign tool's outputSchema is corrected to match SignerSignResult (was advertising status / signedDocumentPath, which the handler never returned). Coverage in mcp-server.test.ts, mcp-new-tools.test.ts, and mcp-serve-read-only.test.ts.

  • STORAGE_UNWRITABLE error code (#177) — openDatabase (src/lib/db.ts) now wraps EACCES / EROFS / EPERM from the parent-directory mkdirSync into a structured SignCliError with a code, message naming the unwritable directory, and a hint pointing at SIGN_DB_PATH, ~/.sign-cli/, or a profile dbPath. Replaces a raw Node.js stack trace with an agent-actionable envelope. Test in db-eacces.test.ts.

  • validateConfigPath helper for profile-stored paths (#177) — new validator in src/lib/validate.ts, used by sign profile init --db <path>. Permissive about home-relative paths (~/.sign-cli/prod.db resolves without the opt-in, since that's the canonical example) and CWD-relative paths; rejects paths outside both $HOME and CWD unless SIGN_ALLOW_ABSOLUTE_DOCS=1. Same toggle as the existing input/output path validators — one knob for "let me touch paths outside the working directory." Coverage in validate.test.ts.

  • Security: path-traversal guard on output paths (#176) — sign pdf stamp, sign pdf stamp-text, sign preview, and sign document now reject --out paths that escape the working directory. Same SIGN_ALLOW_ABSOLUTE_DOCS=1 opt-in as the existing input-path validator. Closes a gap where a malicious or buggy caller could write the stamped/sealed PDF anywhere on disk. New helper validateOutputPath (src/lib/validate.ts); regression test in validate.test.ts plus an end-to-end check in preview-command.test.ts.

  • Security: profile credentials now flow through the error-message redactor (#176) — applyCredentialsToProcessEnv (src/lib/profiles.ts) calls registerSecretKey for every credential it injects into process.env. collectKnownSecrets (src/lib/secret.ts) merges the dynamic set with its hardcoded list, so an error containing a profile-injected secret like ACME_API_KEY gets redacted just like DROPBOX_SIGN_API_KEY already was. Closes a gap where custom credential keys leaked in adapter logs and stack traces.

  • Output-shape parity polish across stamping commands (#176) — sign pdf stamp-text now emits the warnings array (matching pdf stamp and sign preview); sign preview now emits drawnRects (post-aspect-preserve rectangles, read back from the PDF — matching sign document); sign sign's success JSON includes ok: true (matching every other command's success envelope). All three are additive; no flag changes; locked in by regression tests.

  • Profiles — sign profile list / show / use / set / unset / delete / init + global --profile <name> / SIGN_PROFILE=<name> (#175) — named bundles of provider, dbPath, strictProvider, defaultTokenTtlMinutes, defaultSignerEmail, and a credentials block. Activated by --profile, SIGN_PROFILE, the user file's defaultProfile, or implicitly via a sign-profile.json discovered by walking upward from CWD (matches git/npm/pnpm semantics). Resolution order for any field: flag > env > project profile > user profile > built-in default. Atomic credentials — the layer that resolved provider is the only one that contributes credentials; cross-profile reuse via {{env:VAR}} references that point at shell-managed secrets. Unset {{env:VAR}} references error loudly (PROFILE_ENV_VAR_UNSET) with a hint naming the missing var. Schema versioning with in-place migration chain. sign profile show redacts credentials by default; pass --show-secrets true to reveal resolved values. User file location overridable via SIGN_PROFILES_FILE; default is XDG-compliant $XDG_CONFIG_HOME/sign-cli/profiles.json (mode 0600). New error codes: INVALID_PROFILE, INVALID_PROFILE_NAME, PROFILE_NOT_FOUND, PROFILE_ALREADY_EXISTS, PROFILE_ENV_VAR_UNSET. Provider banner gains new source strings: via active profile, via project sign-profile.json. sign doctor preflight adds a profile:active check. See docs/profiles-design.md for the full rationale and docs/agent-guide.md §6.7 for usage.

  • sign doctor preflight — structured per-check report on sign doctor preflight (subcommand). Each check is { name: "<category>:<specific>", status: "ok"|"failed"|"skipped", detail, hint? }; the wrapping object is { provider, summary: { passed, failed, skipped, verdict: "ok"|"failed" }, checks[] }. Exit 0 if verdict == "ok", 1 if verdict == "failed". Env-health checks run on every provider: runtime:node_version (Node ≥ 22 for node:sqlite), storage:db_path (SIGN_DB_PATH parent writable with a probe-file round-trip, default ./data/sign.db). Provider-scoped checks layer on: env:* (required env vars present), connectivity:* (provider API reachable), permissions:* (filesystem paths writable + RSA key files exist for DocuSign), fixture:canonical_unsigned (for local). Bare sign doctor (no subcommand) remains the unstructured env-report; always exits 0. Stderr always prints [sign] preflight: <verdict> (provider=<p>, N ok, N failed, N skipped) so the one-line summary is grep-able without parsing JSON.

  • sign workflow nda — one command that renders the bundled mutual-NDA template into a PDF and creates a signing request in one shot. Wraps scripts/render-template.mjs + request create. Flags: --values <file.json> (variable map), --value KEY=VALUE (repeatable, overrides values), --party-a-email <addr>, --party-b-email <addr> (both required, must differ), --template <path> (override the bundled template), --out <file> (PDF output), --token-ttl-minutes, --auto-approve. Signer names are read from PARTY_A_SIGNATORY / PARTY_B_SIGNATORY in values. Missing placeholders surface all gaps at ...

Read more