Skip to content

Changelog

6 October 2026

10 changes:

  • The suite's speed is back: 394 s of tests down to 303
  • The suite: 5:24 down to 3:30 sequentially, and 1:25 in parallel
  • The OIDC phone_number claim falls back to the phone when there is no mobile
  • Projects get the suite's process isolation, and a scaffolded MySQL no longer logs every statement
  • A fatal error says what was running: fatal.log
  • Email's debug log no longer writes the start of the SMTP password
  • A logged stack trace no longer carries its calls' arguments
  • Mail from a customer's own domain: optional DKIM signing and a per-message server
  • A reply can go into the thread of the message it answers
  • A queued message keeps everything it was queued with

5 October 2026

13 changes:

  • Deleting a role deletes its grants
  • Event::listen() does not register the same listener twice
  • Erasing an account deletes its passkeys, devices, roles, memberships and notifications
  • createTestUser(): a test's users are erased when it ends
  • What a test class leaves behind is removed and named, and an overgrown test database is rebuilt
  • An OAuth2 access token survives a second of clock skew, and five intermittent tests stop failing
  • The framework's suite: 380 s of tests down to 303
  • Three more slow test classes: 303 s down to 288
  • symfony/console 6.4
  • The framework's suite runs in parallel: ./dockertest --parallel
  • Flaky tests: the clock and one-second margins
  • The suite passes on a database built from nothing
  • Tests that passed or failed by the order they ran in

4 October 2026

6 changes:

  • Change-log events can belong to a tenant, and be read back by it
  • Permissions::allow() and deny() with a list of privileges grant to the right subject
  • A failed migration no longer marks the schema as up to date
  • The scaffolded SPA's navigation fits a phone
  • Permissions::allowMany(): an organisation's roles in one write
  • The PostgreSQL test database commits without waiting for the disk

3 October 2026

7 changes:

  • Permission checks can be made in one organisation
  • Every push notification has its own page in the administration
  • A test notification says whether the device received it
  • The scaffolded Omnibox test no longer prints a warning on every run
  • An API test no longer makes the next test's requests go to the API
  • Mailing-list and unsubscribe pages render in the theme, and a confirmation can be counted
  • StatTile: the figure-with-a-label every dashboard draws

2 October 2026

9 changes:

  • Two workers refreshing one OAuth connection no longer kill it
  • A faked request says its method, URL, headers and body
  • QueueManager::addMany() queues a pass as one batch
  • A pass can tell when its batch is done
  • A failed queue task waits before it is retried
  • A queue worker pool that grows and shrinks with its backlog
  • An operator's stop is kept, and pools can be set from the database
  • The services screen shows pools, batches and the scaling decision, and its Stop stays stopped
  • A model's column cache tells two databases with the same table apart

1 October 2026

13 changes:

  • User groups, as an optional feature
  • Roles held by a user group
  • A permission check about a group was answered about a user
  • The SQL and column caches tell apart databases with the same name
  • Cookie consent switched off allows everything in the browser too
  • A SPA that declined the cookie banner never released its data-consent scripts
  • The scaffolded daemons container runs as www-data
  • api:docs declares the responses every operation shares
  • userlog is deprecated, and new installations do not get it
  • An application can exclude individual framework migrations
  • Two framework migrations said they were the application's
  • Destroying a cloned Database no longer disconnects the original
  • Client::multipart() sends fields and files in one request

30 September 2026

12 changes:

  • "Don't ask again on this device" — trusted devices
  • "Is it you trying to sign in?" — approving a sign-in from a trusted phone
  • Registration, trusted devices and the phone prompt in System Settings
  • push:setup never found the subscriptions table
  • The account screens on a phone
  • A scaffold view's partials were lost beside an application's own view directory
  • EU cookie consent — banner, preferences dialog and Consent Mode v2
  • Four findings from glideday: a way back from the sign-in screens, User::current(), a fresh caller per TestClient request, and resync --js --all
  • The cookie banner in production: a centred dialog, the Consent Mode default before anything, and versioned asset URLs
  • The suite passes in any order and under any --filter
  • User::setupDb() builds the user tables from their migrations
  • The users migration gives four columns a default of 0
  • A blank organisation setting named a table authserver. and a column ""

29 September 2026

54 changes:

  • Claude.ai and ChatGPT can connect to the MCP endpoint
  • The authorization endpoint's codes could not be redeemed, and tokens name the resource they are for
  • A client that cannot keep a secret needs a registered redirect URI
  • A token from /oauth/token never authenticated an API call
  • Invitations: the four gaps glideday found moving onto them
  • /oauth/userinfo refused the token /oauth/token had just issued
  • The deploy webhook answered twice when it answered first
  • A new continuous aggregate froze its open bucket
  • A rollup can be compressed
  • Http\Client can download straight to a file
  • Opt-in mailing lists: a newsletter, with or without an account
  • The mailing-list form and its confirmation page
  • Opt-in lists on the account screen and the preferences page
  • Sending to a mailing list from /admin/MassMessages
  • A deactivated role went on granting its permissions
  • Saving an organisation deactivated it
  • Admin actions could be triggered from another site — addWriteAction(), starting with Users and Privacy
  • Every other admin screen's write actions are declared too
  • A deny and an allow at the same priority decided differently on MySQL and PostgreSQL
  • permissions_changed for every change, and a Permissions form with every column
  • Every framework admin screen opens at usertype 98; roles open the rest
  • /api/internal/permissions answers for one organisation
  • /admin/MailingLists: who is on each opt-in list
  • The compose form follows the chosen mailing list
  • Who opened and clicked one mass message
  • An account's email choices on its administration page
  • A WYSIWYG editor for messages, chosen per application
  • A server without its signing key minted tokens anybody could forge
  • The message editor's headings and lists look like headings and lists; push is written plain
  • An application can be marked trusted without SQL
  • A user's page lists the roles they hold
  • A client's Allowed Scopes are enforced
  • OpenID Connect: an ID token for openid, and a discovery document that says what works
  • Token lifetimes are set per server and per client
  • permissions_changed reaches only the applications it concerns
  • A migration's new column is saved at once, on PostgreSQL too
  • An organisation's own administrator
  • An array setting is not sent to the settings table
  • Redirects the framework issues can be seen by a test
  • The suite runs clean: no failure, warning or deprecation, and it fails on the next one
  • Two administrators can be given different administration areas
  • Registration by invitation, within a domain list, and with a confirmed address
  • A subset of the suite runs as clean as the whole of it
  • A cached query answered for another database behind the same Redis
  • A mass message filtered by organization reached nobody
  • The suite passes on Linux, not only on a case-insensitive disk
  • RateLimitMiddleware can count an API key, a user, or a tier
  • An application can send webhooks about its own events
  • An API key is held to the scopes its application was granted
  • Something writes application_stats
  • Applications can be managed from an application's own screen
  • The MySQL test container keeps its tables in one tablespace
  • TestClient sends a PUT's data the way PHP receives it
  • The scaffolded API client says whose contract it speaks

28 September 2026

5 changes:

  • Two of the three paths that run a migration left the column cache stale
  • …and the cache they flush was keyed by table only, so two databases answered for each other
  • BotDetector can be widened to about 1,500 crawlers, for anybody counting visits
  • The migration ledger records the version it reached, so an application can report it
  • The auto-run fingerprint could not see a migration that was not timestamped

27 September 2026

3 changes:

  • The mail-template editor had no way in for an application's own categories
  • getData() on an ORM model published the ORM's bookkeeping, including the write allowlist
  • Http\Client handed back a compressed body it had not asked for and never decoded

26 September 2026

4 changes:

  • A migration that queued its statements reported Ran and changed nothing
  • Changing a column was documented on the page nobody writing a migration reads
  • System emails can be rewritten from the mail templates screen, which sent nothing until now
  • …and the framework's own categories are in that screen now, instead of having to be guessed

25 September 2026

4 changes:

  • A second cache of the request meant a declared-public endpoint answered 403 in tests
  • With no mail configured, nothing asks for a mailed code any more
  • The authenticator app names the site, not Pramnos
  • Queued mail is sent: mail:flush is on the framework schedule

24 September 2026

10 changes:

  • A working single server was being shown a red badge for a correct choice
  • Http\Client could not refuse a redirect, so it could not fetch a URL a user chose
  • api:docs could not see routes registered on the router, and wrote nothing over something
  • A relying party could point the server's webhook deliveries at its own network
  • Webhook endpoints on the administration screen, and the private network allowed by default
  • Carrier-grade NAT counted as a public address, so allow_private => false admitted the VPN
  • The scaffold put the API documentation in .gitignore, so production answered 403
  • ApiAuthMiddleware owned the whole auth surface, with no way in for an application
  • The SPA bundle was ignored, and no deploy builds it — so production had no front end
  • A CSRF test passed for the wrong reason, because a declared POST did not survive

23 September 2026

17 changes:

  • Pramnos\User\User was not notifiable, and the guide said it was
  • Scheduled tasks can run on one server instead of on each of them
  • Sessions can be put in a shared store without touching php.ini
  • …and health:check says when they are not
  • The session store setting was never read
  • Pramnos\Storage was unreachable, and that is why nobody used it
  • The media system can publish to a storage disk
  • A generated detail view renders a picture, and a guard keeps it asking for the address
  • A guide for taking a monolith to more than one server
  • Three of the four things that guide said were not solved, are
  • The scaffolded test runner reported success on a red suite
  • A scheduled command that failed recorded its status and not its reason
  • The deploy webhook did its work before it answered, so a slow push was dropped
  • A middleware could not decide what a request is, because init() already had
  • session_tracking => false was read by one of the two places that run the tracker
  • A password toggle rendered inside an attribute broke three screens, and a guard found two
  • A check that said it must not page for a correct installation paged for one

22 September 2026

6 changes:

  • The screen sweep's seeder invented values for generated and foreign keys
  • A capped fetch threw away everything it had already read
  • The scaffolded SPA stylesheet let Tailwind scan the whole repository
  • new MediaObject($id) looked like it loaded and silently did not
  • The media system has an answer for transparency now
  • …and a row for the new methods, which is the half of BC that was missing