diff --git a/.gitignore b/.gitignore index 9eee28b..5a1e33b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ cosign.key _build_* output _build-*/** +bluefin-package-diff.md +bluefin-package-diff-prioritized.md +bluefin-package-baseline-recommendation.md +software-inventory.md diff --git a/Containerfile b/Containerfile index f58d6ab..faed507 100644 --- a/Containerfile +++ b/Containerfile @@ -44,8 +44,8 @@ COPY custom /custom COPY --from=ghcr.io/projectbluefin/common:latest /system_files /oci/common COPY --from=ghcr.io/ublue-os/brew:latest /system_files /oci/brew -# Base Image - GNOME included -FROM ghcr.io/ublue-os/silverblue-main:latest +# Base Image - Bluefin GNOME included +FROM ghcr.io/ublue-os/bluefin:stable ## Alternative base images, no desktop included (uncomment to use): # FROM ghcr.io/ublue-os/base-main:latest diff --git a/README.md b/README.md index 8251d7e..6479466 100644 --- a/README.md +++ b/README.md @@ -1,395 +1,180 @@ -# BlueForge +# My OS: BlueForge -A template for building custom bootc operating system images based on the lessons from [Universal Blue](https://universal-blue.org/) and [Bluefin](https://projectbluefin.io). It is designed to be used manually, but is optimized to be bootstraped by GitHub Copilot. After set up you'll have your own custom Linux. +BlueForge is the desktop OS I build for myself: secure, practical, and ready for daily dev work from first boot. -This template uses the **multi-stage build architecture** from , combining resources from multiple OCI containers for modularity and maintainability. See the [Architecture](#architecture) section below for details. - -**Unlike previous templates, you are not modifying Bluefin and making changes.**: You are assembling your own Bluefin in the same exact way that Bluefin, Aurora, and Bluefin LTS are built. This is way more flexible and better for everyone since the image-agnostic and desktop things we love about Bluefin lives in @projectbluefin/common. - - Instead, you create your own OS repository based on this template, allowing full customization while leveraging Bluefin's robust build system and shared components. - -> Be the one who moves, not the one who is moved. +It is based on `ghcr.io/ublue-os/bluefin:stable`, but tuned to my workflow: Ghostty-first terminal behavior, security tools I actually use, and runtime app management that stays simple. ## What Makes This BlueForge Different? -This image is based on `ghcr.io/ublue-os/silverblue-main:latest` and keeps the Bluefin-style multi-stage architecture while pre-wiring practical defaults so a new repository can bootstrap quickly. +Compared to stock Bluefin, this is the setup I want to live in every day. ### Added Packages (Build-time) -- **System packages**: none added yet by default (template stays minimal) -### Added Applications (Runtime) -- **CLI Tools (Homebrew)**: `bat`, `eza`, `fd`, `rg`, `gh`, `git`, `starship`, `zoxide`, `htop`, `tmux` for productivity and day-one terminal workflows -- **GUI Apps (Flatpak)**: Firefox, Thunderbird, GNOME utilities, Flatseal, Mission Center, Warehouse, and selected Universal Blue tools for a complete first-boot desktop +- **System packages**: Ghostty, Helium (`helium-bin`), 1Password, Mullvad VPN. +- **Why these are baked in**: login/network security are non-negotiable, and the terminal should feel right immediately. -### Removed/Disabled -- No core desktop components removed -- Cosign signing and SBOM attestation stay disabled by default until you complete GitHub secret setup - -### Configuration Changes -- Enables `podman.socket` during image build -- Copies Bluefin shared `ujust` files and layers custom `ujust`, Brewfiles, and Flatpak preinstall manifests into the image +### Added Applications (Runtime) -*Last updated: 2026-02-16* +- **CLI tools (Homebrew)**: `bat`, `eza`, `fd`, `rg`, `gh`, `git`, `neovim`, `bun`, `nvm`, `opencode`, `claude-code`, `starship`, `htop`, `tmux`. +- **GUI apps (Flatpak)**: Thunderbird, GNOME utilities, Flatseal, Mission Center, Warehouse, Ignition, Impression, DistroShelf, Bazaar, Refine, plus GTK theme runtimes. +- **Why most apps stay runtime-managed**: faster iteration and easier updates without rebuilding the base image. -## Guided Copilot Mode +### Removed/Disabled -Here are the steps to guide copilot to make your own repo, or just use it like a regular image template. +- **Removed**: `ptyxis` (Ghostty is preferred instead). +- **Not preinstalled**: Firefox Flatpak is intentionally not in defaults. +- **Disabled by default**: Cosign signing and SBOM attestation stay off until secrets are configured. -1. Click the green "Use this as a template" button and create a new repository -2. Select your owner, pick a repo name for your OS, and a description -3. In the "Jumpstart your project with Copilot (optional)" add this, modify to your liking: +### Configuration Changes -``` -Use @projectbluefin/finpilot as a template, name the OS the repository name. Ensure the entire operating system is bootstrapped. Ensure all github actions are enabled and running. Ensure the README has the github setup instructions for cosign and the other steps required to finish the task. -``` +- Sets Ghostty as preferred terminal via `/etc/xdg/xdg-terminals.list`. +- Enables `podman.socket` at build time. +- Copies and merges custom Brewfiles, `ujust` recipes, and Flatpak preinstall manifests into system locations. +- Uses isolated COPR installs for Ghostty and Helium to avoid repo persistence. -## What's Included - -### Build System -- Automated builds via GitHub Actions on every commit -- Awesome self hosted Renovate setup that keeps all your images and actions up to date. -- Automatic cleanup of old images (90+ days) to keep it tidy -- Pull request workflow - test changes before merging to main - - PRs build and validate before merge - - `main` branch builds `:stable` images -- Validates your files on pull requests so you never break a build: - - Brewfile, Justfile, ShellCheck, Renovate config, and it'll even check to make sure the flatpak you add exists on FlatHub -- Production Grade Features - - Container signing and SBOM Generation - - See checklist below to enable these as they take some manual configuration - -### Homebrew Integration -- Pre-configured Brewfiles for easy package installation and customization -- Includes curated collections: development tools, fonts, CLI utilities. Go nuts. -- Users install packages at runtime with `brew bundle`, aliased to premade `ujust commands` -- See [custom/brew/README.md](custom/brew/README.md) for details - -### Flatpak Support -- Ship your favorite flatpaks -- Automatically installed on first boot after user setup -- See [custom/flatpaks/README.md](custom/flatpaks/README.md) for details - -### ujust Commands -- User-friendly command shortcuts via `ujust` -- Pre-configured examples for app installation and system maintenance for you to customize -- See [custom/ujust/README.md](custom/ujust/README.md) for details - -### Build Scripts -- Modular numbered scripts (10-, 20-, 30-) run in order -- Example scripts included for third-party repositories and desktop replacement -- Helper functions for safe COPR usage -- See [build/README.md](build/README.md) for details +_Last updated: 2026-02-16_ -## Quick Start +## Build Architecture (Bluefin Pattern) -### 1. Create Your Repository +BlueForge uses a multi-stage Containerfile: -Click "Use this template" to create a new repository from this template. +1. **`ctx` stage** assembles: + - local `build/` + - local `custom/` + - `ghcr.io/projectbluefin/common` system files + - `ghcr.io/ublue-os/brew` system files +2. **final stage** starts from `ghcr.io/ublue-os/bluefin:stable` and runs `build/10-build.sh` with `/ctx` mounted. -### 2. Rename the Project +This keeps customization modular and reproducible while staying aligned with Universal Blue conventions. -Important: Set the OS identity to your repository name (`blueforge` in this repo) in these 6 files: +## BlueForge Personality -1. `Containerfile` (line 4): `# Name: your-repo-name` -2. `Justfile` (line 1): `export image_name := env("IMAGE_NAME", "your-repo-name")` -3. `README.md` (line 1): `# your-repo-name` -4. `artifacthub-repo.yml` (line 5): `repositoryID: your-repo-name` -5. `custom/ujust/README.md` (~line 175): `localhost/your-repo-name:stable` -6. `.github/workflows/clean.yml` (line 23): `packages: your-repo-name` +If I had to describe this OS in one line: Bluefin reliability, but with my defaults already made. -### 3. Enable GitHub Actions and Required Permissions +- Security-first without being heavy-handed. +- Terminal-first without abandoning desktop quality-of-life. +- Runtime apps and tooling so iteration stays fast. +- Minimal surprises in day-to-day use. -- Go to the "Actions" tab in your repository -- Click "I understand my workflows, go ahead and enable them" -- Go to `Settings -> Actions -> General` and set **Workflow permissions** to **Read and write permissions** -- Ensure **Allow GitHub Actions to create and approve pull requests** is enabled +## Who This Is For -Your first build will start automatically! +- You want Bluefin stability with curated defaults. +- You prefer a terminal-first workflow but still want polished desktop UX. +- You want security tools available immediately. +- You want most tools managed at runtime instead of rebaking images constantly. -Note: Image signing is disabled by default. Your images will build successfully without any signing keys. Once you're ready for production, see "Optional: Enable Image Signing" below. +## Repo Layout -### 3.1 Verify Actions Are Enabled and Running +- `Containerfile` - base image, layered context, and build execution. +- `build/10-build.sh` - build-time packages and system configuration. +- `custom/brew/*.Brewfile` - runtime CLI/dev/font package bundles. +- `custom/flatpaks/default.preinstall` - first-boot GUI app manifest. +- `custom/ujust/*.just` - user-facing app/system helpers. +- `.github/workflows/*.yml` - CI build, cleanup, Renovate, and validation workflows. -After the first push, confirm workflows in GitHub: +## Quick Start -- `Build container image` -- `Cleanup Old Images` -- `Renovate` -- Validation workflows (`Validate Brewfiles`, `Validate Flatpaks`, `Validate Justfiles`, `Validate Renovate Config`, `Validate Shell Scripts`) +### 1) Use this template -CLI verification (optional): +Create a new GitHub repo from this template. -```bash -gh workflow list -gh run list --limit 10 -``` +### 2) Rename identity everywhere -### 4. Customize Your Image +Set your OS/repo name consistently in: -Choose your base image in `Containerfile` (line 23): -```dockerfile -FROM ghcr.io/ublue-os/bluefin:stable -``` +1. `Containerfile` (`# Name: your-name`) +2. `Justfile` (`export image_name := env("IMAGE_NAME", "your-name")`) +3. `README.md` title +4. `artifacthub-repo.yml` (`repositoryID: your-name`) +5. `custom/ujust/README.md` (`localhost/your-name:stable` examples) +6. `.github/workflows/clean.yml` package reference -Add your packages in `build/10-build.sh`: -```bash -dnf5 install -y package-name -``` +### 3) Enable Actions permissions -Customize your apps: -- Add Brewfiles in `custom/brew/` ([guide](custom/brew/README.md)) -- Add Flatpaks in `custom/flatpaks/` ([guide](custom/flatpaks/README.md)) -- Add ujust commands in `custom/ujust/` ([guide](custom/ujust/README.md)) +In GitHub repo settings: -### 5. Development Workflow +- Enable Actions. +- Set workflow permissions to **Read and write**. +- Enable permission for Actions to create/approve PRs. -All changes should be made via pull requests: +### 4) Customize your image -1. Open a pull request on GitHub with the change you want. -3. The PR will automatically trigger: - - Build validation - - Brewfile, Flatpak, Justfile, and shellcheck validation - - Test image build -4. Once checks pass, merge the PR -5. Merging triggers publishes a `:stable` image +- **Build-time system changes**: edit `build/10-build.sh`. +- **Runtime CLI tools**: edit `custom/brew/default.Brewfile` and related Brewfiles. +- **Runtime GUI apps**: edit `custom/flatpaks/default.preinstall`. +- **User shortcuts**: edit `custom/ujust/custom-apps.just` and `custom/ujust/custom-system.just`. -### 6. Deploy Your Image +## Local Build and Test -Switch to your image: ```bash -sudo bootc switch ghcr.io/your-username/your-repo-name:stable -sudo systemctl reboot +just build +just build-qcow2 +just run-vm-qcow2 ``` -## Optional: Enable Image Signing +Optional ISO flow: -Image signing is disabled by default to let you start building immediately. However, signing is strongly recommended for production use. - -### Why Sign Images? - -- Verify image authenticity and integrity -- Prevent tampering and supply chain attacks -- Required for some enterprise/security-focused deployments -- Industry best practice for production images - -### Setup Instructions (Cosign + GitHub) - -1. Generate signing keys: ```bash -cosign generate-key-pair +just build-iso +just run-vm-iso ``` -This creates two files: -- `cosign.key` (private key) - Keep this secret -- `cosign.pub` (public key) - Commit this to your repository - -2. Add the private key to GitHub Secrets: - - Copy the entire contents of `cosign.key` - - Go to your repository on GitHub - - Navigate to Settings → Secrets and variables → Actions ([GitHub docs](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository)) - - Click "New repository secret" - - Name: `SIGNING_SECRET` - - Value: Paste the entire contents of `cosign.key` - - Click "Add secret" - -3. Replace the contents of `cosign.pub` with your public key: - - Open `cosign.pub` in your repository - - Replace the placeholder with your actual public key - - Commit and push the change +## Deploy -4. Enable signing in the workflow: - - Edit `.github/workflows/build.yml` - - Find the "OPTIONAL: Image Signing with Cosign" section. - - Uncomment the steps to install Cosign and sign the image (remove the `#` from the beginning of each line in that section). - - Commit and push the change - -5. Run a new workflow and verify signed output: - -```bash -gh workflow run build.yml -gh run list --workflow "Build container image" --limit 5 -``` - -6. Your next completed build will produce signed images. - -Important: Never commit `cosign.key` to the repository. It's already in `.gitignore`. - -## Love Your Image? Let's Go to Production - -Ready to take your custom OS to production? Enable these features for enhanced security, reliability, and performance: - -### Production Checklist - -- [ ] **Enable Image Signing** (Recommended) - - Provides cryptographic verification of your images - - Prevents tampering and ensures authenticity - - See "Optional: Enable Image Signing" section above for setup instructions - - Status: **Disabled by default** to allow immediate testing - -- [ ] **Enable SBOM Attestation** (Recommended) - - Generates Software Bill of Materials for supply chain security - - Provides transparency about what's in your image - - Requires image signing to be enabled first - - To enable: - 1. First complete image signing setup above - 2. Edit `.github/workflows/build.yml` - 3. Find the "OPTIONAL: SBOM Attestation" section around line 232 - 4. Uncomment the "Add SBOM Attestation" step - 5. Commit and push - - Status: **Disabled by default** (requires signing first) - -- [ ] **Enable Image Rechunking** (Recommended) - - Optimizes bootc image layers for better update performance - - Reduces update sizes by 5-10x - - Improves download resumability with evenly sized layers - - To enable: - 1. Edit `.github/workflows/build.yml` - 2. Find the "Build Image" step - 3. Add a rechunk step after the build (see example below) - - Status: **Not enabled by default** (optional optimization) - -#### Adding Image Rechunking - -After building your bootc image, add a rechunk step before pushing to the registry. Here's an example based on the workflow used by [zirconium-dev/zirconium](https://github.com/zirconium-dev/zirconium): - -```yaml -- name: Build image - id: build - run: sudo podman build -t "${IMAGE_NAME}:${DEFAULT_TAG}" -f ./Containerfile . - -- name: Rechunk Image - run: | - sudo podman run --rm --privileged \ - -v /var/lib/containers:/var/lib/containers \ - --entrypoint /usr/libexec/bootc-base-imagectl \ - "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" \ - rechunk --max-layers 96 \ - "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" \ - "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" - -- name: Push to Registry - run: sudo podman push "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" "${IMAGE_REGISTRY}/${IMAGE_NAME}:${DEFAULT_TAG}" -``` - -Alternative approach using a temporary tag for clarity: - -```yaml -- name: Rechunk Image - run: | - sudo podman run --rm --privileged \ - -v /var/lib/containers:/var/lib/containers \ - --entrypoint /usr/libexec/bootc-base-imagectl \ - "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" \ - rechunk --max-layers 67 \ - "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" \ - "localhost/${IMAGE_NAME}:${DEFAULT_TAG}-rechunked" - - # Tag the rechunked image with the original tag - sudo podman tag "localhost/${IMAGE_NAME}:${DEFAULT_TAG}-rechunked" "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" - sudo podman rmi "localhost/${IMAGE_NAME}:${DEFAULT_TAG}-rechunked" -``` - -**Parameters:** -- `--max-layers`: Maximum number of layers for the rechunked image (typically 67 for optimal balance) -- The first image reference is the source (input) -- The second image reference is the destination (output) - - When using the same reference for both, the image is rechunked in-place - - You can also use different tags (e.g., `-rechunked` suffix) and then retag if preferred - -**References:** -- [CoreOS rpm-ostree build-chunked-oci documentation](https://coreos.github.io/rpm-ostree/build-chunked-oci/) -- [bootc documentation](https://containers.github.io/bootc/) - -### After Enabling Production Features - -Your workflow will: -- Sign all images with your key -- Generate and attach SBOMs -- Provide full supply chain transparency - -Users can verify your images with: ```bash -cosign verify --key cosign.pub ghcr.io/your-username/your-repo-name:stable +sudo bootc switch ghcr.io//:stable +sudo systemctl reboot ``` -## Detailed Guides - -- [Homebrew/Brewfiles](custom/brew/README.md) - Runtime package management -- [Flatpak Preinstall](custom/flatpaks/README.md) - GUI application setup -- [ujust Commands](custom/ujust/README.md) - User convenience commands -- [Build Scripts](build/README.md) - Build-time customization - -## Architecture +## Optional: Enable Signing (Production) -This template follows the **multi-stage build architecture** from @projectbluefin/distroless, as documented in the [Bluefin Contributing Guide](https://docs.projectbluefin.io/contributing/). +Signing is intentionally disabled by default so first builds work immediately. -### Multi-Stage Build Pattern +When ready: -**Stage 1: Context (ctx)** - Combines resources from multiple sources: -- Local build scripts (`/build`) -- Local custom files (`/custom`) -- **@projectbluefin/common** - Desktop configuration shared with Aurora -- **@projectbluefin/branding** - Branding assets -- **@ublue-os/artwork** - Artwork shared with Aurora and Bazzite -- **@ublue-os/brew** - Homebrew integration +1. Generate keys with `cosign generate-key-pair`. +2. Add private key contents to GitHub Actions secret `SIGNING_SECRET`. +3. Commit your real `cosign.pub`. +4. Uncomment signing steps in `.github/workflows/build.yml`. +5. (Optional) Uncomment SBOM generation + attestation steps. -**Stage 2: Base Image** - Default options: -- `ghcr.io/ublue-os/silverblue-main:latest` (Fedora-based, default) -- `quay.io/centos-bootc/centos-bootc:stream10` (CentOS-based alternative) +Never commit `cosign.key`. -### Benefits of This Architecture +## Useful Commands After First Boot -- **Modularity**: Compose your image from reusable OCI containers -- **Maintainability**: Update shared components independently -- **Reproducibility**: Renovate automatically updates OCI tags to SHA digests -- **Consistency**: Share components across Bluefin, Aurora, and custom images +Install runtime packages: -### OCI Container Resources - -The template imports files from these OCI containers at build time: - -```dockerfile -COPY --from=ghcr.io/ublue-os/base-main:latest /system_files /oci/base -COPY --from=ghcr.io/projectbluefin/common:latest /system_files /oci/common -COPY --from=ghcr.io/ublue-os/brew:latest /system_files /oci/brew +```bash +ujust install-default-apps +ujust install-dev-tools +ujust install-fonts +ujust install-all-brew ``` -Your build scripts can access these files at: -- `/ctx/oci/base/` - Base system configuration -- `/ctx/oci/common/` - Shared desktop configuration -- `/ctx/oci/branding/` - Branding assets -- `/ctx/oci/artwork/` - Artwork files -- `/ctx/oci/brew/` - Homebrew integration files - -**Note**: Renovate automatically updates `:latest` tags to SHA digests for reproducible builds. - -## Local Testing - -Test your changes before pushing: +Extra helpers: ```bash -just build # Build container image -just build-qcow2 # Build VM disk image -just run-vm-qcow2 # Test in browser-based VM +ujust install-jetbrains-toolbox +ujust configure-dev-groups +ujust clean-containers +ujust update-and-reboot ``` -## Community +## Validation and CI -- [Universal Blue Discord](https://discord.gg/WEu6BdFEtp) -- [bootc Discussion](https://github.com/bootc-dev/bootc/discussions) +PRs run validations for: -## Learn More - -- [Universal Blue Documentation](https://universal-blue.org/) -- [bootc Documentation](https://containers.github.io/bootc/) -- [Video Tutorial by TesterTech](https://www.youtube.com/watch?v=IxBl11Zmq5wE) +- Shell scripts (`shellcheck`) +- Brewfile syntax +- Flatpak IDs +- Justfile syntax +- Renovate config -## Security +Main branch builds and publishes the `stable` image tags. -This template provides security features for production use: -- Optional SBOM generation (Software Bill of Materials) for supply chain transparency -- Optional image signing with cosign for cryptographic verification -- Automated security updates via Renovate -- Build provenance tracking +## Learn More -These security features are disabled by default to allow immediate testing. When you're ready for production, see the "Love Your Image? Let's Go to Production" section above to enable them. +- Universal Blue: +- Bluefin: +- bootc: +- Flatpak app IDs: diff --git a/build/10-build.sh b/build/10-build.sh index 2179fd3..8a0249f 100755 --- a/build/10-build.sh +++ b/build/10-build.sh @@ -44,7 +44,36 @@ echo "::endgroup::" echo "::group:: Install Packages" # Install packages using dnf5 -# Example: dnf5 install -y tmux +copr_install_isolated "scottames/ghostty" ghostty +copr_install_isolated "imput/helium" helium-bin + +# Install 1Password from official repository +rpm --import https://downloads.1password.com/linux/keys/1password.asc +cat > /etc/yum.repos.d/1password.repo << 'EOF' +[1password] +name=1Password Stable Channel +baseurl=https://downloads.1password.com/linux/rpm/stable/$basearch +enabled=1 +gpgcheck=1 +repo_gpgcheck=1 +gpgkey=https://downloads.1password.com/linux/keys/1password.asc +EOF +dnf5 install -y 1password +rm -f /etc/yum.repos.d/1password.repo + +# Install Mullvad VPN from official repository +dnf5 config-manager addrepo --from-repofile=https://repository.mullvad.net/rpm/stable/mullvad.repo +dnf5 install -y mullvad-vpn +rm -f /etc/yum.repos.d/mullvad.repo + +# Replace default terminal +dnf5 remove -y ptyxis + +# Prefer Ghostty as default terminal launcher target +mkdir -p /etc/xdg +cat > /etc/xdg/xdg-terminals.list << 'EOF' +com.mitchellh.ghostty.desktop +EOF # Example using COPR with isolated pattern: # copr_install_isolated "ublue-os/staging" package-name diff --git a/custom/brew/default.Brewfile b/custom/brew/default.Brewfile index 5732042..2441cd5 100644 --- a/custom/brew/default.Brewfile +++ b/custom/brew/default.Brewfile @@ -12,10 +12,17 @@ brew "rg" # ripgrep - faster grep # Development tools brew "gh" # GitHub CLI brew "git" # Git version control +brew "neovim" # Neovim text editor +brew "oven-sh/bun/bun" # JavaScript runtime and package manager +brew "nvm" # Node version manager + +# AI coding tools +tap "anomalyco/tap" +brew "anomalyco/tap/opencode" # OpenCode CLI assistant +cask "claude-code" # Anthropic CLI assistant # Shell enhancements brew "starship" # Cross-shell prompt -brew "zoxide" # Smarter cd command # Utilities brew "htop" # Interactive process viewer diff --git a/custom/flatpaks/default.preinstall b/custom/flatpaks/default.preinstall index 7132a45..4499fa7 100644 --- a/custom/flatpaks/default.preinstall +++ b/custom/flatpaks/default.preinstall @@ -4,9 +4,6 @@ # See: https://docs.flatpak.org/en/latest/flatpak-command-reference.html#flatpak-preinstall # Web Browsers -[Flatpak Preinstall org.mozilla.firefox] -Branch=stable - [Flatpak Preinstall org.mozilla.Thunderbird] Branch=stable