Skip to content

feat: sign several documents and several places with one PIN - #6

Merged
lorenzocorallo merged 1 commit into
mainfrom
feat/batch-signing
Oct 17, 2026
Merged

lorenzocorallo merged 1 commit into
mainfrom
feat/batch-signing

Conversation

@lorenzocorallo

Copy link
Copy Markdown
Member

What

Two things the dashboard needs, both with a single PIN:

  1. Several places in one document. Each place is its own incremental PDF signature, so the backend hands out one digest per step. The agent keeps the card session open and repeats prepare → card signature → complete until the backend answers completed.
  2. Several documents at once. The dashboard can open /sign?origin=…&ticket=t1&ticket=t2… with 1 to 10 tickets (repeats are dropped, order kept). The page lists the documents, asks for the PIN once and shows a result for each.

How

  • Backend client (internal/backend): Job gains signatures, marks, pages. Prepare always sends protocol: 2 and returns the digest with step/steps. Complete accepts only next or completed. If a backend sends no steps, its jobs are treated as one signature each.
  • Local API (internal/server): /api/job, /api/sign and /api/cancel take tickets: string[].
    • /api/job returns { jobs: [{ticket, job} | {ticket, error}] }, fetched in parallel. A failing ticket doesn't fail the call.
    • /api/sign fetches the jobs first (so it can size the timeout and skip ones that have already ended), then opens one session with the existing provider fallback rules (token.PINSent is unchanged: no retry and no fallback after the PIN was sent).
      • If the session doesn't open, Fail is called on every ticket and the token error is returned as before, retriesLeft included.
      • Otherwise it returns 200 { results: [{ticket, status: completed|failed|skipped, error?}], signer }. A backend error (document_changed, job_done, …) fails only that job, and Fail is called on it. A card error fails that job and skips the rest; Fail is called with the same code on each.
      • The agent only signs as many digests as the first prepare announced.
    • Timeout: 60 s + 30 s per signature across all jobs, capped at 15 min. WriteTimeout is now 16 min.
    • The body limit is now 16 KB. Guard, headers and origin checks are unchanged.
    • The log has one line per signed job: job id, number of signatures, duration.
  • Page:
    • One document keeps the current layout, plus a "Requested by" row and a line like "2 signatures · pages 1 and 3".
    • Several documents get a "Sign N documents" heading, a scrollable list (title, requester and role, signatures and pages) and a "Sign N documents" button. Documents whose fetch failed are shown with their error and left out of signing.
    • A wrong PIN keeps the form open with the attempts left. The results screen shows a check or an error per document and a summary ("All 3 documents signed" / "2 of 3 signed"). After a card error there is a "Try again" button for the documents it affected.
    • The page still posts {type: "pn-sign-agent"} to the opener with the dashboard origin.
  • README: describes the new flow and the local API.

Tests

go test ./..., go test -race on server/backend, go vet ./..., gofmt and go build ./... all pass on macOS. New server tests:

  • a job with 3 steps
  • a batch of 3 where the 2nd gets document_changed and the others are still signed
  • a card error mid-batch (rest skipped, Fail called on each)
  • an open error that calls Fail on every ticket
  • a job that ended before signing (with nothing left to sign, the card is not opened)
  • duplicate tickets in the page URL and in requests
  • cancel with several tickets
  • the timeout formula

A new backend test checks protocol: 2, the step parsing, a backend that sends no steps, and the next/completed statuses.

I also checked it by hand: the dev soft token and a throwaway fake backend, with the page open in a 460×600 viewport (batch list, wrong PIN, partial results, one document).

Before merging and releasing

  • Needs the backend change (protocol 2) released first. The new backend refuses prepare without protocol: 2 (HTTP 426, agent_outdated), so agents older than this one stop working once it ships.
  • A v0.2.0 release should follow this merge.
  • Not tested on a real CIE: whether a CIE accepts several signatures after a single VERIFY PIN in one session, with each provider (cie-mw, opencie, built-in cie). Needs a test with a real card and a multi-place or multi-document job.

A signer can now sign the same PDF in several places, and the dashboard can
open the agent's page with up to 10 tickets: the user types the PIN once.

- Backend client: jobs carry signatures, marks and pages; prepare sends
  protocol 2 and returns step and steps; complete answers "next" or
  "completed". A backend that sends no steps is treated as one signature.
- /api/job, /api/sign and /api/cancel take a list of tickets (repeats are
  dropped). Sign opens one card session, then for each job repeats prepare,
  card signature and complete until the job is complete. A backend error
  fails only its job; a card error fails the job and skips the rest. Every
  failure is reported to the backend. If the session does not open, every
  job is told why and the PIN error is returned as before.
- The timeout grows with the number of signatures (60 s + 30 s each, at most
  15 minutes); the server's write timeout is now 16 minutes.
- The page lists the documents, signs them with one PIN and shows a result
  for each, with a retry after a card error.
@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 54 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 24428792-f609-4d6a-b9aa-699cb691a01f
📥 Commits

Reviewing files that changed from the base of the PR and between 97faba2 and 55b8214.

📒 Files selected for processing: 8
  • README.md
  • internal/backend/backend.go
  • internal/backend/backend_test.go
  • internal/server/server.go
  • internal/server/server_test.go
  • internal/server/web/assets/sign.js
  • internal/server/web/assets/style.css
  • internal/server/web/sign.html
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lorenzocorallo
lorenzocorallo marked this pull request as ready for review October 17, 2026 10:37
@lorenzocorallo
lorenzocorallo merged commit 0cb0f7b into main Oct 17, 2026
15 checks passed
@lorenzocorallo
lorenzocorallo deleted the feat/batch-signing branch October 17, 2026 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant