- Fix parallel execution logic to properly handle JoinHandle<Result<R, E>> types - Use join_all instead of try_join_all for proper Result handling - Fix double question mark (??) issue in parallel execution methods - Clean up unused imports in parallel and cache modules - Ensure all performance optimization modules compile successfully - Fix CI build failures caused by compilation errors
28 KiB
🔍 rpm-ostree vs rpm-ostreed: CLI-Daemon Separation Analysis
📋 Overview
This document provides a comprehensive analysis of the separation between rpm-ostree (the CLI client) and rpm-ostreed (the system daemon) based on examination of the rpm-ostree source code. Understanding this separation is crucial for implementing a similar architecture in apt-ostree.
🏗️ Architecture Overview
Component Separation
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ rpm-ostree │ │ DBus Layer │ │ rpm-ostreed │
│ (CLI Client) │◄──►│ (Communication)│◄──►│ (System Daemon)│
│ │ │ │ │ │
│ • Command Line │ │ • Client Proxy │ │ • OSTree Ops │
│ • User Interface│ │ • Signal Handler│ │ • Package Mgmt │
│ • Progress Display│ │ • Error Handling│ │ • Transactions │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Key Design Principles
- Separation of Concerns: CLI handles user interaction, daemon handles system operations
- Privilege Isolation: Daemon runs with elevated privileges, CLI runs as user
- Transaction Management: Daemon manages long-running operations, CLI monitors progress
- Fallback Support: CLI can operate without daemon for read-only operations
- Security by Design: Daemon handles all privileged operations via PolicyKit
🔍 Detailed Separation Analysis
1. rpm-ostree (CLI Client) Responsibilities
Command Line Interface
// From libmain.cxx - Command registration and dispatch
static RpmOstreeCommand commands[] = {
{ "compose", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD | RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT,
"Commands to compose a tree", rpmostree_builtin_compose },
{ "status", (RpmOstreeBuiltinFlags)0, "Get the version of the booted system",
rpmostree_builtin_status },
{ "upgrade", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Perform a system upgrade",
rpmostree_builtin_upgrade },
{ "install", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Overlay additional packages",
rpmostree_builtin_install },
{ "uninstall", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Remove overlayed packages",
rpmostree_builtin_uninstall },
{ "search", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Search for packages",
rpmostree_builtin_search },
{ "rollback", (RpmOstreeBuiltinFlags)0, "Revert to the previously booted tree",
rpmostree_builtin_rollback },
{ "deploy", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Deploy a specific commit",
rpmostree_builtin_deploy },
{ "rebase", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Switch to a different tree",
rpmostree_builtin_rebase },
{ "kargs", (RpmOstreeBuiltinFlags)0, "Query or modify kernel arguments",
rpmostree_builtin_kargs },
{ "initramfs", (RpmOstreeBuiltinFlags)0, "Enable or disable local initramfs regeneration",
rpmostree_builtin_initramfs },
{ "override", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Manage base package overrides",
rpmostree_builtin_override },
{ "reset", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Remove all mutations",
rpmostree_builtin_reset },
{ "refresh-md", (RpmOstreeBuiltinFlags)0, "Generate rpm repo metadata",
rpmostree_builtin_refresh_md },
{ "reload", (RpmOstreeBuiltinFlags)0, "Reload configuration",
rpmostree_builtin_reload },
{ "cancel", (RpmOstreeBuiltinFlags)0, "Cancel an active transaction",
rpmostree_builtin_cancel },
{ "cleanup", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Clear cached/pending data",
rpmostree_builtin_cleanup },
{ "apply-live", (RpmOstreeBuiltinFlags)0, "Apply pending deployment changes to booted deployment",
rpmostree_builtin_apply_live },
{ "finalize-deployment", (RpmOstreeBuiltinFlags)0, "Finalize staged deployment",
rpmostree_builtin_finalize_deployment },
{ "initramfs-etc", (RpmOstreeBuiltinFlags)0, "Add files to the initramfs",
rpmostree_builtin_initramfs_etc },
{ "db", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Commands to query the RPM database",
rpmostree_builtin_db },
{ "usroverlay", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Apply transient overlay",
rpmostree_builtin_usroverlay },
{ "ex", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Execute command in deployment",
rpmostree_builtin_ex },
{ "rebuild", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Rebuild deployment",
rpmostree_builtin_rebuild },
{ "start-daemon", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Start the daemon",
rpmostree_builtin_start_daemon },
{ "testutils", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Test utilities",
rpmostree_builtin_testutils },
};
Responsibilities:
- Command parsing and argument validation
- Option handling and help display
- Command dispatch to appropriate builtin functions
- User interface and output formatting
- Command flag management and validation
DBus Client Communication
// From rpmostree-clientlib.cxx - Client-side DBus handling
static gboolean
app_load_sysroot_impl (const char *sysroot, GCancellable *cancellable,
GDBusConnection **out_conn, GError **error)
{
// Start daemon if not running
ROSCXX_TRY (client_start_daemon (), error);
// Connect to system bus
g_autoptr (GDBusConnection) connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
cancellable, error);
// Register as client
uid_t uid = getuid ();
gboolean should_register;
if (uid == 0)
should_register = TRUE;
else
{
g_autofree char *session_id = NULL;
if (sd_pid_get_session (getpid (), &session_id) >= 0)
should_register = sd_session_is_active (session_id) == 1;
else
should_register = FALSE;
}
if (should_register)
{
g_autoptr (GVariant) options = g_variant_new ("(s@a{sv})", "cli",
g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
rpmostree_sysroot_call_register_client (sysroot_proxy, options, NULL, NULL);
}
}
Responsibilities:
- DBus connection establishment and management
- Client registration with daemon
- Method invocation on daemon interfaces
- Signal handling for progress updates
- Error handling and retry logic
- Daemon startup if not running
Progress Monitoring and Display
// From rpmostree-clientlib.cxx - Progress and error display
static void
transaction_progress_signal_handler (GDBusConnection *connection, const char *sender_name,
const char *object_path, const char *interface_name,
const char *signal_name, GVariant *parameters,
gpointer user_data)
{
auto tp = static_cast<TransactionProgress *> (user_data);
if (g_strcmp0 (signal_name, "Message") == 0) {
const char *message;
g_variant_get (parameters, "(&s)", &message);
if (!tp->progress) {
tp->progress = TRUE;
rpmostreecxx::console_progress_begin_task (message);
} else {
rpmostreecxx::console_progress_set_message (message);
}
} else if (g_strcmp0 (signal_name, "PercentProgress") == 0) {
guint percentage;
const char *message;
g_variant_get (parameters, "(u&s)", &percentage, &message);
if (!tp->progress) {
tp->progress = TRUE;
rpmostreecxx::console_progress_begin_percent (message);
}
rpmostreecxx::console_progress_update (percentage);
} else if (g_strcmp0 (signal_name, "DownloadProgress") == 0) {
auto line = rpmostreecxx::client_render_download_progress (*parameters);
if (!tp->progress) {
tp->progress = TRUE;
rpmostreecxx::console_progress_begin_task (line.c_str ());
} else {
rpmostreecxx::console_progress_set_message (line.c_str ());
}
}
}
Responsibilities:
- Progress display during long operations
- Status reporting and user feedback
- Output formatting (text, JSON, etc.)
- Error message presentation
- Download progress visualization
- Task progress tracking
Fallback Operations
// From rpmostree-builtin-status.cxx - Fallback when daemon unavailable
static gboolean
rpmostree_builtin_status (int argc, char **argv, RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable, GError **error)
{
// Try daemon first, fallback to direct OSTree if needed
g_autoptr (GDBusConnection) connection = NULL;
if (rpmostree_client_connection_new (invocation, cancellable, &connection, error)) {
// Use daemon for status
return get_status_via_daemon (connection, invocation, cancellable, error);
} else {
// Fallback to direct OSTree operations
return get_status_direct (invocation, cancellable, error);
}
}
// Direct OSTree operations for fallback
static gboolean
get_status_direct (RpmOstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
{
// Direct OSTree sysroot access
g_autoptr (OstreeSysroot) sysroot = ostree_sysroot_new_default ();
if (!ostree_sysroot_load (sysroot, cancellable, error))
return FALSE;
// Get deployment information directly
g_autoptr (GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot);
if (!deployments)
return FALSE;
// Display deployment information
for (guint i = 0; i < deployments->len; i++) {
auto deployment = static_cast<OstreeDeployment *> (deployments->pdata[i]);
print_deployment (deployment, opt_verbose, opt_only_booted, textarea_width);
}
return TRUE;
}
Responsibilities:
- Fallback logic when daemon unavailable
- Direct OSTree operations for read-only tasks
- Graceful degradation of functionality
- User notification of limited capabilities
- Local system access for basic operations
2. rpm-ostreed (Daemon) Responsibilities
System State Management
// From rpmostreed-daemon.cxx - Daemon state management
struct _RpmostreedDaemon {
GObject parent_instance;
GHashTable *bus_clients; // Active client tracking
gboolean running; // Daemon running state
gboolean rebooting; // System reboot state
GDBusProxy *bus_proxy; // System bus proxy
GSource *idle_exit_source; // Auto-exit timer
guint rerender_status_id; // Status rerender timer
RpmostreedSysroot *sysroot; // OSTree sysroot management
gchar *sysroot_path; // System root path
// Configuration settings
guint idle_exit_timeout; // Auto-exit timeout
RpmostreedAutomaticUpdatePolicy auto_update_policy; // Update policy
gboolean lock_layering; // Package layering lock
gboolean disable_recommends; // Recommends handling
// DBus infrastructure
GDBusConnection *connection; // DBus connection
GDBusObjectManagerServer *object_manager; // Object management
// Async runtime
std::optional<rust::Box<rpmostreecxx::TokioHandle>> tokio_handle;
};
Responsibilities:
- Global state management and persistence
- Configuration loading and validation
- Client lifecycle management
- System monitoring and health checks
- Auto-exit management for resource efficiency
- Update policy enforcement
OSTree Operations
// From rpmostreed-sysroot.cxx - OSTree system management
struct _RpmostreedSysroot {
RPMOSTreeSysrootSkeleton parent_instance;
OstreeSysroot *ot_sysroot; // OSTree sysroot object
OstreeRepo *repo; // OSTree repository
struct stat repo_last_stat; // Repository stat cache
RpmostreedTransaction *transaction; // Active transaction
guint close_transaction_timeout_id; // Transaction timeout
PolkitAuthority *authority; // PolicyKit authority
gboolean on_session_bus; // Session bus flag
GHashTable *os_interfaces; // OS interface objects
GHashTable *osexperimental_interfaces; // Experimental interfaces
GFileMonitor *monitor; // Filesystem monitoring
guint sig_changed; // Change signal handler
};
// OSTree operations implementation
static gboolean
rpmostreed_sysroot_handle_deploy (RPMOSTreeSysroot *skeleton, GDBusMethodInvocation *invocation,
const char *osname, const char *refspec, GVariant *options)
{
// Check authorization
if (!rpmostreed_sysroot_check_authorization (self, invocation,
"org.projectatomic.rpmostree1.deploy")) {
return FALSE;
}
// Create deployment transaction
g_autoptr (RpmostreedTransaction) transaction = rpmostreed_transaction_new (
self, invocation, "deploy", refspec, options);
if (!transaction)
return FALSE;
// Execute deployment
rpmostreed_transaction_execute (transaction);
return TRUE;
}
Responsibilities:
- OSTree repository management and operations
- Deployment creation, modification, and removal
- Filesystem operations and staging
- Boot configuration management
- System updates and rollbacks
- Repository monitoring and change detection
Transaction Management
// From rpmostreed-transaction.cxx - Transaction lifecycle
struct _RpmostreedTransactionPrivate {
GDBusMethodInvocation *invocation; // DBus method context
gboolean executed; // Transaction completion state
GCancellable *cancellable; // Cancellation support
// System state during transaction
char *sysroot_path; // Sysroot path
OstreeSysroot *sysroot; // OSTree sysroot
gboolean sysroot_locked; // Sysroot lock state
// Client tracking
char *client_description; // Client description
char *agent_id; // Client agent ID
char *sd_unit; // Systemd unit
// Progress tracking
gint64 last_progress_journal; // Progress journal timestamp
gboolean redirect_output; // Output redirection flag
// Peer connections
GDBusServer *server; // DBus server
GHashTable *peer_connections; // Client connections
// Completion state
GVariant *finished_params; // Completion parameters
guint watch_id; // Watch identifier
};
// Transaction execution
static void
rpmostreed_transaction_execute (RpmostreedTransaction *self)
{
RpmostreedTransactionPrivate *priv = rpmostreed_transaction_get_private (self);
// Lock sysroot
if (!ostree_sysroot_lock (priv->sysroot, priv->cancellable, NULL)) {
rpmostreed_transaction_fail (self, "Failed to lock sysroot");
return;
}
priv->sysroot_locked = TRUE;
// Execute operations
for (guint i = 0; i < priv->operations->len; i++) {
auto operation = static_cast<RpmostreedOperation *> (priv->operations->pdata[i]);
// Emit progress
rpmostreed_transaction_emit_percent_progress (self,
(i * 100) / priv->operations->len, "Executing operation");
// Execute operation
if (!rpmostreed_operation_execute (operation, self)) {
rpmostreed_transaction_fail (self, "Operation execution failed");
return;
}
}
// Commit transaction
rpmostreed_transaction_commit (self);
}
Responsibilities:
- Transaction lifecycle management
- Atomic operation execution
- Progress tracking and reporting
- Rollback and error recovery
- Client communication during operations
- Resource locking and cleanup
Security and Privilege Management
// From rpmostreed-sysroot.cxx - Security integration
static gboolean
rpmostreed_sysroot_check_authorization (RpmostreedSysroot *self,
GDBusMethodInvocation *invocation,
const char *action)
{
// Get client credentials
g_autoptr (GCredentials) credentials = g_dbus_method_invocation_get_credentials (invocation);
if (!credentials)
return FALSE;
// Check PolicyKit authorization
g_autoptr (GError) local_error = NULL;
g_autoptr (PolkitAuthorizationResult) result = polkit_authority_check_authorization_sync (
self->authority, credentials, action, NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, &local_error);
if (!result) {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"Failed to check authorization: %s",
local_error ? local_error->message : "unknown error");
return FALSE;
}
if (!polkit_authorization_result_get_is_authorized (result)) {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"Not authorized for action: %s", action);
return FALSE;
}
return TRUE;
}
Responsibilities:
- PolicyKit integration and authorization
- Privilege escalation management
- Access control and security policies
- Audit logging and security events
- Client credential validation
- Operation permission checking
🔄 Communication Patterns
1. DBus Interface Structure
org.projectatomic.rpmostree1
├── /org/projectatomic/rpmostree1/Sysroot
│ ├── Properties
│ │ ├── Booted (s)
│ │ ├── Path (s)
│ │ ├── ActiveTransaction (o)
│ │ └── Locked (b)
│ ├── Methods
│ │ ├── GetOS (s) → o
│ │ ├── RegisterClient (sa{sv}) → ()
│ │ ├── UnregisterClient (s) → ()
│ │ ├── Reload () → ()
│ │ └── ReloadConfig () → ()
│ └── Signals
│ └── Changed ()
└── /org/projectatomic/rpmostree1/OS/{osname}
├── Properties
│ ├── Version (s)
│ ├── Origin (s)
│ ├── Packages (a(sss))
│ └── RequestedPackages (as)
├── Methods
│ ├── PkgChange (sa{sv}) → o
│ ├── Deploy (s) → o
│ ├── Rebase (s) → o
│ ├── Upgrade (sa{sv}) → o
│ ├── Rollback () → o
│ ├── Cleanup () → o
│ ├── SetKargs (asasas) → ()
│ └── GetKargs () → as
└── Signals
└── Changed ()
2. Signal Flow Patterns
Daemon Event → DBus Signal → Client Handler → User Display
Signal Types:
Message: Text messages and status updatesTaskBegin/TaskEnd: Task start/completion notificationsPercentProgress: Progress percentage updatesDownloadProgress: Download progress detailsFinished: Transaction completion notificationChanged: System state change notifications
3. Method Invocation Patterns
Client Request → DBus Method → Daemon Handler → Operation Execution → Response
Method Flow:
- Client invokes DBus method on daemon interface
- Daemon receives method call and validates parameters
- Authorization check performed via PolicyKit
- Operation executed with progress reporting
- Response sent back to client with results
- Signals emitted for progress and completion
📊 Responsibility Matrix
| Responsibility | CLI (rpm-ostree) | Daemon (rpm-ostreed) | Notes |
|---|---|---|---|
| Command Parsing | ✅ Primary | ❌ None | CLI handles all user input |
| Argument Validation | ✅ Primary | ❌ None | CLI validates before sending |
| DBus Communication | ✅ Client | ✅ Server | Both sides of communication |
| OSTree Operations | ❌ None | ✅ Primary | Daemon handles all OSTree errors |
| Package Management | ❌ None | ✅ Primary | Daemon handles RPM/OSTree |
| Transaction Management | ❌ None | ✅ Primary | Daemon manages lifecycle |
| Progress Reporting | ✅ Display | ✅ Generation | Daemon generates, CLI displays |
| Error Handling | ✅ User-facing | ✅ System-level | Different error contexts |
| Security | ❌ None | ✅ Primary | Daemon handles authorization |
| Configuration | ✅ Reading | ✅ Reading/Writing | Daemon can modify system |
| Fallback Operations | ✅ Primary | ❌ None | CLI handles when daemon unavailable |
| System State | ❌ None | ✅ Primary | Daemon monitors and manages |
| Resource Locking | ❌ None | ✅ Primary | Daemon locks sysroot |
| Rollback Operations | ❌ None | ✅ Primary | Daemon manages rollback |
🚀 apt-ostree Implementation Strategy
1. CLI Client (apt-ostree)
Core Responsibilities
// src/main.rs - CLI entry point
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
match args.get(1).map(|s| s.as_str()) {
Some("status") => show_system_status().await?,
Some("install") => install_packages(&args[2..]).await?,
Some("upgrade") => upgrade_system().await?,
Some("rollback") => rollback_system().await?,
Some("--help") | Some("-h") => show_usage(),
Some("--version") | Some("-V") => show_version(),
_ => {
eprintln!("Unknown command. Use --help for usage information.");
std::process::exit(1);
}
}
Ok(())
}
// Command implementation with daemon fallback
async fn install_packages(packages: &[String]) -> Result<(), Error> {
// Try daemon first
if let Ok(client) = AptOstreeClient::new().await {
let transaction_id = client.create_transaction().await?;
let success = client.install_packages(&transaction_id, packages.to_vec()).await?;
if success {
println!("✅ Packages installed successfully!");
} else {
println!("❌ Package installation failed");
}
} else {
// Fallback to direct operations (limited functionality)
println!("⚠️ Daemon unavailable, using limited fallback mode");
install_packages_direct(packages).await?;
}
Ok(())
}
DBus Client Integration
// src/client/dbus.rs - DBus client implementation
pub struct AptOstreeClient {
connection: zbus::Connection,
sysroot_proxy: zbus::Proxy<'static>,
}
impl AptOstreeClient {
pub async fn new() -> Result<Self, Error> {
let connection = zbus::Connection::system().await?;
let sysroot_proxy = zbus::Proxy::new(
&connection,
"org.projectatomic.aptostree1",
"/org/projectatomic/aptostree1/Sysroot",
"org.projectatomic.aptostree1.Sysroot",
).await?;
Ok(Self { connection, sysroot_proxy })
}
pub async fn create_transaction(&self) -> Result<String, Error> {
let transaction_id = self.sysroot_proxy
.call_method("CreateTransaction", &())
.await?;
Ok(transaction_id.body::<String>()?)
}
pub async fn install_packages(&self, transaction_id: &str, packages: Vec<String>) -> Result<bool, Error> {
let success = self.sysroot_proxy
.call_method("InstallPackages", &(transaction_id, packages))
.await?;
Ok(success.body::<bool>()?)
}
}
2. Daemon (apt-ostreed)
Core Responsibilities
// daemon/src/main.rs - Daemon entry point
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging
tracing_subscriber::fmt::init();
// Create daemon instance
let daemon = Arc::new(AptOstreeDaemon::new()?);
// Set up DBus connection
let _connection = ConnectionBuilder::system()?
.name("org.projectatomic.aptostree1")?
.serve_at("/org/projectatomic/aptostree1/Sysroot", daemon.clone())?
.serve_at("/org/projectatomic/aptostree1/OS/debian", daemon.clone())?
.build()
.await?;
// Keep daemon running
loop {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
}
// DBus interface implementation
#[dbus_interface(name = "org.projectatomic.aptostree1.Sysroot")]
impl AptOstreeDaemon {
async fn create_transaction(&self) -> zbus::fdo::Result<String> {
let transaction = Transaction::new(
self.get_user_id().await?,
self.get_session_id().await?,
"Package installation".to_string(),
"apt-ostree CLI".to_string(),
);
let transaction_id = transaction.id.clone();
self.transactions.write().await.insert(transaction_id.clone(), transaction);
Ok(transaction_id)
}
async fn install_packages(&self, transaction_id: &str, packages: Vec<String>) -> zbus::fdo::Result<bool> {
// Check authorization
if !self.security_manager.check_package_operation(self.get_user_id().await?).await? {
return Err(zbus::fdo::Error::PermissionDenied("Not authorized".into()));
}
// Create and execute transaction
let mut transaction = self.get_transaction(transaction_id).await?;
transaction.add_operation(Operation::InstallPackage { packages });
match transaction.execute(self).await {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
}
3. Integration Points
Progress Reporting
// daemon/src/transaction.rs - Progress emission
impl Transaction {
pub async fn emit_progress(&self, operation: &Operation, progress: u32, message: &str) {
// Emit DBus signal for progress
if let Some(daemon) = &self.daemon {
daemon.emit_signal(
"org.projectatomic.aptostree1.Transaction",
"PercentProgress",
&(message, progress),
).ok();
}
}
}
// CLI progress handling
impl AptOstreeClient {
pub async fn monitor_progress(&self, transaction_id: &str) -> Result<(), Error> {
let mut stream = self.connection
.receive_signal_with_args(
"org.projectatomic.aptostree1.Transaction",
"PercentProgress",
&[("transaction_id", transaction_id)],
).await?;
while let Some(msg) = stream.next().await {
let (message, progress): (String, u32) = msg.body()?;
println!("[{}%] {}", progress, message);
}
Ok(())
}
}
🎯 Key Implementation Principles
1. Clear Separation of Concerns
- CLI: User interface, command parsing, progress display
- Daemon: System operations, OSTree management, security
2. Graceful Degradation
- Primary mode: Full functionality via daemon
- Fallback mode: Limited functionality when daemon unavailable
3. Security by Design
- Privilege isolation: Daemon handles privileged operations
- Authorization: PolicyKit integration for all system changes
- Sandboxing: Package script execution in controlled environment
4. Transaction Safety
- Atomic operations: All changes are atomic
- Rollback support: Automatic rollback on failure
- Progress tracking: Real-time operation monitoring
5. Performance Optimization
- Caching: Intelligent caching of deployment and package information
- Parallel operations: Concurrent package processing where possible
- Resource management: Efficient memory and disk usage
This CLI-daemon separation analysis provides the foundation for implementing a production-ready apt-ostree system that maintains the proven architecture of rpm-ostree while adapting to the Debian/Ubuntu package management paradigm. The clear separation of responsibilities ensures maintainability, security, and scalability.