1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
|
/*
The app program is a proof-of-concept frontend for cmd/hakurei.
This program is not covered by the compatibility promise. The command line
interface and configuration syntax may change at any time.
# Installation
Compile cmd/app with these arguments:
go build -trimpath \
-ldflags='-s -w
-buildid=
-X hakurei.app/internal/info.hsuPath=/usr/local/bin/hsu' \
./cmd/app
Replace /usr/local/bin/hsu with the hakurei installation's absolute hsu
pathname.
# Sharing files
Sharing files between apps is only possible with the permissionless shared
filesystem ([cmd/sharefs]), as apps generally do not share credentials. It can
be built without any special linker flags. Create a symlink at
/usr/bin/mount.fuse.sharefs pointing to the sharefs binary when mounting sharefs
via fstab:
sharefs /sdcard fuse.sharefs rw,noexec,nosuid,nodev,noatime,allow_other,mkdir,source=/var/lib/sdcard,setuid=1023,setgid=1023 0 0
Replace /var/lib/sdcard with the location of the backing directory. User and
group id must be specified in numerical form, it must own the backing directory.
The mount point does not have to be /sdcard.
# General setup
The entire directory structure containing persistent app data must be created
manually for now, due to the different ownership requirements being impossible
to set up directly through cmd/hsu.
The environment variable ROSA_APP_PATH should be set to the absolute pathname of
the persistent state directory. This document will use $ROSA_APP_PATH to refer
to the persistent state directory. The $ROSA_APP_PATH directory should be owned
by the user declared in /etc/hsurc and invoking cmd/app, and must be readable
and executable by all subordinate users. This can be done by making it
world-readable and executable, or by adding a group directive to the common
file.
The $ROSA_APP_PATH/app directory contains app configuration files, named after
their reverse-DNS style application identifier string. It should be owned by
the user invoking cmd/app.
The $ROSA_APP_PATH/initial directory contains the read-only base of every
app template. Everything within it, including the directory itself, should be
owned by the reserved user, its user and group id obtained by the command:
app id
This document assumes a [Void Linux rootfs tarball] is unpacked here. It is
possible to use other Linux distributions; Void linux is selected here because
its package manager works correctly out of the box in the hakurei container
environment. If the use case does not require glibc, [Alpine Linux] or
[Chimera Linux] might be more suitable.
Once the tarball is unpacked at $ROSA_APP_PATH/initial, create a directory named
chronos in $ROSA_APP_PATH/initial/home, and directories block, bus, class, dev,
and devices in $ROSA_APP_PATH/initial/sys. Then, recursively change ownerships
of files and directories in $ROSA_APP_PATH/initial to the reserved user/group.
The $ROSA_APP_PATH/lock directory contains lock files guarding entry into
mutable and app containers of every template. It must be owned by the user
invoking cmd/app.
The $ROSA_APP_PATH/state directory contains app home directories, named after
their reverse-DNS style application identifier string. The directory itself
should be owned by the user invoking cmd/app; each directory inside must be
owned by the subordinate user/group id of its app, obtained by the command:
app id "$APPID"
where "$APPID" is the second component of the first line in the configuration
file of the corresponding app. It is considered good practice, but not required,
for these directory to have the permission bits set to 0700.
The $ROSA_APP_PATH/template directory contains templates that apps are based
on, which are all derived from the initial layer. The directory itself
should be owned by the user invoking cmd/app, and each directory inside should
be owned by the reserved user/group. Their names are used in the first component
of the first line in the configuration file of each app, to specify that the
file is derived from that template.
The $ROSA_APP_PATH/work directory contains overlay work directories for mutable
containers. It must contain one manually created, empty directory for each
template, named after the template directory itself, owned by the reserved
user/group. The directory itself and its contents should have the permission
bits set to 0700.
# Configuring the base template
This section describes basic setup required for the typical desktop use case.
Generally, multiple templates are created to mitigate the global nature of
conventional package managers. The setup described in this section applies to
all templates. For convenience, a "base" template should be created, containing
this setup, and all future templates should be copied from the base template.
To reduce disk space used, [ZFS snapshots] or an equivalent can be used. This
section assumes the template is named "base".
Create an empty directory at $ROSA_APP_PATH/template/base, owned by the reserved
user, with permission bits set to 0755. Create its corresponding work directory
at $ROSA_APP_PATH/work/base, also owned by the reserved user, with permission
bits set to 0700. Once this is complete, enter its mutable container with the
command:
app enter --shell=/bin/sh base
This command overrides the container configuration to use the shell program at
/bin/sh. Normally, cmd/app uses zsh, which is not yet installed.
Once in the container, populate /etc/resolv.conf with some well-known DNS
service, for example:
nameserver 8.8.8.8
nameserver 8.8.4.4
The mutable container does not bind the host /etc/resolv.conf. It is generally
recommended to populate it with a well-known service here to avoid trouble in
future template changes. A later section configures regular app containers to
use host configuration.
After populating nameserver configuration, install zsh using the package manager
provided by the distribution unpacked earlier. On Void Linux, this is done by
the command:
xbps-install zsh
Wait for the package installation to complete, configure zsh if necessary, then
exit from the shell, and re-enter the container without overriding the login
shell:
app enter base
Some packages are generally required in the container for the typical desktop
use case: xdg-user-dirs to reconfigure "well known" user directories to point
to the inner sharefs mount point, mesa to interact with the GPU, dconf to
configure GTK, xdg-desktop-portal-gtk to have gtk talk to dconf. Fonts generally
need to be installed as well. Additionally, a text editor can be installed for
editing configuration files later on. On Void Linux, these packages are
installed by the command:
xbps-install \
vim \
mesa \
mesa-dri \
mesa-intel-dri \
mesa-vaapi \
mesa-vulkan-intel \
mesa-vulkan-radeon \
mesa-vulkan-overlay-layer \
dconf \
xdg-user-dirs \
xdg-desktop-portal-gtk \
noto-fonts-ttf \
noto-fonts-ttf-variable \
noto-fonts-ttf-extra \
noto-fonts-emoji \
noto-fonts-cjk \
noto-fonts-cjk-variable \
noto-fonts-cjk-sans \
noto-fonts-cjk-sans-variable \
noto-fonts-cjk-serif \
noto-fonts-cjk-serif-variable
Add -32bit variants of mesa packages if multilib support is required. Adjust
the package selection based on use case.
Edit /etc/xdg/user-dirs.defaults and point directories to the inner sharefs
mount point as required. The resulting file should look like this:
# Default settings for user directories
#
# The values are relative pathnames from the home directory and
# will be translated on a per-path-element basis into the users locale
DESKTOP=../../sdcard/Desktop
DOWNLOAD=../../sdcard/Download
TEMPLATES=../../sdcard/Templates
PUBLICSHARE=../../sdcard/Public
DOCUMENTS=../../sdcard/Documents
MUSIC=../../sdcard/Music
PICTURES=../../sdcard/Pictures
VIDEOS=../../sdcard/Movies
PROJECTS=../../sdcard/Projects
# Another alternative is:
#MUSIC=Documents/Music
#PICTURES=Documents/Pictures
#VIDEOS=Documents/Videos
If this is configured, it must be applied to the home directory of each app
individually. This can be done for all apps via the shell expression:
app run | xargs -n 1 echo app run --command=xdg-user-dirs-update
This must also be done for every newly created app.
The /etc/dconf directory can be set up to provide defaults across all apps. To
do so, edit /etc/dconf/profile/user:
user-db:user
system-db:local
system-db:site
system-db:distro
Then, place files in /etc/dconf/db/local.d containing dconf configuration. For
example:
[org/gnome/desktop/interface]
gtk-enable-primary-paste=true
color-scheme='prefer-dark'
gtk-theme='adw-gtk3-dark'
icon-theme='Papirus-Dark'
Themes must be installed in the container. Change colour-scheme and themes
accordingly. Setting gtk-enable-primary-paste restores clipboard behaviour that
GNOME maintainers decided to break.
After editing these configuration files, update dconf system databases:
dconf update
# Common configuration
The optional $ROSA_APP_PATH/common file contains common configuration included
after the specific configuration of each app. Some bind mounts are required for
almost every graphical program, and many widely used libraries are configured
through the environment. For most setups, these directives are generally
required:
; for libudev
ro "/sys/block"
ro "/sys/bus"
ro "/sys/class"
ro "/sys/dev"
ro "/sys/devices"
env EDITOR=vim
; for apps to play nice with xdg-dbus-proxy
ro+ "/etc/machine-id"
; template must have a /etc/resolv.conf file
ro+ "/etc/resolv.conf"
; group name of the sharefs group configured on host
group media_rw
; for sharefs
rw "/sdcard"
; refer to /usr/share/zoneinfo
env TZ=Asia/Tokyo
; must be installed first
env XCURSOR_THEME=volantes_cursors
; must be generated first if using glibc
env LANG=en_GB.UTF-8
env LC_COLLATE=C
# Installing an app
An app is defined by a configuration file in $ROSA_APP_PATH/app, and a
persistent state directory in $ROSA_APP_PATH/state. Before creating an app, its
identity must be decided. Different apps should generally not share an identity.
The next unused identity can be found using the command:
app next
If the -v argument is added before the "next" command, cmd/app will additionally
show apps sharing the same identity.
Once the identity is decided, the subordinate user/group id can be obtained by
the command:
app id "$APPID"
where "$APPID" is the identity of this app. A directory must be created under
$ROSA_APP_PATH/state, owned by this user and group id. Its permission bits
should be set to 0700. A configuration file with the same name, owned by the
user invoking cmd/app, must be placed in $ROSA_APP_PATH/app. Its contents are
described in the next section. The reverse-DNS style application identifier
string is submitted to the Wayland display server, and used as part of the
dbus preset if enabled, so the correct identifier must be obtained. If no such
identifier exist, use the domain of the app home page.
# Configuring an app
The configuration file starts with two structural directives, and the remaining
lines are freestanding directives applied in order.
The first structural directive is a line containing two components separated by
the ':' byte. The first component is the template used by the app. the second
component is the decimal representation of the app identity. The second
structural directive is a shell expression passed to the shell serving as the
initial process of the app.
After the structural directives, each line contains exactly one directive or
comment. Comment lines begin with a ';' byte: these lines are not interpreted by
cmd/app in any way.
The following section documents currently available freestanding directives.
# Directives
interactive start initial process as an interactive shell
gpu expose GPU devices to the container
system_bus enable system bus in the dbus proxy
wayland expose a Wayland pathname socket via security-context-v1
x11 expose the X11 pathname socket
dbus enable the per-container xdg-dbus-proxy daemon
pipewire expose a pipewire pathname socket via SecurityContext
multiarch unblock system calls required for multiarch to work on
multiarch-enabled targets (amd64, arm64)
devel unblock ptrace and friends
userns unblock userns creation and container setup syscalls
net enable network access
abstract enable access to external abstract unix sockets
tty unblock dangerous terminal I/O (faking input)
mapuid map the target user id to the user id of the user
invoking cmd/app in the container user namespace
device mount /dev/ from the init mount namespace as is in the
container mount namespace
share_runtime share XDG_RUNTIME_DIR between containers under the same
identity
share_tmpdir share TMPDIR between containers under the same identity
username <name> set username of the emulated user
hostname <name> set container hostname
env KEY=VALUE set an environment variable for the initial process
ro "pathname" make a host path available to the container; the string
must be presented in Go string literal syntax, to
specify a different inner pathname, end the outer
pathname with NUL and specify the inner pathname after
the NUL byte
rw "pathname" like ro, but the resulting mount entry is made writable
ro+ "pathname" like ro, but is skipped if pathname does not exist
rw+ "pathname" like ro+, but the resulting mount entry is made writable
own name add an own policy for the dbus proxy
own_system name like own, but for the system bus if enabled
talk name add a talk policy for the dbus proxy
talk_system name like talk, but for the system bus if enabled
[cmd/sharefs]: https://pkg.go.dev/hakurei.app/cmd/sharefs
[Void Linux rootfs tarball]: https://voidlinux.org/download
[Alpine Linux]: https://alpinelinux.org
[Chimera Linux]: https://chimera-linux.org
[ZFS snapshots]: https://openzfs.github.io/openzfs-docs/man/v2.4/8/zfs-snapshot.8.html
*/
package main
|