# Copyright 2025 GNOME Foundation, Inc.
# SPDX-License-Identifier: CC-BY-SA-3.0

Screen time feature design
===

Use cases
---

 - Allow parents to review amount of screen time spent on computer by child
   accounts, initially at a whole session level, but ultimately at a per-app
   level, regardless of whether the child is currently logged in
 - Allow limits to be placed on this screen time, so that a child’s session (or
   use of a particular app) is prevented after a configurable limit has been
   reached (according to a schedule)
 - Preserve the child’s privacy by not revealing usage information to people
   other than the parents in their family, and deleting it altogether if the
   child is removed from parental controls
 - Be reasonably resistant to a child’s attempts to circumvent screen time
   limits, but this is not a hard security boundary
 - Work with encrypted home directories and new technologies like systemd-homed
 - Impose low system overhead
 - Allow a child to request an extension to their screen time on a case-by-case
   basis, and support multiple mechanisms for a parent to respond to this (for
   example, perhaps by doing a polkit authentication in the child’s computer
   session, or perhaps by responding to a notification on the parent’s phone
   remotely)

Architecture
---

 - A child’s screen time data needs to be stored outside of their home
   directory, by a system service, as otherwise the data would be editable or
   deletable by the child, or not accessible to the parent (particularly if the
   child’s home directory is encrypted when they’re logged out).
 - The data needs to be pushed to the system service by the child’s gnome-shell
   instance, as a system service can’t connect to a user’s D-Bus session bus.
 - The system service can enforce access controls on who can request the data
   through its interface.
 - If all data is stored on disk, the service can be stateless and hence can
   exit after a short period of inactivity, conserving resources.
 - This requires gnome-shell to frequently push data to the service, with the
   period between pushes being the maximum permissible error on timekeeping (the
   amount of extension a child might be able to get on their session by
   overrunning and then killing gnome-shell just before it’s about to push
   another update).
   - I considered having the system service monitor the presence of gnome-shell
     on the bus, and use that for timekeeping, but it’s not precise enough: the
     shell might be running with the screen locked; and monitoring means that
     the system service can’t exit on inactivity.
 - gnome-shell, or a helper process, within a child’s session can implement
   warning notifications about an upcoming screen time limit, by querying the
   system service for the limit deadline. They can also show an interface for
   requesting a time limit extension.
 - Responding to a time limit extension request would be implemented by a
   responder (agent) process registered with the system daemon. This separates
   user interface and network implementation from the daemon.

Data storage
---

As above, the data has to be stored within the system service, rather than user
home directories, as it needs to be accessible (via access control) to other
users.

Data could be encrypted when at rest, although this is not strictly necessary as
it should only be accessible to the daemon user through normal Unix file
permissions.

There was some investigation of whether to use systemd’s
[user records](https://systemd.io/USER_RECORD/) or
[blob directories](https://systemd.io/USER_RECORD_BLOB_DIRS/) for data storage,
and the conclusion was that they’re not suitable:
  - Would be nice for the data to move around when systemd-homed does things
    with users.
  - The data must not be modifiable/deletable by the user, or they could modify/
    delete their session usage information and hence effectively reset the
    timer.
  - Blob directory data is world-readable, so is not suitable, as then child
    session usage information would be readable by non-parent users.
  - If we used a user record, the data would have to go in the `privileged`
    section (as it must only be visible to administrators and the user), and
    *not* listed in `selfModifiablePrivileged` (so the user can’t modify/delete
    it).
  - All the fields listed on https://systemd.io/USER_RECORD/ are configuration
    or long-term status, rather than usage information which changes frequently;
    the nearest field there is is `diskUsage` in the `status` section.
  - So updating the record would require re-signing the data every time it
    changes; that’s quite the overhead.
  - Hence user records don’t seem to be suitable.

Access control
---

The on-disk data should only be accessible by the system service. By querying
the owner of the D-Bus connection making requests to it
([`GetConnectionCredentials()`](https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-get-connection-credentials)),
it can impose an access policy on requests to add usage records and to query
them.

The policy should be:
 - A user can only ever add usage records for themselves, not other users.
 - A user can always query their own usage records.
 - A parent can additionally query usage records for any user which is their
   child.

Family group relationships are determined using policy in `MctUserManager`.

Extension requests
---

The system needs to support extension requests to the time limits, allowing
children to request more time in their session (or on a particular app) as a
one-off.

As above, such extension requests should be responded to by an agent process
separate from the timer daemon, as this process will need different sandboxing
to the timer daemon. Some examples of possible ways to handle extension
requests:
 - Pop up a polkit dialogue on the child’s session.
 - Notify the parent in their session on the same computer (unlikely, as
 - typically families are not using multi-head computers).
 - Send a push notification to the parent’s phone
   (using, for example, https://f-droid.org/en/packages/org.unifiedpush.distributor.nextpush/
   and https://apps.nextcloud.com/apps/uppush).

One consideration is whether to allow multiple agent processes to respond to a
request, or to be registered with the timer daemon. This would introduce the
need for the timer daemon to arbitrate between multiple (potentially
conflicting) replies to an extension request, though, which would introduce the
need for policy in the timer daemon. Part of the reason for separating the agent
from the timer daemon is to separate the policy of how to handle extension
requests from the mechanics of tracking session and app time usage.
Consequently, only one agent process is allowed. If parallel paths for handling
an extension request are needed (for example, notifying several parents on
several devices), a more complex agent should implement that, including the
necessary policy for reconciling multiple answers to the extension request.

Rather than implementing a registration process for agents with the timer
daemon, the name ownership mechanism in D-Bus can be used. The child’s process
which is requesting the extension should not be able to talk to the agent
process directly, as that would introduce another possible privilege escalation
vector. It’s more robust for it to just be able to talk to the timer daemon, and
for the timer daemon to talk to the agent.

So, in order to handle a screen time extension request:
 1. The timer daemon launches the well-known name of the agent on the system
    bus.
 2. The timer daemon calls a method on it to request an extension, passing the
    details from the child’s process’ extension request call, plus details of
    the child’s login session (which would be needed for a polkit authentication
    check, for example).
 3. The agent creates an object on the system bus to represent the ongoing
    request, only accessible to the timer daemon. This request object is
    necessary to allow the API to be expanded in future to support richer
    request state or conversations; even if the initial implementation is quite
    simple.
 4. The agent communicates with the parent (somehow) to query them about the
    extension request. This may take a long time (potentially tens of minutes).
 5. Once the parent has given a response to the request, the agent can emit a
    response signal to the timer daemon to convey the result. This has to be a
    signal, rather than a method reply, to avoid issues with D-Bus timeouts due
    to the potentially long reply time.
 6. The timer daemon then emits a response signal to the child’s process which
    originally made the extension request. This signal might be visible to other
    unprivileged processes on the bus, so any sensitive details should instead
    be conveyed via a method call to query the updated estimated time remaining
    in the child’s session.

References
---

 * Various existing FOSS projects:
   - https://github.com/vorlonofportland/pam_session_timelimit/ and
     https://manpages.debian.org/testing/pam-session-timelimit/pam_session_timelimit.8.en.html
     * Only one kind of time limit
     * Not possible to extend sessions or integrate with the desktop due to limited IPC options from PAM modules
     * This limits the user interface options too drastically
