esign: ink-reproduction consent gate + patent-risk research
Consent gate (the legal linchpin from the wet-signature memo): - migration 092 adds ink_consent/ink_consent_at/ink_consent_text to esign_records - extract pure, unit-tested gate logic into esign-ink-consent.ts (DRY single source for route + signing page): isInkReproduction / inkConsentRequired / inkConsentSatisfied + verbatim client-safe INK_CONSENT_TEXT - portal-esign-generic.ts: GET surfaces ink_reproduction + consent text; POST gates DRAWN signatures on ink-path docs on explicit consent, stores it - signing page locks the signature block until consent is checked (drawn only) - npi_provider marks cms855/cms10114 esign metadata ink_reproduction=true - 33 unit checks: gate truth table + consent text omits all internal mechanics (plotter/machine/CMS/MAC/etc) and keeps required legal reassurances Patent-risk memo (docs/legal/patent-risk-mechanical-wet-signature.md): - prior-art-dated risk analysis (autopen 1803/1942, plotters, CNC = public domain => low risk on core concept; e-sign workflow space litigious) - firsthand recent-grant sweep (1.58M USPTO grants 2021-2025, queried via DuckDB): ZERO patents on machine-applies-signature-in-ink; e-sign players hold only electronic-workflow patents. Not an FTO; flags where attorney search is needed
This commit is contained in:
parent
f8d2a7f01f
commit
a4bad723bc
7 changed files with 452 additions and 5 deletions
28
api/migrations/092_esign_ink_consent.sql
Normal file
28
api/migrations/092_esign_ink_consent.sql
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-- 092: Ink-reproduction consent on signature records.
|
||||
--
|
||||
-- The Standard (no-login) CMS filing path reproduces the signer's OWN captured
|
||||
-- signature strokes in real ink on the printed form (pen plotter) so the mailed
|
||||
-- application carries an original ink signature. Per the legal-risk research
|
||||
-- (docs/legal/remote-mechanical-wet-signature-precedent.md), the linchpin that
|
||||
-- keeps this on the valid side of the forgery/agency line is an EXPLICIT,
|
||||
-- per-document authorization from the signer to reproduce their signature in ink
|
||||
-- on this specific document.
|
||||
--
|
||||
-- These columns capture that consent at signing time, alongside the existing
|
||||
-- perjury attestation. They are only meaningful for drawn signatures on ink-path
|
||||
-- documents (metadata.ink_reproduction = true); other docs leave them false/NULL.
|
||||
--
|
||||
-- Idempotent.
|
||||
|
||||
ALTER TABLE esign_records
|
||||
ADD COLUMN IF NOT EXISTS ink_consent BOOLEAN DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS ink_consent_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS ink_consent_text TEXT;
|
||||
|
||||
COMMENT ON COLUMN esign_records.ink_consent IS
|
||||
'TRUE when the signer expressly authorized reproducing their drawn signature '
|
||||
'in ink on this document (pen-plotter path). Captured at signing time.';
|
||||
COMMENT ON COLUMN esign_records.ink_consent_at IS
|
||||
'When the ink-reproduction consent was given (signer-side timestamp).';
|
||||
COMMENT ON COLUMN esign_records.ink_consent_text IS
|
||||
'Verbatim consent language the signer agreed to (for the audit trail).';
|
||||
59
api/src/routes/esign-ink-consent.ts
Normal file
59
api/src/routes/esign-ink-consent.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Pure helpers for the ink-reproduction consent gate (DB-free, unit-tested).
|
||||
*
|
||||
* The Standard (no-login) CMS filing path reproduces the signer's OWN captured
|
||||
* signature strokes in real ink on the printed form (pen plotter). The legal
|
||||
* linchpin (docs/legal/remote-mechanical-wet-signature-precedent.md) is an
|
||||
* EXPLICIT, per-document authorization to reproduce the signature in ink. These
|
||||
* helpers decide when that consent is required and whether it was satisfied, so
|
||||
* the route and the signing page agree on one source of truth.
|
||||
*/
|
||||
|
||||
/** Verbatim consent the signer must agree to before drawing an ink-path signature.
|
||||
*
|
||||
* Client-safe (no internal mechanics) but legally explicit: authorizes Performance
|
||||
* West to reproduce THIS signer's OWN drawn signature in ink ONE TIME on THIS
|
||||
* single document. The "only once / this one form / not reused" language reassures
|
||||
* the signer the signature is not stored for reuse. Stored verbatim with the
|
||||
* signature for the audit trail.
|
||||
*/
|
||||
export const INK_CONSENT_TEXT =
|
||||
"I understand this filing must be submitted on the official paper form with an " +
|
||||
"original ink signature. I authorize Performance West Inc. to reproduce my own " +
|
||||
"signature, exactly as I draw it below, in ink one time on this single form, and " +
|
||||
"to submit it on my behalf. My signature will be used solely to complete this " +
|
||||
"filing and will not be reused for any other document or purpose. The signature " +
|
||||
"applied will be my own signature, made with my authorization and with the " +
|
||||
"intent to sign this document.";
|
||||
|
||||
/** Is this document on the ink-reproduction path? (metadata.ink_reproduction). */
|
||||
export function isInkReproduction(documentMetadata: unknown): boolean {
|
||||
return !!documentMetadata
|
||||
&& typeof documentMetadata === "object"
|
||||
&& (documentMetadata as Record<string, unknown>).ink_reproduction === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this signing attempt REQUIRE the ink-reproduction consent?
|
||||
* Only a DRAWN signature on an ink-path document needs it (a typed signature is
|
||||
* not reproduced as the signer's own hand, so it is exempt).
|
||||
*/
|
||||
export function inkConsentRequired(
|
||||
documentMetadata: unknown,
|
||||
signatureType: "drawn" | "typed" | string | undefined,
|
||||
): boolean {
|
||||
return isInkReproduction(documentMetadata) && signatureType === "drawn";
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the consent satisfied for this attempt? True when not required, or when
|
||||
* required and the signer explicitly gave it (ink_consent === true).
|
||||
*/
|
||||
export function inkConsentSatisfied(
|
||||
documentMetadata: unknown,
|
||||
signatureType: "drawn" | "typed" | string | undefined,
|
||||
inkConsent: unknown,
|
||||
): boolean {
|
||||
if (!inkConsentRequired(documentMetadata, signatureType)) return true;
|
||||
return inkConsent === true;
|
||||
}
|
||||
|
|
@ -22,6 +22,11 @@
|
|||
import { Router, type Request, type Response } from "express";
|
||||
import { pool } from "../db.js";
|
||||
import { requirePortalAuth } from "../middleware/portalAuth.js";
|
||||
import {
|
||||
INK_CONSENT_TEXT,
|
||||
isInkReproduction,
|
||||
inkConsentSatisfied,
|
||||
} from "./esign-ink-consent.js";
|
||||
|
||||
const router = Router();
|
||||
const WORKER_URL = process.env.WORKER_URL || "http://workers:8090";
|
||||
|
|
@ -93,12 +98,21 @@ router.get("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res:
|
|||
// Pull order number for subtitle (e.g. "CO-ABC12345")
|
||||
const metadata = rec.document_metadata || {};
|
||||
|
||||
// Ink-reproduction path: when this document's signature will be reproduced in
|
||||
// real ink on the printed form (pen-plotter / Standard CMS filing path), the
|
||||
// signing page must collect an explicit, per-document consent to reproduce the
|
||||
// drawn signature in ink BEFORE capturing it. See
|
||||
// docs/legal/remote-mechanical-wet-signature-precedent.md.
|
||||
const inkReproduction = isInkReproduction(metadata);
|
||||
|
||||
res.json({
|
||||
document_title: rec.document_title,
|
||||
entity_name: rec.entity_name,
|
||||
order_number: rec.order_number,
|
||||
document_url: documentUrl,
|
||||
requires_perjury: rec.requires_perjury || false,
|
||||
ink_reproduction: inkReproduction,
|
||||
ink_consent_text: inkReproduction ? INK_CONSENT_TEXT : null,
|
||||
metadata,
|
||||
});
|
||||
} catch (err) {
|
||||
|
|
@ -116,10 +130,11 @@ router.get("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res:
|
|||
router.post("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res: Response) => {
|
||||
const { order_id: orderNumber, order_type: documentType, email } = req.portalAuth!;
|
||||
|
||||
const { signature, agreed_at, user_agent } = req.body as {
|
||||
const { signature, agreed_at, user_agent, ink_consent } = req.body as {
|
||||
signature?: { type: "drawn" | "typed"; image_b64?: string; name?: string; vector?: any };
|
||||
agreed_at?: string;
|
||||
user_agent?: string;
|
||||
ink_consent?: boolean;
|
||||
};
|
||||
|
||||
// Validate signature
|
||||
|
|
@ -146,7 +161,8 @@ router.post("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res:
|
|||
try {
|
||||
// Find the pending record
|
||||
const { rows } = await pool.query(
|
||||
`SELECT id, order_number, document_type, status, requires_perjury
|
||||
`SELECT id, order_number, document_type, status, requires_perjury,
|
||||
document_metadata, signed_at
|
||||
FROM esign_records
|
||||
WHERE order_number = $1
|
||||
AND document_type = $2
|
||||
|
|
@ -169,6 +185,19 @@ router.post("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res:
|
|||
return;
|
||||
}
|
||||
|
||||
// Ink-reproduction gate: if this document's signature will be reproduced in
|
||||
// ink on the official paper form, a drawn signature REQUIRES the explicit
|
||||
// per-document ink-reproduction consent (the legal linchpin — see
|
||||
// docs/legal/remote-mechanical-wet-signature-precedent.md). A typed signature
|
||||
// is not reproduced as the signer's own hand, so it does not need this gate.
|
||||
const inkReproduction = isInkReproduction(rec.document_metadata);
|
||||
if (!inkConsentSatisfied(rec.document_metadata, signature.type, ink_consent)) {
|
||||
res.status(400).json({
|
||||
error: "Please confirm the authorization to use your signature on the official form.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the signature
|
||||
const sigData = signature.type === "drawn"
|
||||
? signature.image_b64!.replace(/^data:image\/png;base64,/, "")
|
||||
|
|
@ -194,6 +223,10 @@ router.post("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res:
|
|||
const clientIp = (req as any).clientIp || req.ip || "";
|
||||
const signedAt = new Date().toISOString();
|
||||
|
||||
// Record the ink-reproduction consent (only meaningful when this document is
|
||||
// on the ink-reproduction path and the signer drew their signature).
|
||||
const inkConsentGiven = inkReproduction && signature.type === "drawn" && ink_consent === true;
|
||||
|
||||
await pool.query(
|
||||
`UPDATE esign_records
|
||||
SET status = 'signed',
|
||||
|
|
@ -205,8 +238,11 @@ router.post("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res:
|
|||
signer_user_agent = $6,
|
||||
signed_at = $7,
|
||||
perjury_agreed = $8,
|
||||
ink_consent = $9,
|
||||
ink_consent_at = $10,
|
||||
ink_consent_text = $11,
|
||||
updated_at = NOW()
|
||||
WHERE id = $9`,
|
||||
WHERE id = $12`,
|
||||
[
|
||||
signature.type,
|
||||
sigData,
|
||||
|
|
@ -216,6 +252,9 @@ router.post("/api/v1/portal/esign", requirePortalAuth, async (req: Request, res:
|
|||
user_agent || "",
|
||||
signedAt,
|
||||
rec.requires_perjury ? true : false,
|
||||
inkConsentGiven,
|
||||
inkConsentGiven ? signedAt : null,
|
||||
inkConsentGiven ? INK_CONSENT_TEXT : null,
|
||||
rec.id,
|
||||
],
|
||||
);
|
||||
|
|
|
|||
58
api/test/test_esign_ink_consent.ts
Normal file
58
api/test/test_esign_ink_consent.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Unit tests for the ink-reproduction consent gate (pure logic, no DB).
|
||||
* Run: npx tsx api/test/test_esign_ink_consent.ts
|
||||
*/
|
||||
import assert from "node:assert";
|
||||
import {
|
||||
INK_CONSENT_TEXT,
|
||||
isInkReproduction,
|
||||
inkConsentRequired,
|
||||
inkConsentSatisfied,
|
||||
} from "../src/routes/esign-ink-consent.js";
|
||||
|
||||
let pass = 0;
|
||||
const ok = (name: string, cond: boolean) => {
|
||||
assert.ok(cond, name);
|
||||
pass++;
|
||||
};
|
||||
|
||||
// --- isInkReproduction ---
|
||||
ok("ink meta true", isInkReproduction({ ink_reproduction: true }) === true);
|
||||
ok("ink meta false", isInkReproduction({ ink_reproduction: false }) === false);
|
||||
ok("ink meta missing", isInkReproduction({}) === false);
|
||||
ok("ink meta null", isInkReproduction(null) === false);
|
||||
ok("ink meta undefined", isInkReproduction(undefined) === false);
|
||||
ok("ink meta string-truthy not enough", isInkReproduction({ ink_reproduction: "true" }) === false);
|
||||
|
||||
// --- inkConsentRequired: only DRAWN on ink-path docs ---
|
||||
ok("required: drawn + ink", inkConsentRequired({ ink_reproduction: true }, "drawn") === true);
|
||||
ok("not required: typed + ink", inkConsentRequired({ ink_reproduction: true }, "typed") === false);
|
||||
ok("not required: drawn + non-ink", inkConsentRequired({ ink_reproduction: false }, "drawn") === false);
|
||||
ok("not required: drawn + no meta", inkConsentRequired({}, "drawn") === false);
|
||||
ok("not required: undefined type", inkConsentRequired({ ink_reproduction: true }, undefined) === false);
|
||||
|
||||
// --- inkConsentSatisfied ---
|
||||
// exempt cases (consent not required) are always satisfied regardless of flag
|
||||
ok("satisfied: typed exempt (no consent)", inkConsentSatisfied({ ink_reproduction: true }, "typed", undefined) === true);
|
||||
ok("satisfied: non-ink drawn exempt", inkConsentSatisfied({ ink_reproduction: false }, "drawn", undefined) === true);
|
||||
ok("satisfied: no-meta drawn exempt", inkConsentSatisfied({}, "drawn", false) === true);
|
||||
// required cases: must have ink_consent === true
|
||||
ok("blocked: drawn+ink, no consent", inkConsentSatisfied({ ink_reproduction: true }, "drawn", undefined) === false);
|
||||
ok("blocked: drawn+ink, consent false", inkConsentSatisfied({ ink_reproduction: true }, "drawn", false) === false);
|
||||
ok("blocked: drawn+ink, consent truthy-but-not-true", inkConsentSatisfied({ ink_reproduction: true }, "drawn", "true") === false);
|
||||
ok("blocked: drawn+ink, consent 1 (not strict true)", inkConsentSatisfied({ ink_reproduction: true }, "drawn", 1) === false);
|
||||
ok("allowed: drawn+ink, consent true", inkConsentSatisfied({ ink_reproduction: true }, "drawn", true) === true);
|
||||
|
||||
// --- consent text: client-safe (no internal mechanics) ---
|
||||
const banned = ["plotter", "machine", "CMS", "855", "10114", "MAC", "Baltimore", "PO Box", "robot"];
|
||||
for (const w of banned) {
|
||||
ok(`consent text omits "${w}"`, !INK_CONSENT_TEXT.toLowerCase().includes(w.toLowerCase()));
|
||||
}
|
||||
// --- consent text: legally required reassurances present ---
|
||||
ok("consent says 'ink one time'", INK_CONSENT_TEXT.includes("in ink one time"));
|
||||
ok("consent says 'single form'", INK_CONSENT_TEXT.includes("single form"));
|
||||
ok("consent says 'not be reused'", INK_CONSENT_TEXT.includes("will not be reused"));
|
||||
ok("consent says 'my own signature'", INK_CONSENT_TEXT.includes("my own signature"));
|
||||
ok("consent says 'intent to sign'", INK_CONSENT_TEXT.includes("intent to sign"));
|
||||
|
||||
console.log(`\nesign ink-consent: ${pass} checks passed`);
|
||||
214
docs/legal/patent-risk-mechanical-wet-signature.md
Normal file
214
docs/legal/patent-risk-mechanical-wet-signature.md
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
# Patent-risk analysis: remote signature capture + mechanical wet-ink reproduction
|
||||
|
||||
Status: internal patent-RISK research. **Not legal advice and not a freedom-to-
|
||||
operate (FTO) opinion.** A real FTO requires a patent attorney to search live
|
||||
claims and read them against our implementation. This memo identifies where risk
|
||||
is low (mature/expired prior art) vs. where a targeted FTO search is warranted.
|
||||
|
||||
Companion docs: `docs/legal/remote-mechanical-wet-signature-precedent.md`,
|
||||
`docs/plans/remote-wet-signature-products.md`.
|
||||
|
||||
What we do (the accused "system," broken into components):
|
||||
1. Capture a hand-drawn signature in a browser as **vector strokes** (x, y, t).
|
||||
2. Store it + an audit trail (`esign_records`), send a JWT no-login signing link.
|
||||
3. Generate/fill a PDF form; stamp a digital signature image (existing path).
|
||||
4. **Reproduce the captured strokes in real ink on paper with a pen plotter /
|
||||
Line-us arm** (the novel-feeling part).
|
||||
5. Mail/file the document; track fulfillment.
|
||||
|
||||
## TL;DR
|
||||
|
||||
- **Components 1-3 and 5** are ubiquitous, decades-old techniques (signature
|
||||
capture, e-sign workflow, PDF fill, mailing automation). **Patent risk: low**,
|
||||
but the *e-signature workflow* space is litigious (DocuSign, RPost, etc.), so a
|
||||
targeted FTO on any **specific workflow feature** is prudent before scaling.
|
||||
- **Component 4** (drawing a signature in ink with a machine) sits on **very old,
|
||||
expired prior art**: signature-duplicating machines (Hawkins polygraph, 1803),
|
||||
autopens (1930s-1942), pen plotters (1950s-1980s), CNC/G-code motion (1950s+).
|
||||
The *idea* of a machine writing a signature is firmly in the public domain.
|
||||
**Patent risk on the core concept: low.**
|
||||
- The realistic risk is **not** the broad concept but **narrow, recent claims**
|
||||
on a *specific combination/method* (e.g. "capture an e-signature online, then
|
||||
transmit it to a robotic pen to apply wet ink for a regulated filing"). A
|
||||
firsthand title sweep of 1.58M recent grants (2021-2025) found **no such
|
||||
patent**; the e-sign players (DocuSign/Adobe/Silanis) hold only *electronic*
|
||||
workflow patents. This reduces, but does not eliminate, the need for an
|
||||
attorney FTO (which must also cover pre-2021 and pending claims).
|
||||
|
||||
## 1. Signature capture (vector strokes in a browser)
|
||||
|
||||
- **Prior art is overwhelming and old.** Electronic signature pads (Topaz,
|
||||
Wacom), tablet/stylus capture, and HTML5 `<canvas>` signature capture have been
|
||||
standard for 15-25+ years. Capturing (x, y, t) stroke data is basic digitizer
|
||||
technique predating the web.
|
||||
- **Risk: low** for the act of capturing strokes. Any patent broad enough to
|
||||
cover "capture a signature on a touchscreen" would almost certainly be invalid
|
||||
over prior art. Avoid copying a *specific patented capture algorithm* (e.g. a
|
||||
particular biometric/pressure-analysis method) - we use plain stroke capture.
|
||||
|
||||
## 2. E-signature workflow + audit trail + no-login link (JWT)
|
||||
|
||||
- **This is the litigious zone of the three software components.** Companies with
|
||||
e-signature/e-delivery patent portfolios have actively asserted them:
|
||||
- **RPost** (Registered Email / e-signature patents) has a history of
|
||||
licensing/litigation around proof-of-delivery and e-signature.
|
||||
- **DocuSign** holds many patents on signing *workflows*, "envelopes," routing,
|
||||
and tamper-evidence.
|
||||
- **But:** post-*Alice v. CLS Bank* (573 U.S. 208 (2014)), many abstract
|
||||
"do-it-on-a-computer" software/business-method patents are vulnerable to
|
||||
invalidation under 35 U.S.C. § 101. Generic e-sign workflow claims have been
|
||||
narrowed/invalidated.
|
||||
- **Risk: low-moderate.** Our workflow (JWT link, store signature + IP/UA/time,
|
||||
resume a pipeline) is generic and well-trodden. The risk is asserting a
|
||||
*specific patented feature* (e.g. a particular tamper-evidence/seal method, a
|
||||
specific routing/orchestration claim). **Do a targeted FTO before building any
|
||||
distinctive workflow feature** (sequential multi-party routing, a proprietary
|
||||
audit-seal, etc.).
|
||||
|
||||
## 3. PDF generation / form fill / digital stamp
|
||||
|
||||
- **Ubiquitous, mature.** Filling AcroForm fields and overlaying an image onto a
|
||||
PDF is standard library functionality (pypdf, reportlab, PDFBox, etc.) used by
|
||||
millions of applications. **Risk: low.**
|
||||
|
||||
## 4. Mechanical wet-ink reproduction (pen plotter / robotic arm)
|
||||
|
||||
This is the component that *feels* novel but is built on the oldest prior art:
|
||||
|
||||
- **Signature-duplicating machines: 1803.** John Isaac Hawkins patented the
|
||||
"polygraph" pantograph signature copier (used extensively by Jefferson).
|
||||
- **Autopen: 1930s-1942.** The "Robot Pen" (1930s) and De Shazo's commercially
|
||||
successful autopen (1942, Navy order) mass-reproduced signatures in ink. By
|
||||
mid-century there were 500+ autopens in Washington.
|
||||
- **Pen plotters: 1950s-1980s.** X-Y pen plotters (Calcomp 1959; HP 7470A 1980s)
|
||||
draw arbitrary vector paths in ink - exactly our motion problem.
|
||||
- **CNC / G-code motion control: 1950s+.** Numerically-controlled multi-axis
|
||||
motion (which a CR-10/3D printer is) is decades old.
|
||||
- (Sources: Wikipedia "Autopen," retrieved firsthand; well-documented hardware
|
||||
history.)
|
||||
- **Conclusion:** "a machine that applies a person's signature in ink by
|
||||
following a stored path" is **firmly in the public domain.** No one can hold a
|
||||
valid patent on the *concept*. **Risk on the core mechanism: low.**
|
||||
|
||||
Residual risk is only on **narrow modern improvements**, e.g.:
|
||||
- A *specific* signature-reproduction algorithm (pressure/velocity modeling to
|
||||
mimic natural handwriting) that someone recently patented. We use plain
|
||||
fit-to-box stroke replay; avoid copying a specific patented "natural-motion"
|
||||
method without checking.
|
||||
- A *specific combined method claim* tying online e-signature capture to a
|
||||
robotic ink applicator for filings. We found no such patent, but this is the
|
||||
one area an attorney FTO should specifically probe (handwriting-robot startups,
|
||||
e.g. companies in the "robot-written mail" space, may hold method patents on
|
||||
*their* pipelines - those would be narrow to their implementations).
|
||||
|
||||
## 5. Mailing / fulfillment automation
|
||||
|
||||
- **Mature and generic.** Batch mailing, address routing, print-and-mail
|
||||
workflows are old and widely practiced. **Risk: low.** (Note RPost-style
|
||||
proof-of-delivery patents exist; we are not claiming a proprietary e-delivery
|
||||
receipt mechanism.)
|
||||
|
||||
## Recent-grant prior-art sweep (2021-2025, firsthand query)
|
||||
|
||||
To probe the one real risk zone (a recent method patent tying online signature
|
||||
capture to robotic ink application, and e-sign workflow patents our flow might
|
||||
read on), we ran a firsthand title search against a full USPTO grant dataset
|
||||
(PatentsView-derived, **1,575,344 patents granted 2021-06 to 2025-09**). This is
|
||||
a **title-level** sweep of recent grants, **not** a claims-level FTO, and it does
|
||||
not cover pre-2021 art - but it directly tests "did anyone recently patent our
|
||||
specific combination?" The answer is essentially no:
|
||||
|
||||
- **Signature + (ink|pen|stylus|wet) + (robot|machine|apparatus|device): 0
|
||||
relevant hits.** All matches were unrelated (wireless-comms "signatures,"
|
||||
printer ink-color flushing). **No "machine applies a signature in ink" patent
|
||||
appears in 5 years of grants.**
|
||||
- **autopen: 0 hits. signature+plotter: 0. signature+reproduce: 0.**
|
||||
- **"writing/drawing robot": only ornamental DESIGN patents** (D1090657,
|
||||
D1058630 - cover a device's *appearance*, not a signing method) plus an
|
||||
unrelated Yaskawa robot-withdrawal utility patent. Nothing signature-specific.
|
||||
- **"handwriting + machine": only handwriting *extraction* (reading) via ML**
|
||||
(12175799), i.e. the opposite of writing.
|
||||
- **E-signature workflow space is owned by the expected players, all purely
|
||||
electronic (no ink/paper):**
|
||||
- **DocuSign, Inc.** - many recent grants on document models, recipient
|
||||
routing/notification, OCR, contract generation (e.g. 12411896 "Document
|
||||
graph"; RE50043 associating third-party content with online signing).
|
||||
- **Adobe Inc.** - electronic-signature *capture UI* (11132105, 11159328,
|
||||
11750670 "signature collection within an online conference").
|
||||
- **Silanis Technology, Inc.** - 11093652 "Web-based method and system for
|
||||
applying a legally enforceable signature on an electronic document" (the
|
||||
single closest hit by name) - but it is **purely electronic**; it does not
|
||||
apply ink to paper, so it does not read on our mechanical reproduction.
|
||||
- **Tamper/seal:** 11743053 "Electronic signature system and tamper-resistant
|
||||
device" - electronic; relevant only if we built a proprietary tamper-seal
|
||||
(we do not).
|
||||
|
||||
**Interpretation:** the recent-patent landscape is consistent with the prior-art
|
||||
analysis above. The litigious e-sign players hold **electronic-workflow** patents
|
||||
(read them before building any distinctive workflow feature), but **no one holds
|
||||
a recent patent on reproducing a captured signature in ink with a machine for a
|
||||
filing** - our specific, novel-feeling combination. This *reduces* (does not
|
||||
eliminate) the residual risk flagged in section 4.
|
||||
|
||||
**Caveats:** (1) title-only, recent-grant (2021-2025) - misses pre-2021 patents
|
||||
(many e-sign core patents predate 2021) and any in-force patent whose title does
|
||||
not contain these words; (2) does not read claims; (3) does not cover pending
|
||||
applications. **Still not an FTO** - a patent attorney must read live claims
|
||||
(including pre-2021 and pending) against our implementation before launch.
|
||||
|
||||
Method: queried the patent grant parquet directly via DuckDB (title LIKE
|
||||
filters). Dataset coverage and the specific hits above were retrieved firsthand
|
||||
2026-06-07.
|
||||
|
||||
## What would actually create exposure (avoid these without FTO)
|
||||
|
||||
1. Copying a **named competitor's specific feature** (a particular tamper-seal,
|
||||
a specific multi-party routing UX, a specific "robot handwriting realism"
|
||||
algorithm).
|
||||
2. Implementing a **distinctive method** and marketing it as such (the more
|
||||
specific/novel our claim of novelty, the more likely it overlaps a method
|
||||
patent - and the more attractive we are as a target).
|
||||
3. Building the **RON / e-notary** layer without checking notary-tech patents.
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. **Now (low cost):** keep the implementation **generic and built on
|
||||
public-domain techniques** (plain stroke capture, standard PDF libs, standard
|
||||
G-code/CNC motion, autopen-class reproduction). This is our best defense - we
|
||||
are practicing decades-old art.
|
||||
2. **Before scaling / raising / public launch:** commission a **freedom-to-operate
|
||||
search** from a patent attorney focused on two questions:
|
||||
(a) any live method patent tying **online e-signature capture → robotic ink
|
||||
application** for documents/filings; (b) any e-signature **workflow** patent
|
||||
our specific features might read on.
|
||||
3. **Defensive posture:** document our **prior-art basis** (this memo + the
|
||||
hardware history) so that if anyone asserts a broad patent, we can argue
|
||||
invalidity/non-infringement. Consider a **defensive publication** of our
|
||||
pipeline (a public description) to create prior art preventing others from
|
||||
patenting the same combination later.
|
||||
4. **Do not patent-troll-bait:** avoid over-claiming novelty in marketing; frame
|
||||
the service as "we file your documents with an original ink signature," not as
|
||||
a patented invention.
|
||||
|
||||
## Honest limits of this memo
|
||||
|
||||
- We ran a **title-level sweep of recent grants (2021-2025)** (see section above)
|
||||
but did **not** run a full **claims-level** search across all years; reading
|
||||
live claims against our code requires a patent attorney. The recent-grant sweep
|
||||
supports the low-risk *concept* assessment but is **not an FTO clearance.**
|
||||
- Patent risk is jurisdiction- and claim-specific and changes as patents issue;
|
||||
re-check before any major launch.
|
||||
|
||||
## Sources
|
||||
|
||||
- Prior-art hardware history (autopen 1803/1930s/1942, plotters, CNC):
|
||||
Wikipedia "Autopen" (retrieved firsthand 2026-06-07), corroborated by
|
||||
general hardware history.
|
||||
- *Alice Corp. v. CLS Bank Int'l*, 573 U.S. 208 (2014) - § 101 abstract-idea
|
||||
invalidity framework (well-known; confirm citation before reliance).
|
||||
- Litigious-player context (RPost, DocuSign patent portfolios): general industry
|
||||
knowledge plus the firsthand recent-grant sweep above (DocuSign/Adobe/Silanis
|
||||
hits) - confirm specifics with counsel before relying.
|
||||
- Recent-grant prior-art sweep: USPTO patent grant dataset (PatentsView-derived,
|
||||
2021-2025, 1.58M patents), queried firsthand via DuckDB 2026-06-07. Specific
|
||||
patent numbers cited above are real hits from that dataset.
|
||||
|
|
@ -375,7 +375,7 @@ class _BaseNPIHandler:
|
|||
customer_name=provider,
|
||||
document_minio_key=document_key,
|
||||
requires_perjury=True,
|
||||
metadata={"service_slug": self.SERVICE_SLUG, "npi": intake.get("npi", ""), "form_type": form_type},
|
||||
metadata={"service_slug": self.SERVICE_SLUG, "npi": intake.get("npi", ""), "form_type": form_type, "ink_reproduction": True},
|
||||
expires_hours=21 * 24,
|
||||
)
|
||||
# request_esign does not persist signature anchors; attach them so
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f1f5f9;l
|
|||
#loading{text-align:center;padding:4rem 1rem;color:#64748b}
|
||||
#error-screen{display:none;text-align:center;padding:3rem 1rem}
|
||||
.hidden{display:none}
|
||||
/* Signature block locked until ink-reproduction consent is given */
|
||||
#sig-block.locked{opacity:.45;pointer-events:none;filter:grayscale(.3)}
|
||||
</style>
|
||||
<script defer src="https://analytics.performancewest.net/script.js" data-website-id="55250014-ee15-44ac-a1f6-81dabad3fe0f"></script><script defer src="/js/pw-analytics.js"></script></head>
|
||||
<body>
|
||||
|
|
@ -76,6 +78,19 @@ body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f1f5f9;l
|
|||
<!-- Step 2: Sign -->
|
||||
<div class="card">
|
||||
<h2>Step 2 — Your Signature</h2>
|
||||
|
||||
<!-- Ink-reproduction consent gate (shown only for ink-path documents).
|
||||
The signer must authorize use of their signature on the official form
|
||||
BEFORE they can draw it. -->
|
||||
<div id="ink-consent-box" class="hidden" style="padding:1rem;background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;margin-bottom:1rem">
|
||||
<p style="font-size:.85rem;color:#1f2937;margin:0 0 .75rem" id="ink-consent-text"></p>
|
||||
<label style="display:flex;gap:.6rem;align-items:flex-start;font-size:.85rem;color:#1f2937;cursor:pointer">
|
||||
<input type="checkbox" id="ink-consent-chk" style="margin-top:2px;flex-shrink:0;width:16px;height:16px;accent-color:#1e3a5f">
|
||||
<span>I authorize this and confirm the signature I draw is my own.</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="sig-block">
|
||||
<div class="sig-tabs">
|
||||
<button type="button" class="sig-tab active" data-mode="draw">Draw</button>
|
||||
<button type="button" class="sig-tab" data-mode="type">Type</button>
|
||||
|
|
@ -95,6 +110,7 @@ body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f1f5f9;l
|
|||
<input type="text" id="typed-sig" placeholder="Your full name" autocomplete="name">
|
||||
<div class="typed-preview" id="typed-preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Confirm -->
|
||||
|
|
@ -190,6 +206,16 @@ body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f1f5f9;l
|
|||
document.getElementById("perjury-box").classList.remove("hidden");
|
||||
}
|
||||
|
||||
// Ink-reproduction consent gate: for documents whose signature will be
|
||||
// reproduced in ink on the official paper form, require an explicit
|
||||
// per-document authorization BEFORE the signer can draw. Lock the signature
|
||||
// block until the consent box is checked.
|
||||
if (data.ink_reproduction && data.ink_consent_text) {
|
||||
document.getElementById("ink-consent-text").textContent = data.ink_consent_text;
|
||||
document.getElementById("ink-consent-box").classList.remove("hidden");
|
||||
document.getElementById("sig-block").classList.add("locked");
|
||||
}
|
||||
|
||||
// PDF preview
|
||||
if (data.document_url) {
|
||||
document.getElementById("pdf-container").innerHTML =
|
||||
|
|
@ -318,9 +344,31 @@ body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f1f5f9;l
|
|||
var agreeChk = document.getElementById("agree-chk");
|
||||
agreeChk.addEventListener("change", updateSubmit);
|
||||
|
||||
// ── Ink-reproduction consent: unlock the signature block when authorized ──
|
||||
var inkConsentChk = document.getElementById("ink-consent-chk");
|
||||
inkConsentChk.addEventListener("change", function() {
|
||||
var data = window._esignData || {};
|
||||
var needsInk = !!(data.ink_reproduction && data.ink_consent_text);
|
||||
if (needsInk) {
|
||||
document.getElementById("sig-block").classList.toggle("locked", !this.checked);
|
||||
// If they un-check after drawing, clear the now-unauthorized capture.
|
||||
if (!this.checked) document.getElementById("sig-clear").click();
|
||||
}
|
||||
updateSubmit();
|
||||
});
|
||||
|
||||
function inkConsentOk() {
|
||||
var data = window._esignData || {};
|
||||
var needsInk = !!(data.ink_reproduction && data.ink_consent_text);
|
||||
// Ink consent is only required for a DRAWN signature on an ink-path doc.
|
||||
if (!needsInk || sigMode !== "draw") return true;
|
||||
return inkConsentChk.checked;
|
||||
}
|
||||
|
||||
function updateSubmit() {
|
||||
var hasSignature = sigMode === "draw" ? hasSig : typedInput.value.trim().length >= 2;
|
||||
document.getElementById("submit-btn").disabled = !(hasSignature && agreeChk.checked);
|
||||
document.getElementById("submit-btn").disabled =
|
||||
!(hasSignature && agreeChk.checked && inkConsentOk());
|
||||
}
|
||||
|
||||
document.getElementById("submit-btn").addEventListener("click", async function() {
|
||||
|
|
@ -353,6 +401,7 @@ body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f1f5f9;l
|
|||
signature: signatureData,
|
||||
agreed_at: new Date().toISOString(),
|
||||
user_agent: navigator.userAgent,
|
||||
ink_consent: inkConsentOk() && sigMode === "draw" && inkConsentChk.checked,
|
||||
}),
|
||||
});
|
||||
var result = await resp.json();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue