Userspace permissions: getting ready for Q1 2023

Skip to first unread message


Dec 12, 2022, 5:47:21 PM12/12/22
In Q1 of 2023, we will be releasing an initial implementation for userspace
permissions. Users will be prompted to approve permissions for applications
when they install them, and whenever the scope of permissions broadens during
an update. Developers will need to specify what permissions their applications
need ahead of time. If you are an application developer, read on for more
details about how this works, and what you need to do.

Permissions work on the level of desks, representing applications, which may
contain multiple agents. Applications will need to specify what behaviors they
*need* to make use of, and installation and upgrade will be gated on user
approval of those behaviors. Applications may also specify what behaviors they
*want* to make use of, and users may toggle these on and off at any time.

To elaborate, "behavior" here means everything an application's agents do,
except `%give`ing `$gift`s. (So, producing `%fact`s or `%kick`ing
subscriptions.) In other words, every `%pass` card, and every `.^` scry, is
gated behind a matching permission. This includes `%poke`ing or scrying into
agents within the same application (on the same desk), even agents `%poke`ing
themselves. There is no such thing as implicitly granted permissions.

(Note that not everything requires permission in the first place. Some of
jael's endpoints, for example, expose inherently public data (`%public-keys`,
`%turf`) and as such do not need to be guarded. Additionally, you are always
allowed to close previously-opened subscriptions into both user- and
kernelspace. You can always emit `%leave`s, `%c %warp` cancels, and similar to
clean up after yourself.)

If an application tries to do things it does not have sufficient permissions
for, this crashes. During `+on-init` and `+on-load`, this crashes the whole
event, preventing installation or upgrade. In all other cases, the crash is
caught by Gall, and the agent is told about it through `+on-fail` with a
`%not-permitted` `term`. In both cases, the stack trace will include a
description of the things that were attempted, and the missing permissions
that would be needed for them.

For those who have already published software on Urbit, the minimum viable
migration is saying that all existing application behavior is necessary, and
writing the permissions file accordingly. In doing so, it is good practice to
keep permissions as small and specific as possible. To find all the
permissions an existing agent needs, you can do the following steps. If your
application contains multiple agents, make sure to do this for every agent.

- Search your agent for any `%pass` cards it emits. If you are using a helper
library for generating cards, be mindful of that: you need permissions for
these as well.
- For `%agent` cards, you will need a corresponding `$perm-gall`[1]. `%write`
permission for `%poke`s, and a `%watch` permission for `%watch`es.
- Both of these take a `jump` flag. False corresponds to local
communications only. True lets you talk to agents on other ships as well.
- Both of these also take a `dude=?(~ dude)`, specifying which agent you
want to talk to. `~` represents "any agent", which you may use if you do
not know all agent names ahead of time.
- `%watch` permissions can be narrowed down by the (prefix of) the path
you're subscribing to. You may want to request multiple permissions, if
you subscribe to different paths on the same agent.
- For `%arvo` cards, you will need a corresponding `$perm-arvo`[2]. Please
refer to their type definition for the corresponding required permission.
Some of them, mostly the clay ones, let you narrow down by desk, path, or
even care. Make use of this narrowing when you can.
- Search your agent for any `.^` invocations. Again, if you are using a helper
library for doing scries, be mindful of that: you need permissions for these
as well.
- You will need a corresponding `%reads` `$perm-gall`[1]. If you know ahead
of time which care, desk, or path you will be scrying on, please include
these in the permission, and use multiple if you need to.

As an example, consider the following permission file for an imaginary
application that subscribes to arbitrary chats, gives us notifications, and
writes log files to its own desk. This file would be stored in `/desk.seal`.

:_ ~
:~ [%watch & %chat /chat]
[%write | %hark-store]
[%clay %write `%imaginary /log]

Note that the permissions file contains two lists: required and optional
permissions. Here, because we have no optional permissions, the second list
is empty.

But perhaps notifications are not a core part of the app's functionality:
it would behave fine even if it could not emit them. We can make them optional
instead, letting users install our app without granting it permission to poke
`%hark-store`. The `/desk.seal` in that scenario might look something like

:- :~ [%watch & %chat /chat]
[%clay %write `%imaginary /log]
:~ [%write | %hark-store]

Users must still approve of the `%chat` and `%clay` permissions to install the
imaginary application, but may grant or revoke the `%hark-store` permission at
any time.

Within agents, to determine whether something is allowed at any given time,
they may refer to `pes.bowl`, a `(set perm)` containing all the permissions the
agent currently has. For our `%hark-store` example, we might emit a `%poke`
only if `(~(has in pes.bowl) [%write | %hark-store])`.

(It is also possible to check on a card you wish to emit directly, by calling
`+cred:gall`. Likewise `+rite:gall` for scries. But these are often more
cumbersome to invoke, and potentially less performant.)

The latest iteration of this work lives on the `m/userspace-permissions`
branch[3]. It is not fully ready for release yet, there is still UX and
testing work to be done. Developers are encouraged to start tracking the
permissions their applications need, to be ready when this releases in
Q1 of 2023.



Reply all
Reply to author
0 new messages