Skip to content

Commit c80d7bb

Browse files
authored
Merge pull request #189 from Open-Document-Alliance/codex/lumin-public-workflow-20260905
Expose consent-gated Lumin signing workflow
2 parents b3f191d + afd5474 commit c80d7bb

26 files changed

Lines changed: 3381 additions & 106 deletions

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ separate example to locate.
1616
### Tools currently shipped
1717
This is the complete registered set, not a selection.
1818

19-
- `display_pdf`, `list_pdfs`, `read_pdf_fields`, `fill_pdf`, `bulk_fill_from_csv`, `save_profile`, `load_profile`, `list_profiles`, `fill_with_profile`, `extract_to_csv`, `validate_pdf`, `read_pdf_content`, `read_pdf_pages`, `read_pdf_layout`, `search_pdf_text`, `convert_pdf_to_markdown`, `verify_table_proposal`, `get_pdf_identity`, `get_pdf_info`, `get_page_analysis`, `inspect_pdf_accessibility`, `compare_pdfs`, `render_pdf_page`, `render_pdf_region`, `fetch_pdf_from_url`, `merge_pdfs`, `split_pdf`, `rotate_pdf_pages`, `reorder_pdf_pages`, `apply_page_plan`, `create_signature`, `list_signatures`, `load_signature`, `detect_signature_zones`, `add_signature_field`, `prepare_signing_packet`, `apply_signature`, `apply_text`, `get_active_document`, `set_active_document`, `get_allowed_directories`, `get_pdf_resource_uri`, `create_extraction_workspace`, `inspect_extraction_state`, `read_extraction_workspace`, `read_extraction_chunk`, `submit_extraction_proposal`, `verify_extraction_proposal`, `delete_extraction_workspace`, `reveal_in_finder`, `read_pdf_bytes` (app-only).
19+
- `display_pdf`, `list_pdfs`, `read_pdf_fields`, `fill_pdf`, `bulk_fill_from_csv`, `save_profile`, `load_profile`, `list_profiles`, `fill_with_profile`, `extract_to_csv`, `validate_pdf`, `read_pdf_content`, `read_pdf_pages`, `read_pdf_layout`, `search_pdf_text`, `convert_pdf_to_markdown`, `verify_table_proposal`, `get_pdf_identity`, `get_pdf_info`, `get_page_analysis`, `inspect_pdf_accessibility`, `compare_pdfs`, `render_pdf_page`, `render_pdf_region`, `fetch_pdf_from_url`, `merge_pdfs`, `split_pdf`, `rotate_pdf_pages`, `reorder_pdf_pages`, `apply_page_plan`, `create_signature`, `list_signatures`, `load_signature`, `detect_signature_zones`, `add_signature_field`, `prepare_signing_packet`, `apply_signature`, `apply_text`, `start_lumin_authorization`, `finish_lumin_authorization`, `prepare_lumin_request`, `send_lumin_request`, `check_lumin_status`, `download_lumin_artifact`, `get_active_document`, `set_active_document`, `get_allowed_directories`, `get_pdf_resource_uri`, `create_extraction_workspace`, `inspect_extraction_state`, `read_extraction_workspace`, `read_extraction_chunk`, `submit_extraction_proposal`, `verify_extraction_proposal`, `delete_extraction_workspace`, `reveal_in_finder`, `read_pdf_bytes` (app-only).
2020

2121
`get_pdf_info` returns bounded source-bound observations. Widget annotations
2222
belong to form fields; ordinary annotations remain separate and their targets

CLAUDE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,15 +412,17 @@ future local OCR engine is optional planned work, not a current capability.
412412
The signature tools implement a **two-tier model** agreed with Max Ferguson on 2026-04-09:
413413

414414
- **Tier 1 (this repo, local, free)**: Visible stamp via pdf-lib. `apply_signature` stamps a saved signature + writes an audit trail to PDF metadata. NOT legally-binding. NOT cryptographic.
415-
- **Tier 2 (Lumin API handoff, future)**: Cryptographic signing with timestamp and certificate. `request_lumin_signature` will route prepared packets to Lumin.
415+
- **Tier 2 (optional Lumin API handoff)**: The six `*_lumin_*` workflow tools connect a public PKCE client, preview an exact prepared packet, send one confirmed request without automatic retry, poll status, and download a completed artifact. The PDF and listed recipient details leave the device only at the confirmed send step. Lumin, not PDF Tools, provides the remote signing service and its completion certificate.
416416

417417
**Human-intent constraint** (critical): `apply_signature` requires `user_intent_statement` + `user_confirmed_at` (ISO-8601, within last 24h). This is a legal requirement per Max: *"there's gotta be intent. Having the agent just kind of go and stamp signatures on a document without someone telling it to is not really allowed."* Agents MUST obtain these from the user and never fabricate. The validation enforces length/recency sanity checks; the intent is stored in PDF Keywords metadata for audit.
418418

419419
**Coordinate system**: All signature tools use **top-left origin** (x from left, y from top) in PDF points (72pt = 1 inch). Internally converted to pdf-lib's bottom-left — agents/users never need to think about it.
420420

421421
**Agent-safe vs human-gated split**:
422422
- Agent-safe (no intent check): `create_signature`, `list_signatures`, `add_signature_field`, `prepare_signing_packet`
423-
- Human-gated (requires intent): `apply_signature` only
423+
- Human-gated (requires intent): `apply_signature` and `send_lumin_request`. The
424+
Lumin send tool validates exact recent confirmation values, but the host and
425+
agent remain responsible for passing only values the user actually supplied.
424426

425427
## Code Standards
426428

README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Claude already knows how to read PDFs in limited ways. PDF Tools goes much furth
5656

5757
- **Interactive viewer:** page navigation, zoom, search, fullscreen, text selection, and form-field sidebar
5858
- **Form workflows:** `fill_pdf`, `read_pdf_fields`, `bulk_fill_from_csv`, and reusable profiles
59-
- **Sign mode:** signature/date zone detection, saved or drawn local signatures, text stamping, inspect-region, and preview-to-zone flows
59+
- **Sign mode:** signature/date zone detection, saved or drawn local signatures, text stamping, inspect-region, preview-to-zone flows, and optional consent-gated Lumin e-signing
6060
- **URL-to-PDF workflows:** fetch HTTP(S) PDF links to the local machine when sandboxed web fetches are blocked
6161
- **Page organization:** merge, split, rotate, reorder, and apply full page plans in one pass
6262
- **Extraction and analysis:** page-bounded reads, text search, page/region rendering, CSV export, page-level analysis, metadata, and validation
@@ -99,6 +99,37 @@ content, so the complete workflow is not necessarily zero egress.
9999
- Inspect a region, preview it, and turn it into a typed signing zone when automatic detection is not enough
100100
- Prepare a provider-neutral handoff receipt that binds the exact local input/output, typed zones, participant roles, page geometry, and unresolved inputs without contacting a signing provider
101101
- Keep signing edits local, with active-document tracking and backup behavior for same-file mutations
102+
- Optionally connect a Lumin account with browser-based PKCE, preview the exact recipients and disclosure locally, and send the prepared PDF only after the user confirms the exact sending statement
103+
- Check an existing Lumin request by polling and download an agreement or completion certificate without exposing the temporary signed URL or replacing an existing local file
104+
105+
### Optional Lumin e-signing
106+
107+
Lumin e-signing is an explicit external workflow. The rest of PDF Tools stays
108+
local-first. PDF Tools contacts Lumin only when a Lumin tool is called. Sending
109+
requires a provider-ready `prepare_signing_packet` receipt, a local preview, a
110+
connected Lumin session, and the user's fresh verbatim confirmation. The
111+
prepared PDF plus listed names and email addresses then leave the device and are
112+
handled by Lumin.
113+
114+
PDF Tools can validate the exact confirmation text and its freshness, but it
115+
cannot independently prove who typed it. The MCP host must present the
116+
destructive tool action, and the agent must pass only the user's actual words
117+
and time. Agents must never fabricate either value.
118+
119+
Configure a public OAuth client ID in the extension's **Lumin OAuth Client ID**
120+
setting. Other stdio hosts may set `LUMIN_OAUTH_CLIENT_ID`; Agent Plugin users
121+
may set `luminOAuthClientId` in the plugin's private `config.json`. Register the
122+
exact redirect URI `http://127.0.0.1/callback`. The OAuth access token stays only
123+
in the running PDF Tools process. It is not returned, logged, or written to
124+
disk, and a restart requires connecting again.
125+
126+
The create call is one-shot and has no automatic retry. If the provider outcome
127+
is uncertain, PDF Tools preserves that uncertainty and will not create another
128+
request under the same authority. Status polling is the current desktop path.
129+
Lumin app webhooks require a private server app and are not part of this public
130+
PKCE workflow. The durable signing-operation store currently supports macOS and
131+
Linux. The public Lumin workflow fails closed on Windows until a reviewed
132+
ACL-aware state adapter exists.
102133

103134
### Page Organization Tools
104135

@@ -229,6 +260,12 @@ the `.mcpb` manifest that ordinary model workflows discover.
229260
- `detect_signature_zones`
230261
- `add_signature_field`
231262
- `prepare_signing_packet`
263+
- `start_lumin_authorization`
264+
- `finish_lumin_authorization`
265+
- `prepare_lumin_request`
266+
- `send_lumin_request`
267+
- `check_lumin_status`
268+
- `download_lumin_artifact`
232269
- `create_signature`
233270
- `list_signatures`
234271
- `load_signature`

docs/MAINTAINERS.md

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,19 @@ host and operating system being claimed.
7070
- PDF.js text extraction and canvas-backed page/region rendering are lazy loaded to avoid startup overhead.
7171
- Interactive viewer UI lives in `ui/`, built to `dist-ui/` via Vite.
7272

73-
### Lumin signing preparation
73+
### Optional Lumin signing workflow
7474

7575
`server/lumin-oauth-loopback.js` is an internal native-app OAuth helper.
7676
`server/lumin-sign-v1-mapper.js` validates and maps the narrow request shape,
7777
`server/lumin-sign-v1-transport.js` can execute one explicitly authorized
7878
direct-PDF request through an injected transport, and
7979
`server/lumin-sign-v1-operation.js` adds the private durable operation boundary.
80-
All four are packaged for source parity, but none is registered as an MCP tool
81-
or runs merely because the server starts. The mapper itself continues to return
82-
`transport_allowed: false`.
80+
`server/lumin-signing-tools.js` is the public orchestration layer. It registers
81+
six tools for browser authorization, local request preparation, one-shot send,
82+
status polling, and artifact download. None runs merely because the server
83+
starts. The mapper itself continues to return `transport_allowed: false`; only
84+
the public layer's fresh exact confirmation can construct the short-lived
85+
authority consumed by the durable wrapper.
8386
Calling the transport requires a separate, short-lived authority object that
8487
binds the exact prepared-PDF receipt, PDF digest, complete validated request
8588
mapping, mapper contract, and participant identities. The low-level transport
@@ -131,10 +134,10 @@ would require a separately approved server integration and signing-secret
131134
custody boundary.
132135

133136
Portable Node cannot verify the required private Windows ACL or open NTFS
134-
directories for `fsync`. The durable wrapper therefore fails closed on Windows
135-
until a separately reviewed ACL-aware state adapter exists. This does not limit
136-
the existing cross-platform PDF tools because the Lumin modules remain internal
137-
and unregistered.
137+
directories for `fsync`. The public Lumin workflow therefore fails closed on
138+
Windows before OAuth or operation state begins, until a separately reviewed
139+
ACL-aware state adapter exists. The existing local PDF tools remain
140+
cross-platform.
138141

139142
Lumin currently registers `http://127.0.0.1/callback` for public PKCE clients
140143
and ignores the loopback port when matching the redirect. At authorization
@@ -146,9 +149,10 @@ non-loopback listener, or a custom URI scheme without new provider evidence.
146149
The OAuth helper uses PKCE S256, a separate random state value, a one-use callback,
147150
strict callback parameter and Host validation, bounded inputs and responses,
148151
and a public-client token exchange with no client secret. It never writes or
149-
logs the verifier, authorization code, or returned tokens. Credential storage,
150-
refresh, revocation, callback verification, and provider artifact handling are
151-
separate incomplete product boundaries.
152+
logs the verifier, authorization code, or returned tokens. The public layer
153+
holds only the access token in process memory behind an opaque session ID,
154+
discards the refresh token, and requires a new browser connection after restart
155+
or expiry. Inputs, results, local operation state, and errors contain no token.
152156

153157
The direct-PDF transport is pinned to Lumin's exact multipart example at Git
154158
commit `cd8ddd73e32c016038691dad21d0e4594c8eeebb`. It accepts an access token only
@@ -168,11 +172,11 @@ publishes a new snapshot, fetch it to a temporary path, run
168172
projected change, then replace the fixture and pinned identity together. Do not
169173
refresh the fixture or projection from the network during CI.
170174

171-
This remains an internal vertical slice, not a shipped signing feature. There is
172-
no public tool, token store, callback listener, cancellation path, artifact
173-
download transaction, or release claim. The caller still owns secure state-root
174-
selection, OAuth token custody, webhook endpoint hosting, signing-secret custody,
175-
retention, and user-visible consent. The filesystem design assumes a trusted
175+
This is a source-complete public MCP workflow but not a released capability until
176+
its release gate is separately approved. It has no cancellation tool and no
177+
webhook endpoint. The public layer owns private state-root selection, ephemeral
178+
OAuth token custody, user-visible disclosure, exact confirmation, polling, and
179+
no-overwrite artifact download. The filesystem design assumes a trusted
176180
same-user and administrator boundary; it prevents accidental and concurrent
177181
reuse but is not a cryptographic defense against a hostile process with the same
178182
OS authority. Tests inject fake transports and must not make a live Lumin
@@ -389,7 +393,7 @@ mcp__<display_name, spaces underscored, non [A-Za-z0-9_-] stripped>__<tool_name>
389393
Identifiers over **64 characters** fail in the host. This shipped as a real
390394
defect (issue #44): the original benefit-led directory title
391395
`PDF Tools - Fill, Sign, Merge, Split, Extract` normalizes to 41 characters and
392-
pushes 23 of the current 50 packed tool identifiers past the ceiling.
396+
pushes 29 of the current 56 packed tool identifiers past the ceiling.
393397

394398
The naming strategy is therefore **dual**:
395399

@@ -405,7 +409,7 @@ Budgets are computed by `scripts/tool-identifier-budget.mjs` and gated in
405409

406410
- `PDF Tools`: longest identifier 43, headroom 21
407411
- `PDF Tools: Fill, Sign & Edit`: longest identifier 59, headroom 5
408-
- Original long title: longest identifier 75, 23 identifiers over the limit
412+
- Original long title: longest identifier 75, 29 identifiers over the limit
409413

410414
**The trap when adding a tool.** The shipped short brand has generous headroom,
411415
so a new long tool name will not break it and every host-facing check stays

docs/MCP_CONTRACT.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ template discovery is also unsupported and deterministically returns JSON-RPC
2727

2828
### Tools
2929

30-
The runtime returns 51 uniquely named tools. Every tool has an object input
30+
The runtime returns 57 uniquely named tools. Every tool has an object input
3131
schema plus `title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, and
3232
`openWorldHint` annotations. Annotations are user-interface hints, never an
3333
authorization boundary; path allowlists and signature-intent checks remain the
@@ -36,7 +36,7 @@ every tool in both runtime copies. The handler evidence and classification
3636
rules are recorded in
3737
[`TOOL_ANNOTATION_AUDIT_2026-07-21.md`](TOOL_ANNOTATION_AUDIT_2026-07-21.md).
3838

39-
The source manifest lists all 51 tools. The packed MCPB manifest lists the 50
39+
The source manifest lists all 57 tools. The packed MCPB manifest lists the 56
4040
normal model-workflow tools and omits `read_pdf_bytes`, whose runtime metadata
4141
marks it `ui.visibility: ["app"]`. `tools_generated: true` explicitly tells MCPB
4242
hosts that runtime discovery includes an additional tool. That visibility hint
@@ -53,7 +53,7 @@ Tools path policy. Consequently the active folder list is defense in depth, not
5353
a source-confidentiality boundary against a Full Access host. Content returned
5454
through MCP remains subject to the host and model provider's data terms.
5555

56-
Forty-seven tool handlers advertise strict `outputSchema` contracts and return
56+
Fifty-three tool handlers advertise strict `outputSchema` contracts and return
5757
`structuredContent`. They also return a human-readable `content` text block so
5858
non-Apps and older clients remain usable. Successful structured output is
5959
validated before it leaves the server, with separate generic and tool-specific
@@ -85,7 +85,7 @@ exact-output-identity preconditions. New evaluation suites must bind v3
8585
explicitly. The grader selects the allowlisted contract and trust registry
8686
declared by each suite, so historical evidence remains valid under its original
8787
stack and is not silently rescored. The six existing trajectory jobs do not
88-
constitute behavioral trajectory coverage of all 51 tools.
88+
constitute behavioral trajectory coverage of all 57 tools.
8989
`get_pdf_identity` is covered by its contract, handler, filesystem-race, and
9090
agent-workflow tests rather than by those six retained jobs.
9191

0 commit comments

Comments
 (0)