Upgrading to 1.2.0
A security release, and the one release so far that changes behaviour you may be relying on. Read this before you upgrade, not after.
Upgrade regardless. 1.2.0 closes eight authentication and authorization defects present in every version up to and including 1.1.1, several of them critical. See GHSA-p2wx-wvfr-7h3p. Nothing on this page is a reason to stay on 1.1.1 — it is a list of things to have ready when you go.
Closing those defects meant enforcing rules that were previously declared and not applied. Anything that was working because a rule went unenforced stops working. That is the shape of every item below.
| What changes | Who notices |
|---|---|
| Credentials lose access | Every integration using an API key or OAuth client made in the admin UI |
| Sessions are invalidated | Every signed-in user, once |
| Auth settings need a person | Automation that configures SAML, OIDC, MFA or signup |
| Email carries no ticket content | Everyone who reads notifications in their inbox |
| Guest addresses are validated | Anything filing tickets with a guest address |
Every credential the 1.1.1 admin UI created stops working
Scopes existed before 1.2.0 and were never checked: a credential holding no scopes at all could call anything its role allowed. The 1.1.1 admin UI created credentials with an empty scope list, because an empty list cost nothing. From 1.2.0 scopes are enforced, and an empty list grants nothing.
So the credentials most likely to break are the ones made through the UI — which is to say, most of them.
- Find them. The API key and OAuth client pages show
Noneagainst every credential with no scopes. Those are the dead ones. - See what you can grant.
GET /api/v1/admin/scopeslists every scope, eleven resources across read and write. - Re-issue with the scopes each one needs. Grant the narrowest set that does the job — that is the point of the change.
Credentials created directly through the API with scopes keep working unchanged.
There is no full-access scope. A credential that needs everything is granted every scope explicitly. This is deliberate: a wildcard is a credential nobody has to think about, and thinking about it is the whole exercise.
Everyone is signed out once
Sessions moved server-side so that they can be revoked — before 1.2.0 a session cookie could not be invalidated, which meant a compromised account stayed compromised until its password changed and sometimes after. Cookies issued by an earlier version cannot be validated against the new store, so every existing session ends at upgrade.
Session lifetime is now 7 days, down from 30.
Plan for it: a maintenance window, or simply the knowledge that everyone signs in again the first time they load the app.
Changing authentication settings now requires a signed-in administrator
SAML, OIDC, MFA and signup settings can no longer be changed by an API key or OAuth client, whatever scopes it holds. The response is 403 session_required.
The reason is that each of those settings is a route to a session: repoint the identity provider, or switch on open registration, and an attacker signs in as somebody. A machine credential should not be able to do that, and until 1.2.0 it could.
If you configure a new instance from a script, the script now needs an administrator session rather than a key — or configure those settings by hand once, and leave the rest to automation.
Notification email no longer contains ticket content
This is the change your users will notice.
Before 1.2.0, a reply notification contained the reply text and the ticket subject. It does not any more. A notification now says what happened, names the ticket by its tracking number, and links to it:
Subject: There is a new reply on [GHD-2026-000001]
There is a new reply on your ticket.
Ticket: GHD-2026-000001
Read it here:
https://help.example.com/tickets/…
Mail leaving the help desk is sent from your domain, so anything in it is said with your organisation's reputation behind it — and anyone who can file a ticket chooses that text. A ticket subject of "Your account is suspended, call 555-0100" was previously delivered verbatim, from you, to an address the sender picked. The content now stays in the application, behind the access rules that already govern it.
The recipient's own address is also written bare, with no display name, for the same reason.
Guest tickets. A guest recipient would have nowhere to read the reply, since the link leads to the sign-in page. Nothing is affected today, because guest submission does not work yet — see issue #154, which covers the submission path and the tokenised guest view together.
Guest email addresses are validated
Nothing checked them before: any string at all was accepted, stored, and handed to the mailer. A guest address that is not a single bare address is now refused with 400, and the stored value is normalised — trimmed and lower-cased. Nothing meets this rule today, since guest submission is not live; it is the rule it will meet.
If you create tickets through the API with a guest address, check that what you send parses as one address and carries no display name.
Doing the upgrade
- Take a database backup. Migrations are forward-only; there is no automatic rollback.
- List your credentials and note what each integration actually calls, so you can re-issue with the right scopes rather than guessing.
- Upgrade.
Migrations run automatically on startup, before the server accepts traffic.cd go-help-desk git pull cd docker docker compose up -d --build - Sign in as an administrator — your old session is gone — and re-issue the credentials showing
None. - Tell your users that notification emails now link to the ticket instead of quoting it.
For the general upgrade mechanics, including zero-downtime upgrades, see Getting Started → Upgrading.