Release Notes — Week of 2026-10-08
This is a security release. It follows an end-to-end security review of the whole product: every API route was re-checked for tenant isolation, permissions and record access, and the gaps that were found are closed. The headline for admins is that field permissions now apply everywhere — lists, boards, exports, search, reports, dashboards, imports and PDFs — not just on forms. Alongside that ship a password policy and sign-in lockout, show-once links for the client portal and dashboard sharing, bot protection on public forms, signed Xero webhooks, and Active / Expired contracts and Void invoices.
Tenant migrations 098 – 103, 105 and 107 – 109 ship with this release (there is no 104 or 106). Run them on every environment with:
npx ts-node apps/api/src/scripts/run-tenant-migrations.ts
| Migration | Does |
|---|---|
| 098 | Adds columns and indexes that newly registered workspaces were missing (account create, activity logging, notes / documents lists). No-op on existing workspaces. |
| 099 | Approval-rule triggers project_start / task_complete; workflow run status completed with errors; users.tokens_valid_after (lets sign-ins be revoked); removes duplicate empty Standard price books. |
| 100 | Finishes the duplicate Standard price book clean-up. |
| 101 | New permission modules Proposals, Contracts and Invoices. Each role gets the same access it had on Deals, so nobody loses access. |
| 102 | Sign-in sessions (auth_refresh_sessions) — one row per refresh token, rotated on refresh and revoked on sign-out. |
| 103 | Password-reset and invitation links are stored hashed. Turns the Client Portal flag on for projects that already had portal links. Portal comment columns (shared with client / from client). |
| 105 | Contracts can be Active and Expired; invoices record who voided them, when and why. |
| 107 | Outlook mailbox subscription renewal and the polling fallback. |
| 108 | Client-portal links and dashboard share links are stored hashed. |
| 109 | Client-portal sessions are stored hashed. |
Existing links in people's inboxes (password reset, invitations, portal and share links) keep working — only the stored copy changes.
JWT_SECRETis now required in production (at least 16 characters; 32+ recommended). The API refuses to start without it.- Set
TRUST_PROXYwhen the API sits behind nginx or a load balancer, otherwise every user shares one IP address for sign-in rate limiting. - The API container now runs as a non-root user — make sure the uploads volume is writable by it.
See Environment Variables and Deployment.
What admins need to do
- Review your roles' field permissions. They now hide data everywhere, not just on forms. A field set to Hidden disappears from lists, exports, search results, reports and dashboards for that role. See Field Permissions.
- Review Proposals, Contracts and Invoices permissions. They are now separate modules, copied from each role's Deals access by migration 101. Tighten them if, for example, sales reps shouldn't see invoices. See Roles & Permissions.
- Delegated settings admins lose admin-only screens. Screens that were open to roles with
settings.edit/admin.edit— Integrations, API Keys, Approval Rules, Audit Logs, Calendar Connections, Customer Success settings, and changes to General / Lead / Opportunity / Task settings — now need a real administrator (the Admin role or level 100). Give those people the Admin role if they still need them. - Xero webhooks: paste your Xero app's Webhook signing key into Admin → Integrations → Xero. Until you do, Xero webhook calls are rejected (manual and scheduled sync keep working). See Xero Integration.
- Client-portal links can no longer be copied again. The link is shown once, when it's created. If a client has lost theirs, use Regenerate link — the old link stops working. The same applies to dashboard share links.
- Forms submitted through the API that send the submitter an auto-reply must include the form's fill token and an empty honeypot field (or use reCAPTCHA). Without them the submission is still saved, but the auto-reply isn't sent and the submission is marked Flagged. Re-copy the snippet from Embed → Submit via API. See Form Builder.
- Tell users about the password policy. Existing passwords keep working; the policy applies the next time someone sets or changes a password.
- Users should sign out and back in once after the upgrade so their session picks up the new permission modules.
Behaviour changes
- Passwords must be 10–128 characters, use at least 3 of lowercase, uppercase, numbers and symbols, and not contain the part of your email before the
@. - Sign-in lockout — after 10 failed attempts on one account within 15 minutes, sign-in for that account pauses until the window ends ("Too many failed sign-in attempts for this account. Try again in 15 minutes."). A wrong workspace, unknown email and wrong password all show the same Invalid credentials message.
- Signing out ends the session on the server, not just in the browser. Changing your password signs out your other sessions.
- Deactivating a user or changing their role takes effect within about 10 seconds, without them signing out.
- Board view of leads or opportunities isn't available to a role that can't see Stage. If Amount is hidden, the opportunity board is sorted by last updated and stage totals are hidden.
- Read-only Stage blocks converting, disqualifying, closing and reopening for that role.
- Imports refuse column mappings onto fields that are hidden or read-only for the person importing.
- Public form leads now get the duplicate check, SLA timer and audit trail, like leads created in the app.
- Uploads accept only images (PNG, JPG, GIF, WebP), PDF, Word, Excel, PowerPoint, CSV, TXT and ZIP. Other documents download instead of opening in the browser; images and PDFs still open inline.
- Record history (the History / Change History panel) is visible to anyone who can view the record. The full Audit Logs screen is admins only.
- Integration secrets (API keys, client secrets, SMTP passwords) are shown masked, e.g.
••••a1b2. Leaving a masked value unchanged keeps the stored secret. - Outgoing SMTP verifies the mail server's TLS certificate. Self-signed servers need
SMTP_ALLOW_INVALID_CERTS=trueon the server. - Report Builder only lists the data sources you have permission for.
Security
- Tenant isolation — fixes to report building, imports, Google Calendar connection, the Xero webhook and email sending so that no request can reach another workspace's data. Imported spreadsheets are deleted once the import finishes.
- Record access everywhere — bulk update / delete / assign, projects, Customer 360, invoices, proposals, contracts, uploads, reports, dashboards, inbox, import jobs, workflow runs and the client portal all apply the user's record access (own / team / department / all), not just the main lists.
- Privilege escalation — admin-only screens require a real administrator; non-admins can't edit system roles, their own role, or roles at or above their level, can't grant permissions or record access they don't have, and can't manage users ranked at or above themselves.
- Sessions — sign-in issues a 1-hour access token and a 7-day refresh token that rotates on every refresh. A refresh token that's used twice signs that user out everywhere. Sign-out revokes the session.
- Rate limits on sign-in, registration, password reset, invitations, portal and share-link codes, public forms, bookings and public proposal / contract pages.
- Stored links — password-reset, invitation, client-portal, dashboard-share and portal-session tokens are stored only as a hash.
- Safe content — landing pages, page designer content, email reply / forward, booking and form emails are sanitised; CSV / Excel exports can't carry spreadsheet formulas.
- Outbound webhooks can't target private or internal network addresses.
Permissions
- Field permissions on every module — Contacts, Accounts (including emails, phones and addresses), Leads, Opportunities, Tasks, Products, Projects, Project Tasks, Proposals, Contracts and Invoices, each with Hidden / Read-only / Editable per field and custom fields where the module has them.
- Applied everywhere — lists and column settings, boards, detail pages, forms and modals, bulk update, imports, exports, global search, reports and dashboards, Customer 360, record history, and generated PDFs and emails.
- What users see — a lock and — where a value is hidden, a (read-only) tag on locked fields. See Hidden and read-only fields.
- Derivable totals warning — hiding an invoice, proposal or opportunity total while Line items stay visible shows a warning, because the total can be calculated from the lines.
- New modules — Proposals, Contracts and Invoices replace Deals for those features (the Invoices sidebar link and the opportunity tabs use them).
- Admins always pass every permission check, in the interface as well as on the server.
See Field Permissions and Roles & Permissions.
Sales documents
- Contracts become Active and Expired — a fully signed contract becomes Active on its start date (immediately if it has none) and Expired after its end date, checked daily and whenever the contract is opened. The opportunity's Contracts tab shows Starts … / Expired … and filters by status. See Opportunity Detail Page.
- Void an invoice — Void on a sent or overdue invoice with no payments recorded asks for a reason, sets the amount due to zero and stops recurrence. The invoice shows Voided on … with the reason. See Managing Invoices.
- PDFs and emails for proposals, contracts and invoices leave out fields hidden for the person generating them.
Projects & portal
- Share a task comment with the client — tick Share with client when posting, or toggle Visible to client on an existing comment (the task must be client-visible). Client comments carry a Client badge; shared team comments show Shared with client.
- Show-once portal links with Regenerate link; switching the project's Client Portal off blocks existing links and sessions.
- Dashboard share links are shown once with Regenerate link; email-restricted links need a one-time code.
See Managing Projects and Dashboard Customization.
Forms
- Bot check before a form emails the submitter: reCAPTCHA when the form requires it, otherwise a hidden honeypot field plus a minimum fill time.
- Auto-reply limits — at most 3 auto-replies per recipient, per form, per 24 hours; links typed by the submitter are removed from the auto-reply.
- Submissions that fail a check are still saved (no lost leads) and marked Flagged in the submissions list.
- API embed snippets include the fill token when the form emails the submitter.
See Form Builder.
Email
- Outlook mailbox subscriptions are renewed automatically and mail is re-synced every 6 hours; mailboxes without working push notifications are polled every 15 minutes.
- Gmail push watches are renewed before they lapse, with history polling as a fallback.
- Sending, replying, forwarding and manual sync are limited to your own or a shared mailbox.
- SMTP certificates are verified (see Behaviour changes).
See Email Integration.
Accessibility
- Form labels are linked to their inputs, icon-only buttons have names, and toggles, tabs and radio groups are announced correctly by screen readers — across 20 checked pages.
Platform & upgrades
- Dependency upgrades closing 41 published advisories (including NestJS 11.2, nodemailer, multer, axios, socket.io, React Router, DOMPurify, TypeORM and Vite).
- Swagger API docs are off in production unless enabled; default request body limit 1 MB.
- The API container runs as a non-root user; CI actions are pinned.
- Dates such as close and due dates no longer show a day early.
See Deployment, Security Architecture and Database Migrations.
Index of changes
03bb124,7d8fb39security hardening and QA fixes, Playwright end-to-end suite, migrations 098–10057aafc0injection and cross-tenant fixes (reports, Xero webhook, Google OAuth state, imports, email)48ecc53privilege-escalation fixes (admin-only, roles, users, masked integration secrets)5fd24e3record-level access across bulk operations, projects, Customer 360, sales documents, uploads, reports2979e09,2097e34Proposals / Contracts / Invoices permission modules, upload type checks, admin-only controls74d6261,268da3esessions, rate limiting, password policy, field-level permissions everywhere, show-once links, form protection, contract / invoice lifecycle, mailbox renewal, accessibilityd310752dependency upgrades