15 | The following documents explain how an AppBundle behaves, how, and why.
16 | They also explain the reasoning behind these design choices.
17 |
18 | These documents can be a starting point for reimplementing the existing AppBundle tooling.
19 |
20 |
--------------------------------------------------------------------------------
/docs/format.md:
--------------------------------------------------------------------------------
1 | # AppBundle File Format Specification
2 |
3 | This document outlines the structure and composition of an AppBundle, a self-contained executable format designed to package applications with their dependencies for portable execution on Linux systems.
4 |
5 | ## File Structure
6 |
7 | An AppBundle is a single executable file that combines an ELF (Executable and Linkable Format) runtime with an appended filesystem image containing the application's data. The structure is as follows:
8 |
9 | 1. **ELF Runtime**:
10 | - The AppBundle begins with an ELF executable, identifiable by the magic bytes "AB" or optionally "AI" at the start of the file.
11 | - This runtime is responsible for handling the execution logic, including mounting or extracting the filesystem image and setting up the environment.
12 |
13 | 2. **Runtime Information Section (.pbundle_runtime_info)**:
14 | - The ELF file contains a section named `.pbundle_runtime_info`, which stores metadata in CBOR (Concise Binary Object Representation) format.
15 | - The structure of this section is defined in Go as:
16 | ```go
17 | type RuntimeInfo struct {
18 | AppBundleID string `json:"AppBundleID"` // Unique identifier for the AppBundle
19 | PelfVersion string `json:"PelfVersion"` // Version of the pelf tool used to create the AppBundle
20 | HostInfo string `json:"HostInfo"` // System information from `uname -mrsp(v)`
21 | FilesystemType string `json:"FilesystemType"` // Filesystem type: "dwarfs" or "squashfs"
22 | Hash string `json:"Hash"` // Hash of the filesystem image
23 | DisableRandomWorkDir bool `json:"DisableRandomWorkDir"` // Whether to use a fixed working directory
24 | MountOrExtract uint8 `json:"MountOrExtract"` // Run behavior: 0 (FUSE only), 1 (Extract only), 2 (FUSE with extract fallback), 3 (FUSE with extract fallback for files < 350MB)
25 | }
26 | ```
27 |
28 | 3. **Static Tools Section (.pbundle_static_tools)**:
29 | - The ELF file includes a section named `.pbundle_static_tools`, containing a Zstandard (ZSTD)-compressed tar archive.
30 | - This archive holds tools necessary for mounting or extracting the filesystem image, such as `dwarfs`, `dwarfsextract`, `squashfuse`, or `unsquashfs`, depending on the filesystem type.
31 |
32 | 4. **Filesystem Image**:
33 | - Immediately following the ELF runtime, the AppBundle contains the compressed filesystem image (either DwarFS or SquashFS).
34 | - This image encapsulates the application's AppDir, including all necessary files and dependencies.
35 |
36 | ## Creation of an AppBundle
37 |
38 | An AppBundle is created using the `pelf` tool, which performs the following steps:
39 |
40 | 1. **Prepare the AppDir**:
41 | - The `pelfCreator` tool constructs an AppDir, a directory containing the application's files, including:
42 | - `AppRun`: The entrypoint script that orchestrates the execution.
43 | - `.DirIcon`: An optional icon file (PNG, in sizes 512x512, 256x256, or 128x128).
44 | - `.DirIcon.svg`: An optional SVG icon.
45 | - `program.desktop`: An optional desktop entry file.
46 | - `program.appdata.xml`: An optional AppStream metadata file.
47 | - `proto` or `rootfs`: A directory containing the application's filesystem, typically based on a minimal Linux distribution like Alpine or ArchLinux.
48 | - The AppDir may also include additional binaries and configuration files as needed.
49 |
50 | 2. **Embed Runtime Information**:
51 | - The `pelf` tool embeds the `.pbundle_runtime_info` section with metadata about the AppBundle, including its ID, filesystem type, and runtime behavior.
52 |
53 | 3. **Embed Static Tools**:
54 | - Tools required for mounting or extracting the filesystem (e.g., `dwarfs`, `squashfuse`) are compressed into a ZSTD tar archive and embedded in the `.pbundle_static_tools` section.
55 |
56 | 4. **Append Filesystem Image**:
57 | - The AppDir is compressed into a DwarFS or SquashFS image, depending on the configuration, and appended to the ELF runtime.
58 | - The offset of the filesystem image is recorded in the runtime configuration for access during execution.
59 |
60 | 5. **Finalize the Executable**:
61 | - The `pelf` tool combines the ELF runtime, runtime information, static tools, and filesystem image into a single executable file with the `.AppBundle` extension.
62 |
63 | ## Run Behaviors
64 |
65 | The `MountOrExtract` field in the `.pbundle_runtime_info` section determines how the AppBundle behaves when executed:
66 |
67 | - **0 (FUSE Mounting Only)**: The AppBundle uses FUSE to mount the filesystem image. If FUSE is unavailable, it fails without falling back to extraction.
68 | - **1 (Extract and Run)**: The AppBundle extracts the filesystem image to a temporary directory (typically in `tmpfs`) and executes from there, ignoring FUSE even if available.
69 | - **2 (FUSE with Fallback)**: The AppBundle attempts to use FUSE to mount the filesystem. If FUSE is unavailable, it falls back to extracting the filesystem to `tmpfs`.
70 | - **3 (FUSE with Conditional Fallback)**: Similar to option 2, but fallback to extraction only occurs if the AppBundle file is smaller than 350MB.
71 |
72 | ## Expected Contents of the Filesystem Image
73 |
74 | The filesystem image within the AppBundle is expected to be an AppDir with at least the following:
75 |
76 | - **AppRun**: A shell script that serves as the entrypoint for the application. It sets up the environment and executes the main program.
77 | - **Optional Files**:
78 | - `.DirIcon`: A PNG icon in a standard size (512x512, 256x256, or 128x128).
79 | - `.DirIcon.svg`: An SVG icon
80 | - `program.desktop`: A desktop entry file for integration with desktop environments.
81 | - `program.appdata.xml`: An AppStream metadata file for application metadata.
82 | - `proto` or `rootfs`: A directory containing the application's filesystem, including binaries, libraries, and configuration files.
83 |
84 | ## Notes
85 |
86 | - The AppBundle format is designed to be self-contained, requiring no external dependencies for execution in most cases, assuming the necessary tools are embedded or available on the host system.
87 | - The choice of filesystem (DwarFS or SquashFS) affects the tools included in the `.pbundle_static_tools` section and the runtime behavior.
88 |
--------------------------------------------------------------------------------
/docs/runtime.md:
--------------------------------------------------------------------------------
1 | # AppBundle Runtime Execution
2 |
3 | This document describes how the AppBundle runtime operates, including how it reads its own information, extracts static tools, determines environment variables, and handles runtime flags.
4 |
5 | ## Execution Flow
6 |
7 | When an AppBundle is executed, the runtime performs the following steps:
8 |
9 | 1. **Read Runtime Information**:
10 | - The runtime reads the `.pbundle_runtime_info` section from the ELF file, which contains CBOR-encoded metadata.
11 | - This section includes:
12 | - `AppBundleID`: A unique identifier for the AppBundle. (e.g: "com.brave.Browser-xplshn-2025-05-19". You're not forced to follow this format, but if you do, you can create a [dbin](https://github.com/xplshn/dbin) repository that countains your AppBundle by using our [appstream-helper](https://github.com/xplshn/pelf/blob/master/cmd/misc/appstream-helper/appstream-helper.go) tool. `$NAME-$MAINTAINER-$DATE` or preferably: `$APPSTREAM_ID-$MAINTAINER-$DATE`, so that you don't have to include an AppStream file within the AppDir for appstream-helper to get metadata from it)
13 | - `PelfVersion`: The version of the `pelf` tool used to create the AppBundle.
14 | - `HostInfo`: System information from `uname -mrsp(v)` of the build machine.
15 | - `FilesystemType`: Either "dwarfs" or "squashfs".
16 | - `Hash`: A hash of the filesystem image for integrity verification.
17 | - `DisableRandomWorkDir`: A boolean indicating whether to use a fixed working directory.
18 | - `MountOrExtract`: A uint8 value (0–3) specifying the run behavior (see below).
19 | - The runtime uses this information to configure its behavior and locate the filesystem image.
20 |
21 | 2. **Extract Static Tools**:
22 | - The runtime accesses the static tools required for mounting or extracting the filesystem (e.g., `dwarfs`, `dwarfsextract`, `squashfuse`, `unsquashfs`).
23 | - The handling of static tools depends on the build mode:
24 | - **noEmbed Edition**: The tools are embedded in the `.pbundle_static_tools` ELF section as a ZSTD-compressed tar archive. The runtime determines the filesystem mounting and extraction commands at runtime, extracts the needed files from this archive to a temporary directory (`cfg.staticToolsDir`), and uses them to either mount or extract the filesystem.
25 | - **Embed Edition**: The tools are embedded directly in the binary using Go’s `embed` package, without compression. The runtime accesses these tools directly from the embedded filesystem, without needing to extract a compressed archive.
26 |
27 | 3. **Exported Env Variables**:
28 | - The runtime sets up several environment variables to facilitate execution:
29 | - **HOME**: If a portable home directory (`.AppBundleID.home`) exists in the same directory as the AppBundle, it is used as `$HOME`.
30 | - **XDG_DATA_HOME**: If a portable share directory (`.AppBundleID.share`) exists, it is used as `$XDG_DATA_HOME`.
31 | - **XDG_CONFIG_HOME**: If a portable config directory (`.AppBundleID.config`) exists, it is used as `$XDG_CONFIG_HOME`.
32 | - **APPDIR**: Set to the mount or extraction directory
33 | - **SELF**: The absolute path to the AppBundle executable.
34 | - **ARGV0**: The basename of `$SELF`
35 | - **PATH**: Augmented to include the AppBundle's `bin` directory and the directory containing the static tools.
36 |
37 | 4. **Mount or Extract Filesystem**:
38 | - The runtime decides whether to mount or extract the filesystem image based on the `MountOrExtract` value:
39 | - **0**: Mounts the filesystem using FUSE (e.g., `dwarfs` or `squashfuse`) and fails if FUSE is unavailable.
40 | - **1**: Extracts the filesystem to a temporary directory (usually in `tmpfs`) and runs from there.
41 | - **2**: Attempts to mount with FUSE; falls back to extraction if FUSE is unavailable.
42 | - **3**: Similar to 2, but only falls back to extraction if the AppBundle is smaller than 350MB.
43 |
44 | 5. **Execute the Application**:
45 | - The runtime executes the `AppRun` script within the AppDir.
46 | - If a specific command is provided via `--pbundle_link`, the runtime executes that command within the AppBundle's environment, instead of executing the AppRun.
47 |
48 | ## Runtime Flags
49 |
50 | The AppBundle runtime supports several command-line flags to modify its behavior:
51 |
52 | - **`--pbundle_help`**: Displays help information, including the `PelfVersion`, `HostInfo`, and internal configuration variables (e.g., `cfg.exeName`, `cfg.mountDir`).
53 | - **`--pbundle_list`**: Lists the contents of the AppBundle's filesystem, including static tools.
54 | - **`--pbundle_link `**: Executes a specified command within the AppBundle's environment, leveraging its `PATH` and other variables.
55 | - **`--pbundle_pngIcon`**: Outputs the base64-encoded `.DirIcon` (PNG) if it exists; otherwise, exits with error code 1.
56 | - **`--pbundle_svgIcon`**: Outputs the base64-encoded `.DirIcon.svg` if it exists; otherwise, exits with error code 1.
57 | - **`--pbundle_appstream`**: Outputs the base64-encoded first `.xml` file (AppStream metadata) found in the AppDir.
58 | - **`--pbundle_desktop`**: Outputs the base64-encoded first `.desktop` file found in the AppDir.
59 | - **`--pbundle_portableHome`**: Creates a portable home directory (`.AppBundleID.home`) in the same directory as the AppBundle.
60 | - **`--pbundle_portableConfig`**: Creates a portable config directory (`.AppBundleID.config`) in the same directory as the AppBundle.
61 | - **`--pbundle_cleanup`**: Unmounts and removes the AppBundle's working directory and mount point, affecting only instances of the same AppBundle.
62 | - **`--pbundle_mount`**: Mounts the filesystem to a specified or default directory and keeps the mount active.
63 | - **`--pbundle_extract [globs]`**: Extracts the filesystem to a directory (default: `_` or `squashfs-root` for AppImage compatibility). Supports selective extraction with glob patterns.
64 | - **`--pbundle_extract_and_run`**: Extracts the filesystem and immediately executes the entrypoint.
65 | - **`--pbundle_offset`**: Outputs the offset of the filesystem image within the AppBundle.
66 | - **AppImage Compatibility Flags**:
67 | - `--appimage-extract`: Same as `--pbundle_extract`, but uses `squashfs-root` as the output directory.
68 | - `--appimage-extract-and-run`: Same as `--pbundle_extract_and_run`.
69 | - `--appimage-mount`: Same as `--pbundle_mount`.
70 | - `--appimage-offset`: Same as `--pbundle_offset`.
71 |
72 | ## Notes
73 |
74 | - The choice between `noEmbed` and embed modes affects how static tools are stored and accessed. The `noEmbed` mode uses a compressed archive for flexibility, while the embed mode simplifies access by avoiding compression.
75 | - The `AppRun` script (e.g., `AppRun.rootfs-based`, `AppRun.sharun`, or `AppRun.sharun.ovfsProto`) determines sandboxing and execution behavior, such as using `bwrap` or `unionfs-fuse`.
76 | - The runtime ensures cleanup of temporary directories unless `--pbundle_cleanup` is explicitly called or `noCleanup` is set.
77 | - The `noEmbed` build tag for the `appbundle-runtime` allows you to build a single appbundle-runtime binary, that determines which filesystem to use at runtime, after having read its .pbundle_runtime_info and decompressed the .tar.zst data within the .pbundle_static_tools ELF section
78 | - If you're writting a new runtime, I recommend you implement appbundle-runtime.go, cli.go and noEmbed.go. This edition of the runtime is the most portable and flexible. It is simplifies a lot the build process.
79 |
--------------------------------------------------------------------------------
/docs/tooling.md:
--------------------------------------------------------------------------------
1 | # pelf
2 |
3 | The `pelf` command is responsible for assembling an AppBundle by combining an ELF runtime, runtime information, static tools, and a compressed filesystem image.
4 |
5 | ### Functionality
6 |
7 | - **Purpose**: Creates an AppBundle from an AppDir, embedding necessary metadata and tools.
8 | - **Key Operations**:
9 | - Reads an AppDir, verifies that it contains an executable AppRun
10 | - Copies the runtime to the output file
11 | - Embeds runtime information (MessagePack format) in the `.pbundle_runtime_info` section of the output file
12 | - If the runtime is a universal runtime (e.g: noEmbed edition), it puts a ZSTD-compressed tar archive of static tools (depending the chosen filesystem: e.g., `dwarfs`, `squashfuse`, `unsquashfs`) in the `.pbundle_static_tools` section of the output file.
13 | - Compresses the AppDir into a DwarFS or SquashFS filesystem image and appends it to the output file
14 | - Sets the AppBundle's executable permissions and finalizes the output file.
15 |
16 | ### Command-Line Usage
17 |
18 | The pelf tool is can be invoked with the following flags:
19 |
20 | - **--add-appdir, -a **: Specifies the AppDir to package.
21 | - **--appbundle-id, -i **: Sets the unique AppBundleID for the AppBundle.
22 | - **--output-to, -o **: Specifies the output file name (e.g., app.dwfs.AppBundle).
23 | - **--compression, -c **: Specifies compression flags for the filesystem.
24 | - **--static-tools-dir **: Specifies a custom directory for static tools.
25 | - **--runtime **: Specifies the runtime binary to use.
26 | - **--upx**: Enables UPX compression for static tools. (upx must be in the host system)
27 | - **--filesystem, -j :** Selects the filesystem type (squashfs or [dwarfs]).
28 | - **--prefer-tools-in-path:** Prefers tools in `$PATH` over embedded ones.
29 | - **--list-static-tools:** Lists embedded tools with their B3SUMs.
30 | - **--disable-use-random-workdir, -d:** Disables random working directory usage. This making AppBundles leave their mountpoint open and reusing it in each launch. This is ideal for big programs that need to launch ultra-fast, such as web browsers, messaging clients, etc
31 | - **--run-behavior, -b <0|1|2|3>:** Sets runtime behavior (0: FUSE only, 1: Extract only, 2: FUSE with extract fallback, 3: FUSE with extract fallback if ≤ 350MB).
32 | - **--appimage-compat, -A:** Sets the "AI" magic-bytes, so that AppBundles are detected as AppImages by AppImage integration software like [AppImageUpdate](https://github.com/AppImageCommunity/AppImageUpdate)
33 | - **--add-runtime-info-section :** Adds custom runtime information fields. (e.g: '.MyCustomRuntimeInfoSection:Hello')
34 | - **--add-elf-section :** Adds a custom ELF section from a .elfS file., where the filename of the .elfS file minus the extension is the section name, and the file contents are the data
35 | - **--add-updinfo :** Adds an upd_info ELF section with the given string.
36 |
37 | # pelfCreator
38 |
39 | The `pelfCreator` command is a higher-level utility that prepares an AppDir and invokes `pelf` to create an AppBundle. It supports multiple modes for different use cases.
40 |
41 | ### Functionality
42 |
43 | - **Purpose**: Creates an AppDir, populates it with a root filesystem, application files, and dependencies, and then packages it into an AppBundle.
44 | - **Key Operations**:
45 | - Sets up a temporary directory for processing.
46 | - Downloads or uses a local root filesystem (e.g., Alpine or ArchLinux).
47 | - Installs specified packages using `apk` (Alpine) or `pacman` (ArchLinux).
48 | - Configures the AppRun script and entrypoint.
49 | - Optionally processes binaries with `lib4bin` for `sharun` mode.
50 | - Trims the filesystem based on `--keep` or `--getrid` flags.
51 | - Calls `pelf` to finalize the AppBundle.
52 |
53 | ### Command-Line Usage
54 |
55 | The `pelfCreator` tool is invoked with the following flags:
56 |
57 | - **`--maintainer `**: Specifies the maintainer's name (required).
58 | - **`--name `**: Sets the application name (required).
59 | - **`--appbundle-id `**: Sets the `AppBundleID` (optional; defaults to `--`).
60 | - **`--pkg-add `**: Specifies packages to install in the root filesystem (required).
61 | - **`--entrypoint `**: Sets the entrypoint command or desktop file (required unless using `--multicall`).
62 | - **`--keep `**: Specifies files to keep in the `proto` directory.
63 | - **`--getrid `**: Specifies files to remove from the `proto` directory.
64 | - **`--filesystem `**: Selects the filesystem type (`dwfs` or `squashfs`; default: `dwfs`).
65 | - **`--output-to `**: Specifies the output AppBundle file (optional; defaults to `..AppBundle`).
66 | - **`--local `**: Specifies a directory or archive containing resources (e.g., `rootfs.tar`, `AppRun`, `bwrap`).
67 | - **`--preserve-rootfs-permissions`**: Preserves original filesystem permissions.
68 | - **`--dontpack`**: Stops short of packaging the AppDir into an AppBundle, leaving only the AppDir.
69 | - **`--sharun `**: Processes specified binaries with `lib4bin` and uses `AppRun.sharun` or `AppRun.sharun.ovfsProto`.
70 | - **`--sandbox`**: Enables sandbox mode using `AppRun.rootfs-based` with `bwrap`.
71 |
72 | ### Modes of Operation
73 |
74 | 1. **Sandbox Mode** (`--sandbox`):
75 | - Retains and binds the `proto` directory as the root filesystem, with host directory bindings (e.g., `/home`, `/tmp`, `/etc`).
76 | - Supports trimming of the `proto` directory using `--keep` or `--getrid` flags to reduce size.
77 | - Uses `AppRun.rootfs-based` to run the application in a `bwrap` sandbox.
78 | - Can be customized via env vars such as `SHARE_LOOK`, `SHARE_FONTS`, `SHARE_AUDIO`, and `UID0_GID0` for fine-grained control over sandboxing.
79 | - Suitable for applications requiring strict isolation from the host system. Or those that refuse to work with the default mode (hybrid)
80 |
81 | 2. **Sharun Mode** (`--sharun `):
82 | - Processes specified binaries with `lib4bin` to ensure compatibility and portability.
83 | - Uses `AppRun.sharun` (if `proto` is removed) or `AppRun.sharun.ovfsProto` (if `proto` is retained).
84 | - When using `AppRun.sharun.ovfsProto`, employs `unionfs-fuse` to create a copy-on-write overlay of the `proto` directory.
85 | - Sets `LD_LIBRARY_PATH` to include library paths from the AppDir, ensuring binaries can find their dependencies.
86 | - Ideal for lightweight applications or when minimizing filesystem size is a priority.
87 |
88 | 3. **Default Mode** (can be combined with `--sharun`, to ship lightweight AppBundles that include a default config file, etc, but otherwise use the system's files unless they're missing):
89 | - Retains the `proto` directory
90 | - Supports trimming of the `proto` directory using `--keep` or `--getrid` flags to reduce size.
91 | - Uses `AppRun.sharun.ovfsProto` to execute the application with a `unionfs-fuse` overlay of the user's `/` & the AppDir's `proto`.
92 | - Suitable for most applications, as it allows the AppBundle to use files from the system if they don't exist in the AppDir's `proto` and vice-versa
93 |
94 | ## Notes
95 |
96 | - The `pelfCreator` tool supports extensibility through custom root filesystems and package managers via the `--local` flag.
97 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module pelf
2 |
3 | go 1.24
4 |
5 | require (
6 | fyne.io/fyne/v2 v2.5.5
7 | github.com/adrg/xdg v0.5.3
8 | github.com/emmansun/base64 v0.7.0
9 | github.com/fxamacker/cbor/v2 v2.8.0
10 | github.com/goccy/go-json v0.10.5
11 | github.com/joho/godotenv v1.5.1
12 | github.com/klauspost/compress v1.18.0
13 | github.com/liamg/memit v0.0.3
14 | github.com/liamg/tml v0.7.0
15 | github.com/mholt/archives v0.1.1
16 | github.com/minio/md5-simd v1.1.2
17 | github.com/pkg/xattr v0.4.10
18 | github.com/shamaton/msgpack/v2 v2.2.3
19 | github.com/shirou/gopsutil/v4 v4.25.4
20 | github.com/u-root/u-root v0.14.0
21 | github.com/urfave/cli/v3 v3.3.3
22 | github.com/zeebo/blake3 v0.2.4
23 | golang.org/x/image v0.25.0
24 | golang.org/x/sys v0.33.0
25 | gopkg.in/yaml.v3 v3.0.1
26 | lukechampine.com/blake3 v1.4.1
27 | pgregory.net/rand v1.0.2
28 | )
29 |
30 | require (
31 | fyne.io/systray v1.11.0 // indirect
32 | github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
33 | github.com/STARRY-S/zip v0.2.3 // indirect
34 | github.com/andybalholm/brotli v1.1.1 // indirect
35 | github.com/bodgit/plumbing v1.3.0 // indirect
36 | github.com/bodgit/sevenzip v1.6.0 // indirect
37 | github.com/bodgit/windows v1.0.1 // indirect
38 | github.com/davecgh/go-spew v1.1.1 // indirect
39 | github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
40 | github.com/ebitengine/purego v0.8.4 // indirect
41 | github.com/fredbi/uri v1.1.0 // indirect
42 | github.com/fsnotify/fsnotify v1.7.0 // indirect
43 | github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
44 | github.com/fyne-io/glfw-js v0.0.0-20241126112943-313d8a0fe1d0 // indirect
45 | github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect
46 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
47 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
48 | github.com/go-ole/go-ole v1.3.0 // indirect
49 | github.com/go-text/render v0.2.0 // indirect
50 | github.com/go-text/typesetting v0.2.0 // indirect
51 | github.com/godbus/dbus/v5 v5.1.0 // indirect
52 | github.com/gopherjs/gopherjs v1.17.2 // indirect
53 | github.com/hashicorp/errwrap v1.1.0 // indirect
54 | github.com/hashicorp/go-multierror v1.1.1 // indirect
55 | github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
56 | github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect
57 | github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
58 | github.com/klauspost/cpuid/v2 v2.2.10 // indirect
59 | github.com/klauspost/pgzip v1.2.6 // indirect
60 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
61 | github.com/minio/minlz v1.0.0 // indirect
62 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
63 | github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect
64 | github.com/nwaples/rardecode/v2 v2.1.1 // indirect
65 | github.com/pierrec/lz4/v4 v4.1.22 // indirect
66 | github.com/pmezard/go-difflib v1.0.0 // indirect
67 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
68 | github.com/rymdport/portal v0.3.0 // indirect
69 | github.com/sorairolake/lzip-go v0.3.7 // indirect
70 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
71 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
72 | github.com/stretchr/testify v1.10.0 // indirect
73 | github.com/therootcompany/xz v1.0.1 // indirect
74 | github.com/tklauser/go-sysconf v0.3.15 // indirect
75 | github.com/tklauser/numcpus v0.10.0 // indirect
76 | github.com/ulikunitz/xz v0.5.12 // indirect
77 | github.com/x448/float16 v0.8.4 // indirect
78 | github.com/yuin/goldmark v1.7.1 // indirect
79 | github.com/yusufpapurcu/wmi v1.2.4 // indirect
80 | go4.org v0.0.0-20230225012048-214862532bf5 // indirect
81 | golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
82 | golang.org/x/net v0.40.0 // indirect
83 | golang.org/x/text v0.25.0 // indirect
84 | )
85 |
--------------------------------------------------------------------------------
/pelf_linker:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Initialize variables
4 | PELF_BINDIRS=""
5 | PELF_LIBDIRS=""
6 |
7 | # Function to concatenate existing directories from *_binDir environment variables into PELF_BINDIRS
8 | concatenate_bindirs() {
9 | # Find all environment variables ending with _binDir
10 | vars="$(env | grep ".*_binDir=" | cut -f 1 -d '=')"
11 | for v in $vars; do
12 | # Get the value of the variable
13 | eval "vval=\$$v"
14 |
15 | # Save the current IFS and change it to handle colon-separated paths
16 | old_ifs="$IFS"
17 | IFS=":"
18 |
19 | # Loop through each path in the variable
20 | for dir in $vval; do
21 | # Check if the directory exists
22 | if [ -d "$dir" ]; then
23 | # Append to PELF_BINDIRS if the directory exists
24 | if [ -z "$PELF_BINDIRS" ]; then
25 | PELF_BINDIRS="$dir"
26 | else
27 | PELF_BINDIRS="$PELF_BINDIRS:$dir"
28 | fi
29 | fi
30 | done
31 |
32 | # Restore the original IFS
33 | IFS="$old_ifs"
34 | done
35 |
36 | # Print the concatenated PELF_BINDIRS
37 | if [ -z "$1" ]; then
38 | echo "PELF_BINDIRS=\"$PELF_BINDIRS\""
39 | fi
40 | }
41 |
42 | # Function to concatenate existing directories from *_libDir environment variables into PELF_LIBDIRS
43 | concatenate_libdirs() {
44 | # Find all environment variables ending with _libDir
45 | vars="$(env | grep ".*_libDir=" | cut -f 1 -d '=')"
46 | for v in $vars; do
47 | # Get the value of the variable
48 | eval "vval=\$$v"
49 |
50 | # Save the current IFS and change it to handle colon-separated paths
51 | old_ifs="$IFS"
52 | IFS=":"
53 |
54 | # Loop through each path in the variable
55 | for dir in $vval; do
56 | # Check if the directory exists
57 | if [ -d "$dir" ]; then
58 | # Append to PELF_LIBDIRS if the directory exists
59 | if [ -z "$PELF_LIBDIRS" ]; then
60 | PELF_LIBDIRS="$dir"
61 | else
62 | PELF_LIBDIRS="$PELF_LIBDIRS:$dir"
63 | fi
64 | fi
65 | done
66 |
67 | # Restore the original IFS
68 | IFS="$old_ifs"
69 | done
70 |
71 | # Print the concatenated PELF_LIBDIRS
72 | if [ -z "$1" ]; then
73 | echo "PELF_LIBDIRS=\"$PELF_LIBDIRS\""
74 | fi
75 | }
76 |
77 | # Call the functions
78 | concatenate_bindirs "$1"
79 | concatenate_libdirs "$1"
80 |
81 | if [ "$1" = "--export" ]; then
82 | export PELF_LIBDIRS="$PELF_LIBDIRS"
83 | export PELF_BINDIRS="$PELF_BINDIRS"
84 | else
85 | LD_LIBRARY_PATH="$PELF_LIBDIRS" PATH="$PATH:$PELF_BINDIRS" "$@"
86 | fi
87 |
--------------------------------------------------------------------------------
/www/archetypes/default.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '{{ .Date }}'
3 | draft = true
4 | title = '{{ replace .File.ContentBaseName "-" " " | title }}'
5 | +++
6 |
--------------------------------------------------------------------------------
/www/config.toml:
--------------------------------------------------------------------------------
1 | title = "AppBundle Documentation & Implementation Details"
2 | baseURL = "https://pelf.xplshn.com.ar/"
3 | languageCode = "en-us"
4 | theme = "werx"
5 | publishDir = "pub"
6 | enableRobotsTXT = true
7 |
8 | ignoreFiles = ["\\.Rmd$", "_files$", "_cache$"]
9 | preserveTaxonomyNames = true
10 | enableEmoji = true
11 | footnotereturnlinkcontents = "↩"
12 |
13 | [module]
14 | [[module.mounts]]
15 | source = 'assets'
16 | target = 'assets'
17 | [[module.mounts]]
18 | source = 'static'
19 | target = 'assets'
20 |
21 | [permalinks]
22 | post = "/post/:year/:month/:day/:slug/"
23 |
24 | [[menu.main]]
25 | name = "Home"
26 | url = "/"
27 | weight = 1
28 | #[[menu.main]]
29 | # name = "Categories"
30 | # url = "/categories/"
31 | # weight = 2
32 | #[[menu.main]]
33 | # name = "Tags"
34 | # url = "/tags/"
35 | # weight = 3
36 | [[menu.feed]]
37 | name = "Subscribe"
38 | url = "/index.xml"
39 | weight = 100
40 | [[menu.feed]]
41 | name = "neoblog"
42 | url = "https://fatbuffalo.neocities.org/def"
43 | weight = 90
44 | [[menu.feed]]
45 | name = "dbin"
46 | url = "https://github.com/xplshn/dbin"
47 | weight = 80
48 | [[menu.feed]]
49 | name = "harmful.cat-v.org"
50 | url = "https://harmful.cat-v.org"
51 | weight = 70
52 | [[menu.feed]]
53 | name = "nosystemd.org"
54 | url = "https://nosystemd.org"
55 | weight = 60
56 | [[menu.feed]]
57 | name = "suckless.org"
58 | url = "https://suckless.org"
59 | weight = 50
60 | [[menu.feed]]
61 | name = "copacabana.pindorama.net.br"
62 | url = "https://copacabana.pindorama.net.br"
63 | weight = 40
64 | [[menu.feed]]
65 | name = "shithub.us"
66 | url = "https://shithub.us"
67 | weight = 30
68 | [[menu.feed]]
69 | name = "managainstthestate.blogspot.com"
70 | url = "https://web.archive.org/web/20231123031907/https://managainstthestate.blogspot.com/2011/08/anarcho-capitalist-resources-by-wesker.html"
71 | weight = 20
72 | [[menu.feed]]
73 | name = "musl.libc.org"
74 | url = "https://musl.libc.org"
75 | weight = 10
76 |
77 | [taxonomies]
78 | category = "categories"
79 | series = "series"
80 | tag = "tags"
81 |
82 | [params]
83 | subtitle = "Labs"
84 | brandIconFile = "assets/images/icon.svg"
85 | abbrDateFmt = "Jan 2"
86 | dateFmt = "01.02.2006 15:04"
87 | themeVariant = "theme_blue.css"
88 | printSidebar = false
89 |
90 | #[[social]]
91 | # name = "Github"
92 | # url = "https://github.com/xplshn/alicelinux"
93 | #[[social]]
94 | # name = "Telegram"
95 | # url = "https://t.me/alicelinux"
96 |
97 | [markup.goldmark.renderer]
98 | hardWraps = false
99 | unsafe = true
100 |
101 | [markup.goldmark.renderHooks.image]
102 | enableDefault = true
103 |
--------------------------------------------------------------------------------
/www/content/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Home'
3 | ---
4 | ### PELF - The AppBundle format and the AppBundle Creation Tool
5 | ###### PELF used to stand for Pack an Elf, but we slowly evolved into a much simpler yet more featureful alternative to .AppImages
6 | ###### PELF now refers to the tool used to create .AppBundles
7 |
8 | ---
9 |
10 | > .AppBundles are an executable *packaging format* designed to pack applications, toolchains, window managers, and multiple programs into a *single portable file*.
11 |
12 | AppBundles can serve as a drop-in replacement for AppImages. Both AppBundles and AppImages utilize the AppDir specification, making it easy to unpack an AppImage and re-package it as an AppBundle, gaining many features, such as faster start-up times, better compression and file de-duplication, and faster build-time. A completely customizable and flexible format.
13 |
14 | #### Advantages
15 | - **Support for multiple filesystem formats**: Support for multiple mountable filesystem formats, we currently support `squashfs` and `dwarfs`. With ongoing efforts to add a third alternative that isn't copylefted/propietary
16 | - **Simplicity**: PELF is a minimalistic Go program that makes creating portable POSIX executables a trivial task.
17 | - **Flexibility of AppBundles**: AppBundles do not force compliance with the AppDir standard. For example, you can bundle window managers and basic GUI utilities into a single file (as done with `Sway.AppBundle`). You can even package toolchains as single-file executables.
18 | - **Endless Possibilities**: With a custom AppRun script, you can create versatile `.AppBundles`. For instance, packaging a Rick Roll video with a video player that works on both glibc and musl systems is straightforward. You can even generate AppBundles that overlay on top of each other.
19 | - **Complete tooling**: The `pelfd` daemon (and its GUI version) are available for use as system integrators, they're in charge of adding the AppBundles that you put under ~/Applications in your "start menu". This is one of the many programs that are part of the tooling, another great tool is pelfCreator, which lets you create programs via simple one-liners (by default it uses an Alpine rootfs + bwrap, but you can get smaller binaries via using -x to only keep the binaries you want), a one-liner to pack Chromium into a single-file executable looks like this: `pelfCreator --maintainer "xplshn" --name "org.chromium.Chromium" --pkg-add "chromium" --entrypoint "chromium.desktop"`
20 | - **Predictable mount directories**: Our mount directories contain the AppBundle's ID, making it clear to which AppBundle the mount directory belongs
21 | - **Reliable unmount**: The AppBundle starts a background task to unmount the filesystem, and it retries 5 times, then it forces the unmount if all 5 tries failed
22 | - **Leverages many handy env variables**: Thus making .AppBundles very flexible and scriptable
23 | - **AppImage compatibility**: The --appimage-* flags are supported by our runtime, making us an actual drop-in replacement
24 |
25 | ### Usage
26 | ```
27 | ./pelf --add-appdir "nano-14_02_2025.AppDir" --appbundle-id "nano-14_02_2025-xplshn" --output-to "nano-14_02_2025.dwfs.AppBundle"
28 | ```
29 | OR
30 | ```
31 | ./pelf --add-appdir "nano-14_02_2025.AppDir" --appbundle-id "nano-14_02_2025-xplshn" --output-to "nano-14_02_2025.sqfs.AppBundle"
32 | ```
33 |
34 | ### Build ./pelf
35 | 1. Get yourself an up-to-date `go` toolchain and install `dbin` into your system or put it anywhere in your `$PATH`
36 | 2. execute `./cbuild.sh`
37 | 3. Put the resulting `./pelf` binary in your `$PATH`
38 | 4. Spread the joy of AppBundles! :)
39 |
40 | ### Usage of the Resulting `.AppBundle`
41 | > By using the `--pbundle_link` option, you can access files contained within the `./bin` or `./usr/bin` directories of an `.AppBundle`, inheriting environment variables like `PATH`. This allows multiple AppBundles to stack on top of each other, sharing libraries and binaries across "parent" bundles.
42 |
43 | #### Explanation
44 | You specify an `AppDir` to be packed and an ID for the app. This ID will be used when mounting the `.AppBundle` and should include the packing date, the project or program name, and the maintainer's information. While you can choose an arbitrary name, it’s not recommended.
45 |
46 | Additionally, we embed the tools used for mounting and unmounting the `.AppBundle`, such as `dwarfs` when using `pelf`.
47 |
48 |
49 |
50 |
51 |
52 | #### Known working distros/OSes:
53 | - Ubuntu (10.04 onwards) & derivatives, Ubuntu Touch
54 | - Alpine Linux 2.+ onwards
55 | - Void Linux Musl/Glibc
56 | - Debian/Devuan, and derivatives
57 | - Fedora
58 | - *SUSE
59 | - Maemo leste
60 | - AliceLinux
61 | - FreeBSD's Linuxlator
62 | - FreeBSD native
63 | - Chimera Linux
64 | - LFS (Linux from Scratch)
65 | - Most if not all Musl linux distributions
66 | - etc (please contribute to this list if you're a user of AppBundles)
67 |
68 | #### Resources:
69 | - [AppBundle format documentation & specifications](https://xplshn.github.io/pelf/docs)
70 | - The [AppBundleHUB](https://github.com/xplshn/AppBundleHUB) a repo which builds a ton of portable AppBundles in an automated fashion, using GH actions. (we have a [webStore](https://xplshn.github.io/AppBundleHUB) too, tho that is WIP)
71 | - [dbin](https://github.com/xplshn/dbin) a self-contained, portable, statically linked, package manager, +4000 binaries (portable, self-contained/static) are available in its repos at the time of writting. Among these, are the AppBundles from the AppBundleHUB and from pkgforge
72 |
--------------------------------------------------------------------------------
/www/content/docs/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2025-04-25T15:48:50'
3 | draft = false
4 | title = 'Documentation Index'
5 | [params.author]
6 | name = 'xplshn'
7 | email = 'xplshn@murena.io'
8 | +++
9 |
10 |
15 | The following documents explain how an AppBundle behaves, how, and why.
16 | They also explain the reasoning behind these design choices.
17 |
18 | These documents can be a starting point for reimplementing the existing AppBundle tooling.
19 |
20 |
--------------------------------------------------------------------------------
/www/content/docs/format.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2025-05-25T00:00:29'
3 | draft = false
4 | title = 'format.md'
5 | [params.author]
6 | name = 'xplshn'
7 | email = 'xplshn@murena.io'
8 | +++
9 | # AppBundle File Format Specification
10 |
11 | This document outlines the structure and composition of an AppBundle, a self-contained executable format designed to package applications with their dependencies for portable execution on Linux systems.
12 |
13 | ## File Structure
14 |
15 | An AppBundle is a single executable file that combines an ELF (Executable and Linkable Format) runtime with an appended filesystem image containing the application's data. The structure is as follows:
16 |
17 | 1. **ELF Runtime**:
18 | - The AppBundle begins with an ELF executable, identifiable by the magic bytes "AB" or optionally "AI" at the start of the file.
19 | - This runtime is responsible for handling the execution logic, including mounting or extracting the filesystem image and setting up the environment.
20 |
21 | 2. **Runtime Information Section (.pbundle_runtime_info)**:
22 | - The ELF file contains a section named `.pbundle_runtime_info`, which stores metadata in CBOR (Concise Binary Object Representation) format.
23 | - The structure of this section is defined in Go as:
24 | ```go
25 | type RuntimeInfo struct {
26 | AppBundleID string `json:"AppBundleID"` // Unique identifier for the AppBundle
27 | PelfVersion string `json:"PelfVersion"` // Version of the pelf tool used to create the AppBundle
28 | HostInfo string `json:"HostInfo"` // System information from `uname -mrsp(v)`
29 | FilesystemType string `json:"FilesystemType"` // Filesystem type: "dwarfs" or "squashfs"
30 | Hash string `json:"Hash"` // Hash of the filesystem image
31 | DisableRandomWorkDir bool `json:"DisableRandomWorkDir"` // Whether to use a fixed working directory
32 | MountOrExtract uint8 `json:"MountOrExtract"` // Run behavior: 0 (FUSE only), 1 (Extract only), 2 (FUSE with extract fallback), 3 (FUSE with extract fallback for files < 350MB)
33 | }
34 | ```
35 |
36 | 3. **Static Tools Section (.pbundle_static_tools)**:
37 | - The ELF file includes a section named `.pbundle_static_tools`, containing a Zstandard (ZSTD)-compressed tar archive.
38 | - This archive holds tools necessary for mounting or extracting the filesystem image, such as `dwarfs`, `dwarfsextract`, `squashfuse`, or `unsquashfs`, depending on the filesystem type.
39 |
40 | 4. **Filesystem Image**:
41 | - Immediately following the ELF runtime, the AppBundle contains the compressed filesystem image (either DwarFS or SquashFS).
42 | - This image encapsulates the application's AppDir, including all necessary files and dependencies.
43 |
44 | ## Creation of an AppBundle
45 |
46 | An AppBundle is created using the `pelf` tool, which performs the following steps:
47 |
48 | 1. **Prepare the AppDir**:
49 | - The `pelfCreator` tool constructs an AppDir, a directory containing the application's files, including:
50 | - `AppRun`: The entrypoint script that orchestrates the execution.
51 | - `.DirIcon`: An optional icon file (PNG, in sizes 512x512, 256x256, or 128x128).
52 | - `.DirIcon.svg`: An optional SVG icon.
53 | - `program.desktop`: An optional desktop entry file.
54 | - `program.appdata.xml`: An optional AppStream metadata file.
55 | - `proto` or `rootfs`: A directory containing the application's filesystem, typically based on a minimal Linux distribution like Alpine or ArchLinux.
56 | - The AppDir may also include additional binaries and configuration files as needed.
57 |
58 | 2. **Embed Runtime Information**:
59 | - The `pelf` tool embeds the `.pbundle_runtime_info` section with metadata about the AppBundle, including its ID, filesystem type, and runtime behavior.
60 |
61 | 3. **Embed Static Tools**:
62 | - Tools required for mounting or extracting the filesystem (e.g., `dwarfs`, `squashfuse`) are compressed into a ZSTD tar archive and embedded in the `.pbundle_static_tools` section.
63 |
64 | 4. **Append Filesystem Image**:
65 | - The AppDir is compressed into a DwarFS or SquashFS image, depending on the configuration, and appended to the ELF runtime.
66 | - The offset of the filesystem image is recorded in the runtime configuration for access during execution.
67 |
68 | 5. **Finalize the Executable**:
69 | - The `pelf` tool combines the ELF runtime, runtime information, static tools, and filesystem image into a single executable file with the `.AppBundle` extension.
70 |
71 | ## Run Behaviors
72 |
73 | The `MountOrExtract` field in the `.pbundle_runtime_info` section determines how the AppBundle behaves when executed:
74 |
75 | - **0 (FUSE Mounting Only)**: The AppBundle uses FUSE to mount the filesystem image. If FUSE is unavailable, it fails without falling back to extraction.
76 | - **1 (Extract and Run)**: The AppBundle extracts the filesystem image to a temporary directory (typically in `tmpfs`) and executes from there, ignoring FUSE even if available.
77 | - **2 (FUSE with Fallback)**: The AppBundle attempts to use FUSE to mount the filesystem. If FUSE is unavailable, it falls back to extracting the filesystem to `tmpfs`.
78 | - **3 (FUSE with Conditional Fallback)**: Similar to option 2, but fallback to extraction only occurs if the AppBundle file is smaller than 350MB.
79 |
80 | ## Expected Contents of the Filesystem Image
81 |
82 | The filesystem image within the AppBundle is expected to be an AppDir with at least the following:
83 |
84 | - **AppRun**: A shell script that serves as the entrypoint for the application. It sets up the environment and executes the main program.
85 | - **Optional Files**:
86 | - `.DirIcon`: A PNG icon in a standard size (512x512, 256x256, or 128x128).
87 | - `.DirIcon.svg`: An SVG icon
88 | - `program.desktop`: A desktop entry file for integration with desktop environments.
89 | - `program.appdata.xml`: An AppStream metadata file for application metadata.
90 | - `proto` or `rootfs`: A directory containing the application's filesystem, including binaries, libraries, and configuration files.
91 |
92 | ## Notes
93 |
94 | - The AppBundle format is designed to be self-contained, requiring no external dependencies for execution in most cases, assuming the necessary tools are embedded or available on the host system.
95 | - The choice of filesystem (DwarFS or SquashFS) affects the tools included in the `.pbundle_static_tools` section and the runtime behavior.
96 |
--------------------------------------------------------------------------------
/www/content/docs/runtime.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2025-05-24T23:55:33'
3 | draft = false
4 | title = 'runtime.md'
5 | [params.author]
6 | name = 'xplshn'
7 | email = 'xplshn@murena.io'
8 | +++
9 | # AppBundle Runtime Execution
10 |
11 | This document describes how the AppBundle runtime operates, including how it reads its own information, extracts static tools, determines environment variables, and handles runtime flags.
12 |
13 | ## Execution Flow
14 |
15 | When an AppBundle is executed, the runtime performs the following steps:
16 |
17 | 1. **Read Runtime Information**:
18 | - The runtime reads the `.pbundle_runtime_info` section from the ELF file, which contains CBOR-encoded metadata.
19 | - This section includes:
20 | - `AppBundleID`: A unique identifier for the AppBundle. (e.g: "com.brave.Browser-xplshn-2025-05-19". You're not forced to follow this format, but if you do, you can create a [dbin](https://github.com/xplshn/dbin) repository that countains your AppBundle by using our [appstream-helper](https://github.com/xplshn/pelf/blob/master/cmd/misc/appstream-helper/appstream-helper.go) tool. `$NAME-$MAINTAINER-$DATE` or preferably: `$APPSTREAM_ID-$MAINTAINER-$DATE`, so that you don't have to include an AppStream file within the AppDir for appstream-helper to get metadata from it)
21 | - `PelfVersion`: The version of the `pelf` tool used to create the AppBundle.
22 | - `HostInfo`: System information from `uname -mrsp(v)` of the build machine.
23 | - `FilesystemType`: Either "dwarfs" or "squashfs".
24 | - `Hash`: A hash of the filesystem image for integrity verification.
25 | - `DisableRandomWorkDir`: A boolean indicating whether to use a fixed working directory.
26 | - `MountOrExtract`: A uint8 value (0–3) specifying the run behavior (see below).
27 | - The runtime uses this information to configure its behavior and locate the filesystem image.
28 |
29 | 2. **Extract Static Tools**:
30 | - The runtime accesses the static tools required for mounting or extracting the filesystem (e.g., `dwarfs`, `dwarfsextract`, `squashfuse`, `unsquashfs`).
31 | - The handling of static tools depends on the build mode:
32 | - **noEmbed Edition**: The tools are embedded in the `.pbundle_static_tools` ELF section as a ZSTD-compressed tar archive. The runtime determines the filesystem mounting and extraction commands at runtime, extracts the needed files from this archive to a temporary directory (`cfg.staticToolsDir`), and uses them to either mount or extract the filesystem.
33 | - **Embed Edition**: The tools are embedded directly in the binary using Go’s `embed` package, without compression. The runtime accesses these tools directly from the embedded filesystem, without needing to extract a compressed archive.
34 |
35 | 3. **Exported Env Variables**:
36 | - The runtime sets up several environment variables to facilitate execution:
37 | - **HOME**: If a portable home directory (`.AppBundleID.home`) exists in the same directory as the AppBundle, it is used as `$HOME`.
38 | - **XDG_DATA_HOME**: If a portable share directory (`.AppBundleID.share`) exists, it is used as `$XDG_DATA_HOME`.
39 | - **XDG_CONFIG_HOME**: If a portable config directory (`.AppBundleID.config`) exists, it is used as `$XDG_CONFIG_HOME`.
40 | - **APPDIR**: Set to the mount or extraction directory
41 | - **SELF**: The absolute path to the AppBundle executable.
42 | - **ARGV0**: The basename of `$SELF`
43 | - **PATH**: Augmented to include the AppBundle's `bin` directory and the directory containing the static tools.
44 |
45 | 4. **Mount or Extract Filesystem**:
46 | - The runtime decides whether to mount or extract the filesystem image based on the `MountOrExtract` value:
47 | - **0**: Mounts the filesystem using FUSE (e.g., `dwarfs` or `squashfuse`) and fails if FUSE is unavailable.
48 | - **1**: Extracts the filesystem to a temporary directory (usually in `tmpfs`) and runs from there.
49 | - **2**: Attempts to mount with FUSE; falls back to extraction if FUSE is unavailable.
50 | - **3**: Similar to 2, but only falls back to extraction if the AppBundle is smaller than 350MB.
51 |
52 | 5. **Execute the Application**:
53 | - The runtime executes the `AppRun` script within the AppDir.
54 | - If a specific command is provided via `--pbundle_link`, the runtime executes that command within the AppBundle's environment, instead of executing the AppRun.
55 |
56 | ## Runtime Flags
57 |
58 | The AppBundle runtime supports several command-line flags to modify its behavior:
59 |
60 | - **`--pbundle_help`**: Displays help information, including the `PelfVersion`, `HostInfo`, and internal configuration variables (e.g., `cfg.exeName`, `cfg.mountDir`).
61 | - **`--pbundle_list`**: Lists the contents of the AppBundle's filesystem, including static tools.
62 | - **`--pbundle_link `**: Executes a specified command within the AppBundle's environment, leveraging its `PATH` and other variables.
63 | - **`--pbundle_pngIcon`**: Outputs the base64-encoded `.DirIcon` (PNG) if it exists; otherwise, exits with error code 1.
64 | - **`--pbundle_svgIcon`**: Outputs the base64-encoded `.DirIcon.svg` if it exists; otherwise, exits with error code 1.
65 | - **`--pbundle_appstream`**: Outputs the base64-encoded first `.xml` file (AppStream metadata) found in the AppDir.
66 | - **`--pbundle_desktop`**: Outputs the base64-encoded first `.desktop` file found in the AppDir.
67 | - **`--pbundle_portableHome`**: Creates a portable home directory (`.AppBundleID.home`) in the same directory as the AppBundle.
68 | - **`--pbundle_portableConfig`**: Creates a portable config directory (`.AppBundleID.config`) in the same directory as the AppBundle.
69 | - **`--pbundle_cleanup`**: Unmounts and removes the AppBundle's working directory and mount point, affecting only instances of the same AppBundle.
70 | - **`--pbundle_mount`**: Mounts the filesystem to a specified or default directory and keeps the mount active.
71 | - **`--pbundle_extract [globs]`**: Extracts the filesystem to a directory (default: `_` or `squashfs-root` for AppImage compatibility). Supports selective extraction with glob patterns.
72 | - **`--pbundle_extract_and_run`**: Extracts the filesystem and immediately executes the entrypoint.
73 | - **`--pbundle_offset`**: Outputs the offset of the filesystem image within the AppBundle.
74 | - **AppImage Compatibility Flags**:
75 | - `--appimage-extract`: Same as `--pbundle_extract`, but uses `squashfs-root` as the output directory.
76 | - `--appimage-extract-and-run`: Same as `--pbundle_extract_and_run`.
77 | - `--appimage-mount`: Same as `--pbundle_mount`.
78 | - `--appimage-offset`: Same as `--pbundle_offset`.
79 |
80 | ## Notes
81 |
82 | - The choice between `noEmbed` and embed modes affects how static tools are stored and accessed. The `noEmbed` mode uses a compressed archive for flexibility, while the embed mode simplifies access by avoiding compression.
83 | - The `AppRun` script (e.g., `AppRun.rootfs-based`, `AppRun.sharun`, or `AppRun.sharun.ovfsProto`) determines sandboxing and execution behavior, such as using `bwrap` or `unionfs-fuse`.
84 | - The runtime ensures cleanup of temporary directories unless `--pbundle_cleanup` is explicitly called or `noCleanup` is set.
85 | - The `noEmbed` build tag for the `appbundle-runtime` allows you to build a single appbundle-runtime binary, that determines which filesystem to use at runtime, after having read its .pbundle_runtime_info and decompressed the .tar.zst data within the .pbundle_static_tools ELF section
86 | - If you're writting a new runtime, I recommend you implement appbundle-runtime.go, cli.go and noEmbed.go. This edition of the runtime is the most portable and flexible. It is simplifies a lot the build process.
87 |
--------------------------------------------------------------------------------
/www/content/docs/tooling.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2025-05-24T23:55:33'
3 | draft = false
4 | title = 'tooling.md'
5 | [params.author]
6 | name = 'xplshn'
7 | email = 'xplshn@murena.io'
8 | +++
9 | # pelf
10 |
11 | The `pelf` command is responsible for assembling an AppBundle by combining an ELF runtime, runtime information, static tools, and a compressed filesystem image.
12 |
13 | ### Functionality
14 |
15 | - **Purpose**: Creates an AppBundle from an AppDir, embedding necessary metadata and tools.
16 | - **Key Operations**:
17 | - Reads an AppDir, verifies that it contains an executable AppRun
18 | - Copies the runtime to the output file
19 | - Embeds runtime information (MessagePack format) in the `.pbundle_runtime_info` section of the output file
20 | - If the runtime is a universal runtime (e.g: noEmbed edition), it puts a ZSTD-compressed tar archive of static tools (depending the chosen filesystem: e.g., `dwarfs`, `squashfuse`, `unsquashfs`) in the `.pbundle_static_tools` section of the output file.
21 | - Compresses the AppDir into a DwarFS or SquashFS filesystem image and appends it to the output file
22 | - Sets the AppBundle's executable permissions and finalizes the output file.
23 |
24 | ### Command-Line Usage
25 |
26 | The pelf tool is can be invoked with the following flags:
27 |
28 | - **--add-appdir, -a **: Specifies the AppDir to package.
29 | - **--appbundle-id, -i **: Sets the unique AppBundleID for the AppBundle.
30 | - **--output-to, -o **: Specifies the output file name (e.g., app.dwfs.AppBundle).
31 | - **--compression, -c **: Specifies compression flags for the filesystem.
32 | - **--static-tools-dir **: Specifies a custom directory for static tools.
33 | - **--runtime **: Specifies the runtime binary to use.
34 | - **--upx**: Enables UPX compression for static tools. (upx must be in the host system)
35 | - **--filesystem, -j :** Selects the filesystem type (squashfs or [dwarfs]).
36 | - **--prefer-tools-in-path:** Prefers tools in `$PATH` over embedded ones.
37 | - **--list-static-tools:** Lists embedded tools with their B3SUMs.
38 | - **--disable-use-random-workdir, -d:** Disables random working directory usage. This making AppBundles leave their mountpoint open and reusing it in each launch. This is ideal for big programs that need to launch ultra-fast, such as web browsers, messaging clients, etc
39 | - **--run-behavior, -b <0|1|2|3>:** Sets runtime behavior (0: FUSE only, 1: Extract only, 2: FUSE with extract fallback, 3: FUSE with extract fallback if ≤ 350MB).
40 | - **--appimage-compat, -A:** Sets the "AI" magic-bytes, so that AppBundles are detected as AppImages by AppImage integration software like [AppImageUpdate](https://github.com/AppImageCommunity/AppImageUpdate)
41 | - **--add-runtime-info-section :** Adds custom runtime information fields. (e.g: '.MyCustomRuntimeInfoSection:Hello')
42 | - **--add-elf-section :** Adds a custom ELF section from a .elfS file., where the filename of the .elfS file minus the extension is the section name, and the file contents are the data
43 | - **--add-updinfo :** Adds an upd_info ELF section with the given string.
44 |
45 | # pelfCreator
46 |
47 | The `pelfCreator` command is a higher-level utility that prepares an AppDir and invokes `pelf` to create an AppBundle. It supports multiple modes for different use cases.
48 |
49 | ### Functionality
50 |
51 | - **Purpose**: Creates an AppDir, populates it with a root filesystem, application files, and dependencies, and then packages it into an AppBundle.
52 | - **Key Operations**:
53 | - Sets up a temporary directory for processing.
54 | - Downloads or uses a local root filesystem (e.g., Alpine or ArchLinux).
55 | - Installs specified packages using `apk` (Alpine) or `pacman` (ArchLinux).
56 | - Configures the AppRun script and entrypoint.
57 | - Optionally processes binaries with `lib4bin` for `sharun` mode.
58 | - Trims the filesystem based on `--keep` or `--getrid` flags.
59 | - Calls `pelf` to finalize the AppBundle.
60 |
61 | ### Command-Line Usage
62 |
63 | The `pelfCreator` tool is invoked with the following flags:
64 |
65 | - **`--maintainer `**: Specifies the maintainer's name (required).
66 | - **`--name `**: Sets the application name (required).
67 | - **`--appbundle-id `**: Sets the `AppBundleID` (optional; defaults to `--`).
68 | - **`--pkg-add `**: Specifies packages to install in the root filesystem (required).
69 | - **`--entrypoint `**: Sets the entrypoint command or desktop file (required unless using `--multicall`).
70 | - **`--keep `**: Specifies files to keep in the `proto` directory.
71 | - **`--getrid `**: Specifies files to remove from the `proto` directory.
72 | - **`--filesystem `**: Selects the filesystem type (`dwfs` or `squashfs`; default: `dwfs`).
73 | - **`--output-to `**: Specifies the output AppBundle file (optional; defaults to `..AppBundle`).
74 | - **`--local `**: Specifies a directory or archive containing resources (e.g., `rootfs.tar`, `AppRun`, `bwrap`).
75 | - **`--preserve-rootfs-permissions`**: Preserves original filesystem permissions.
76 | - **`--dontpack`**: Stops short of packaging the AppDir into an AppBundle, leaving only the AppDir.
77 | - **`--sharun `**: Processes specified binaries with `lib4bin` and uses `AppRun.sharun` or `AppRun.sharun.ovfsProto`.
78 | - **`--sandbox`**: Enables sandbox mode using `AppRun.rootfs-based` with `bwrap`.
79 |
80 | ### Modes of Operation
81 |
82 | 1. **Sandbox Mode** (`--sandbox`):
83 | - Retains and binds the `proto` directory as the root filesystem, with host directory bindings (e.g., `/home`, `/tmp`, `/etc`).
84 | - Supports trimming of the `proto` directory using `--keep` or `--getrid` flags to reduce size.
85 | - Uses `AppRun.rootfs-based` to run the application in a `bwrap` sandbox.
86 | - Can be customized via env vars such as `SHARE_LOOK`, `SHARE_FONTS`, `SHARE_AUDIO`, and `UID0_GID0` for fine-grained control over sandboxing.
87 | - Suitable for applications requiring strict isolation from the host system. Or those that refuse to work with the default mode (hybrid)
88 |
89 | 2. **Sharun Mode** (`--sharun `):
90 | - Processes specified binaries with `lib4bin` to ensure compatibility and portability.
91 | - Uses `AppRun.sharun` (if `proto` is removed) or `AppRun.sharun.ovfsProto` (if `proto` is retained).
92 | - When using `AppRun.sharun.ovfsProto`, employs `unionfs-fuse` to create a copy-on-write overlay of the `proto` directory.
93 | - Sets `LD_LIBRARY_PATH` to include library paths from the AppDir, ensuring binaries can find their dependencies.
94 | - Ideal for lightweight applications or when minimizing filesystem size is a priority.
95 |
96 | 3. **Default Mode** (can be combined with `--sharun`, to ship lightweight AppBundles that include a default config file, etc, but otherwise use the system's files unless they're missing):
97 | - Retains the `proto` directory
98 | - Supports trimming of the `proto` directory using `--keep` or `--getrid` flags to reduce size.
99 | - Uses `AppRun.sharun.ovfsProto` to execute the application with a `unionfs-fuse` overlay of the user's `/` & the AppDir's `proto`.
100 | - Suitable for most applications, as it allows the AppBundle to use files from the system if they don't exist in the AppDir's `proto` and vice-versa
101 |
102 | ## Notes
103 |
104 | - The `pelfCreator` tool supports extensibility through custom root filesystems and package managers via the `--local` flag.
105 |
--------------------------------------------------------------------------------
/www/gen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | check_directory() {
4 | if [ ! "$(basename "$PWD")" = "www" ] || [ ! -f "$PWD/config.toml" ]; then
5 | if [ -d "$PWD/www" ]; then
6 | echo "You must enter ./www"
7 | else
8 | echo "Where the fuck are we? You must enter https://github.com/xplshn/alicelinux/www, run me within of the ./www directory!"
9 | fi
10 | exit 1
11 | fi
12 | }
13 |
14 | process_markdown_files() {
15 | mkdir -p "$2"
16 | for FILE in "$1"/*.md; do
17 | if [ "$FILE" = "_index.md" ]; then
18 | echo "Skipping \"$FILE\""
19 | fi
20 | FILENAME="$(basename "$FILE")"
21 | DATE="$(git log -1 --format="%ai" -- "$FILE" | awk '{print $1 "T" $2}')"
22 | TITLE="$(basename "$FILE")"
23 | AUTHOR_NAME="$(git log --follow --format="%an" -- "$FILE" | tail -n 1)"
24 | AUTHOR_EMAIL="$(git log --follow --format="%ae" -- "$FILE" | tail -n 1)"
25 |
26 | case "$TITLE" in
27 | "_index.md")
28 | cp "$FILE" "./content/docs"
29 | continue
30 | ;;
31 | "index.md")
32 | echo "Skipping \"$FILE\""
33 | continue
34 | ;;
35 | esac
36 |
37 | {
38 | echo "+++"
39 | echo "date = '$DATE'"
40 | echo "draft = false"
41 | echo "title = '$TITLE'"
42 | echo "[params.author]"
43 | echo " name = '$AUTHOR_NAME'"
44 | echo " email = '$AUTHOR_EMAIL'"
45 | echo "+++"
46 | cat "$FILE"
47 | } >"$2/$FILENAME"
48 | done
49 |
50 | # Disabled because I manually created the ./content/docs/_index.md
51 | #if [ "$(find "$2" -maxdepth 1 -type f | wc -l)" -gt 0 ]; then
52 | # {
53 | # echo "---"
54 | # echo "title: '$3'"
55 | # echo "---"
56 | # } >"$2/_index.md"
57 | #fi
58 | }
59 |
60 | # Main script execution
61 | check_directory
62 | rm -rf -- ./content/docs/*
63 | rm -rf -- ./static/assets/*
64 | process_markdown_files "../docs" "./content/docs" "Documentation"
65 | find ../assets/ -type f ! -name '*AppRun*' ! -name '*LAUNCH*' -exec cp {} ./static/assets/ \;
66 | {
67 | echo "---"
68 | echo "title: 'Home'"
69 | echo "---"
70 | } >./content/_index.md
71 | sed 's|src="files/|src="assets/|g' ../README.md >>./content/_index.md
72 |
73 | # Build with Hugo
74 | hugo
75 |
--------------------------------------------------------------------------------
/www/static/assets/pin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------