diff --git a/UIPS/UIP-userperms.md b/UIPS/UIP-userperms.md new file mode 100644 index 0000000..6b531b9 --- /dev/null +++ b/UIPS/UIP-userperms.md @@ -0,0 +1,238 @@ +--- +title: Userspace Permissions +description: Restricting capabilities of gall agents, and related work. +author: ~palfun-foslup +status: Draft +type: Standards Track +category: Kernel +created: 2026-01-27 +--- + + +## Abstract + +Gall agents have always had access to the entirety of the kernel API, as well as all other gall agents. Here we propose a permissions system, restricting the capabilities of agents on a given desk until a user grants corresponding permissions. We distinguish between static permissions, required by the desk prior to installation, and dynamic permissions, requested by a desk's agents at runtime. + + +## Status + +Draft UIP in the process of being refined. At the same time, implementation is under active development by members of the Tentative Gall Working Group. Concrete type specifications in this UIP will depend on the outcome of design work in that Working Group. + + +## Motivation + +Gall implements a userspace model wherein "agents" can emit effects when they are evoked. While the shape and scope of these effects is dictated by gall, they map very closely onto the kernel API, with some additions for agent-to-agent interactions. Gall makes no effort to control, restrict or even monitor the effects emitted by the agents it runs. + +In 2021, "software distribution" made it easy to install third-party userspace software. That work punted on most security-related questions, including ways to protect both kernel- and userspace against malicious third-party software. + +This particular known security hole has been a big factor in preventing development and adoption of "high-stakes" software like secrets storage and cryptocurrency wallets. + + +## Specification + +We first give an overview of the behavior of permissions. Then we describe the requirements made of the API, the interplay between permissions and desk lifecycles, we make recommendations about permission presentation and management, and describe best practices for userspace developers regarding permission requirements and requests. + +This model puts restrictions on what agents can do. It does not let agents restrict what can be done to them. This is not a capabilities-based model. This does not aim to solve the "confused deputy" problem. Local provenance is utilized to realize this model. This does not aim to extend provenance over the network in any way. + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Overview + +All `%pass` cards emitted by agents and all `.^` namespace reads performed by agents MUST be subject to permission control by gall. Permissions MUST be granted at the desk level in clay. Permissions on a desk MUST apply to all agents running from that desk. + +Agents on the `%base` desk MUST be exempted from this permission control ("run as root"), and any agent MUST always be allowed to issue cards which would delete/revoke one of its created resources/subscriptions. Outside of that, there are no implicit permissions. Even interactions or reads to an agent running on the same desk MUST require a corresponding permission. + +Desks MAY statically specify required permissions, in a `/desk.seal` file. The desk MUST NOT run (install, revive) until the specified permissions are granted. + +Agents MAY dynamically request optional permissions for their desk at runtime. These permissions can be granted and revoked at any time. Clay MUST track requested permissions and notify about additions (see below). + +The `$bowl:gall` MUST include the permissions available to the agent at the time of its invocation. The standard library SHOULD provide helpers for checking a card or scry path against those permissions. + +When an agent emits a `%pass` or performs a `.^` scry for which it does not have a corresponding permission, the agent invocation MUST be treated as having crashed. During `+on-init` and `+on-load`, this crashes the whole event. During all other invocations, this calls the agent's `+on-fail` with `%not-permitted`. + +Both gall and clay need to know the current permission state (required, requested, granted) for a desk. Because permissions apply at the desk level and exist even when no agents are running, clay MUST be treated as the canonical store, with gall subscribing to clay for permission updates. + +### Interface and Types + +Clay MUST support granting and revoking permissions through a `%seal` task and requesting permissions through a `%pine` task. +To support userspace reactivity (and gall's syncing), clay MUST expose a kernel-style subscription endpoint for notifications about permission requests and updates through a `%ward` task, and closing of that subscription through a `%wink` task. Agents that want to use this MUST be granted the corresponding permission. + +The type that describes a permission MUST allow for "scoping" if possible. That is to say, for example, a permission to read resources from a path namespace must be able to specify a path at _and below_ which read permission is to be granted. Or, a permission for interacting with a named resource must optionally specify the name of the resource, such that it allows access to either a specific resource, or all possible resources. + +Where possible, the permission type MUST distinguish between local-only and over-the-network permissions. Local-only permissions MUST be "scopeable" by agent and/or mark. Network permissions MUST NOT be scopeable at all. + +xx concrete type description once gall api settles + +Clay MUST track permission requests in a way that distinguishes between runtime-requested optional permissions and install- or commit-blocking required permissions. Requested optional permissions MUST be remembered as such even after they have been granted. + +### Permissions and Desk Lifecycles + +A desk MUST be granted its required permissions before being set to live. (That is, before clay signals to gall that the agents on that desk should run.) +Once live, a desk's required permissions MUST NOT be able to be revoked. To do so, the desk must first be suspended. +A commit to a live desk MUST fail to apply if doing so would add required permissions that have not yet been granted. This failure (or anticipation of it) MUST register the to-be-required permissions as requested-for-upgrade and send the corresponding permission request notification. + +Desks that are installed as part of the boot sequence MUST have all their required permissions granted automatically. + +Desks that are installed at the time of the upgrade which introduces userspace permissions MUST have all their required permissions granted automatically. + +### Permission Type Upgrades + +To ensure it is always possible to present and reason about permissions, permission type definitions and usage is structured such that an "appetizer" update is always required. + +There MUST be two versions of the permission type: one representing the latest supported permissions (A), and one representing the permissions that will be supported in the next kelvin upgrade (B). Any kelvin upgrade MUST replace type A with that of B, and MAY change type B to be any new permission type. + +Type A MUST be used for all instances of permissions described in this specification, with the following exceptions: +- The `%seal` task MUST support _granting_ permissions of both type A and B in clay. + - But only type A permissions should _apply_ in gall. +- Permission requests originating from a desk upgrade attempt (see Lifecycles above) MAY use type B permissions. +- Permission management interfaces MUST support rendering both type A and B permissions. + +To allow for manual "step-wise" kelvin upgrading where necessary, authoritative base desk sources SHOULD use clay labels to aid discovery and downloading of specific kelvin releases. + +### Presentation and Management + +The `%base` desk SHOULD ship with a permission manager for each interface modality. +- Generators SHOULD be available for granting and revoking permissions on a given desk, either case-by-case or "all requested". +- `|install` SHOULD support granting whatever permissions are required. +- Eyre SHOULD serve a primitive permission management web interface. + +When presenting permissions to the user, if the agent affected by a permission is locally known, it SHOULD be presented as or alongside the name or description of the desk it is running from. A "poweruser" SHOULD be empowered to view the "raw" underlying permission. + +When an agent affected by a permission is not locally known, a permission manager MAY prevent the granting of the permission, even if this would prevent installation. + +The recommendations around "known" agents SHOULD only apply to local agent interactions. Interactions that go over the network SHOULD be presented generically as "sharing data" or "reading". + +When an agent affected by a permission is on the desk requesting the permission, a permission manager MAY collate it with similar permissions, or hide them completely. + +Permissions that are functionally equivalent to root access (see Security Considerations below) SHOULD be presented as such. + +### Requesting Permissions + +Userspace developers SHOULD require (through the `/desk.seal` file) permissions that are essential to the core functioning of their desks. +For permissions without which the desk can reasonably function, these should be requested and checked at runtime (on-init/load, or on-demand). Developers SHOULD use the standard library-provided utilities for checking permission availability. + +Developers SHOULD request permissions at the smallest reasonable scope. For example, when subscribing at paths of the shape `/data/[some-id]`, request permissions for `/data`, not for `/` (too broad), and not for each individual `/data/whatever` (too narrow). + +### Spider and Khan + +Spider, at `/app/spider` on the base desk, SHOULD be made to restrict the effects and scries of its threads by the permissions granted to the caller of the thread. It can determine the relevant provenance from the `sap.bowl`, retrieve the corresponding desk from gall if necessary, and scry current permission state out of clay. + +Khan SHOULD ensure that it passes on the appropriate provenance when it invokes spider. + + +## Rationale + +Permissions apply at the desk level, not at the agent level, because desks map more closely to the concept of "apps" than agents do. A desk may contain many agents, but they are commonly intended to function together as a pre-packaged suite of software. +Desks are less volatile than agents. Agents within a desk may or may not be running, and an agent may move from running on one desk, to running on a different desk. +Lastly, desks are more closely tied to specific publishers than agents are. That tie is not absolute though (only "running foreign desks" would be), so switching sources for a desk remains a risk. See also security considerations below. + +The `%base` desk is exempt from permission checking because it runs the kernel. Any code that makes it onto the `%base` desk is implicitly trusted. If the `%base` desk code cannot be trusted, neither can the permissions implementation. + +There are no implicit permissions granted, even for interactions between agents from the same desk, because this complicates the model and implementation, and raises additional security concerns. Hiding or auto-granting self-referential permissions can be done at the display or userspace level if desired. + +There is separation between required/static and optional/dynamic permissions for a couple of reasons. +- Strictly requiring permissions improves the ergonomics of writing agents. No matter how good the helpers are, a check is a check, necessitates code branching, increases complexity. +- Specifying permissions ahead-of-time helps users make more-informed decisions about whether to install any given software. +- Necessary permissions are not always knowable ahead of time. This is almost necessarily true for software that intends to interact with other apps in a generic way: it cannot know what apps the user will make it interact with. + +Treating agent invocations as crashes when effects would violate permissions matches the behavior of `.^` on non-existent or not-allowed paths and makes it obvious that the entire invocation is null and void. The latter is important to avoid internal inconsistencies in agents. +The alternative would be to inject a "permission nack" into `+on-agent` or `+on-arvo` to notify them that their effect was prevented from executing. But this cannot be done for `.^` read failures, and proliferates error handling throughout agent code. +Developers should be taught and strongly encouraged to check permissions prior to emitting effects so they can handle failure cases eagerly/synchronously, rather than firing them off indiscriminately and handling failures lazily/asynchronously. +(To improve developers' ability to handle resulting `+on-fail` calls appropriately, that interface should be expanded to provide more details about the failed invocation. However, doing so is out of scope for this UIP.) + +### Interface and Types + +Considering the possibility of building "app managers" in userspace, it is important for the kernel to expose permission information and management capabilities. + +xx kernel-style subscriptions follow established pattern, see examples +xx example: %tire + +The permission type must allow scoping so that developers do not need to request permissions broader than what they will be using, and conversely such that a generic permission can be requested. +For example, no need to request permission to write to all of the files on a desk if you only want to update `/log.txt`. And conversely, no use specifying any path at all if the main feature revolves around editing arbitrary files. + +Separating local-only and over-the-network permissions is important, because behavior of local agents is knowable, but that is not the case for agents on other ships. As such, putting any restrictions on effects that result in network activity is not meaningful beyond that network activity happening or not. +In the presence of stateful reads (i.e. `+on-watch`), there is also no difference between reads and writes from a security (data leaking) perspective, but it may still be good to make that distinction for semantic reasons. + +Separating blocking (required) and non-blocking (optional) permission requests is important for UX purposes. Remembering non-blocking permission requests even after the corresponding permission has been granted helps permission management interfaces to display them separately as toggleable. + +### Permissions and Desk Lifecycles + +Clay already manages desk "liveness" status and transitions. Permissions, applying at the desk level, overlap with this nicely. +Putting restrictions on liveness transitions of and file changes on desks is necessary to maintain the invariant of desk code being able to assume its required permissions. + +Automatically granting required permissions for desks present/installed during the boot sequence ensures the immediate post-boot state is "complete" according to the sequence's intent, without requiring additional permission-granting events to be formalized into the boot sequence. + +Automatically granting required permissions for desks present/installed during the permissions upgrade ensures the upgrade can happen without user intervention. + +### Permission Type Upgrades + +The upgrade approach, requiring an "appetizer" update to prepare for permission type transitions, was chosen as the solution with the smallest scope and lowest complexity. Providing workable UX, which can always present blocking permissions in a legible way, necessitates a way to get "ahead" of incoming changes. + +One can imagine, in theory, building files from the newer version of the desk without upgrading to it, and using that newer version of the desk to translate the relevant types and utilities back to a format legible to the old version of the desk. (That is to say, downgrade the type of type if needed.) This is neither simple nor self-contained. + +One can imagine enforcing that `/desk.seal` files cannot change at the same time as `/sys.kelvin` files, or other approaches in that direction. This would result in a similar "appetizer" requirement, but for each desk individually rather than the kernel desk alone. + +There is no mechanism for "[stepwisdom](https://gist.github.com/philipcmonk/de5ba03b3ea733387fd13b758062cfce)", but there is precedent for awkward manually step-wise upgrade paths. Ergonomics for that can be upgraded slightly be making use of clay's revision labels. We also do not expect to change the permission type very often. + +xx consider and discuss interaction with kelvin shimming + +### Presentation and Management + +Permission managers are advised to prevent the granting of permissions affecting unknown agents. Disregarding this advice saddles the user with an unanswerable question: what does it mean to read from or write to an app I have no awareness of? +"I trust it will be fine" can be used as an answer, but doing so is not risk-free. It is possible to lower that risk by, when installing a new app, showing all permissions granted to other desks that affect the to-be-installed app. + +All of that assumes the `/desk.bill` file is a complete list of all agents that will be running on a desk. For scenarios where there are "optional" agents on a desk, the above becomes more nuanced. Arguably, in those cases, the relevant permissions should be optional, requested at runtime only once the optional agent has been observed to be running. + +Permissions concerning agents on other ships should be presented generically, because there can be no guarantee about what software is running under what name on remote ships. + +### Requesting Permissions + +Requesting permissions ahead-of-time improves both developer and user ergonomics: the developer never has to check permission status, and the user does not need to be prompted for those permissions after installing the app. +Requesting permissions dynamically grants users meaningful control over the behavior of the software they run, in practice letting them en- or disable features according to their level of trust in the software. + +### Spider and Khan + +Spider implements a "userspace within userspace". As such, if it wants to restrict the capabilities of the threads it runs, it itself should be responsible for doing so. Restricting by the thread's caller rather than the thread provider (source desk) prevents escalations and remain possible for inline threads. + + +## Backwards Compatibility + +Userspace developers will need to specify the permissions which their agents require. Failure to do so will result in runtime crashes. + +xx summarize upgrade instructions for userspace devs + +xx gifts from old resources + + +## Security Considerations + +### Eyre Security + +This UIP describes a permission system for gall-level interactions. Other vanes also interact with gall's userspace. In general, the kernel is trusted software, so does not need to be checked for permissions. Eyre is an exception to this: it allows outside callers (anything capable of making HTTP requests) to interact with gall. To make userspace permissions hold water in real-world scenarios, permission checks must be extended to these outside callers. + +A detailed specification for that work is outside of the scope of this UIP. In summary: eyre will append a desk to its provenance path when interacting with gall. Gall will check for permission to perform that interaction, as if it had originated from that desk. Eyre determines that desk based on the scope of the authenticating cookie. Eyre safeguards scoped cookies against trivial theft by serving desks on their own subdomains. + +At the time of writing, that line of work is being pursued, and a working prototype is [available on the `eswg/wip` branch of urbit/urbit](https://github.com/urbit/urbit/compare/develop...eswg/wip). Formal UIP to follow. + +### Implicit Root + +There are many seemingly-narrow permissions that are functionally equivalent to root, usually by interacting with something on the base desk. Care should be taken to enumerate all of these and inform the user about them appropriately whenever possible. A non-exhaustive list of such permissions is given below. + +- pass `%dill` a `%belt` + - to simulate keystrokes. +- pass `%clay` a `[%park %base ...]` + - to modify base desk (fully trusted) source code. +- poke `%hood` with `&helm-pass` + - to enact arbitrary kernel tasks. +- poke `%herm` with `&herm-task` + - to simulate keystrokes, by virtue of `%herm` being allowed to do so. + +### Desk Sources + +Changing the installation source of a desk for which permissions have been granted implies trusting the new source with all of the granted permissions. Revoking permissions when changing installation source may be excessive, but warning about this in installation UI could be sensible. +Updating clay to support running off source desks directly (that is, running agents from a `[=ship =desk]` instead of a necessarily-local `desk`) would mitigate this, but such a change is outside the scope of this UIP. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md).