Skip to content

perf: load the admin client key lazily - #76

Merged
oleksandr-nc merged 1 commit into
mainfrom
perf/lazy-app-config
Oct 22, 2026
Merged

oleksandr-nc merged 1 commit into
mainfrom
perf/lazy-app-config

Conversation

@oleksandr-nc

@oleksandr-nc oleksandr-nc commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Since Nextcloud 29 every request loads the non-lazy app config values of every app, and NUITEQ's admin client key was one of them, although only the app's own requests and settings use it.

The admin client key was loaded on every request

The app read and wrote its app config through the deprecated IConfig::getAppValue() and setAppValue(), and setAppValue() always stores a value non-lazy. So the admin's client key, encrypted with ICrypto, was loaded on every request to the instance, while only NuiteqAPIService (the login and the board requests), the admin settings and the 1.0.8 migration that encrypted it read it.

The client key is lazy, the Stage URL and the link override stay non-lazy

Every app config read and write goes through the typed IAppConfig methods now, with the same flag for a key everywhere:

  • client_key is lazy. NuiteqAPIService, the admin settings and the 1.0.8 migration read it, the admin-config endpoint and that migration write it, all with lazy: true. A non-lazy read of a lazy value answers the default, so no read of it is left on the old API: the requests would go out with the shipped key instead of the admin's.
  • base_url stays non-lazy. Application::boot() reads it on every request for the script it adds to every page, and the CSP listener reads it for every page.
  • override_link_click stays non-lazy. Application::boot() reads it on every request, for the same script.

The stored values do not change: the key stays encrypted with ICrypto, and every value stays a string.

The admin endpoint takes only the two admin settings

setAdminConfig() stored whatever keys a request named. Through the typed setter that does harm: it sets the type of the row it writes, and Nextcloud's own rows of the app are untyped. Once installed_version is a string, the next occ upgrade fails on the type conflict and leaves the instance in maintenance mode, and once enabled is one, enabling the app fails the same way. The endpoint takes only what the admin settings send, base_url and client_key, and only as strings, and answers anything else with a 400 before it writes anything. override_link_click has no setting and is left to occ.

Existing installs mark the key lazy when they upgrade

A new migration, Version1301Date202610211026, calls updateLazy() for client_key, which the old API stored non-lazy. It does nothing on an install without a key, and until it runs, the lazy reads still find the non-lazy row.

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Since Nextcloud 29 every request loads the non-lazy app config values
of every app, and the lazy ones only once some code asks for one
lazily. The app wrote its app config with IConfig::setAppValue(),
which always stores a value non-lazy, so the admin's NUITEQ client key
was loaded on every request, while only the app's own requests, its
admin settings and its migrations read it.

The client key is read and written through IAppConfig with lazy: true
everywhere now: in NuiteqAPIService, the admin settings, the
admin-config endpoint and the 1.0.8 migration that encrypted it.
IConfig::getAppValue() reads only the non-lazy values, so no read of a
lazy key may stay on the old API: it would answer the default, and the
requests would go out with the shipped key instead of the admin's.

base_url and override_link_click stay non-lazy. Application::boot()
reads both on every request, for the script it adds to every page, and
the CSP listener reads base_url for every page. Their other reads, and
the admin-config endpoint's write of base_url, move to IAppConfig with
the same flag.

The admin-config endpoint stored whatever keys a request named. Through
the typed setter that does harm: it sets the type of the row it writes,
and Nextcloud's own rows of the app are untyped. Once installed_version
is a string, the next upgrade fails on the type conflict and leaves the
instance in maintenance mode, and once enabled is one, enabling the app
fails the same way. The endpoint takes only what the admin settings
send, base_url and client_key, and only as strings, and answers
anything else with a 400 before it writes anything.
override_link_click has no setting and is left to occ.

A new migration marks client_key lazy on existing installs, where the
old API stored it non-lazy; it does nothing on an install without a
key, and until it runs the lazy reads still find the non-lazy row. The
stored values do not change: the key stays encrypted with ICrypto, and
every value stays a string.

Signed-off-by: Oleksander Piskun <oleksandr2088@icloud.com>
@oleksandr-nc
oleksandr-nc merged commit 6f3c987 into main Oct 22, 2026
26 checks passed
@oleksandr-nc
oleksandr-nc deleted the perf/lazy-app-config branch October 22, 2026 06:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant