/* 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. # Snapshots and cloning To reduce disk space used by templates, [ZFS clones] or an equivalent can be used. In such a setup, where each template occupies a different filesystem, the "merge" build tag is required, and the work directory can be omitted during directory creation. Instead, the individual template directories contain both upper and work directories. # 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. 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 set username of the emulated user hostname 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 clones]: https://openzfs.github.io/openzfs-docs/man/v2.4/8/zfs-clone.8.html */ package main