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
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ slack = ["openab-core/slack"]
secrets-aws = ["openab-core/secrets-aws"]
agentcore = ["openab-core/agentcore"]
config-s3 = ["openab-core/config-s3"]
pre-seed = ["openab-core/pre-seed"]

# Gateway adapters (each pulls in the gateway crate + axum for embedded server)
telegram = ["dep:openab-gateway", "dep:axum", "openab-gateway/telegram"]
Expand Down
19 changes: 19 additions & 0 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,22 @@ error_hold_ms = 2500
# openab = "~/projects/openab"
# infra = "~/projects/infra-cdk"
# web = "~/projects/frontend"

# --- Pre-seed: download & extract S3 zip archives before pre_boot ---
# Seeds the agent environment (configs, tools, memory) before any hook runs.
# Archives are extracted in order — later layers overwrite earlier ones (like container layers).
# Max 5 sources. Only s3:// URIs with .zip format are supported.
# Requires the `pre-seed` feature flag (opt-in).
#
# [hooks.pre_seed]
# sources = [
# "s3://my-bucket/base-env.zip", # Layer 1: base tools & configs
# "s3://my-bucket/shared-memory.zip", # Layer 2: shared team memory
# "s3://my-bucket/agent-specific.zip", # Layer 3: agent-specific overrides
# ]
# target = "/home/agent" # extraction target (default: $HOME)
# max_bytes = 104857600 # max compressed zip size (default: 100 MiB)
# timeout_seconds = 300 # per-source timeout (default: 300)
# on_failure = "abort" # "abort" or "warn" (default: "abort")
# region = "us-west-2" # optional: override AWS region
# endpoint_url = "http://localhost:4566" # optional: LocalStack / VPC endpoint
2 changes: 2 additions & 0 deletions crates/openab-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ tempfile = "3.27.0"
aws-sdk-secretsmanager = { version = "1", optional = true }
aws-sdk-s3 = { version = "1", optional = true }
aws-config = { version = "1", optional = true }
zip = { version = "2", default-features = false, features = ["deflate"], optional = true }
aws-sigv4 = { version = "1", optional = true }
aws-credential-types = { version = "1", optional = true }
urlencoding = { version = "2", optional = true }
Expand All @@ -54,4 +55,5 @@ discord = ["dep:serenity"]
slack = []
secrets-aws = ["dep:aws-sdk-secretsmanager", "dep:aws-config"]
config-s3 = ["dep:aws-sdk-s3", "dep:aws-config"]
pre-seed = ["dep:aws-sdk-s3", "dep:aws-config", "dep:zip", "dep:hex"]
agentcore = ["dep:aws-config", "dep:aws-sigv4", "dep:aws-credential-types", "dep:urlencoding", "dep:hex", "dep:http", "dep:rustls", "dep:tokio-rustls", "dep:webpki-roots"]
48 changes: 48 additions & 0 deletions crates/openab-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,58 @@ fn default_exec_timeout() -> u64 {

#[derive(Debug, Clone, Default, Deserialize)]
pub struct HooksConfig {
pub pre_seed: Option<PreSeedConfig>,
pub pre_boot: Option<HookConfig>,
pub pre_shutdown: Option<HookConfig>,
}

/// Configuration for the pre_seed phase.
/// Downloads and extracts zip archives from S3 before pre_boot.
#[derive(Debug, Clone, Deserialize)]
pub struct PreSeedConfig {
/// S3 URIs of zip archives to download and extract (max 5).
/// Extracted in order; later layers overwrite earlier ones.
#[serde(default)]
pub sources: Vec<String>,
/// Extraction target directory. Default: $HOME.
pub target: Option<String>,
/// Override AWS region for S3 access.
pub region: Option<String>,
/// Override S3 endpoint URL (for LocalStack, VPC endpoints).
pub endpoint_url: Option<String>,
/// Maximum compressed zip size in bytes. Default: 100 MiB.
#[serde(default = "default_max_zip_bytes")]
pub max_bytes: u64,
/// Timeout in seconds for each download+extract operation. Default: 300.
#[serde(default = "default_pre_seed_timeout")]
pub timeout_seconds: u64,
/// Failure policy. Default: abort.
#[serde(default)]
pub on_failure: OnFailure,
}

impl Default for PreSeedConfig {
fn default() -> Self {
Self {
sources: Vec::new(),
target: None,
region: None,
endpoint_url: None,
max_bytes: default_max_zip_bytes(),
timeout_seconds: default_pre_seed_timeout(),
on_failure: OnFailure::Abort,
}
}
}

fn default_max_zip_bytes() -> u64 {
100 * 1024 * 1024 // 100 MiB
}

fn default_pre_seed_timeout() -> u64 {
300
}

/// Failure policy for a hook.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum OnFailure {
Expand Down
2 changes: 2 additions & 0 deletions crates/openab-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub mod hooks;
pub mod markdown;
pub mod media;
pub mod multibot_cache;
#[cfg(feature = "pre-seed")]
pub mod pre_seed;
pub mod reactions;
#[cfg(feature = "discord")]
pub mod remind;
Expand Down
Loading
Loading