The application core for managing Bottles Next Wine and Proton environments.
bottles-core imports and installs managed components, persists bottles,
executes Windows programs through WineBridge, and provides Virgo storage and
snapshots through the default fvs feature. With this feature enabled,
Bottles::open connects to or starts FVS once and fails if initialization fails.
Config::fvs2d supplies an executable path; when omitted, fvs2d is found through
PATH. The socket remains in the configured runtime directory.
Disable FVS when only conventional, directly mutable prefixes are needed:
[dependencies]
bottles-core = { version = "0.1", default-features = false }Without fvs, snapshot APIs, Virgo storage, and standalone programs are not
compiled. Standard addon changes always use direct writes; failed or cancelled
recipes can leave partial prefix changes. A software edit shares one maintenance
session across its removals and installations, stopping once afterward even on
failure or cancellation. Payloads are accessed as steps run, so missing or invalid
inputs can fail after earlier steps have changed the prefix.
Runtime-only selection changes do not start that session.
The crate is centered around six types:
Bottlesowns the download service and provides addon, bottle, and standalone program managers.Addonspublishes live collections of runners and installable addons. Item values are snapshots; query the manager again after a publication.Manager<Bottle>andManager<Program>manage their respective collections by UUID. Each returned handle exposes its current immutable state for reading or watching. Manager clones share membership and watch both membership and state changes.Libraryasynchronously lists launchable entries from registeredLibraryProviderimplementations and provides launch handles. Calllist()again to refresh.Profilespersists named application identities and the current selection, discovers account providers, and owns account linking and credential persistence.Operation<T>represents long-running work with progress and cooperative cancellation.
Execution settings live in BottleState::config() as an EnvironmentConfig.
Use Bottle::edit or Program::edit to change metadata, startup settings, and
software selections in one draft. Edit::set_component, remove_component, and
add_dependency change only that draft; the final selection is validated and
applied before saving and publishing once. Dependencies remain append-only.
Software changes require a stopped owner. Metadata, environment variables, and
wrappers can change while running; startup settings apply on the next launch.
Bottle::launch(group_id, ProgramSpec) runs an unregistered program;
Bottle::launch_program(uuid) runs a saved registration. Dropping a bottle handle
leaves Wine running; call stop() to shut it down.
Choose a prefix backend when creating a bottle. Standard installs directly into a conventional Wine prefix. Virgo is experimental: it combines shared immutable layers with each bottle's private writable data. Missing layers are prepared before publishing owner selections. Creation and software edits compose the owner's registry once for the final selection. Launch mounts that prepared storage without registry recomposition or an automatic history checkpoint. The initial base uses the greatest valid local Soda version and remains pinned. Cached artifacts can be reused without their shared source payloads.
Fetch addons from the component and dependency catalogs, or use
Addons::import_component to import a component archive. Local releases are
identified by UUID. Removing a release deletes its installation inputs;
runtime executables and new installations still require those files. Standard
removal uses saved recipes and existing backups.
Operations are lazy. Await them, call cancel().await, or spawn them and
await the spawned task; dropping an operation abandons it. Account operations
finish entered credential writes during cooperative cancellation. Core does not
track these tasks or schedule automatic refreshes.
Context owns the ready Arc<Fvs2dClient>. The shared VirgoManager owns a
LayerStore with Directories, a clone of that client, and one construction
and publication lock. The store has no Context, addon, runner, or owner knowledge.
Virgo policy chooses Soda and build-time WineBridge, supplies relative addresses
and layer order, and executes recipes through the existing runners.
The store owns cache lookup, staging, filesystem and registry capture, immutable publication, composition, and workspace mounts. Bases retain their initial hives; overlays store registry patches separately from filesystem effects. Composition uses the supplied overlay order, then replays private registry changes. Artifact paths and manifest version 1 are unchanged. Virgo edits release stopped owner storage before checkpointing, then compose registry changes and save the draft together. Composition or save failures restore the checkpoint before returning; nothing is published on failure. Shared builds retain their separate scratch prefixes and shutdowns. Owner edits need no Wine startup or mount.
Virgo owner directories and snapshots created under the earlier launch-time composition lifecycle must be recreated. Schemas remain at version 1; there is no migration, launch fallback, or automatic deletion. Existing shared layers remain reusable.
get_or_build holds coordination through the complete cache-miss workflow.
Policy resolves inputs, prepares storage, executes work, and stops Wine before
passing the execution result to finalization. Finalization publishes successful
work or cleans up failed work. Failed shutdown, mount creation, or unmount retains
staging; dropping a workspace never performs asynchronous cleanup. Environment
owns edit checkpoints, discovery cleanup and configuration publication.
Snapshots include the prepared registry baseline, private data and saved
selections; restoration does not rebuild shared layers.
Internal listing reads immediate artifacts in a supplied collection. Removal withdraws an explicit address into trash under the publication lock before best-effort cleanup. Its caller must ensure the artifact is unmounted and no longer needed. There is no owner tracking, garbage collection, or public layer API.
Add bottles-core and an async runtime to your application:
[dependencies]
bottles-core = "0.1"
bottles-plugin-host = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
futures-lite = "2"Open the library, inspect the current bottles, and stop its download service:
use bottles_core::{Bottles, Config, Directories, ProgramSpec};
#[tokio::main]
async fn main() -> Result<(), bottles_core::error::Error> {
let directories = Directories::new().await?;
let plugins = bottles_plugin_host::Plugins::open(
directories.plugins(), directories.staging(),
).await?;
let bottles = Bottles::open(Config::default(), plugins.clone()).await?;
println!("profile: {}", bottles.profiles().selected().name());
for bottle in bottles.bottles().list() {
let state = bottle.state()?;
println!("{}\t{}", state.id(), state.name());
}
if let Some(bottle) = bottles.bottles().list().into_iter().next() {
let program = ProgramSpec::new("Example", "C:/Games/example.exe");
let id = bottle.edit(move |edit| Ok(edit.add_program(program))).await?;
println!("registered {id}");
}
let installed = bottles.library().list().await?;
println!("{} installed programs", installed.len());
bottles.shutdown().await
}Build the API documentation locally with cargo doc -p bottles-core --open.
Report bugs through the issue tracker.
Licensed under the GNU General Public License, version 3.