Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions crates/client/src/agent_os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,36 @@ impl AgentOs {
}
}

async fn create_owned(options: AgentOsConfig) -> Result<AgentOs, ClientError> {
async fn create_owned(mut options: AgentOsConfig) -> Result<AgentOs, ClientError> {
// Resolve registry names before allocating a sidecar, so an unknown
// package or release fails without booting a VM. The packages are
// downloaded and verified by the sidecar once the VM exists.
let mut boot_sources = Vec::with_capacity(options.software_sources.len());
for source in std::mem::take(&mut options.software_sources) {
let source = crate::software_registry::resolve_software_source(
source,
options.software_registry.as_ref(),
)
.await?;
if !boot_sources.contains(&source) {
boot_sources.push(source);
}
}
let client = Self::create_vm(options).await?;
// A failed install disposes the VM instead of returning one without the
// requested software.
for source in boot_sources {
if let Err(error) = client.install_software(source).await {
if let Err(shutdown_error) = client.shutdown().await {
tracing::error!(%shutdown_error, "failed to dispose VM after create-time software install failed");
}
return Err(error);
}
}
Ok(client)
}

async fn create_vm(options: AgentOsConfig) -> Result<AgentOs, ClientError> {
let config = Arc::new(options);

// 1. Resolve the sidecar handle (shared "default" pool unless configured otherwise) and
Expand Down Expand Up @@ -764,11 +793,20 @@ impl AgentOs {

/// Resolve, verify, pin, and project one exact software artifact inside the
/// sidecar. The client sends only the source, never a resolved host path.
///
/// A [`crate::SoftwareSource::Registry`] source resolves on this host to the
/// release manifest's URL and digest, so the sidecar verifies the bytes
/// exactly as for an explicit URL source.
pub async fn install_software(
&self,
source: crate::software::PackageSource,
source: impl Into<crate::software_registry::SoftwareSource>,
) -> Result<crate::software::InstalledSoftware, ClientError> {
let _vm_configuration = try_vm_mutation(&self.inner.vm_configuration_operation)?;
let source = crate::software_registry::resolve_software_source(
source.into(),
self.inner.config.software_registry.as_ref(),
)
.await?;
let source = match source {
crate::software::PackageSource::Url {
url,
Expand Down
23 changes: 23 additions & 0 deletions crates/client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ pub struct AgentOsConfig {
/// Trusted Core package acquisition limits and local-test transport policy.
/// Hosted actor callers cannot configure this process-owned policy.
pub package_resolver: Option<crate::software::PackageResolverOptions>,
/// Registry packages and URL or path sources installed after boot, before
/// [`crate::AgentOs::create`] returns (= TS `software` registry and URL
/// entries). Registry names resolve before the VM is created. Default `[]`.
pub software_sources: Vec<crate::software_registry::SoftwareSource>,
/// Where registry sources resolve (= TS `softwareRegistry`). Default:
/// `https://releases.rivet.dev/agent-os/` at this client's release.
pub software_registry: Option<crate::software_registry::SoftwareRegistryOptions>,
/// Loopback ports exempt from the default outbound-to-host block.
pub loopback_exempt_ports: Vec<u16>,
/// Allowed Node.js builtins. Default: the hardened native-bridge set.
Expand Down Expand Up @@ -123,6 +130,22 @@ impl AgentOsConfigBuilder {
self
}

pub fn software_sources(
mut self,
sources: Vec<crate::software_registry::SoftwareSource>,
) -> Self {
self.config.software_sources = sources;
self
}

pub fn software_registry(
mut self,
options: crate::software_registry::SoftwareRegistryOptions,
) -> Self {
self.config.software_registry = Some(options);
self
}

pub fn loopback_exempt_ports(mut self, ports: Vec<u16>) -> Self {
self.config.loopback_exempt_ports = ports;
self
Expand Down
7 changes: 7 additions & 0 deletions crates/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod output_replay;
pub mod process;
pub mod sidecar;
pub mod software;
pub mod software_registry;
pub mod stream;
pub mod transport;

Expand Down Expand Up @@ -163,6 +164,12 @@ pub use software::{
DEFAULT_MAX_PACKAGE_BYTES,
};

pub use software_registry::{
default_registry_version, resolve_registry_software, ResolvedRegistrySoftware,
SoftwareRegistryOptions, SoftwareSource, DEFAULT_SOFTWARE_REGISTRY_URL,
SOFTWARE_REGISTRY_URL_ENV,
};

pub use fs::{
BatchReadResult, BatchWriteEntry, BatchWriteResult, DirEntry, DirEntryType,
DynamicMountDescriptor, FileContent, FilesystemEntry, FilesystemEntryEncoding,
Expand Down
Loading
Loading