From 1b68276cb9dd24eb343fdbd8f68628000d1bb492 Mon Sep 17 00:00:00 2001 From: Astral <208268648+AstralZX@users.noreply.github.com> Date: Tue, 1 Sep 2026 08:00:05 +0200 Subject: [PATCH] Initial scaffold --- Makefile | 25 +++++++++ README.md | 76 ++++++++++++++++++++++++++ docs/DESIGN.md | 19 +++++++ docs/recipe-format.md | 121 ++++++++++++++++++++++++++++++++++++++++++ installer/install.sh | 90 +++++++++++++++++++++++++++++++ iso/build-iso.sh | 51 ++++++++++++++++++ profile/make.conf | 68 ++++++++++++++++++++++++ scripts/build-root.sh | 49 +++++++++++++++++ scripts/common.sh | 20 +++++++ scripts/mkfs-root.sh | 28 ++++++++++ 10 files changed, 547 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 docs/DESIGN.md create mode 100644 docs/recipe-format.md create mode 100755 installer/install.sh create mode 100755 iso/build-iso.sh create mode 100644 profile/make.conf create mode 100755 scripts/build-root.sh create mode 100755 scripts/common.sh create mode 100755 scripts/mkfs-root.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..918d570 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +# Keru OS — top-level convenience targets. +# These are thin wrappers around the real shell scripts. + +SHELL := sh + +# sibling repos (override if cloned elsewhere) +KAMA ?= ../kama +PKGS ?= ../kama-packages + +.PHONY: iso install recipe-check clean + +iso: + sh scripts/build-root.sh + sh iso/build-iso.sh + +install: + sh installer/install.sh + +recipe-check: + @for f in $(PKGS)/*.sh; do \ + sh -n "$$f" && echo "OK $$f" || echo "FAIL $$f"; \ + done + +clean: + rm -rf iso/work /tmp/keru diff --git a/README.md b/README.md new file mode 100644 index 0000000..3a8a362 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Keru OS + +An independent, source-only Linux distribution built around one idea: you +don't install a system, you *craft* one. + +This repository holds the **operating system itself** — the installer, the ISO +build tooling, the profile system, and the build pipeline. The two things Keru +depends on live in their own repositories: + +- **[kama](https://github.com/AstralZX/kama)** — the package manager (a shell script) +- **[kama-packages](https://github.com/AstralZX/kama-packages)** — the recipe repository + +## What is Keru + +Every package on Keru is built from source by **Kama**, a deliberately small +package manager that simply runs the commands in each recipe. No binary +packages, no opaque package machinery. The whole system — toolchain included — +is compiled from original source, and the install image is assembled by hand +rather than with a stock tool like archiso. + +Runit handles services. The standard Linux kernel drives the hardware. +`nmtui` manages networking. Nothing you didn't ask for ends up on the disk. + +## The differentiator: craft your system at install time + +Keru's installer is a TUI that walks you through designing your exact machine +*before* it ever boots: + +- **init system** — Runit (default), s6, OpenRC, systemd +- **C library** — glibc or musl +- **filesystem** — ext4, btrfs, xfs, zfs, f2fs +- **kernel** — linux, linux-lts, linux-hardened + +Swappable at install time, fixed at first boot. The system that lands on your +disk is precisely the one you chose. + +## Repository layout + +``` +installer/ TUI installer — writes make.conf from your choices +iso/ from-scratch ISO build tooling (xorriso + squashfs + syslinux) +profile/ make.conf template (the swappables + build flags) +scripts/ build pipeline: build-root, mkfs-root, common helpers +docs/ design docs + the recipe format spec +``` + +## Building + +The three repos should sit next to each other (or point at each other via +`KAMA_DIR` / `PKGS_DIR` / `PKGDIR`): + +``` +~/dev/ +├── KeruOS/ +├── kama/ +└── kama-packages/ +``` + +Then, from `KeruOS/`: + +```sh +make install # run the TUI installer +# or, headlessly: +sh scripts/build-root.sh # build a rootfs into /tmp/keru/root +make iso # assemble an install image +``` + +Requires the sibling `kama` and `kama-packages` repos to be present. + +## Status + +Design + scaffolding. Nothing is building end-to-end yet. + +## License + +AGPL-3.0 diff --git a/docs/DESIGN.md b/docs/DESIGN.md new file mode 100644 index 0000000..a924721 --- /dev/null +++ b/docs/DESIGN.md @@ -0,0 +1,19 @@ +# design/index + +This directory holds the working design for Keru OS. The code is scaffolding +and not yet functional end-to-end. + +## What exists + +- `recipe-format.md` — the Kama recipe format (easy/advanced tiers) +- `README.md` (repo root) — project identity + layout + +## Open design questions + +- **Bootstrapping toolchain**: chicken-and-egg — the first compiler must be + obtained somehow. Options: a documented one-time bootstrap from a binary + seed, or a multi-stage rebuild (host -> minimal -> full). Not yet decided. +- **No dependency resolver**: how upgrades/rebuilds are handled for a system + that's "built once at install." Currently framed as install-time-only. +- **Cache artifacts vs. rebuilds**: whether the build cache stores binaries + for fast re-install or always recompiles. diff --git a/docs/recipe-format.md b/docs/recipe-format.md new file mode 100644 index 0000000..91d527d --- /dev/null +++ b/docs/recipe-format.md @@ -0,0 +1,121 @@ +# Kama recipe format +# +# A package is a plain POSIX shell script that describes how to compile and +# install one piece of software. The syntax is split into two tiers: +# +# EASY - declare three variables, fill in build + install, done. +# The common case for 90% of packages (autotools/cmake/meson). +# +# ADVANCED - full control when you need it: custom fetch/extract, staging +# layout, packaging hooks, multi-arch, verification, etc. +# Everything the easy tier does is just sugar over this. +# +# In both tiers, Kama sources the recipe and runs the functions you define. +# +# +# ========================================================================= +# EASY TIER — most packages only need this +# ========================================================================= +# +# name=busybox +# version=1.36.1 +# url=https://busybox.net/downloads/busybox-1.36.1.tar.bz2 +# deps=() # build-time deps, purged after (AUR-style) +# +# build() { # configure + compile +# make defconfig +# make +# } +# +# install() { # install into $pkgdir (staging), not $ROOT +# make install CONFIG_PREFIX="$pkgdir" +# } +# +# That's it. Kama supplies the conventional pkg_fetch (download + extract -- +# auto-detects tar.xz/.gz/.bz2/.zip, strips the top dir) and pkg_clean. +# +# +# ========================================================================= +# ADVANCED TIER — override any step, add packaging control +# ========================================================================= +# +# name=busybox +# version=1.36.1 +# url=https://busybox.net/downloads/busybox-1.36.1.tar.bz2 +# deps=(gcc musl-headers) # temp build deps (AUR-style) +# runtime_deps=(musl) # stays installed +# license=(GPL-2.0) +# provides=(sh) # capability this package provides +# conflicts=(dash) # mutual exclusion +# arch=(x86_64) # if non-empty, restrict to archs +# noextract=() # URLs to download but NOT auto-extract +# +# # override auto extract (e.g. patching before configure) +# pkg_fetch() { +# default_fetch # run the easy-tier default +# cd "$src" +# patch -p1 < ../my.patch +# } +# +# build() { ./configure --prefix=/usr "$@" && make; } +# install() { make install DESTDIR="$pkgdir"; } +# +# # split headers/binary into separate sub-package stage (advanced) +# pkg_split() { ... } +# +# # run after install, before purge (e.g. generate ldconfig cache) +# pkg_post() { msg "post-install hook"; } +# +# +# ========================================================================= +# FULL FUNCTION REFERENCE +# ========================================================================= +# +# Variables (easy tier): +# name, version, url, deps +# +# Variables (advanced, all optional): +# runtime_deps, license, provides, conflicts, arch, noextract +# +# Functions: +# build() - configure + compile into $src (also callable `pkg_build`) +# install() - install into $pkgdir (also callable `pkg_install`) +# pkg_fetch - override download/extract (call `default_fetch` for the +# easy-tier default) +# pkg_clean - override cleanup +# pkg_post - run after install, before temp deps are purged +# pkg_split - split staging into multiple sub-packages (advanced) +# +# Easy-tier aliases: `build` and `install` are sugar; if you define them, +# Kama maps them to pkg_build / pkg_install automatically. Defining the +# pkg_* forms directly also works (that IS the advanced way). +# +# Provided by Kama (do not redefine): +# $src - build dir (cannot be changed) +# $pkg - pkg dir alt alias for $pkgdir +# $pkgdir - staging dir; Kama moves $pkgdir/* into $ROOT after success +# $CACHEDIR, $ROOT, $CFLAGS, ... from make.conf +# msg() warn() die() +# default_fetch - the easy-tier fetch/extract implementation + +# ---- easy-tier defaults (override as needed) ---------------------------- +name= +version= +url= +deps=() + +# ---- advanced overrides (all optional) ---------------------------------- +runtime_deps=() +license=() +provides=() +conflicts=() +arch=() +noextract=() + +# ---- functions ----------------------------------------------------------- +build() { :; } # or pkg_build +install() { :; } # or pkg_install +pkg_fetch() { :; } +pkg_clean() { :; } +pkg_post() { :; } +pkg_split() { :; } diff --git a/installer/install.sh b/installer/install.sh new file mode 100755 index 0000000..0ef924c --- /dev/null +++ b/installer/install.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env sh +# keru-install — the Keru OS TUI installer +# +# Walks you through crafting your exact system, then writes make.conf and +# kicks off the build. Everything is swappable here, at install time, and +# locked in when you boot. +# +# Designed to run from the Keru live ISO. Backed by dialog/whiptail if +# present; falls back to plain prompted input. All shell, no magic. + +set -eu + +. "$(dirname "$0")/../scripts/common.sh" + +# ---- defaults (mirror profile/make.conf) -------------------------------- +OUT="${MAKE_CONF_OUT:-/tmp/keru/make.conf}" +DROP="/tmp/keru" +mkdir -p "$DROP" + +# ---- menu helpers ------------------------------------------------------- +pick() { # prompt, array-of-labels, defaults -> writes choice to $CHOICE + local prompt="$1"; shift + local default="$1"; shift + CHOICE="$default" + if command -v dialog >/dev/null 2>&1; then + local args=() + local i=0 + for opt in "$@"; do + args+=("$i" "$opt") + i=$((i+1)) + done + local sel + sel=$(dialog --stdout --no-cancel --menu "$prompt" 15 60 8 \ + --default-item "$default" "${args[@]}") || true + CHOICE=$(eval "printf '%s' \"\${$((sel+1)):-\$default}\"" 2>/dev/null || printf '%s' "$default") + else + printf '%s\n' "== $prompt ==" + local n=1 + for opt in "$@"; do + printf ' %s) %s\n' "$n" "$opt" + n=$((n+1)) + done + printf 'Select [%s]: ' "$default" + read -r sel + [ -n "$sel" ] && CHOICE=$(eval "printf '%s' \"\${$sel}\"") || CHOICE="$default" + fi +} + +# ---- swappable selections ------------------------------------------------ +banner "Keru OS — craft your system" + +pick "Init system" runit runit s6 openrc systemd +INIT="$CHOICE" + +pick "C library" glibc glibc musl +LIBC="$CHOICE" + +pick "Filesystem" ext4 ext4 btrfs xfs zfs f2fs +TARGET_FS="$CHOICE" + +pick "Kernel" linux linux linux-lts linux-hardened +KERNEL="$CHOICE" + +pick "Network tools" nmtui networkmanager nmtui connman +NET="$CHOICE" + +# ---- assemble make.conf ------------------------------------------------ +msg "writing $OUT" +cat > "$OUT" < $ISO_OUT" +[ -d "$ROOT" ] || die "rootfs not found at $ROOT (run build-root.sh first)" + +have xorriso || die "xorriso not installed" +have mksquashfs || die "squashfs-tools not installed" + +rm -rf "$WORK"; mkdir -p "$ISOIMG" +info "generating isolinux config" +cp /usr/lib/syslinux/bios/isolinux.bin "$ISOIMG/" || true +cp /usr/lib/syslinux/bios/ldlinux.c32 "$ISOIMG/" || true +cat > "$ISOIMG/isolinux.cfg" <<'EOF' +UI vesamenu.c32 +DEFAULT keru +LABEL keru + LINUX /boot/vmlinuz-keru + INITRD /boot/initramfs-keru.img + APPEND root=live:LABEL=KERU_LIVE toram quiet +EOF + +info "packing rootfs into squashfs" +mksquashfs "$ROOT" "$WORK/keru.sfs" -noappend -comp xz + +info "writing ISO (isohybrid)" +xorriso -as mkisofs \ + -o "$ISO_OUT" \ + -V KERU_LIVE \ + -b isolinux/isolinux.bin \ + -c isolinux/boot.cat \ + -no-emul-boot -boot-load-size 4 -boot-info-table \ + -isohybrid-mbr /usr/lib/syslinux/bios/isohdpfx.bin \ + "$WORK" + +info "done: $ISO_OUT" diff --git a/profile/make.conf b/profile/make.conf new file mode 100644 index 0000000..e90b862 --- /dev/null +++ b/profile/make.conf @@ -0,0 +1,68 @@ +# Keru OS / Kama — global make.conf template +# +# This is the single source of truth for a build. The TUI installer writes +# this file; Kama reads it. Every swappable choice in Keru lives here. + + +## --------------------------------------------------------------------------- +## Profile (the "swappables") — chosen at install time +## --------------------------------------------------------------------------- + +# Which init system gets installed +# runit (default) | s6 | openrc | systemd +INIT=runit + +# Which libc the toolchain and packages build against +# glibc (default) | musl +LIBC=glibc + +# Which filesystem the target root is formatted as +# ext4 (default) | btrfs | xfs | zfs | f2fs +TARGET_FS=ext4 + +# Which kernel you want built +# linux (sane default) | linux-lts | linux-hardened +KERNEL=linux + +# Base package set pulled into every install +BASE_PACKAGES=(base toolchain kernel runit nmtui openssh) + +# Locale / clock / keymap applied to the target +LOCALE=en_US.UTF-8 +TIMEZONE=UTC +KEYMAP=us + + +## --------------------------------------------------------------------------- +## Build environment +## --------------------------------------------------------------------------- + +# Compiler / linker flags passed to every recipe build +CFLAGS="-O2 -march=x86-64" +CXXFLAGS="$CFLAGS" +LDFLAGS="-s" + +# Parallelism +JOBS=$(nproc) +MAKEFLAGS="-j$JOBS" + +# Mirrors for source tarballs +SOURCE_MIRROR="https://mirror.keru.org/sources" + +# Where the new target root lives on the build host +ROOT=/tmp/keru/root + +# Persistent source + build cache (survives across installs) +CACHEDIR=/var/cache/kama + + +## --------------------------------------------------------------------------- +## Kama behaviour +## --------------------------------------------------------------------------- + +# Remove temp build-time deps as soon as they're done (AUR-style). +# Set to 0 to keep them for debugging. +PURGE_TEMP_DEPS=1 + +# Stop on first recipe failure +FAIL_FAST=1 diff --git a/scripts/build-root.sh b/scripts/build-root.sh new file mode 100755 index 0000000..04d45d1 --- /dev/null +++ b/scripts/build-root.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env sh +# build-root.sh — build a full Keru rootfs from scratch using Kama +# +# Given a make.conf (or the profile default), this: +# 1. bootstraps the minimal toolchain (host -> cross/self) +# 2. installs the base package set into $ROOT via Kama +# 3. applies profile choices (init, libc, fs, net) +# +# This is the "fully from scratch" path — no binary base image is seeded. + +set -eu +. "$(dirname "$0")/common.sh" + +# the target rootfs is the "$ROOT" from make.conf; default it here +ROOT="${ROOT:-/tmp/keru/root}" +MAKE_CONF="${1:-$KERU_ROOT/profile/make.conf}" +[ -f "$MAKE_CONF" ] || die "make.conf not found: $MAKE_CONF" +. "$MAKE_CONF" +ROOT="${ROOT:-/tmp/keru/root}" + +KAMA="$KAMA_DIR/kama" +[ -x "$KAMA" ] || die "kama not found at $KAMA (clone the kama repo or set KAMA_DIR)" +[ -d "$PKGS_DIR" ] || die "kama-packages repo not found at $PKGS_DIR (set PKGS_DIR)" + +info "building Keru rootfs into $ROOT" +info "profile: init=$INIT libc=$LIBC fs=$TARGET_FS kernel=$KERNEL" + +# stage 0 — bootstrap toolchain +info "stage 0: bootstrap toolchain (gcc binutils glibc/musl)" +for p in binutils gcc "$LIBC" toolchain; do + PKGDIR="$PKGS_DIR" "$KAMA" make "$p" || die "toolchain stage failed on $p" +done + +# stage 1 — base system +info "stage 1: base system" +for p in "${BASE_PACKAGES[@]}"; do + PKGDIR="$PKGS_DIR" "$KAMA" make "$p" || die "base stage failed on $p" +done + +# stage 2 — kernel + init + swappables +info "stage 2: kernel + init" +PKGDIR="$PKGS_DIR" "$KAMA" make "$KERNEL" || die "kernel build failed" +PKGDIR="$PKGS_DIR" "$KAMA" make "$INIT" || die "init build failed ($INIT)" + +# stage 3 — fstab/mkinitramfs for the chosen FS +info "stage 3: finalize $TARGET_FS root" +ROOT="$ROOT" "$KERU_ROOT/scripts/mkfs-root.sh" "$TARGET_FS" + +info "rootfs ready: $ROOT" diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100755 index 0000000..303c0ed --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,20 @@ +# common.sh — shared helpers for Keru scripts (installer, iso, kama) +PREFIX="\033[1;35m[ keru ]\033[0m" +msg() { printf '%b %s\n' "$PREFIX" "$*"; } +info() { printf '%b \033[1;34m==>\033[0m %s\n' "$PREFIX" "$*"; } +warn() { printf '%b \033[1;33mwarning:\033[0m %s\n' "$PREFIX" "$*"; } +die() { printf '%b \033[1;31merror:\033[0m %s\n' "$PREFIX" "$*" >&2; exit 1; } + +banner() { printf '\033[1;36m%s\033[0m\n' "------------------------------------------"; printf '\033[1;36m %s\033[0m\n' "$*"; printf '\033[1;36m%s\033[0m\n' "------------------------------------------"; } + +have() { command -v "$1" >/dev/null 2>&1; } + +# KeruOS source root (this repo) +KERU_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +# Sibling repos (clone next to this one by default) +# kama -> the package manager +# kama-packages -> the recipe repository +# Override with env if you keep them somewhere else. +KAMA_DIR="${KAMA_DIR:-$(dirname "$KERU_ROOT")/kama}" +PKGS_DIR="${PKGS_DIR:-$(dirname "$KERU_ROOT")/kama-packages}" diff --git a/scripts/mkfs-root.sh b/scripts/mkfs-root.sh new file mode 100755 index 0000000..8aa5ae7 --- /dev/null +++ b/scripts/mkfs-root.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env sh +# mkfs-root.sh — format/seed the target root for the chosen filesystem +# Usage: mkfs-root.sh [ext4|btrfs|xfs|zfs|f2fs] +. "$(dirname "$0")/common.sh" + +FS="${1:-ext4}" + +# Keru builds to a plain directory staged by Kama; the filesystem choice is +# applied at install/flash time. This script writes the fstab + initramfs +# config so the built system knows how to mount its own root. +ROOT="${ROOT:-/tmp/keru/root}" +case "$FS" in + ext4) FSTYPE=ext4; MK="mkfs.ext4" ;; + btrfs) FSTYPE=btrfs; MK="mkfs.btrfs" ;; + xfs) FSTYPE=xfs; MK="mkfs.xfs" ;; + zfs) FSTYPE=zfs; MK="zpool" ;; + f2fs) FSTYPE=f2fs; MK="mkfs.f2fs" ;; + *) die "unsupported filesystem: $FS" ;; +esac +info "target filesystem: $FSTYPE ($MK)" + +# drop fstab template +mkdir -p "$ROOT/etc" +cat > "$ROOT/etc/fstab" <