CHANGE CONTROL RECORD 21 CFR Part 11 Compliant

System Changelog

Authoritative record of all system changes with validation impact classifications. Each release includes GxP assessment data for change control and impact analysis.

High impact
Medium impact
Low impact
No validation impact
v0.23.0 Medium Validation Impact

Feature release covering signer-workflow usability and notification gaps.

Signature request cancellation now notifies signers: cancelling a request previously only updated status and wrote the audit log; every assigned signer now receives a cancellation email (async, Procrastinate emails queue).

Guided Add Signers step and reusable signer contacts: the Add Signers form reads as three numbered steps (prominent autofocused search, then name and meaning of signature, dimmed until an email is chosen); added signers list below the form with remove, and the form clears after each add. External signers are saved per tenant as SignerContact (new RLS-enabled table), suggested on future requests with their last signature date, and manageable by org admins under Admin → Signer Contacts.

Documents list de-duplication: re-signing an already-signed document clones it (GxP: completed records are never mutated), which listed the same title twice. Clones now record provenance (Document.cloned_from) and the Documents list shows each uploaded document once. Pre-existing clone pairs need a one-off manual cloned_from backfill (clone vs. re-upload is not distinguishable retroactively).

Password criteria disclosure: every password-creation form (signup, invited-signer signup, org invitation accept, password reset) lists the acceptable-password rules, rendered directly from AUTH_PASSWORD_VALIDATORS (URS-ACC-08) so displayed criteria cannot drift from enforcement.

Smaller items: Documents nav link hidden from tenantless signer accounts; signing-complete overlay shows the GxPSign logo with a brand checkmark; request-list badge shows "Pending N Signature(s)" instead of "Pending All Signatures"; help-docs logo returns to the app natively; help-docs mermaid diagrams render (syntax + theme fixes); support links route to Jira Service Management.

Traceability note: changes 1–4 below were deployed to production on 2026-07-25 under tag v0.13.3 (off-scheme version, no changelog entry at the time). They are recorded here so this release's record is complete.

v0.22.0 Medium Validation Impact

Two signature-rendering and verification improvements.

Stamp whitespace: captured signature/initials images arrive as full-canvas PNGs with the ink floating inside transparent padding; the stamp preserved that padding, so the rendered ink sat small inside the box with dead space around it and between the ink and the metadata text. The stamp now trims the capture to its ink bounding box before placing it, so the signature/initials fill their zone.

Verification of both artefacts: the "Verify PDF Signatures" panel verified only one file without saying which. It now verifies each downloadable signed artefact separately and labels the results: "Signed PDF" and — when built — "Signed PDF + Certificate of Completion". The JSON endpoint's response shape changed accordingly (a results list of labelled verdicts).

v0.21.2 Low Validation Impact

Brings the on-screen signed-field previews back to parity with the v0.21.0 PDF stamp layout. The signer portal's signed-field overlay and the requester's field-placement preview still showed the pre-0.21.0 appearance: a fixed image/text split and, for initials fields, the full signature metadata (Signed by / Meaning) instead of the initials-only content. Both previews now mirror the actual stamp: metadata text sized to its content at the bottom with the image filling the space above, and initials showing only the stacked date/time. The signed PDF output itself is unchanged (it was already correct as of v0.21.0).

v0.21.1 Low Validation Impact

Removes the legacy "(v2 wizard)" wording from the user-visible audit activity displays. New request_sent audit rows are recorded as "Signature request sent to signers"; historical rows are not modified — the stored audit trail is untouched — but the legacy suffix is hidden at display time (Recent Activity on the request detail page and the organisation audit log view) so old and new entries render consistently.

v0.21.0 Medium Validation Impact

Redesigns the visible signature and initials stamp appearance on signed PDFs to remove wasted whitespace and make the initials stamp compact.

The initials stamp no longer prints the signer's name: it now carries only the initials image with the date and time stacked beneath it. The signer's printed name, the signature meaning, and the full manifestation remain on the same signer's full signature block (whose presence on the document is enforced at send time), in each field's cryptographic /Reason, and on the Certificate of Completion — so the 21 CFR 11.50 manifestation content is unchanged at the document level.

For both stamp types, the metadata text zone now shrinks to the height the text actually needs (previously a fixed 60% of the block) and the signature/initials image scales up as well as down to fill the remaining space, anchored directly above the metadata text. Signed PDFs produced from this release onward render fuller, more legible stamps; previously signed documents are unaffected.

v0.20.0 Medium Validation Impact

Operationalises database backups and removes duplicated validation content from the application.

Scheduled database backups are now actually performed: a daily logical dump (pg_dump | gzip) is written to /home/devmo/apps/data/backups on the production host at 02:00 UTC with 14-day retention, satisfying URS-PER-07 (RPO ≤ 24h). Previously the backup tooling was deployed but no backup job ran and the backup directory was empty — a gap against the stated RPO. The script is version-controlled at deploy/backup-gxpsign-prod.sh and verified by new IQ step B-07 in GXP-IQ-PROD-001.

The in-application copies of the validation documents (baked IQ/OQ/PQ/summary HTML pages and the in-app qualification-acceptance memo) have been removed. The canonical validation package lives in the controlled QMS, not the application, so these were an uncontrolled duplicate. Because the product is GxP-only by definition, the associated per-organisation qualification gate is retired: can_access_gxp_features() now returns true for any tenant, so GxP features are available without an in-app acceptance step. The qualification document upload/download feature (used to distribute the controlled package) is retained.

Corresponding controlled-document updates were made in the QMS (not in this repo): URS-ACC-10 session inactivity default raised to 30 days (GXP-URS-001 v1.7), and IQ step B-07 for database backups (GXP-IQ-PROD-001 v1.5).

v0.19.0 Medium Validation Impact

The Certificate of Completion can now travel inside a cryptographically valid signed PDF, the way DocuSign bakes its certificate into the envelope. Previously the only combined file was a review copy produced by concatenating the certificate onto the already-signed PDF — a rewrite that invalidated the digital signature (it was labelled _UNSIGNED for that reason).

Completion now produces a second verifiable artefact, Document.signed_with_certificate_file: the certificate pages are appended to the source document and the whole thing is then signed, so the certificate falls inside the signed byte range and the signature validates in Adobe Acrobat, macOS Preview, and other readers. Both copies are offered for download — the plain signed PDF (no certificate) and the signed PDF with the certificate baked in. The old crypto-invalid "review copy" download and its "manifest" / "signing log" labels are replaced throughout by "Certificate of Completion".

A "?" help affordance was added to the in-app signature verification panel explaining why readers may show the signer as "Unknown" (GxPSign signs with its own company certificate, which does not chain to a public Certificate Authority).

MINOR per the versioning policy: it adds a signed artefact to a validated workflow.

v0.18.0 Medium Validation Impact

Re-using an already-signed document to start a new signature request no longer signs on top of the previous signatures by accident. Previously, re-using a signed document attached the new request to the same document record, so the new signature was stamped onto the already-signed PDF and would have overwritten the prior request's signed file — mutating a completed record.

Re-use now clones the document into a fresh draft and lets the user choose the source explicitly from the Documents listing: Sign original seeds the clone from the pristine upload, and Add signature to signed copy seeds it from the already-signed PDF so a new signature layers on top. Either way the original document and its prior signed artefacts are left untouched.

MINOR per the versioning policy: it changes signature-request creation, a validated workflow.

v0.17.0 Medium Validation Impact

Two changes. First, authenticated login sessions now last up to 30 days instead of timing out after 30 minutes of inactivity. The idle logout was redundant friction: signing is protected by per-signature re-authentication (URS-ACC-04), which is independent of and unaffected by the login session, so a short idle timeout added friction without protecting the critical action. The per-org inactivity control (URS-ACC-10) is retained and can still be shortened by any organisation; only its default changes.

Second, the "signed PDF + manifest" download is now a human-readable review copy — the signed pages followed by the manifest pages as one file — rather than the manifest embedded as a PDF attachment. Mac Preview and Chrome's viewer do not display embedded attachments, so the embedded manifest (shipped in v0.16.0) was invisible to most users. The review copy is readable in any viewer and keeps the visible signature stamps, but is a flattened copy whose cryptographic signature is not valid — it is clearly marked as such in the UI and its filename. The verifiable record remains the plain "Signed PDF" download.

MINOR per the versioning policy: it changes an access-control default (session lifetime) and how the signed record is presented for download.

v0.16.0 High Validation Impact

Makes the signed record self-contained and makes the product's description of its own signatures true.

Three things ship together. First, a signer or requester can now download the signed PDF with the signing manifest (the Certificate of Completion) embedded inside it as an attachment, so the signed record and its signing log travel as one file and cannot be separated. The manifest is added in an incremental update after signing, so the signature's byte range is untouched and the signature stays cryptographically valid — verified by test.

Second, signatures are now emitted as PAdES B-B (ETSI.CAdES.detached) rather than the older adbe.pkcs7.detached. Same cryptographic core (detached CMS, SHA-256, X.509) but the current ISO 32000-2 / ETSI baseline profile, and it carries the mandatory signing-certificate-v2 attribute that binds which certificate signed. No RFC 3161 timestamp and no long-term-validation (LTV) data are added — those are PAdES B-T / B-LT, which are deliberately out of scope: GxPSign's model proves that the signed bytes are unaltered since a verified, authenticated user signed, not a third-party-anchored signing time.

Third, a documentation-and-marketing correction: the product previously described itself as providing TSA timestamps and LTV, neither of which was ever wired into the signing path. All TSA feature scaffolding (a dormant, default-off feature flag that did nothing in signing) and all TSA/LTV claims have been removed from the app UI, help documentation, qualification documents, and marketing so the product's claims match what it does.

MINOR per the versioning policy: this changes the signature workflow (format) and adds a new download of the signed record. Marked High validation impact because the signature format is a core validated-workflow change; the system is pre-validation, so this establishes the signing format to be qualified against.

v0.15.0 Medium Validation Impact

Makes an initials block say what an initials block should say.

It was being asked to carry a full signature manifestation — signer, email, meaning and date — in a box a fraction of a signature block's size. At its 73 pt minimum that needed eleven lines of text in a five-line budget, so pyHanko shrank the whole thing to roughly 5.6 pt. The initials were legible; nothing else was.

An initials block now prints the mark, the signer's name, and the timestamp. That is what makes it small enough to be worth having: initials exist to be repeated — down a page, down a table — and repeating a forty-character signature meaning beside each one defeats the purpose.

The meaning is not lost. It is stored per signer rather than per field, so an initial's meaning is by construction the same text as that signer's signature block on the same document. It also remains in each initial's own cryptographic /Reason and on the Certificate of Completion. The one gap that leaves — a document initialled by someone who never signs it, whose printout would then state no meaning anywhere — is closed by a send-time rule rather than left to chance.

MINOR per the versioning policy: this changes a signature workflow and what a signed record displays.

v0.14.0 Medium Validation Impact

Reworks the path a person with no GxPSign account takes from a signature-request email to a signed document, and stops signup from creating an organization.

A signer who had no account previously had to prove their address twice: once by receiving the signature request, and again by clicking a confirmation email after signing up. The second step is now removed — possession of the signature token is the proof, on the same reasoning the org invitation flow has used since 0.11.0. Because this substitutes one identity control for another, it is 21 CFR Part 11 §11.100(b) relevant and classified Medium; the equivalence argument is set out in the Impact Assessment below.

Separately, signup no longer creates a personal organization for every new account. Organization membership now arises only from accepting an invitation, SSO provisioning, or explicitly creating one — signing never requires an organization at all.

The release also makes the signature reasons an organization can offer its signers into data it owns. They were a hardcoded list shared by every organization, so adding one meant a code change and a deployment. There is now a platform-wide set maintained in Django Admin, copied into each organization when it is created; Organization Admins and Owners then edit and re-order their own copy in Organization Settings. To keep that editability from rewriting history, a request freezes the reason wording its signers were shown at the moment it is sent — this is the Part 11 control that makes an editable list safe, and is assessed below.

Two smaller items: the signature block in the signed PDF is narrower and its border is now a light hairline rather than heavy black, and the unused Training Status field is removed from user records.

MINOR per the versioning policy: the release adds features and changes an authentication path.

v0.13.0 Low Validation Impact

Adds "I authored this document" to the list of signature meanings a requestor can assign to a signer, placed directly after "I have reviewed and approved this document". The meaning of a signature is a 21 CFR Part 11 concern — it is recorded against the signature and printed on the Certificate of Completion — so this release is classified MINOR per the versioning policy, which places changes to signature workflows at that level. The change adds one option to an existing list; it does not alter the other meanings, the workflow that assigns them, the values already stored against existing signatures, or how any meaning is captured, rendered or audited.

v0.12.2 No Validation Impact

The four Quick Actions cards on the signed-in dashboard were rendering with their bottom edge cut off. The cards were styled as tabs — top-only corner rounding and no bottom border — sitting in a panel with no bottom padding and overflow-hidden, so the panel sheared off the bottom of each card. Nothing follows the cards inside that panel, so the tab styling served no purpose. The cards now use full corner rounding and a complete border, and the panel has bottom padding. This is a presentation-only change to one template: the links, their targets, and every view, model and permission behind them are unchanged.

v0.12.1 No Validation Impact

The document upload page now shows progress while an upload is being processed. Word → PDF conversion runs synchronously inside DocumentUploadView.form_valid, so the request does not return until headless LibreOffice has finished; the page previously gave no indication of this, leaving the user looking at an idle form and able to submit it again. The submit button is now disabled on submit and shows a spinner reading "Converting to PDF..." for .doc/.docx or "Uploading..." for a PDF. This is a presentation-only change to one template: no view, model, conversion, signing, audit-trail or schema behaviour is altered, and the server accepts and processes an upload exactly as before.

v0.12.0 Medium Validation Impact

Word documents are now converted to PDF by headless LibreOffice instead of pandoc + WeasyPrint. The previous pipeline converted .docx to HTML with pandoc before rendering it, and pandoc's docx reader has no concept of headers and footers — they were absent from its output, so the rendered PDF could never contain them regardless of styling. Because the converted PDF is stored as Document.file and is the artefact that gets signed, a signed record produced from a Word source was missing header and footer content that the source document carried (for example "CONTROLLED COPY" banners and "Confidential / Page X of Y" footers). LibreOffice renders using Word's own page model, so headers, footers, page geometry and styles are preserved. The same change fixes legacy .doc upload, which the file validator and the upload UI have always accepted but which pandoc could not read at all, so every such upload failed. No schema change, no migration, and no change to signing, audit-trail or PDF-signature behaviour: conversion still happens once at upload and pyHanko continues to operate on the resulting PDF bytes unchanged.

v0.11.2 No Validation Impact

Outbound email now shows a sender name. Signature requests, invitations, and every other notification arrived from a bare noreply@mail.gxpsign.app, so inboxes displayed the raw address; they now show GxPSign App. The name is composed in settings rather than written into DEFAULT_FROM_EMAIL because the deployed .env files supply a bare address and Docker's env_file format offers no dependable quoting for a value containing spaces and angle brackets — so no deployment configuration changes. Only the From header changes: the sending address, SES identity, routing, and every message body are untouched, and no application logic or schema is affected.

v0.11.1 No Validation Impact

Relabels the product logo from ALPHA to BETA. The badge is drawn in the logo SVG itself, so the change is a single asset edit; the pill was sized for a five-character label and is narrowed to four so its padding stays even and the badge keeps its position above the "n" in Sign. No application source, signing, audit-trail, authentication, RLS, or schema behaviour is touched, and there is no migration.

v0.11.0 Medium Validation Impact

Adapts an invitation to who is being invited and how their organization signs in, and corrects three defects on the paths this widens. (1) An invitation now offers the right way to authenticate. Routing was decided solely by sso_enforced and never consulted the invitee's email domain, so an organization with SSO enabled-but-optional sent a domain-matching employee to a password form, while an enforced organization sent an outside contractor to an identity provider that had never heard of them. An invitee whose email domain matches the organization's is now emailed that organization's SSO link and joins by signing in; everyone else gets the password signup page, including a domain-mismatched invitee to an SSO-enforced organization. (2) An existing account can be invited. Previously any email that already had an account was rejected outright, so nobody could move between organizations. Because User.tenant is a single foreign key, joining is a move: the invitee must sign in and confirm, and is warned that they leave their current organization, lose access to its documents and signature requests, need no organization at all to sign a document, and need one only to be a Signature Manager. An invitee who is the last owner of their current organization is blocked — leaving would strand it with nobody able to manage it. (3) SSO joiners silently lost their invited role: the join hook set the organization and marked the invitation accepted but never applied role, so an invited Organization Admin arrived as an ordinary user. (4) Expired invitations still authorized SSO — the policy filtered on status="pending" and never expires_at, and nothing flips status when an invitation lapses, so the email path rejected a stale invitation while SSO honoured it indefinitely. (5) The invite form's seat checkboxes did nothing — they were never stored on the invitation and never applied on accept; promised seats are now granted, re-checked against live availability. Includes migration tenants/0016.

The release also repairs a regression from v0.10.0: the damsso v0.4.0 route rename left the SSO settings page showing a hardcoded, now-404 "SSO Login URL" for administrators to share. Organizations may additionally choose their slug — their subdomain — when they are created rather than always having it derived from their name, with hostnames that would shadow the marketing site or infrastructure reserved. Finally, it adds the Single Sign-On administrator guide and makes this changelog reachable and readable. The public /changelog/ page already rendered every release, but nothing linked to it except the About page footer, so it could only be reached by typing the URL; it is now a top-level navbar item across the marketing site. Release summaries and change descriptions were also rendered as escaped text, so readers saw literal backticks and asterisks instead of formatting; they are now converted to HTML. Rendering uses the markdown library, which is promoted from a development-only transitive dependency to a runtime dependency so it is present in the production image. No application logic, signing, audit trail, RLS, authentication, or schema behaviour is affected — the changes are confined to the public marketing site and the presentation of this document.

v0.10.0 High Validation Impact

Restores organization administration, which was broken in ways that silently produced the wrong result. (1) User edits could not be saved by the people who administer them. The user edit page nested three <form> elements (the seat assign/revoke buttons) inside the main edit form. HTML forbids nested forms: the parser discards the inner start tags and the first </form> closes the outer form, leaving the "Save Changes" button outside any form — so it submitted nothing, raised no error, and the page simply did not change. The seat buttons render only for owners, admins, and staff, so Save was inert for exactly the roles permitted to change roles: an Organization Owner promoting another user to Organization Owner saw the change silently revert on refresh, which read as a permissions restriction that did not exist in the code. Seat actions now post via the HTML5 formaction attribute instead of a nested form. (2) Any user could grant themselves ownership. The role field was restricted with a disabled widget attribute, which is cosmetic — Django still binds a submitted value — while the edit view permits an ordinary user to edit their own record; posting role=org_owner therefore self-promoted. The field is now disabled at the form-field level, so a submitted value is ignored. (3) Organization Admins now perform all organization administration — user management, SSO, billing, and the audit log — via a single User.can_administer_org() predicate replacing roughly twenty copies of the same check; deleting the organization and signing the qualification memo remain owner-only. (4) Organizations can be deleted. An owner confirms by typing the organization name; the organization is soft-deleted and every member deactivated (Django then refuses them at login and invalidates live sessions), and a monthly task permanently erases the organization 180 days later on the BYPASSRLS admin connection. (5) The Django admin is pinned to the BYPASSRLS admin role, fixing cross-tenant operations that previously saw zero rows — deleting a tenant failed at COMMIT with a foreign-key violation because the cascade was invisible to the delete collector. This release also carries the previously staged SSO-at-login work and a damsso bump to v0.4.0, which changes SSO callback URLs and requires IdP reconfiguration. It includes migration tenants/0015_tenant_deleted_at.

v0.9.45 No Validation Impact

Fixes broken navigation links in the public marketing website footers and headers. The Privacy Policy, Terms of Service, and About pages were originally authored as standalone static HTML and used relative hrefs (e.g. href="privacy.html"). Because the pages are served at trailing-slash routes (/privacy/, /terms/, /about/), the browser resolved those relative links against the current path — so clicking "Privacy Policy" in the footer of /terms/ navigated to /terms/privacy.html (a 404) instead of /privacy/. All relative .html links were rewritten to root-relative route paths, and hardcoded https://gxpsign.app/... anchor links were normalized to root-relative paths so navigation also resolves correctly on non-production hosts. Cosmetic navigation only; no signing, audit-trail, authentication, or data-handling behavior is affected, and no application logic or schema changed.

v0.9.44 High Validation Impact

Bumps damsso to v0.3.4 so signing-time SSO step-up re-authentication actually forces a fresh IdP credential prompt. GxPSign already routes SSO-only signers through a step-up that sends prompt=login to damsso, but damsso (≤ v0.3.1) silently dropped the parameter — the IdP reused its existing session and returned without re-prompting, so the step-up completed without the signer proving anything (EU Annex 11 §13.3 / 21 CFR Part 11 §11.10(d)). damsso v0.3.4 now forwards prompt=login (adds max_age=0) to the OIDC authorize request and verifies the ID token's auth_time is fresh on return; the SAML path sets ForceAuthn and verifies the assertion issue-instant. The existing step-up round-trip (begin → IdP → SsoStepUpReturnMiddleware bounce → callback → nonce) is unchanged — damsso still returns to LOGIN_REDIRECT_URL, so no GxPSign application code changes. Ordinary (non-signing) SSO logins are unaffected; the prompt is only sent on the signing step-up.

v0.13.3 Low Validation Impact

Routes in-app support links to the new external help desk (Jira Service Management). Adds a /support/ URL that redirects to the help desk contact form (configurable via the SUPPORT_PORTAL_URL environment variable) and repoints the "Support" / "Contact Support" links in the application navigation and footer from mailto:support@gxpsign.app to that route. The help desk contact form opens directly (no request-type picker) and accepts submissions without a login, so external signers can also use it. No changes to signing, audit trail, authentication, or data handling; the previous support email address remains active as an intake channel on the help desk side.

v0.9.42 Medium Validation Impact

Bumps the damsso SSO dependency to v0.3.1, which fixes a bug that broke every SSO login for GxPSign. GxPSign's User model removes the username field (username = None), but damsso's SSO user-provisioning code detected the field with hasattr(user, "username") — which still returns True for a removed field — then called user.save(update_fields=["username"]), raising FieldDoesNotExist and aborting the login. damsso now detects the field via user._meta.concrete_fields, so it correctly skips the username assignment for models that drop the field. No GxPSign application code or schema changes; dependency pin only.

v0.9.41 High Validation Impact

Three changes ship together. (1) Organization invitations fixed: accepting an emailed invite returned HTTP 404 for every user, so invited users could not create an account or join an organization at all. The invite email linked to /accounts/invite/accept/<token>/, but no URL or view for that path was ever implemented (the link was a hardcoded string that never matched the URL configuration); the only code that ever marked an invitation "accepted" was the SSO login path. This adds the missing accept view — it resolves the token, has the (always brand-new) invited user set their name and password, creates their account with the invited role and organization, marks the invitation accepted, and signs them in; SSO-enforced organizations are routed through their identity provider instead. The email now builds the link via reverse() so it can no longer drift from the URL configuration. (2) Audit-trail scope and authentication auditing: the signature audit log stops recording non-critical field-view events and now records signing re-authentication outcomes as first-class events; failed password logins that resolve to an org user are recorded to an org-scoped login-failure audit trail (unattributable attempts go to the security log only, so they never leak across organizations), surfaced in a new "Login Failures" view. (3) Production release gate: prod deploys pin APP_VERSION in the compose env and fail the release if running containers don't match the qualified version tag; test images get an immutable test-<sha> tag for traceability. The audit-trail changes make this a High validation-impact release (Part 11 § 11.10(d), Annex 11 § 13.3) requiring audit-log regression in addition to the invite → accept → login flow.

v0.9.40 Medium Validation Impact

Fixes the password reset flow, which returned HTTP 500 for every user. The "set a new password" page (reached from the emailed reset link) reversed the allauth account_reset_password_from_key URL with a nonexistent token= keyword argument, raising NoReverseMatch on render; it now uses the action_url allauth already provides in the template context. Because password reset is an account-authentication support flow, this is treated as a Medium validation-impact change requiring targeted regression of the reset flow. Also fixes the Sentry crash-report widget on the 500 page (it never appeared in production because it read the DSN from an unset environment variable rather than the resolved settings value), and performs deployment-configuration cleanup: corrects the production sender address to the verified SES identity, adds the Sentry DSN to the deploy env templates, and removes dead environment variables that Django never read.

v0.9.39 Low Validation Impact

Adds Stripe billing so organizations can self-serve purchase and manage SignatureManager seats ($950/seat/year): Stripe Checkout for subscription purchase, the Stripe Billing Portal for changes, and a webhook that syncs the subscription seat quantity to Tenant.signature_manager_seats. Billing is a commercial function only — it does not touch electronic signature capture, the audit trail, authentication, tenant RLS isolation, or document integrity. The billing code ships dormant: with no Stripe keys configured it loads normally and simply reports "Could not start checkout" rather than erroring, so activating it later is a configuration step (set keys, register webhook, end-to-end test), not a code change. Also includes a cosmetic fix to the Add Signers search field and Mac Touch ID help documentation.

v0.9.37 No Validation Impact

Corrected the published SignatureManager seat price to read $950 per seat per year (previously shown as per month) across the marketing pricing page, the in-app Billing pages, and the help documentation. The seat price ($950) is unchanged; only the billing period wording was wrong. No change to validated signature behaviour, audit data, or any billing/payment calculation (no payment processing is integrated).

v0.9.36 Low Validation Impact

Fixed a background-task error surfaced in production monitoring (Sentry GXPSIGN-1J / GXPSIGN-1K): when a signature request is deleted between the completion-email task being scheduled and running, the task raised SignatureRequest.DoesNotExist, failing the job and emitting error events. This is a legitimate race with no work to do; the task now logs a warning and returns gracefully. No change to validated signature behaviour, audit data, or email content.

v0.9.34 Low Validation Impact

Fixed the monthly "documents pending external archival" notification email, which was sent as a plain-text message carrying raw HTML markup, so recipients saw HTML source instead of a rendered email. The report now sends as a proper multipart message with an HTML alternative, matching every other outbound email. No change to validated signature behaviour, audit data, or archival status tracking.

v0.9.32 High Validation Impact

Fixed a tenant-isolation defect that crashed document upload with "new row violates row-level security policy". The PostgreSQL RLS context was being derived from the URL subdomain, which could disagree with the row's tenant whenever an authenticated user reached a subdomain other than their own organization (stale/renamed slug, link to another tenant, or an unknown subdomain). The RLS context is now driven solely by the authenticated user's own organization; the subdomain is used only for display/branding and canonical URLs. Document upload is now also restricted to SignatureManager/Demo seat holders, matching the existing rule for sending signature requests.

This release also fixes a related class of RLS defect in background tasks: tenant_context() sets a transaction-local PostgreSQL setting, but several Procrastinate tasks called it outside a transaction, so the setting was discarded before the first query and RLS filtered out every row. This caused send_completion_emails_to_all to fail with SignatureRequest.DoesNotExist — the Certificate of Completion was never generated and completion emails were never sent. The same defect (plus a Tenant.idTenant.slug error, since the tenant primary key is its slug) affected the PDF-signing, scheduled document-deletion, and periodic reminder/report tasks; all now run their tenant-scoped queries inside transaction.atomic(). Separately, the visible signature stamp now shows the signer's email address alongside their name, and account signup now requires the signer's first and last name so their legal name is captured for the signing process.

v0.9.30 Low Validation Impact

Upgraded Django to the latest 6.0.x patch release and fixed two local-development database bootstrap bugs. No change to validated application behaviour or data.

v0.9.29 High Validation Impact

WebAuthn / FIDO2 (passkey) re-authentication for signing, replacing the password-only re-auth path. Aligns the system with the EU Annex 11 Draft Guidance (PIC/S consultation 2025) — § 11.3 (biometrics accepted), § 11.6 (remote MFA), § 13.3 (fresh re-auth per signature, no session reuse). Adds an SSO step-up fallback for SSO-only users who previously could not sign at all because they had no local password.

v0.9.0 Pre-release / Development High Validation Impact

Initial implementation of GxPSign, establishing the core SaaS PDF e-signature platform for GxP (GAMP5 Category 4) and Freelancer/SMB markets. This release includes multi-tenant architecture, electronic signature workflows, audit logging, and GxP qualification documentation framework.

GxP Use: This changelog is the authoritative record for Change Control and Impact Assessment in regulated environments. Validation impact classifications follow GAMP5 Category 4 software guidelines. Each "High" impact release requires OQ/PQ re-execution for affected functionality.

Ready to start your GxP qualification?

Every release ships with updated qualification documentation.