Initial scaffold

This commit is contained in:
Astral
2026-09-01 08:00:05 +02:00
commit 1b68276cb9
10 changed files with 547 additions and 0 deletions
+25
View File
@@ -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
+76
View File
@@ -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
+19
View File
@@ -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.
+121
View File
@@ -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() { :; }
+90
View File
@@ -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" <<EOF
# Generated by keru-install — hand-tune then rebuild if you like.
INIT=$INIT
LIBC=$LIBC
TARGET_FS=$TARGET_FS
KERNEL=$KERNEL
NET=$NET
LICENSE_ACCEPT=(*)
EOF
msg "profile complete:"
cat "$OUT"
read -r -p "Build now? [y/N] " go
case "$go" in
y|Y|yes)
"$(dirname "$0")/../scripts/build-root.sh" "$OUT"
;;
*)
msg "saved profile to $OUT (run scripts/build-root.sh to build)"
;;
esac
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env sh
# build-iso.sh — assemble a Keru OS ISO from the built rootfs and kernel
#
# From-scratch approach (no archiso): we lay out the classic isohybrid
# structure manually, squash the rootfs, and generate the ISO with xorriso +
# isolinux/syslinux (or syslinux's isohybrid). This is the LFS/Debian-live
# style path.
#
# Requires on the build host: xorriso, squashfs-tools, syslinux/isolinux.
set -eu
. "$(dirname "$0")/common.sh"
ROOT="${ROOT:-/tmp/keru/root}" # target rootfs (from make.conf)
ISO_OUT="${ISO_OUT:-$KERU_ROOT/iso/keru-$(date +%Y%m%d).iso}"
WORK="$KERU_ROOT/iso/work"
ISOIMG="$WORK/isolinux"
info "building Keru ISO -> $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"
+68
View File
@@ -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
+49
View File
@@ -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"
+20
View File
@@ -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}"
+28
View File
@@ -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" <<EOF
# Keru generated fstab — root filesystem: $FSTYPE
# /dev/ROOT / $FSTYPE rw,relatime 0 1
EOF
info "wrote fstab for $FSTYPE"