Skip to content
119 changes: 119 additions & 0 deletions app/src/main/assets/de_packages.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# de_packages.yaml -- This file is part of tiny_container.
#
# Config-driven registry of desktop-environment entries offered by the
# on-device DE selector, tiered per the task brief (tier 1 = stable/X11,
# tier 2 = experimental/Wayland). Add a new DE by adding an entry here — no
# Kotlin changes required in the selector UI or installer to pick it up.
#
# Every tier-1 package_managers.<family> value below was verified against a
# real package database as of 2026-07-20 (not guessed):
# - pacman: archlinux.org's package/group search JSON API and group pages
# (xfce4, xfce4-goodies, lxqt, mate, i3 are pacman GROUPS, not single
# packages — `pacman -S <group>` installs everything in the group).
# - dnf: Fedora's real comps group IDs, from
# https://pagure.io/fedora-comps/raw/main/f/comps-f42.xml.in
# (dnf group install "@<id>").
# - apt: sources.debian.org/api (package existence check) PLUS the
# xfce4 line is the literal command from the real, verified
# upstream tiny-computer/images README ("sudo apt install
# dbus-x11 xfce4 tigervnc-standalone-server ... -y").
#
# Fields:
# alias: Short name, used as the feature-type discriminator
# the app writes into the container's boot_command/
# feature config once a DE is chosen.
# display_name: Shown in the selector UI.
# tier: 1 (stable, X11-based) or 2 (experimental, Wayland).
# requires_bridge: Only meaningful for tier 2 — see DeInstaller.kt.
# True means this DE needs a Wayland-compositor bridge
# that hasn't been prototyped/validated yet (see
# docs/tier2-wayland-bridge-findings.md once written).
# package_managers: Map of pacman/dnf/apt -> list of package/group names
# to install for this DE on that package manager
# family. A distro's package_manager (distros.yaml)
# selects which of these lists applies.

des:
# ---------------------------------------------------------------------
# Tier 1 — stable, X11, proven under proot + Termux:X11
# ---------------------------------------------------------------------
- alias: xfce4
display_name: "XFCE4"
tier: 1
# Run inside the container's already-logged-in boot_command session
# (see ProotExec.bootCommandTemplate()) once the X11 socket is ready —
# NOT a separate proot invocation.
session_command: "dbus-launch --exit-with-session startxfce4"
package_managers:
pacman: ["xfce4", "xfce4-goodies", "tigervnc", "dbus"]
dnf: ["@xfce-desktop-environment", "tigervnc-server"]
apt: ["dbus-x11", "xfce4", "tigervnc-standalone-server"]

- alias: lxqt
display_name: "LXQt"
tier: 1
session_command: "dbus-launch --exit-with-session startlxqt"
package_managers:
pacman: ["lxqt", "tigervnc", "dbus"]
dnf: ["@lxqt-desktop-environment", "tigervnc-server"]
apt: ["dbus-x11", "task-lxqt-desktop", "tigervnc-standalone-server"]

- alias: mate
display_name: "MATE"
tier: 1
session_command: "dbus-launch --exit-with-session mate-session"
package_managers:
pacman: ["mate", "tigervnc", "dbus"]
dnf: ["@mate-desktop-environment", "tigervnc-server"]
apt: ["dbus-x11", "task-mate-desktop", "tigervnc-standalone-server"]

- alias: i3wm
display_name: "i3wm"
tier: 1
session_command: "dbus-launch --exit-with-session i3"
package_managers:
pacman: ["i3-wm", "i3status", "dmenu", "tigervnc", "dbus"]
dnf: ["@i3-desktop-environment", "tigervnc-server"]
apt: ["dbus-x11", "i3-wm", "tigervnc-standalone-server"]

# ---------------------------------------------------------------------
# Tier 2 — experimental, Wayland. NOT validated to actually work end to
# end yet (see DeInstaller.kt / the task brief's own instruction to
# prototype against one distro before assuming this works). Package
# lists below are DELIBERATELY pacman-only (Arch), per the brief's own
# instruction to prototype tier 2 against one distro before expanding —
# checked here, not assumed: Fedora has NEITHER niri NOR cosmic-desktop
# in its official repos (packages.fedoraproject.org 404s for both, as of
# 2026-07-20), and there is no single "cosmic" package anywhere — COSMIC
# ships as ~25 separate packages on Arch (cosmic-session, cosmic-comp,
# cosmic-panel, etc., all in the `extra` repo). Debian/Ubuntu's apt
# situation for niri/cosmic-desktop was not verified deeply enough to
# trust yet (a source package existing on sources.debian.org is not the
# same as a real, current, arm64-buildable binary package — that's a
# meaningfully lower bar than what tier 1 above was actually checked
# against). Do not add dnf/apt entries here until that's done for real;
# see DeInstaller.kt's tier-2 install path, which refuses non-pacman
# tier-2 installs outright rather than guessing.
# ---------------------------------------------------------------------
- alias: niri
display_name: "niri (experimental)"
tier: 2
requires_bridge: true
package_managers:
pacman: ["niri", "xwayland-satellite"]

- alias: cosmic
display_name: "COSMIC (experimental)"
tier: 2
requires_bridge: true
package_managers:
pacman:
- "cosmic-session"
- "cosmic-comp"
- "cosmic-panel"
- "cosmic-settings"
- "cosmic-settings-daemon"
- "cosmic-bg"
- "cosmic-launcher"
- "cosmic-files"
- "xdg-desktop-portal-cosmic"
133 changes: 133 additions & 0 deletions app/src/main/assets/distros.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# distros.yaml -- This file is part of tiny_container.
#
# Config-driven registry of base-distro entries offered by the on-device
# distro selector. Add a new distro by adding an entry here — no Kotlin
# changes required in the selector UI or installer to pick it up.
#
# Fields:
# alias: Short name. Used as the on-disk container "code"
# prefix and as the key into the per-distro/per-DE
# install-command manifest (de_packages.yaml).
# display_name: Shown in the selector UI.
# package_manager: One of: pacman, dnf, apt. Selects which install-command
# templates apply for this distro in de_packages.yaml.
# source: Where the base rootfs comes from. Two supported
# shapes (see OciRegistryClient.kt / DistroInstaller.kt):
# type: static_tarball -- a direct HTTPS/HTTP URL to
# a single gzip/xz tarball.
# type: oci -- pulled via the Docker
# Registry v2 HTTP API (registry + repository + tag).
# NOTE: this is a small, purpose-built client, not
# upstream proot-distro itself — proot-distro was
# rewritten in Python for its 2026 OCI-pull update
# and this app does not bundle a Python runtime.
# Verified live against registry-1.docker.io as of
# 2026-07-20: all three OCI-sourced distros below
# resolve to exactly one arm64 layer
# (application/vnd.oci.image.layer.v1.tar+gzip),
# so no layer-squashing/union-merge logic is needed
# — the single layer blob IS the rootfs tarball.
# first_boot_steps: Shell commands run once, inside the new container,
# immediately after extraction and before the first
# real login (keyring init, locale generation, user
# creation, etc.). Run in order, via the same terminal
# session used for the tar extraction step.

distros:
- alias: archlinux
display_name: "Arch Linux"
package_manager: pacman
source:
type: static_tarball
# Redirects to a mirror; verified live 2026-07-20 (HTTP 302 -> 200).
url: "http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz"
first_boot_steps:
- "pacman-key --init"
- "pacman-key --populate archlinuxarm"
- "echo 'Server = http://mirror.archlinuxarm.org/$arch/$repo' > /etc/pacman.d/mirrorlist"
- "pacman -Sy --noconfirm sudo polkit"
- "sed -i 's/#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen"
- "locale-gen"
- "echo 'LANG=en_US.UTF-8' > /etc/locale.conf"
- "ln -sf /usr/share/zoneinfo/UTC /etc/localtime"
- "useradd -m -s /bin/bash tiny"
- "echo 'tiny:tiny' | chpasswd"
- "echo 'root:tiny' | chpasswd"
- "echo '%wheel ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/wheel"
- "gpasswd -a tiny wheel"
- "chmod 0440 /etc/sudoers.d/wheel"
- "mkdir -p /etc/polkit-1/rules.d"
- "printf 'polkit.addRule(function(action, subject) {\\n if (subject.user == \"tiny\") {\\n return polkit.Result.YES;\\n }\\n});\\n' > /etc/polkit-1/rules.d/99-allow-all.rules"

- alias: debian
display_name: "Debian"
package_manager: apt
source:
type: oci
registry: "registry-1.docker.io"
auth_realm: "https://auth.docker.io/token"
auth_service: "registry.docker.io"
repository: "library/debian"
tag: "bookworm"
first_boot_steps:
- "apt-get update"
- "apt-get install -y sudo locales policykit-1"
- "sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen"
- "locale-gen en_US.UTF-8"
- "printf 'LANG=en_US.UTF-8\\nLANGUAGE=en_US:en\\nLC_ALL=en_US.UTF-8\\n' > /etc/default/locale"
- "ln -sf /usr/share/zoneinfo/UTC /etc/localtime"
- "useradd -m -s /bin/bash tiny"
- "echo 'tiny:tiny' | chpasswd"
- "echo 'root:tiny' | chpasswd"
- "echo 'tiny ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers"
- "chmod 0440 /etc/sudoers"
- "mkdir -p /etc/polkit-1/rules.d"
- "printf 'polkit.addRule(function(action, subject) {\\n if (subject.user == \"tiny\") {\\n return polkit.Result.YES;\\n }\\n});\\n' > /etc/polkit-1/rules.d/99-allow-all.rules"

- alias: ubuntu
display_name: "Ubuntu"
package_manager: apt
source:
type: oci
registry: "registry-1.docker.io"
auth_realm: "https://auth.docker.io/token"
auth_service: "registry.docker.io"
repository: "library/ubuntu"
tag: "noble"
first_boot_steps:
- "apt-get update"
- "apt-get install -y sudo locales policykit-1"
- "locale-gen en_US.UTF-8"
- "printf 'LANG=en_US.UTF-8\\nLANGUAGE=en_US:en\\nLC_ALL=en_US.UTF-8\\n' > /etc/default/locale"
- "ln -sf /usr/share/zoneinfo/UTC /etc/localtime"
- "useradd -m -s /bin/bash tiny"
- "echo 'tiny:tiny' | chpasswd"
- "echo 'root:tiny' | chpasswd"
- "echo 'tiny ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers"
- "chmod 0440 /etc/sudoers"
- "mkdir -p /etc/polkit-1/rules.d"
- "printf 'polkit.addRule(function(action, subject) {\\n if (subject.user == \"tiny\") {\\n return polkit.Result.YES;\\n }\\n});\\n' > /etc/polkit-1/rules.d/99-allow-all.rules"

- alias: fedora
display_name: "Fedora"
package_manager: dnf
source:
type: oci
registry: "registry-1.docker.io"
auth_realm: "https://auth.docker.io/token"
auth_service: "registry.docker.io"
repository: "library/fedora"
tag: "latest"
first_boot_steps:
- "dnf install -y sudo passwd shadow-utils glibc-langpack-en polkit"
- "localedef -i en_US -f UTF-8 en_US.UTF-8"
- "echo 'LANG=en_US.UTF-8' > /etc/locale.conf"
- "ln -sf /usr/share/zoneinfo/UTC /etc/localtime"
- "useradd -m -s /bin/bash tiny"
- "echo 'tiny:tiny' | chpasswd"
- "echo 'root:tiny' | chpasswd"
- "echo '%wheel ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/wheel"
- "gpasswd -a tiny wheel"
- "chmod 0440 /etc/sudoers.d/wheel"
- "mkdir -p /etc/polkit-1/rules.d"
- "printf 'polkit.addRule(function(action, subject) {\\n if (subject.user == \"tiny\") {\\n return polkit.Result.YES;\\n }\\n});\\n' > /etc/polkit-1/rules.d/99-allow-all.rules"
14 changes: 14 additions & 0 deletions app/src/main/java/com/fct/tc4/ui/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import com.fct.tc4.ui.misc.WipeLayout
import com.fct.tc4.ui.page.ContainerInstallFragment
import com.fct.tc4.ui.page.ContainerMainFragment
import com.fct.tc4.ui.page.ContainerManageFragment
import com.fct.tc4.ui.page.DeSelectFragment
import com.fct.tc4.ui.page.DistroSelectFragment
import com.fct.tc4.ui.misc.Global
import com.fct.tc4.ui.page.TerminalFragment
import com.fct.tc4.ui.page.ContainerManageViewModel
Expand Down Expand Up @@ -165,6 +167,10 @@ class MainActivity : AppCompatActivity() {
fragment?.onImportAction()
true
}
R.id.containerBuildNew -> {
viewModel.navigateTo(MainViewModel.Screen.DistroSelect)
true
}
R.id.enterGui -> {
val fragment = supportFragmentManager.findFragmentByTag("ContainerMain") as? ContainerMainFragment
fragment?.onEnterGui()
Expand Down Expand Up @@ -215,6 +221,14 @@ class MainActivity : AppCompatActivity() {
}
tag = "ContainerMain"
}
is MainViewModel.Screen.DistroSelect -> {
fragment = DistroSelectFragment()
tag = "DistroSelect"
}
is MainViewModel.Screen.DeSelect -> {
fragment = DeSelectFragment.newInstance(screen.distroAlias)
tag = "DeSelect"
}
}
if (supportFragmentManager.findFragmentByTag(tag) != null) return
supportFragmentManager.commit {
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/fct/tc4/ui/main/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
data object ContainerManage : Screen()
data class ContainerInstall(val code: String, val webpage: String?) : Screen()
data class ContainerMain(val code: String) : Screen()
/** First step of the on-device distro/DE build flow — pick a base distro. */
data object DistroSelect : Screen()
/** Second step — pick a desktop environment for the [distroAlias] chosen in DistroSelect. */
data class DeSelect(val distroAlias: String) : Screen()
}


Expand Down
Loading
Loading