apt-ostree/.notes/ostree/apt-ostree.md
robojerk d295f9bb4d Major milestone: Complete apt-ostree bootc compatibility and OCI integration
-  Real package installation (replaced mock installation)
-  Real OSTree commit creation from installed packages
-  OCI image creation from both commits and rootfs
-  Full bootc compatibility with proper labels
-  Comprehensive test suite (test-bootc-apt-ostree.sh)
-  Container tool validation (skopeo, podman)
-  Updated compatibility reports for Ubuntu Questing
-  Fixed OCI schema version and field naming issues
-  Temporary directory lifecycle fixes
-  Serde rename attributes for OCI JSON compliance

Ready for Aurora-style workflow deployment!
2025-07-20 21:06:44 +00:00

16 KiB

apt-ostree OSTree Implementation

Overview

apt-ostree implements OSTree as its core filesystem management system, providing atomic, immutable deployments with APT/DEB package layering capabilities. This document explains how apt-ostree implements and extends OSTree functionality for Debian/Ubuntu systems.

Core OSTree Integration

Repository Management

apt-ostree maintains an OSTree repository at /ostree/repo that stores:

  • Content-addressable objects: Files and metadata stored by SHA256 hash
  • Commits: Immutable snapshots of filesystem trees
  • Refs: Symbolic pointers to specific commits (e.g., ubuntu/24.04/x86_64/desktop)
  • Static deltas: Pre-calculated differences between commits for efficient updates

Deployment Structure

/ostree/
├── repo/                      # OSTree repository (content-addressable storage)
├── deploy/                    # Active deployments
│   └── ubuntu-desktop/        # State root for Ubuntu Desktop
│       ├── var/               # Shared mutable data across deployments
│       └── <checksum>/        # Individual deployment directories
└── boot/                      # Bootloader configuration

Filesystem Assembly

apt-ostree assembles the live filesystem using:

  • Hardlinks: Deployments use hardlinks to objects in /ostree/repo
  • Bind mounts: Read-only /usr mounted from deployment
  • Symlinks: User directories redirected to /var
  • 3-way merge: /etc merged from old deployment, new deployment, and local changes

APT-OSTree Specific Extensions

Package Layering

apt-ostree extends OSTree with APT/DEB package layering:

// Core layering implementation in src/ostree.rs
pub struct OstreeManager {
    repo: OstreeRepo,
    sysroot: OstreeSysroot,
}

impl OstreeManager {
    // Create new deployment with layered packages
    pub fn create_deployment_with_layers(
        &self,
        base_commit: &str,
        new_commit: &str,
        layered_packages: &[String],
    ) -> Result<(), Box<dyn std::error::Error>> {
        // 1. Extract base filesystem from OSTree commit
        let base_tree = self.repo.read_commit(base_commit, None)?;
        
        // 2. Apply DEB package layers
        let layered_tree = self.apply_deb_layers(&base_tree, layered_packages)?;
        
        // 3. Create new OSTree commit
        let new_commit_checksum = self.repo.write_commit(
            &layered_tree,
            "Package layer update",
            None,
            None,
        )?;
        
        // 4. Update ref to point to new commit
        self.repo.set_ref(None, "ubuntu/24.04/x86_64/desktop", &new_commit_checksum)?;
        
        Ok(())
    }
    
    // Apply DEB package layers to deployment
    pub fn apply_deb_layers(
        &self,
        base_tree: &OstreeRepoFile,
        packages: &[String],
    ) -> Result<OstreeRepoFile, Box<dyn std::error::Error>> {
        // Extract DEB packages and apply to filesystem tree
        for package in packages {
            let extracted_package = self.extract_deb_package(package)?;
            self.merge_package_into_tree(&base_tree, &extracted_package)?;
        }
        
        Ok(base_tree.clone())
    }
}

Transaction Management

apt-ostree implements atomic transactions for all OSTree operations:

// Transaction types for OSTree operations
#[derive(Debug, Clone)]
pub enum TransactionType {
    Deploy,
    Rollback,
    PkgChange,
    Rebase,
    Upgrade,
}

// Transaction execution with OSTree integration
pub struct OstreeTransaction {
    transaction_type: TransactionType,
    sysroot: OstreeSysroot,
}

impl OstreeTransaction {
    // Execute transaction with OSTree commit creation
    pub fn execute_with_ostree_commit(
        &self,
        base_commit: &str,
        new_commit: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        match self.transaction_type {
            TransactionType::Deploy => {
                self.create_deployment(base_commit, new_commit)?;
            }
            TransactionType::Rollback => {
                self.rollback_to_previous_deployment()?;
            }
            TransactionType::PkgChange => {
                self.apply_package_changes(base_commit, new_commit)?;
            }
            _ => {
                return Err("Unsupported transaction type".into());
            }
        }
        
        Ok(())
    }
    
    // Rollback to previous OSTree deployment
    pub fn rollback_to_previous_deployment(&self) -> Result<(), Box<dyn std::error::Error>> {
        let deployments = self.sysroot.get_deployments()?;
        if deployments.len() < 2 {
            return Err("No previous deployment available for rollback".into());
        }
        
        // Switch to previous deployment
        let previous_deployment = &deployments[1];
        self.sysroot.set_default_deployment(previous_deployment)?;
        
        Ok(())
    }
}

OSTree Commit Creation

apt-ostree creates OSTree commits for all system changes:

// Commit creation workflow in src/ostree.rs
impl OstreeManager {
    pub fn create_commit(
        &self,
        base_commit: &str,
        new_commit: &str,
        subject: &str,
        layered_packages: &[String],
    ) -> Result<String, Box<dyn std::error::Error>> {
        // 1. Extract base filesystem from OSTree commit
        let base_tree = self.repo.read_commit(base_commit, None)?;
        
        // 2. Apply DEB package layers
        let layered_tree = self.apply_deb_layers(&base_tree, layered_packages)?;
        
        // 3. Create new OSTree commit
        let new_commit_checksum = self.repo.write_commit(
            &layered_tree,
            subject,
            None,
            None,
        )?;
        
        // 4. Update ref to point to new commit
        self.repo.set_ref(None, "ubuntu/24.04/x86_64/desktop", &new_commit_checksum)?;
        
        Ok(new_commit_checksum)
    }
}

OSTree Environment Detection

apt-ostree detects OSTree environments using multiple methods:

// Environment detection in src/ostree_detection.rs
pub struct OstreeDetection;

impl OstreeDetection {
    pub fn is_ostree_environment() -> bool {
        // Method 1: Check for /ostree directory
        if !std::path::Path::new("/ostree").is_dir() {
            return false;
        }
        
        // Method 2: Check for /run/ostree-booted file
        if !std::path::Path::new("/run/ostree-booted").exists() {
            return false;
        }
        
        // Method 3: Check kernel command line for ostree parameter
        if let Ok(cmdline) = std::fs::read_to_string("/proc/cmdline") {
            if !cmdline.contains("ostree") {
                return false;
            }
        }
        
        // Method 4: Try to load OSTree sysroot
        if let Ok(sysroot) = OstreeSysroot::new_default() {
            if sysroot.load().is_ok() {
                return true;
            }
        }
        
        false
    }
    
    pub fn get_detection_methods() -> Vec<String> {
        vec![
            "Filesystem detection (/ostree directory)".to_string(),
            "Boot detection (/run/ostree-booted file)".to_string(),
            "Kernel parameter detection (ostree in /proc/cmdline)".to_string(),
            "Library detection (OSTree sysroot loading)".to_string(),
            "Service detection (daemon availability)".to_string(),
        ]
    }
}

OSTree Integration Points

Daemon Integration

The apt-ostree daemon integrates with OSTree for privileged operations:

// Daemon OSTree operations in src/bin/apt-ostreed.rs
pub struct AptOstreedDaemon {
    ostree_manager: OstreeManager,
}

impl AptOstreedDaemon {
    // OSTree deployment management
    pub fn deploy_ostree_commit(
        &self,
        commit_checksum: &str,
        osname: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        self.ostree_manager.create_deployment(commit_checksum, osname)
    }
    
    // OSTree rollback operations
    pub fn rollback_ostree_deployment(
        &self,
        osname: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        self.ostree_manager.rollback_deployment(osname)
    }
    
    // OSTree status reporting
    pub fn get_ostree_status(&self) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
        let deployments = self.ostree_manager.get_deployments()?;
        let current_deployment = self.ostree_manager.get_current_deployment()?;
        
        Ok(json!({
            "deployments": deployments,
            "current": current_deployment,
            "booted": self.ostree_manager.is_booted_deployment(&current_deployment)?,
        }))
    }
}

CLI Integration

apt-ostree CLI commands integrate with OSTree operations:

// CLI command integration in src/main.rs
pub fn deploy_command(matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
    let commit = matches.get_one::<String>("COMMIT")
        .ok_or("No commit specified")?;
    
    // Create deployment via D-Bus daemon
    let client = AptOstreeClient::new()?;
    client.deploy(commit)?;
    
    println!("Deployment created successfully");
    Ok(())
}

pub fn rollback_command(_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
    // Rollback via D-Bus daemon
    let client = AptOstreeClient::new()?;
    client.rollback()?;
    
    println!("Rollback completed successfully");
    Ok(())
}

OSTree Performance Optimizations

Static Delta Support

apt-ostree leverages OSTree static deltas for efficient updates:

// Static delta usage in src/ostree.rs
impl OstreeManager {
    pub fn pull_with_static_deltas(
        &self,
        remote_name: &str,
        refspec: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        // Configure static delta usage
        self.repo.set_enable_static_deltas(true);
        
        // Pull with static delta optimization
        self.repo.pull(remote_name, refspec, None)
    }
}

Content Deduplication

apt-ostree benefits from OSTree's content-addressable storage:

// Content deduplication example
impl OstreeManager {
    pub fn commit_with_deduplication(
        &self,
        base_commit: &str,
        new_commit: &str,
    ) -> Result<String, Box<dyn std::error::Error>> {
        // OSTree automatically deduplicates identical files
        // Only unique content is stored in the repository
        let base_tree = self.repo.read_commit(base_commit, None)?;
        
        // Create commit with automatic deduplication
        self.repo.write_commit(&base_tree, "Update", None, None)
    }
}

OSTree Security Features

GPG Signature Verification

apt-ostree verifies OSTree commits using GPG signatures:

// GPG verification in src/ostree.rs
impl OstreeManager {
    pub fn verify_commit_signature(
        &self,
        commit_checksum: &str,
    ) -> Result<bool, Box<dyn std::error::Error>> {
        // Verify commit signature
        let sign = OstreeSign::new()?;
        self.repo.verify_commit(commit_checksum, &sign)
    }
}

Read-Only Filesystem Enforcement

apt-ostree enforces OSTree's read-only filesystem model:

// Read-only enforcement in src/system.rs
impl SystemManager {
    pub fn enforce_readonly_filesystem(
        &self,
        deployment_path: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        // Mount /usr as read-only
        let mount_cmd = format!("mount --bind -o ro {} /usr", deployment_path);
        self.run_command(&mount_cmd)?;
        
        Ok(())
    }
}

OSTree Integration Testing

Deployment Testing

# Test OSTree deployment creation
apt-ostree deploy $(apt-ostree status --json | jq -r '.deployments[0].checksum')

# Test OSTree rollback
apt-ostree rollback

# Test OSTree status
apt-ostree status --json

Performance Testing

# Test OSTree pull performance
time ostree pull ubuntu ubuntu/24.04/x86_64/desktop

# Test static delta usage
ostree pull --enable-static-deltas ubuntu ubuntu/24.04/x86_64/desktop

# Test commit creation performance
time apt-ostree install package-name

OSTree Troubleshooting

Common Issues

  1. OSTree repository corruption: Use ostree fsck to verify repository integrity
  2. Deployment failures: Check /var/log/ostree.log for detailed error messages
  3. Bootloader issues: Verify GRUB configuration with ostree admin boot
  4. Space issues: Use ostree admin cleanup to remove old deployments

Debug Commands

# Check OSTree repository status
ostree fsck --repo=/ostree/repo

# List available deployments
ostree admin status

# Check bootloader configuration
ostree admin boot

# Verify commit signatures
ostree verify --repo=/ostree/repo ubuntu/24.04/x86_64/desktop

OSTree Future Enhancements

Planned Features

  1. Composefs Integration: Enhanced filesystem layering with composefs
  2. OCI Container Support: Direct OSTree to OCI container conversion
  3. Bootc Compatibility: Integration with bootc for container-native deployments
  4. Enhanced Static Deltas: Improved delta generation and application

Integration Roadmap

  • Phase 1: Core OSTree integration ( Complete)
  • Phase 2: Performance optimizations ( Complete)
  • Phase 3: Security enhancements ( Complete)
  • Phase 4: OCI container support (🔄 In Progress)
  • Phase 5: Bootc compatibility (📋 Planned)

Ubuntu/Debian Specific Considerations

Filesystem Layout

apt-ostree adapts OSTree for Ubuntu/Debian filesystem conventions:

/
├── ostree/                    # OSTree repository and deployments
│   ├── repo/                 # OSTree repository
│   ├── deploy/               # Deployed systems
│   └── boot/                 # Boot configurations
├── var/                      # Writable data (shared across deployments)
│   ├── home/                 # User home directories
│   ├── opt/                  # Optional application software
│   ├── usrlocal/             # Locally installed software
│   ├── etc/                  # System configuration (merged on upgrade)
│   └── tmp/                  # Temporary files
├── etc/                      # System configuration (writable)
└── usr/                      # Read-only system software

Package Management Integration

apt-ostree integrates APT package management with OSTree:

// APT-OSTree integration in src/apt.rs
impl AptManager {
    pub fn install_packages_with_ostree(
        &self,
        packages: &[String],
    ) -> Result<(), Box<dyn std::error::Error>> {
        // 1. Download and extract DEB packages
        let extracted_packages = self.download_and_extract_packages(packages)?;
        
        // 2. Create OSTree commit with package layers
        let ostree_manager = OstreeManager::new()?;
        ostree_manager.create_commit_with_layers(
            &self.get_current_commit()?,
            "Package installation",
            &extracted_packages,
        )?;
        
        // 3. Deploy new commit
        ostree_manager.deploy_latest_commit()?;
        
        Ok(())
    }
}

mmdebstrap Integration

apt-ostree uses mmdebstrap for efficient base system creation:

# Create base system with mmdebstrap
sudo mmdebstrap --arch=amd64 --variant=minbase \
    --include=systemd,systemd-sysv,ostree \
    noble /tmp/ubuntu-base

# Create package layer
sudo mmdebstrap --arch=amd64 \
    --include=ubuntu-desktop-minimal,gnome-shell \
    noble /tmp/ubuntu-desktop /tmp/ubuntu-base

# Create OSTree commit
sudo ostree commit --repo=/ostree/repo \
    --branch=ubuntu/24.04/x86_64/desktop \
    --parent=ubuntu/24.04/x86_64/base \
    /tmp/ubuntu-desktop