Releases: DrBaher/sign-cli
v0.6.4
[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.jsonmcpNameandserver.jsonnamenow useio.github.DrBaher/sign-cli. The MCP registry validates the npm tarball'smcpNamefield against the GitHub login (case-sensitive), so the previous lowercase value blocked the registry publish even after the npm publish succeeded.
v0.6.3
[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 overnode:httpinstead of stdio. Flags:--http-port(default$PORTor8080),--http-bind(default0.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,--toolallow-lists,--emit-eventsall apply).GET /healthreturns{ ok, mcp, endpoint }and is always unauthenticated. When a bearer token is set, every/mcprequest must includeAuthorization: Bearer <token>; mismatched/missing tokens get a 401 withWWW-Authenticate: Bearer realm="sign-cli MCP".deploy/Dockerfile.mcp-http+entrypoint-mcp-http.sh— hosted container variant that runssign mcp serve --httpon$PORT. Read-only + ephemeral SQLite + 4h TTL reset, same demo-safety model as the existingservecontainer. Powers sign-cli-mcp-production.up.railway.app/mcp.
Changed
deploy/DockerfileandDockerfile.mcp-httpnow copyscripts/beforenpm installso the postinstalltrim-pdfjs-dist.mjsstep can find itself. Fixes silent build breakage introduced when the postinstall hook was added.railway.tomlno longer pinsdockerfilePath; each service selects its Dockerfile via theRAILWAY_DOCKERFILE_PATHenv variable. This lets the same repo deploy bothsign-cli-demo(HTTP/v1/*) andsign-cli-mcp(MCP-over-HTTP) services from one config.
Fixed
signer_list'soutputSchemawas the only tool whose top-leveltypewas"array". Per the MCP spec and Smithery's scanner,outputSchema.typemust be"object"(it describesstructuredContent, 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
[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.jsonnamefield:"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.2v0.6.1
[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. Exit2when the PDF has no signatures. Pairs withrequest verify-signed-pdf(which adds request-level signer-match against the local DB); usepdf inspectwhen 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 toolpdf_inspect_signaturesand HTTP routePOST /v1/pdf/inspect-signatures— samevalidateDocumentPathguard as the other PDF surfaces.- Pre-sign signature visibility —
signer fetch-document(CLI + MCPsigner_fetch_document+ HTTPPOST /v1/signer/fetch-document) now includes anexistingSignaturesfield 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 tosign document's result for PDF (non-DOCX) inputs. request verify-signed-pdf --recipient <email>— narrows thesigner_matchcheck to a single recipient instead of requiring the full persisted-signer roster. Mirrors the semantics ofrequest show --recipient. Useful mid-flight on multi-signer requests (verify Alice's signature without failing because Bob hasn't signed yet). Refuses unknown recipients withSIGNER_NOT_RECIPIENTrather than silently passing on vacuous truth. The flag was previously silent on this subcommand — that gap is now closed;--helpand--catalog jsonlist it.
Fixed
request verify-signed-pdfno longer falsely reportssigner_mismatchagainst per-signer certs — Node'sX509Certificate.subjectreturns the RFC 4514 / LDAP DN string, which backslash-escapes reserved chars (+,<,>,,). A per-signer cert subject likeCN=Baher Test \<baher\[email protected]\>was being compared with a rawsubject.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 inverify-summary.test.tsasserts 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 intodocs/setup/{dropbox,docusign,signwell,embedded,providers}.mdand concept docs intodocs/reference/{architecture,audit-chain,exit-codes,profiles,security-controls,security-model,legal,comparison}.md. NewAGENTS.mdis the agent quickstart (output contract / exit-code table / discovery commands / failure → recovery). RemovedONBOARDING.mdandCHECKLIST.md(redundant with the new structure).scripts/lint-legal-claims.mjsand the affected tests updated for the new paths.
v0.6.0 — cross-surface parity + profiles + path-traversal guards
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, samevalidateDocumentPath/validateOutputPathguards, sameREAD_ONLY_BLOCKED_ROUTESgating for the three mutating ones. MCP gainssigner_reissue_token,audit_scan,request_receipt— every HTTP-only route now has an MCP tool.signer_reissue_tokenandrequest_receiptjoinREAD_ONLY_BLOCKED_TOOLS;audit_scanis read-only. OpenAPI spec (GET /v1/openapi.json) updated. Coverage: newapi-parity.test.ts(12 tests) + updatedserve-read-only.test.tsandmcp-server.test.tsexhaustive 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, andsign documentnow runvalidateDocumentPathon the--pdf/--inputflag. SameSIGN_ALLOW_ABSOLUTE_DOCS=1opt-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 viapdf_pathor laundering an arbitrary file throughdocument's convert+stamp+seal pipeline. Regression coverage inmcp-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 atsign mcp tools; §1 error-codes table addsSTORAGE_UNWRITABLEandPROFILE_ENV_VAR_UNSET; §7 Decision recipes adds rules for "escapes the working directory" path errors and theSIGN_ALLOW_ABSOLUTE_DOCSopt-in.docs/recipes/agent-loop-mcp.mdregenerates the tool name list to match the catalog.TROUBLESHOOTING.mdadds rows forOutput path escapes,Config path "..." is outside both $HOME,STORAGE_UNWRITABLE, andPROFILE_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), andpdf_stamp_text,preview,document(mutating; gated by the existingREAD_ONLY_BLOCKED_TOOLSset whenmcp serve --read-only true). Wraps the same CLI handlers byte-for-byte, includingvalidateOutputPathfor every write — so an agent callingdocumentover MCP gets the same path-traversal protection thatsign document --outalready has.profile_showredacts credentials by default; passshow_secrets: trueto reveal. The existingsigntool'soutputSchemais corrected to matchSignerSignResult(was advertisingstatus/signedDocumentPath, which the handler never returned). Coverage inmcp-server.test.ts,mcp-new-tools.test.ts, andmcp-serve-read-only.test.ts. -
STORAGE_UNWRITABLEerror code (#177) —openDatabase(src/lib/db.ts) now wrapsEACCES/EROFS/EPERMfrom the parent-directorymkdirSyncinto a structuredSignCliErrorwith a code, message naming the unwritable directory, and a hint pointing atSIGN_DB_PATH,~/.sign-cli/, or a profiledbPath. Replaces a raw Node.js stack trace with an agent-actionable envelope. Test indb-eacces.test.ts. -
validateConfigPathhelper for profile-stored paths (#177) — new validator insrc/lib/validate.ts, used bysign profile init --db <path>. Permissive about home-relative paths (~/.sign-cli/prod.dbresolves without the opt-in, since that's the canonical example) and CWD-relative paths; rejects paths outside both$HOMEand CWD unlessSIGN_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 invalidate.test.ts. -
Security: path-traversal guard on output paths (#176) —
sign pdf stamp,sign pdf stamp-text,sign preview, andsign documentnow reject--outpaths that escape the working directory. SameSIGN_ALLOW_ABSOLUTE_DOCS=1opt-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 helpervalidateOutputPath(src/lib/validate.ts); regression test invalidate.test.tsplus an end-to-end check inpreview-command.test.ts. -
Security: profile credentials now flow through the error-message redactor (#176) —
applyCredentialsToProcessEnv(src/lib/profiles.ts) callsregisterSecretKeyfor every credential it injects intoprocess.env.collectKnownSecrets(src/lib/secret.ts) merges the dynamic set with its hardcoded list, so an error containing a profile-injected secret likeACME_API_KEYgets redacted just likeDROPBOX_SIGN_API_KEYalready 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-textnow emits thewarningsarray (matchingpdf stampandsign preview);sign previewnow emitsdrawnRects(post-aspect-preserve rectangles, read back from the PDF — matchingsign document);sign sign's success JSON includesok: 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 acredentialsblock. Activated by--profile,SIGN_PROFILE, the user file'sdefaultProfile, or implicitly via asign-profile.jsondiscovered 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 resolvedprovideris 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 showredacts credentials by default; pass--show-secrets trueto reveal resolved values. User file location overridable viaSIGN_PROFILES_FILE; default is XDG-compliant$XDG_CONFIG_HOME/sign-cli/profiles.json(mode0600). 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 preflightadds aprofile:activecheck. Seedocs/profiles-design.mdfor the full rationale anddocs/agent-guide.md§6.7 for usage. -
sign doctor preflight— structured per-check report onsign 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[] }. Exit0ifverdict == "ok",1ifverdict == "failed". Env-health checks run on every provider:runtime:node_version(Node ≥ 22 fornode:sqlite),storage:db_path(SIGN_DB_PATHparent 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(forlocal). Baresign 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. Wrapsscripts/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 fromPARTY_A_SIGNATORY/PARTY_B_SIGNATORYin values. Missing placeholders surface all gaps at ...