Windows, macOS, iOS and Android all notice when a notification contains a
verification code, and give you a one-tap way to copy it. GNOME doesn’t, and
as far as I can tell it never has: I found no existing issue or proposal for
it.
I’ve built a working prototype to find out what the feature actually requires,
and most of what I learned was interesting enough that I’d rather bring the
design questions here before opening merge requests. I’m not attached to my
answers; I’d like to know whether the shape is right.
What’s missing today
Two things.
There is no way to say “this notification contains a secret.” PrivacyScope
looks like it should be that, but it isn’t: its only consumer reads SYSTEM as
permission to show details on the lock screen, so the enum has no value
meaning “more sensitive than default.” An application that knows it is sending
a one-time code has no way to ask for it to be withheld.
Actions only render once a notification is expanded. In messageList.js,
_actionBin.visible = actor && this.expanded. For an ordinary application
action that’s a reasonable default. For “copy this code” it removes most of the
value: expanding first and then clicking is barely better than selecting the
digits by hand.
What the prototype does
Three pieces, which I’d expect to be reviewed separately:
- A
sensitive-contentproperty on notifications. Sensitive notifications
never show their body on the lock screen, overriding the per-application
detailsInLockScreenpolicy. - Contextual actions, meaning actions derived from a notification’s
content rather than supplied by the sending application, which may render
without expanding. - Detection, plus an
x-gnome-verification-codehint so an application that
already knows can simply say so and skip detection entirely.
Copied codes go onto the clipboard marked with x-kde-passwordManagerHint, the
mimetype clipboard managers already agree on to mean “don’t keep this.” That
needed a small addition to St, because st_clipboard_set_content() passes a
single mimetype to meta_selection_source_memory_new() and there was no way to
publish a payload alongside a marker. Using KDE’s existing convention rather
than inventing a GNOME one means codes copied from GNOME are handled correctly
by tooling written elsewhere. Issue #8626 proposes a native clipboard
manager that would need this vocabulary anyway.
What I learned
Detection is the contentious piece, so I went looking for how others do it.
Android does not let applications declare this. There is no CATEGORY_*
for verification codes. Detection happens in a replaceable
NotificationAssistantService, which returns an Adjustment carrying
KEY_CONTEXTUAL_ACTIONS and KEY_SENSITIVE_CONTENT. The platform ships the
mechanism and a boundary which the policy sits behind. Nothing in the freedesktop spec, the XDG portal, Android or the Windows toast schema has a category for verification codes, so no cross-platform application is declaring this anywhere today.
Chrome cannot declare it even in principle. A web push payload is arbitrary
text from the site; the browser has no semantics to forward. I captured a real
verification code arriving through Chrome to check, and confirmed it sends no
category and no copy action.
I ported AOSP’s detector to JavaScript and ran it against a corpus. Its
candidate shapes and its false-positive filters, dates, phone numbers, years,
and the neat observation that no known code contains three lowercase letters in
a row, are what my implementation now uses, because my own first attempt was
worse. Google’s algorithm produces false positives on messages like “your
verification code was used on device 4521”, and ships anyway. Their detector
drives redaction, where over-triggering is cheap.
Since ours drives a copy button, I took the mitigation Android and KDE Connect
both use: the button shows the code, “Copy 314159” rather than “Copy Code”,
so a wrong guess is visible and declinable instead of silently reaching the
clipboard.
The real captured notification had the SMS short code in its summary and the actual code in its body. Scanning the two together is always ambiguous, so body is searched first and they are never searched together. AOSP arrived at the same field-by-field approach independently.
What I’d like input on
- Should an action ever render on a collapsed banner? This is the change
I’m least able to judge and the one that most needs design input. I’ve kept
the existing action bin untouched and added a separate slot, so ordinary
application actions behave exactly as they do now, and at most one contextual
action is ever shown. Whether that’s the right visual treatment is your call,
and I have no mockups. I’d welcome direction, or a flat “not
like that.” - Is
sensitive-contentworth having on its own? I think it is. “This
notification’s body is a secret” is a gap regardless of whether GNOME ever
detects codes, and applications could set it today via a hint. - Where should detection live, if anywhere? Right now it’s a module in the
shell, applied only to notifications arriving from applications. If GNOME
would rather not carry locale-specific vocabulary in-tree, a concern I
share, since the keyword list is inherently open-ended, the natural
evolution is Android’s: keep the mechanism, move the policy behind a
replaceable boundary. - Is there appetite for standardising this? A freedesktop notification
category for verification codes would let applications that do know declare
it, and would benefit Plasma and Phosh equally. The spec documents the
process for proposing one. I’d rather find out whether the idea has support
before writing a spec patch.
Status
Working, with the JS logic unit-tested and the behaviour verified against a
real captured notification: the code is offered, the clipboard receives it
marked non-retainable, and the notification’s body is withheld from the lock
screen.
To be precise about the base, since it affects how much weight to give the
testing: this is the upstream gnome-50 branch, built and run against the
mutter 50.1 packaged in Ubuntu 26.04. The shell is upstream, not Ubuntu’s
patched build. Only mutter came from the distribution. It rebases onto main
with no conflicts and produces an identical diff, but main wants mutter 51,
which I don’t have here, so the main port is unbuilt and I’d treat it as
unverified until someone runs it.
Happy to split this into whatever sequence of merge requests is easiest to
review, to drop the detection piece entirely if the mechanism is welcome and the
policy isn’t, or to leave the whole thing here if the direction is wrong.
Cheers,
Chris