Bundles the prior autonomous-session output that was sitting unstaged:
- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
redirects (ocr to ai, reports to dashboard, invitations to users),
docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
let-reassign), set-state-in-effect disables in CountryFlag and
UploadForSigning preview-bytes effect, unused 'confirm' destructures in
interest contract + reservation tabs, unescaped apostrophe in test-template
card copy
Two parallel reviews of the Tier 0–6 work surfaced one CRITICAL
regression and a handful of remaining cross-tenant gaps that the
original audit didn't enumerate. All fixed here:
CRITICAL
* document-reminders.processReminderQueue — the new bulk-fetch
leftJoin to documentTemplates was scoped on `templateType` alone.
Templates of the same type exist in every port; the cartesian
explosion would have fired one Documenso reminder PER matching
template-row per cron tick (a 5-port deploy = 5 reminders to the
same signer per cycle). Added eq(documentTemplates.portId, portId)
to the join.
* All five remaining Documenso webhook handlers (RecipientSigned /
Completed / Opened / Rejected / Cancelled) accept and require an
optional portId now, with a shared resolveWebhookDocument() helper
that refuses to mutate when the lookup is ambiguous across tenants
without a resolved port. Tier 5's port-scoping was applied only to
Expired; the route now forwards the matched portId to every
handler. Tightens the WHERE clauses on subsequent UPDATEs to (id,
portId) for defense-in-depth.
HIGH
* verifyDocumensoSecret rejects when `expected` is empty —
timingSafeEqual(0-bytes, 0-bytes) was returning true, so a dev env
with a blank DOCUMENSO_WEBHOOK_SECRET would accept a request whose
X-Documenso-Secret header was also missing/empty.
listDocumensoWebhookSecrets skips the env entry when blank.
* /api/public/health — the website-intake-secret comparison was a
string `===` (not constant-time). Switched to timingSafeEqual via
Buffer.from().
MEDIUM
* server.ts SIGTERM ordering — Socket.io closes BEFORE the HTTP
drain so long-poll websockets stop holding the server open past
the compose stop_grace_period.
* /api/v1/me PATCH preferences merge — allow-list filter on the
merged JSONB so legacy rows from the old .passthrough() era stop
silently re-shipping their bloat to disk.
Migration fixes (deploy-blocking)
* 0041 referenced `port_role_overrides.permissions` (column is
`permission_overrides`) — overrides are partial JSONB and don't
need backfilling at all (deepMerge resolves edit from the base
role). Removed the override UPDATEs entirely.
* 0042 switched all FK + CHECK adds to NOT VALID + VALIDATE so the
brief table-lock phase is decoupled from the row-scan validation,
giving a cleaner abort-and-restart story if a constraint catches
dirty production data. Added a pre-cleanup UPDATE for
invoices.billing_entity_id = '' rows (backfills from clientName,
falls back to the row id) so the new non-empty CHECK passes on a
dirty table.
Test status: 1175/1175 vitest, tsc clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documenso authenticates outbound webhooks via the X-Documenso-Secret
header carrying the plaintext secret (no HMAC). The previous receiver
verified an HMAC against a non-existent x-documenso-signature header
and switched on parsed.type, neither of which Documenso emits — so
every real delivery was being silently rejected.
- Read X-Documenso-Secret, compare timing-safe to env secret
- Switch on parsed.event with uppercase normalization for both v1.13
(DOCUMENT_SIGNED) and 2.x (lowercase-dotted UI labels) wire formats
- Alias DOCUMENT_RECIPIENT_COMPLETED to DOCUMENT_SIGNED (same
semantics across versions)
- Handle DOCUMENT_OPENED / DOCUMENT_REJECTED / DOCUMENT_CANCELLED in
addition to the existing DOCUMENT_SIGNED + DOCUMENT_COMPLETED paths
- Bypass session middleware for /api/webhooks/* (signature is the auth)
Verified end-to-end against signatures.letsbe.solutions: real
DOCUMENT_RECIPIENT_COMPLETED + DOCUMENT_COMPLETED deliveries now pass
secret verification, dispatch correctly, and the handler updates
state (or warns gracefully when the documensoId is unknown).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>