From 621339b0e1e01ddbb6e50a9e07d5a767458d2b7e Mon Sep 17 00:00:00 2001 From: Context7 Bot Date: Wed, 22 Apr 2026 13:36:10 +0000 Subject: [PATCH] docs: add auto-generated code documentation --- docs/codedocs/api-reference/build-pipeline.md | 122 +++++++++ docs/codedocs/api-reference/config.md | 236 ++++++++++++++++++ .../api-reference/filesystem-policy.md | 139 +++++++++++ docs/codedocs/api-reference/format.md | 108 ++++++++ .../api-reference/manifest-resolution.md | 91 +++++++ .../api-reference/networking-policy.md | 156 ++++++++++++ docs/codedocs/api-reference/resolve.md | 77 ++++++ docs/codedocs/api-reference/runtime-core.md | 198 +++++++++++++++ docs/codedocs/api-reference/transports.md | 94 +++++++ docs/codedocs/architecture.md | 92 +++++++ docs/codedocs/component-host-lifecycle.md | 129 ++++++++++ docs/codedocs/component-packaging.md | 120 +++++++++ docs/codedocs/component-references.md | 115 +++++++++ .../configuring-policies-and-profiles.md | 86 +++++++ .../packing-and-validating-components.md | 85 +++++++ docs/codedocs/guides/serving-components.md | 95 +++++++ docs/codedocs/index.md | 102 ++++++++ docs/codedocs/runtime-policies.md | 150 +++++++++++ 18 files changed, 2195 insertions(+) create mode 100644 docs/codedocs/api-reference/build-pipeline.md create mode 100644 docs/codedocs/api-reference/config.md create mode 100644 docs/codedocs/api-reference/filesystem-policy.md create mode 100644 docs/codedocs/api-reference/format.md create mode 100644 docs/codedocs/api-reference/manifest-resolution.md create mode 100644 docs/codedocs/api-reference/networking-policy.md create mode 100644 docs/codedocs/api-reference/resolve.md create mode 100644 docs/codedocs/api-reference/runtime-core.md create mode 100644 docs/codedocs/api-reference/transports.md create mode 100644 docs/codedocs/architecture.md create mode 100644 docs/codedocs/component-host-lifecycle.md create mode 100644 docs/codedocs/component-packaging.md create mode 100644 docs/codedocs/component-references.md create mode 100644 docs/codedocs/guides/configuring-policies-and-profiles.md create mode 100644 docs/codedocs/guides/packing-and-validating-components.md create mode 100644 docs/codedocs/guides/serving-components.md create mode 100644 docs/codedocs/index.md create mode 100644 docs/codedocs/runtime-policies.md diff --git a/docs/codedocs/api-reference/build-pipeline.md b/docs/codedocs/api-reference/build-pipeline.md new file mode 100644 index 0000000..8e9b1f7 --- /dev/null +++ b/docs/codedocs/api-reference/build-pipeline.md @@ -0,0 +1,122 @@ +--- +title: "API Reference: build pipeline" +description: "Public Rust items in act-build/src that pack skills, write custom sections, and validate completed ACT components." +--- + +Source files: `act-build/src/pack.rs`, `skill.rs`, `validate.rs`, and `wasm.rs` + +## Pack + +### `run` + +```rust +pub fn run(wasm_path: &Path) -> Result<()> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `wasm_path` | `&Path` | — | Path to the compiled component to rewrite in place. | + +Performs the full pack pipeline: + +1. Find the project directory. +2. Resolve and validate metadata. +3. Read the existing `.wasm`. +4. Write `act:component`. +5. Write `version` and `description`. +6. Optionally write `act:skill`. +7. Persist the modified bytes. + +Example: + +```rust +crate::pack::run(wasm_path)?; +``` + +## Skill Packaging + +### `pack_skill_dir` + +```rust +pub fn pack_skill_dir(project_dir: &Path) -> Result>> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `project_dir` | `&Path` | — | Project root checked for a `skill/` directory. | + +Returns `None` when no `skill/` directory exists, and returns a tar archive when `skill/SKILL.md` is present. + +## Validation + +### `run` + +```rust +pub fn run(wasm_path: &Path) -> Result<()> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `wasm_path` | `&Path` | — | Packed component to inspect. | + +Checks that: + +- `act:component` exists. +- The section decodes to `ComponentInfo`. +- `std.name` and `std.version` are non-empty. +- The component exports `act:core/tool-provider`. + +### `check_tool_provider_export` + +```rust +pub fn check_tool_provider_export(wasm: &[u8]) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `wasm` | `&[u8]` | — | Raw component bytes. | + +Returns `true` when the component export section contains a name including `act:core/tool-provider`. + +## Custom Section Utilities + +### `read_custom_section` + +```rust +pub fn read_custom_section<'a>(wasm: &'a [u8], name: &str) -> Result> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `wasm` | `&'a [u8]` | — | Raw component bytes. | +| `name` | `&str` | — | Custom section name to search for. | + +### `set_custom_section` + +```rust +pub fn set_custom_section(wasm: &[u8], name: &str, data: &[u8]) -> Result> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `wasm` | `&[u8]` | — | Raw component bytes. | +| `name` | `&str` | — | Custom section to add or replace. | +| `data` | `&[u8]` | — | Section payload bytes. | + +Adds or replaces a top-level custom section by scanning top-level section framing and splicing in the newly encoded section bytes. + +## Example + +```rust +let mut wasm = std::fs::read(wasm_path)?; +wasm = crate::wasm::set_custom_section(&wasm, "version", b"1.2.3")?; +let has_provider = crate::validate::check_tool_provider_export(&wasm)?; +``` + +## Practical Notes + +- `set_custom_section` requires a component-layer WASM header and will reject module-layer binaries. +- `pack::run` writes in place, so plan artifact immutability accordingly. +- `check_tool_provider_export` performs a substring check on export names rather than a stronger semantic WIT verification. + +Related pages: [Component Packaging](/docs/component-packaging) and [Guides: Packing and Validating Components](/docs/guides/packing-and-validating-components). diff --git a/docs/codedocs/api-reference/config.md b/docs/codedocs/api-reference/config.md new file mode 100644 index 0000000..2ed05e8 --- /dev/null +++ b/docs/codedocs/api-reference/config.md @@ -0,0 +1,236 @@ +--- +title: "API Reference: config" +description: "Public Rust items in act-cli/src/config.rs that load config, resolve profiles, and compute filesystem, HTTP, and metadata settings." +--- + +This module is the policy resolution entry point for `act-cli`. The items below are public in the source tree, but they should be treated as internal implementation API rather than a stable library contract because `act-cli` is published as a binary crate. + +Source file: `act-cli/src/config.rs` + +## Types + +```rust +pub enum PolicyMode { + Deny, + Allowlist, + Open, +} +``` + +```rust +pub struct FsConfig { + pub mode: PolicyMode, + pub allow: Vec, + pub deny: Vec, +} +``` + +```rust +pub struct HttpConfig { + pub mode: PolicyMode, + pub allow: Vec, + pub deny: Vec, +} +``` + +```rust +pub struct HttpRule { + pub net: crate::runtime::network::NetworkRule, + pub scheme: Option, + pub methods: Option>, +} +``` + +```rust +pub struct ConfigFile { + pub listen: Option, + pub log_level: Option, + pub policy: Option, + pub profile: HashMap, +} +``` + +```rust +pub struct PolicyConfig { + pub filesystem: Option, + pub http: Option, +} +``` + +```rust +pub enum FsPolicyToml { + Simple(String), + Structured { + mode: String, + allow: Vec, + deny: Vec, + }, +} +``` + +```rust +pub enum HttpPolicyToml { + Simple(String), + Structured { + mode: String, + allow: Vec, + deny: Vec, + }, +} +``` + +```rust +pub struct ProfileConfig { + pub metadata: Option, + pub policy: Option, +} +``` + +```rust +pub struct CliPolicyOverrides { + pub fs_mode: Option, + pub fs_allow: Vec, + pub fs_deny: Vec, + pub http_mode: Option, + pub http_allow: Vec, + pub http_deny: Vec, +} +``` + +### Field Notes + +| Field | Type | Description | +|-------|------|-------------| +| `FsConfig.allow` | `Vec` | Filesystem allow globs later compiled by `FsMatcher`. | +| `FsConfig.deny` | `Vec` | Deny globs checked before allow globs. | +| `HttpRule.net` | `crate::runtime::network::NetworkRule` | Shared network-level matcher input for host, port, and CIDR checks. | +| `ProfileConfig.metadata` | `Option` | Per-profile metadata merged into outgoing ACT metadata. | + +## Functions + +### `FsConfig::deny` + +```rust +pub fn deny() -> Self +``` + +Returns a default deny-all filesystem configuration. + +Example: + +```rust +let fs = crate::config::FsConfig::deny(); +assert!(matches!(fs.mode, crate::config::PolicyMode::Deny)); +``` + +### `default_config_path` + +```rust +pub fn default_config_path() -> Option +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| — | — | — | Uses the platform config directory and appends `act/config.toml`. | + +Returns `Some(path)` when the platform has a config directory, otherwise `None`. + +### `load_config` + +```rust +pub fn load_config(path: Option<&Path>) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `path` | `Option<&Path>` | `None` | Explicit config path. Falls back to `default_config_path()` and returns `ConfigFile::default()` when no file exists. | + +Reads TOML from disk and deserializes it into `ConfigFile`. + +Example: + +```rust +let cfg = crate::config::load_config(None)?; +``` + +### `get_profile` + +```rust +pub fn get_profile<'a>(config: &'a ConfigFile, name: &str) -> Result<&'a ProfileConfig> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `config` | `&ConfigFile` | — | Loaded config file. | +| `name` | `&str` | — | Profile key under `config.profile`. | + +Returns the named profile or an error when it is missing. + +### `resolve_fs_config` + +```rust +pub fn resolve_fs_config( + config: &ConfigFile, + profile: Option<&ProfileConfig>, + cli: &CliPolicyOverrides, +) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `config` | `&ConfigFile` | — | Top-level config file state. | +| `profile` | `Option<&ProfileConfig>` | `None` | Optional profile chosen by CLI. | +| `cli` | `&CliPolicyOverrides` | — | CLI override layer. Any filesystem override replaces config-file policy resolution. | + +Returns the final filesystem policy used for the invocation. + +### `resolve_http_config` + +```rust +pub fn resolve_http_config( + config: &ConfigFile, + profile: Option<&ProfileConfig>, + cli: &CliPolicyOverrides, +) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `config` | `&ConfigFile` | — | Top-level config file state. | +| `profile` | `Option<&ProfileConfig>` | `None` | Optional profile chosen by CLI. | +| `cli` | `&CliPolicyOverrides` | — | CLI override layer. Any HTTP override replaces config-file policy resolution. | + +Returns the final HTTP policy used for the invocation. + +### `resolve_metadata` + +```rust +pub fn resolve_metadata( + profile: Option<&ProfileConfig>, + cli_metadata: Option<&serde_json::Value>, +) -> serde_json::Value +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `profile` | `Option<&ProfileConfig>` | `None` | Optional profile whose `metadata` object is the base layer. | +| `cli_metadata` | `Option<&serde_json::Value>` | `None` | Optional JSON object from `--metadata` or `--metadata-file`. | + +Merges object keys, with CLI keys taking precedence. Non-object metadata is ignored. + +## Common Pattern + +```rust +let config = crate::config::load_config(None)?; +let profile = crate::config::get_profile(&config, "local").ok(); +let cli = crate::config::CliPolicyOverrides { + fs_mode: Some("allowlist".to_string()), + fs_allow: vec!["/workspace/**".to_string()], + ..Default::default() +}; +let fs = crate::config::resolve_fs_config(&config, profile, &cli)?; +let http = crate::config::resolve_http_config(&config, profile, &cli)?; +let metadata = crate::config::resolve_metadata(profile, None); +``` + +Related pages: [Runtime Policies](/docs/runtime-policies), [API Reference: filesystem policy](/docs/api-reference/filesystem-policy), and [API Reference: networking policy](/docs/api-reference/networking-policy). diff --git a/docs/codedocs/api-reference/filesystem-policy.md b/docs/codedocs/api-reference/filesystem-policy.md new file mode 100644 index 0000000..af9c7e6 --- /dev/null +++ b/docs/codedocs/api-reference/filesystem-policy.md @@ -0,0 +1,139 @@ +--- +title: "API Reference: filesystem policy" +description: "Public Rust items in runtime/effective.rs, runtime/fs_matcher.rs, and runtime/fs_policy.rs that implement capability ceilings and path gating." +--- + +Source files: `act-cli/src/runtime/effective.rs`, `act-cli/src/runtime/fs_matcher.rs`, and `act-cli/src/runtime/fs_policy.rs` + +## Effective Policy + +```rust +pub struct EffectivePolicy { + pub config: T, + pub declared: bool, +} +``` + +```rust +pub fn effective_fs(user: &FsConfig, caps: &Capabilities) -> EffectivePolicy +pub fn effective_http(user: &HttpConfig, caps: &Capabilities) -> EffectivePolicy +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `user` | `&FsConfig` or `&HttpConfig` | — | User-resolved policy from config and CLI. | +| `caps` | `&Capabilities` | — | Declared component capabilities from `act:component`. | + +These functions intersect user policy with the component declaration. Undeclared capabilities collapse to `Deny`. + +## Filesystem Matcher + +```rust +pub enum FsDecision { + Allow, + Deny, +} +``` + +```rust +pub struct FsMatcher { /* compiled allow/deny glob sets */ } +``` + +### `FsMatcher::compile` + +```rust +pub fn compile(cfg: &FsConfig) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `cfg` | `&FsConfig` | — | Resolved filesystem policy. | + +Compiles allow and deny globs into `GlobSet`s and records literal prefixes used for ancestor traversal checks. + +### `FsMatcher::decide` + +```rust +pub fn decide(&self, path: &Path) -> FsDecision +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `path` | `&Path` | — | Absolute, canonical host path. | + +Evaluates deny globs first, then allow globs, then ancestor traversal rules. + +## Filesystem Host Adapter + +```rust +pub struct Preopen { + pub guest: String, + pub host: PathBuf, +} +``` + +```rust +pub struct PolicyFilesystem; +``` + +```rust +pub struct PolicyFilesystemCtxView<'a> { + pub ctx: &'a mut WasiFilesystemCtx, + pub table: &'a mut ResourceTable, + pub matcher: &'a FsMatcher, + pub fd_paths: &'a mut FdPathMap, + pub mode: PolicyMode, +} +``` + +```rust +pub struct FdPathMap { + pub preopens: Vec<(String, PathBuf)>, + pub by_rep: HashMap, +} +``` + +### `derive_preopens` + +```rust +pub fn derive_preopens(cfg: &FsConfig) -> Vec +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `cfg` | `&FsConfig` | — | Resolved filesystem policy. | + +Returns platform-root preopens for `Open` and `Allowlist`, and no preopens for `Deny`. + +### `apply_mount_root` + +```rust +pub fn apply_mount_root(preopens: &mut [Preopen], mount_root: &str) +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `preopens` | `&mut [Preopen]` | — | Derived preopens to rewrite. | +| `mount_root` | `&str` | `/` | Cosmetic guest mount root from component capabilities. | + +Rewrites only the guest-visible path names, not the host paths. + +## Example + +```rust +let effective = crate::runtime::effective::effective_fs(&fs, &info.std.capabilities); +let mut preopens = crate::runtime::fs_policy::derive_preopens(&effective.config); +crate::runtime::fs_policy::apply_mount_root( + &mut preopens, + info.std.capabilities.fs_mount_root().unwrap_or("/"), +); +let matcher = crate::runtime::fs_matcher::FsMatcher::compile(&effective.config)?; +``` + +## Practical Notes + +- `PolicyFilesystemCtxView` exists because the source shadows default Wasmtime filesystem bindings with a policy-aware wrapper. +- `FdPathMap` tracks descriptor-to-host-path mappings so later path-based operations can be checked against the matcher. +- `effective_http` is documented here because the capability-ceiling logic for filesystem and HTTP lives in the same module. + +Related pages: [Runtime Policies](/docs/runtime-policies), [API Reference: runtime core](/docs/api-reference/runtime-core), and [API Reference: networking policy](/docs/api-reference/networking-policy). diff --git a/docs/codedocs/api-reference/format.md b/docs/codedocs/api-reference/format.md new file mode 100644 index 0000000..8a47e6d --- /dev/null +++ b/docs/codedocs/api-reference/format.md @@ -0,0 +1,108 @@ +--- +title: "API Reference: format" +description: "Public Rust items in act-cli/src/format.rs that render act info output as text or JSON." +--- + +This module formats the output of `act info`. It takes a fully assembled `InfoData` value and renders either a human-readable text document or a structured JSON payload. Source file: `act-cli/src/format.rs`. + +## Types + +```rust +pub struct InfoData<'a> { + pub info: &'a ComponentInfo, + pub metadata_schema: Option, + pub tools: Option>, +} +``` + +```rust +pub struct InfoJson { + pub name: String, + pub version: String, + pub description: String, + pub default_language: Option, + pub capabilities: serde_json::Value, + pub skill: Option, + pub metadata_schema: Option, + pub tools: Option>, +} +``` + +```rust +pub struct ToolJson { + pub name: String, + pub description: String, + pub parameters_schema: serde_json::Value, + pub read_only: Option, + pub idempotent: Option, + pub destructive: Option, + pub streaming: Option, + pub timeout_ms: Option, + pub usage_hints: Option, + pub anti_usage_hints: Option, + pub tags: Vec, +} +``` + +## Functions + +### `to_json` + +```rust +pub fn to_json(data: &InfoData<'_>) -> anyhow::Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `data` | `&InfoData<'_>` | — | Component info plus optional metadata schema and tool list. | + +Returns pretty-printed JSON. Invalid tool parameter schemas are preserved as JSON strings instead of crashing the formatter. + +Example: + +```rust +let output = crate::format::to_json(&data)?; +println!("{output}"); +``` + +### `to_text` + +```rust +pub fn to_text(data: &InfoData<'_>) -> String +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `data` | `&InfoData<'_>` | — | Component info plus optional metadata schema and tool list. | + +Returns a markdown-like string that includes component metadata, capability summary, optional embedded skill text, metadata schema, and tool details. + +Example: + +```rust +let output = crate::format::to_text(&data); +print!("{output}"); +``` + +## Notes + +- `to_json` extracts tool annotations such as read-only, idempotent, destructive, streaming, timeout, usage hints, and tags from ACT metadata keys. +- `to_text` summarizes capability classes and prints metadata and tool sections only when the corresponding data exists. +- The module is used directly by `cmd_info` in `act-cli/src/main.rs`. + +Common pattern: + +```rust +let data = crate::format::InfoData { + info: &component_info, + metadata_schema, + tools, +}; + +match format { + OutputFormat::Text => print!("{}", crate::format::to_text(&data)), + OutputFormat::Json => println!("{}", crate::format::to_json(&data)?), +} +``` + +Related pages: [Getting Started](/docs), [API Reference: resolve](/docs/api-reference/resolve), and [API Reference: runtime core](/docs/api-reference/runtime-core). diff --git a/docs/codedocs/api-reference/manifest-resolution.md b/docs/codedocs/api-reference/manifest-resolution.md new file mode 100644 index 0000000..4a71fdc --- /dev/null +++ b/docs/codedocs/api-reference/manifest-resolution.md @@ -0,0 +1,91 @@ +--- +title: "API Reference: manifest resolution" +description: "Public Rust items in act-build/src/manifest that resolve and validate component metadata from Cargo, Python, and JavaScript manifests." +--- + +Source files: `act-build/src/manifest/mod.rs`, `manifest/cargo.rs`, `manifest/pyproject.rs`, `manifest/packagejson.rs`, and `manifest/validate.rs` + +## Manifest Readers + +### Cargo + +```rust +pub fn from_cargo_metadata(dir: &Path) -> Result<(ComponentInfo, Option)> +pub fn from_toml(path: &Path) -> Result<(ComponentInfo, Option)> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `dir` | `&Path` | — | Project directory passed to `cargo metadata`. | +| `path` | `&Path` | — | Direct `Cargo.toml` path for raw TOML fallback. | + +`from_cargo_metadata` is preferred because it respects workspace inheritance. `from_toml` is the fallback when `cargo metadata` is unavailable. + +### Python + +```rust +pub fn from_toml(path: &Path) -> Result<(ComponentInfo, Option)> +``` + +Reads base metadata and `[tool.act]` from `pyproject.toml`. + +### JavaScript + +```rust +pub fn from_json(path: &Path) -> Result<(ComponentInfo, Option)> +``` + +Reads base metadata and top-level `act` from `package.json`. + +## Resolver + +### `resolve` + +```rust +pub fn resolve(dir: &Path) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `dir` | `&Path` | — | Project directory containing `Cargo.toml`, `pyproject.toml`, `package.json`, and or `act.toml`. | + +Merges metadata in this order: + +1. Base language manifest. +2. Inline ACT patch. +3. `act.toml`. + +Returns an error when no recognized manifest is found. + +Example: + +```rust +let info = crate::manifest::resolve(project_dir)?; +``` + +## Capability Validation + +### `validate` + +```rust +pub fn validate(caps: &Capabilities) -> Result<()> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `caps` | `&Capabilities` | — | Capability declaration inside `ComponentInfo.std.capabilities`. | + +Validates: + +- Non-empty filesystem allow paths. +- Valid filesystem glob syntax. +- Non-empty HTTP hosts. +- HTTP schemes restricted to `http` or `https`. + +## Practical Notes + +- The resolver uses merge-patch semantics, not custom deep merge rules. +- `act.toml` can stand alone even when no language-specific manifest exists. +- Validation is designed to fail packaging early rather than allow malformed declarations to reach runtime. + +Related pages: [Component Packaging](/docs/component-packaging) and [API Reference: build pipeline](/docs/api-reference/build-pipeline). diff --git a/docs/codedocs/api-reference/networking-policy.md b/docs/codedocs/api-reference/networking-policy.md new file mode 100644 index 0000000..30f366c --- /dev/null +++ b/docs/codedocs/api-reference/networking-policy.md @@ -0,0 +1,156 @@ +--- +title: "API Reference: networking policy" +description: "Public Rust items in runtime/network.rs, runtime/http_policy.rs, and runtime/http_client.rs that implement host, CIDR, and HTTP request filtering." +--- + +Source files: `act-cli/src/runtime/network.rs`, `act-cli/src/runtime/http_policy.rs`, and `act-cli/src/runtime/http_client.rs` + +## Network Primitives + +```rust +pub struct NetworkRule { + pub host: Option, + pub ports: Option>, + pub cidr: Option, + pub except_ports: Option>, +} +``` + +```rust +pub enum Decision { + Allow, + Deny, +} +``` + +```rust +pub struct NetworkCheck<'a> { + pub host: &'a str, + pub port: u16, + pub resolved_ips: &'a [IpAddr], +} +``` + +### Network functions + +```rust +pub fn cidr_contains(cidr: &str, ip: IpAddr) -> bool +pub fn host_matches(pattern: &str, host: &str) -> bool +pub fn rule_matches(rule: &NetworkRule, check: &NetworkCheck) -> bool +pub fn decide( + mode: PolicyMode, + allow: &[NetworkRule], + deny: &[NetworkRule], + check: &NetworkCheck, +) -> Decision +pub async fn resolve_host(host: &str, port: u16) -> Vec +pub fn any_deny_cidr_matches(deny_rules: &[NetworkRule], ip: IpAddr, port: u16) -> bool +pub async fn first_cidr_deny_hit( + deny_rules: &[NetworkRule], + host: &str, + port: u16, +) -> Option +``` + +### `NetworkCheck` constructors + +```rust +pub fn new(host: &'a str, port: u16) -> Self +pub fn with_resolved(host: &'a str, port: u16, resolved_ips: &'a [IpAddr]) -> Self +``` + +## HTTP Policy Hook + +```rust +pub struct PolicyHttpHooks { /* config + ActHttpClient */ } +``` + +```rust +pub fn new( + config: HttpConfig, + client: Arc, +) -> Self +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `config` | `HttpConfig` | — | Effective HTTP policy for this component instance. | +| `client` | `Arc` | — | Reqwest-backed client used after policy approval. | + +`PolicyHttpHooks` implements both Wasmtime P2 and P3 `WasiHttpHooks`. + +## Reqwest Backend + +```rust +#[derive(Clone)] +pub struct ActHttpClient { /* Arc */ } +``` + +### `ActHttpClient::new` + +```rust +pub fn new(cfg: HttpConfig) -> anyhow::Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `cfg` | `HttpConfig` | — | Effective HTTP policy used to construct redirect and DNS filtering behavior. | + +### `ActHttpClient::send_p2` + +```rust +pub async fn send_p2( + &self, + request: hyper::Request>, + config: wasmtime_wasi_http::p2::types::OutgoingRequestConfig, +) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `request` | `hyper::Request>` | — | Outgoing WASI HTTP request from the guest. | +| `config` | `wasmtime_wasi_http::p2::types::OutgoingRequestConfig` | — | Timeout and TLS behavior from the WASI binding. | + +### `ActHttpClient::send_p3` + +```rust +pub async fn send_p3( + &self, + request: http::Request>, +) -> Result< + ( + http::Response>, + Pin> + Send>>, + ), + P3ErrorCode, +> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `request` | `http::Request>` | — | P3 outgoing request from the guest. | + +## Example + +```rust +let check = crate::runtime::network::NetworkCheck::new("example.com", 443); +let allowed = crate::runtime::network::decide( + crate::config::PolicyMode::Allowlist, + &allow_rules, + &deny_rules, + &check, +); +``` + +```rust +let client = crate::runtime::http_client::ActHttpClient::new(http.clone())?; +let hooks = crate::runtime::http_policy::PolicyHttpHooks::new(http, std::sync::Arc::new(client)); +``` + +## Practical Notes + +- HTTP-layer checks handle scheme and method, then delegate host, port, and CIDR matching to `network.rs`. +- The DNS resolver path matters when rules are CIDR-based, because hostnames must be resolved before the policy can decide which IPs remain valid. +- Redirects are rechecked hop by hop by the custom reqwest redirect policy. + +Related pages: [Runtime Policies](/docs/runtime-policies) and [API Reference: runtime core](/docs/api-reference/runtime-core). diff --git a/docs/codedocs/api-reference/resolve.md b/docs/codedocs/api-reference/resolve.md new file mode 100644 index 0000000..5b26d1b --- /dev/null +++ b/docs/codedocs/api-reference/resolve.md @@ -0,0 +1,77 @@ +--- +title: "API Reference: resolve" +description: "Public Rust items in act-cli/src/resolve.rs that parse component references and resolve them to local cached files." +--- + +Source file: `act-cli/src/resolve.rs` + +## Types + +```rust +pub enum ComponentRef { + Local(PathBuf), + Http(Url), + Oci(String), + Name(String), +} +``` + +`ComponentRef` implements `FromStr` and `Display`. Parsing never fails; unresolved or ambiguous inputs become `Name`. + +## Functions + +### `resolve` + +```rust +pub async fn resolve(component_ref: &ComponentRef, fresh: bool) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `component_ref` | `&ComponentRef` | — | Parsed local, HTTP, OCI, or logical-name reference. | +| `fresh` | `bool` | `false` in most callers | When `true`, bypasses the on-disk cache for remote refs and redownloads the artifact. | + +Returns a local filesystem path to the resolved `.wasm` file. + +Behavior by variant: + +| Variant | Resolution path | +|---------|-----------------| +| `Local` | Verifies the path exists and returns it directly. | +| `Http` | Downloads with `reqwest`, streams to `~/.cache/act/components/.wasm`, and returns the cached path. | +| `Oci` | Pulls the first layer blob from the OCI manifest with `oci-client`, streams it to the same cache, and returns the cached path. | +| `Name` | Returns an error because centralized registry lookup is not implemented yet. | + +## Examples + +Basic HTTP resolution: + +```rust +use std::str::FromStr; + +let component = crate::resolve::ComponentRef::from_str( + "https://example.com/component.wasm" +).unwrap(); +let path = crate::resolve::resolve(&component, false).await?; +``` + +Forced OCI refresh: + +```rust +use std::str::FromStr; + +let component = crate::resolve::ComponentRef::from_str( + "ghcr.io/actpkg/sqlite:0.1.0" +).unwrap(); +let path = crate::resolve::resolve(&component, true).await?; +``` + +## Practical Notes + +- Cache keys are SHA-256 hashes of the original input string, not of the downloaded bytes. +- Progress bars are displayed for downloads through `indicatif`. +- `cmd_pull` in `act-cli/src/main.rs` is the only built-in caller that always sets `fresh = true`. +- Logical names intentionally remain a parsed-but-unresolved state. That lets the command surface reserve the shape now without overloading parse failures with future registry concerns. +- The module is a pure resolution layer. It does not read component metadata, compute policy, or instantiate Wasmtime, which is why callers always hand the returned path off to `runtime::read_component_info` or the runtime bootstrap helpers next. + +Related pages: [Component References](/docs/component-references) and [API Reference: runtime core](/docs/api-reference/runtime-core). diff --git a/docs/codedocs/api-reference/runtime-core.md b/docs/codedocs/api-reference/runtime-core.md new file mode 100644 index 0000000..39cd5e0 --- /dev/null +++ b/docs/codedocs/api-reference/runtime-core.md @@ -0,0 +1,198 @@ +--- +title: "API Reference: runtime core" +description: "Public Rust items in act-cli/src/runtime/mod.rs that create the Wasmtime engine, instantiate the component, and expose the actor API." +--- + +Source file: `act-cli/src/runtime/mod.rs` + +This module is the runtime spine of `act-cli`. It exports the Wasmtime host state, the actor request types, and the helper functions that bootstrap a component instance. + +## Re-exports + +```rust +pub use bindings::*; +pub use act_types::ComponentInfo; +pub use act_types::Metadata; +``` + +`bindings::*` comes from the generated Wasmtime component bindings in `runtime/bindings/mod.rs`, built from `wit/world.wit`. The most important generated item for this module is `ActWorld`. + +## Types + +```rust +pub struct HostState { /* wasi, table, http, fs policy state */ } +``` + +```rust +pub enum ComponentError { + Tool(act::core::types::ToolError), + Internal(anyhow::Error), +} +``` + +```rust +pub enum ComponentRequest { + GetMetadataSchema { + metadata: Metadata, + reply: oneshot::Sender, ComponentError>>, + }, + ListTools { + metadata: Metadata, + reply: oneshot::Sender>, + }, + CallTool { + call: act::core::types::ToolCall, + reply: oneshot::Sender>, + }, + CallToolStreaming { + call: act::core::types::ToolCall, + event_tx: mpsc::Sender, + }, +} +``` + +```rust +pub struct CallToolResult { + pub events: Vec, +} +``` + +```rust +pub enum SseEvent { + Stream(act::core::types::ToolEvent), + Done, + Error(ComponentError), +} +``` + +```rust +pub type ComponentHandle = mpsc::Sender; +``` + +## Functions + +### `create_engine` + +```rust +pub fn create_engine() -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| — | — | — | Creates a Wasmtime engine with component-model and async support enabled. | + +### `load_component` + +```rust +pub fn load_component(engine: &Engine, path: &std::path::Path) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `engine` | `&Engine` | — | The Wasmtime engine created by `create_engine`. | +| `path` | `&std::path::Path` | — | Path to the `.wasm` component on disk. | + +### `create_linker` + +```rust +pub fn create_linker(engine: &Engine) -> Result> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `engine` | `&Engine` | — | Engine used to construct the linker. | + +Builds a linker with WASI P2, WASI P3, policy-aware filesystem bindings, and WASI HTTP bindings. + +### `create_store` + +```rust +pub fn create_store( + engine: &Engine, + preopens: &[crate::runtime::fs_policy::Preopen], + http: &crate::config::HttpConfig, + fs: &crate::config::FsConfig, + info: &ComponentInfo, +) -> Result> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `engine` | `&Engine` | — | Wasmtime engine. | +| `preopens` | `&[crate::runtime::fs_policy::Preopen]` | — | Guest-to-host mount roots derived from filesystem policy. | +| `http` | `&crate::config::HttpConfig` | — | User-resolved HTTP policy before capability intersection. | +| `fs` | `&crate::config::FsConfig` | — | User-resolved filesystem policy before capability intersection. | +| `info` | `&ComponentInfo` | — | Component metadata that provides declared capabilities. | + +Returns a policy-aware store whose `HostState` embeds filesystem and HTTP policy machinery. + +### `read_component_info` + +```rust +pub fn read_component_info(component_bytes: &[u8]) -> Result +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `component_bytes` | `&[u8]` | — | Raw component bytes. | + +Reads the `act:component` custom section and falls back to `version` and `description` custom sections when present. + +### `instantiate_component` + +```rust +pub async fn instantiate_component( + engine: &Engine, + component: &Component, + linker: &Linker, + preopens: &[crate::runtime::fs_policy::Preopen], + http: &crate::config::HttpConfig, + fs: &crate::config::FsConfig, + info: &ComponentInfo, +) -> Result<(ActWorld, Store)> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `engine` | `&Engine` | — | Wasmtime engine. | +| `component` | `&Component` | — | Loaded component. | +| `linker` | `&Linker` | — | Linker with policy-aware bindings. | +| `preopens` | `&[crate::runtime::fs_policy::Preopen]` | — | Preopened directories. | +| `http` | `&crate::config::HttpConfig` | — | User-resolved HTTP policy. | +| `fs` | `&crate::config::FsConfig` | — | User-resolved filesystem policy. | +| `info` | `&ComponentInfo` | — | Metadata for effective-policy calculation. | + +Instantiates the generated `ActWorld` export asynchronously and returns the instance plus store. + +### `spawn_component_actor` + +```rust +pub fn spawn_component_actor(instance: ActWorld, mut store: Store) -> ComponentHandle +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `instance` | `ActWorld` | — | Generated binding to the component's exported world. | +| `store` | `Store` | — | Policy-aware store owned by the actor loop. | + +Starts a Tokio task that receives `ComponentRequest` values and returns a channel sender handle. + +## Example + +```rust +let engine = crate::runtime::create_engine()?; +let component = crate::runtime::load_component(&engine, wasm_path)?; +let linker = crate::runtime::create_linker(&engine)?; +let (instance, store) = crate::runtime::instantiate_component( + &engine, + &component, + &linker, + &preopens, + &http, + &fs, + &info, +).await?; +let handle = crate::runtime::spawn_component_actor(instance, store); +``` + +Related pages: [Component Host Lifecycle](/docs/component-host-lifecycle), [API Reference: filesystem policy](/docs/api-reference/filesystem-policy), and [API Reference: transports](/docs/api-reference/transports). diff --git a/docs/codedocs/api-reference/transports.md b/docs/codedocs/api-reference/transports.md new file mode 100644 index 0000000..d298cf7 --- /dev/null +++ b/docs/codedocs/api-reference/transports.md @@ -0,0 +1,94 @@ +--- +title: "API Reference: transports" +description: "Public Rust items in act-cli/src/http.rs and act-cli/src/mcp.rs that adapt the runtime actor to ACT-HTTP and MCP." +--- + +Source files: `act-cli/src/http.rs` and `act-cli/src/mcp.rs` + +These modules are transport adapters. They do not instantiate components themselves; they take the prepared runtime state and expose it through HTTP or JSON-RPC over stdio. + +## ACT-HTTP + +```rust +pub struct AppState { + pub info: act_types::ComponentInfo, + pub component: runtime::ComponentHandle, + pub metadata: Metadata, +} +``` + +### `create_router` + +```rust +pub fn create_router(state: Arc) -> Router +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `state` | `Arc` | — | Shared component info, actor handle, and default metadata. | + +Returns an Axum router with four routes: + +| Method | Path | Behavior | +|--------|------|----------| +| `GET` | `/info` | Returns `ComponentInfo`. | +| `POST` | `/metadata-schema` | Returns metadata schema or `204 No Content`. | +| `POST` or `QUERY` | `/tools` | Lists tools with optional metadata override. | +| `POST` or `QUERY` | `/tools/{name}` | Calls a tool and optionally streams SSE when `Accept: text/event-stream` is present. | + +Example: + +```rust +let state = std::sync::Arc::new(crate::http::AppState { + info, + component: handle, + metadata, +}); +let router = crate::http::create_router(state); +``` + +## MCP + +### `run_stdio` + +```rust +pub async fn run_stdio( + info: runtime::ComponentInfo, + handle: runtime::ComponentHandle, + metadata: runtime::Metadata, +) -> Result<()> +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `info` | `runtime::ComponentInfo` | — | Component info used for `initialize` responses. | +| `handle` | `runtime::ComponentHandle` | — | Actor handle used for metadata schema, list-tools, and call-tool requests. | +| `metadata` | `runtime::Metadata` | — | Default metadata merged into each request. | + +Runs a line-oriented JSON-RPC server over stdin and stdout. Supported methods are: + +| Method | Behavior | +|--------|----------| +| `initialize` | Returns protocol version, server info, and tool capability declaration. | +| `notifications/initialized` | Ignored. | +| `ping` | Returns an empty success object. | +| `tools/list` | Lists tools and injects `_metadata` into the input schema when metadata schema is available. | +| `tools/call` | Executes a tool and maps ACT content parts into MCP content items. | + +Example request and response: + +```json +{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}} +``` + +```json +{"jsonrpc":"2.0","id":1,"result":{"tools":[...]}} +``` + +## Practical Notes + +- Both adapters convert runtime errors into transport-native error shapes rather than exposing `ComponentError` directly. +- ACT-HTTP exposes streaming through SSE, while MCP currently returns buffered tool results. +- `mcp.rs` injects `_metadata` into tool schemas so MCP clients can pass per-call metadata overrides even though the ACT tool schema does not natively include that property. + +Related pages: [Component Host Lifecycle](/docs/component-host-lifecycle) and [Guides: Serving Components](/docs/guides/serving-components). diff --git a/docs/codedocs/architecture.md b/docs/codedocs/architecture.md new file mode 100644 index 0000000..e445ab9 --- /dev/null +++ b/docs/codedocs/architecture.md @@ -0,0 +1,92 @@ +--- +title: "Architecture" +description: "Understand how act-cli resolves a component, computes policy, instantiates Wasmtime, and exposes ACT over HTTP or MCP." +--- + +The repository is a two-binary Rust workspace. `act` is the runtime host in `act-cli/src`, and `act-build` is the packaging tool in `act-build/src`. Both share the ACT component model defined by the WIT world in `act-cli/wit/world.wit`. + +```mermaid +graph TD + A[CLI entrypoint
act-cli/src/main.rs] --> B[Reference resolution
resolve.rs] + A --> C[Config merge
config.rs] + B --> D[Component bytes] + C --> E[Resolved FS and HTTP policy] + D --> F[Runtime bootstrap
runtime/mod.rs] + E --> F + F --> G[Filesystem gate
runtime/fs_policy.rs + fs_matcher.rs] + F --> H[HTTP gate
runtime/http_policy.rs + http_client.rs] + F --> I[Component actor] + I --> J[Direct CLI call] + I --> K[ACT-HTTP router
http.rs] + I --> L[MCP stdio loop
mcp.rs] + M[act-build/src/main.rs] --> N[Manifest resolution
manifest/mod.rs] + N --> O[Capability validation] + O --> P[Custom section writer
wasm.rs] + P --> Q[Packed component] +``` + +## Module Responsibilities + +- `act-cli/src/main.rs` defines the command surface, parses options, resolves policy and metadata, and wires transports to the runtime. +- `act-cli/src/resolve.rs` turns a local path, HTTP URL, or OCI reference into a local cached `.wasm` file. +- `act-cli/src/config.rs` merges config file state, profile state, and CLI overrides into `FsConfig`, `HttpConfig`, and merged metadata. +- `act-cli/src/runtime/mod.rs` creates the Wasmtime engine, linker, store, and long-lived actor loop that serializes component requests. +- `act-cli/src/runtime/fs_policy.rs` and `act-cli/src/runtime/fs_matcher.rs` enforce the filesystem side of the sandbox. +- `act-cli/src/runtime/http_policy.rs`, `http_client.rs`, and `network.rs` enforce outbound HTTP policy, including redirect and DNS filtering. +- `act-cli/src/http.rs` and `mcp.rs` adapt the same actor API to ACT-HTTP and MCP. +- `act-build/src/manifest/*.rs`, `pack.rs`, `validate.rs`, `skill.rs`, and `wasm.rs` resolve metadata, validate declarations, and write custom sections back into the component. + +## Key Design Decisions + +### Actor-based component access + +`spawn_component_actor` in `act-cli/src/runtime/mod.rs` owns the `Store` and serializes requests over a Tokio channel. That decision matters because Wasmtime component stores are stateful and awkward to share across unrelated async entry points. By routing `call`, HTTP handlers, and MCP messages through the same actor, the code keeps one concurrency boundary and one error-mapping layer. + +### Policy is resolved before instantiation + +`resolve_opts` in `act-cli/src/main.rs` calls `resolve_fs_config`, `resolve_http_config`, and `resolve_metadata` before the component is instantiated. `create_store` in `act-cli/src/runtime/mod.rs` then intersects those grants with the component's declared capabilities through `effective::effective_fs` and `effective::effective_http`. That creates a ceiling model: users cannot grant capabilities the component never declared, and components cannot silently exceed the host policy. + +### Filesystem and HTTP are implemented as separate host adapters + +The filesystem path is enforced by shadowing `wasi:filesystem` bindings with a custom host implementation in `runtime/fs_policy.rs`. HTTP is enforced with `WasiHttpHooks` plus a reqwest-backed client in `runtime/http_policy.rs` and `runtime/http_client.rs`. That split exists because the two capabilities fail differently: filesystem needs path canonicalization and descriptor tracking, while HTTP needs scheme, method, redirect, and DNS-aware filtering. + +### Build-time metadata uses merge-patch instead of a new manifest format + +`act-build` intentionally treats `Cargo.toml`, `pyproject.toml`, `package.json`, and `act.toml` as layered sources in `act-build/src/manifest/mod.rs`. The code applies RFC 7396 merge-patch so project metadata can stay close to the existing build toolchain. This avoids inventing a mandatory new manifest while still letting `act.toml` override everything else. + +## Request and Data Lifecycle + +```mermaid +sequenceDiagram + participant User + participant CLI as act main.rs + participant Resolve as resolve.rs + participant Runtime as runtime/mod.rs + participant Policy as fs/http policy + participant Actor as Component actor + participant Transport as call/http/mcp + + User->>CLI: act call + CLI->>Resolve: resolve(component_ref fresh=false) + Resolve-->>CLI: local wasm path + CLI->>Runtime: read_component_info(bytes) + CLI->>Runtime: create_engine + load_component + create_linker + CLI->>Policy: create_store(fs, http, capabilities) + CLI->>Actor: spawn_component_actor(instance, store) + Transport->>Actor: ComponentRequest::CallTool + Actor->>Actor: provider.call_call_tool(...) + Actor-->>Transport: Tool events or error + Transport-->>User: stdout, JSON, SSE, or MCP response +``` + +The `run` path differs only at the last stage. Instead of immediately sending one `CallTool` request, `main.rs` builds either an `axum::Router` with `http::create_router` or a stdio loop with `mcp::run_stdio`. Both transports reuse the same `ComponentHandle` and the same `ComponentRequest` variants. + +`act-build` has its own lifecycle: + +1. `pack::run` walks up from the `.wasm` file to find a project root. +2. `manifest::resolve` builds `ComponentInfo` from language-native manifests and optional `act.toml`. +3. `manifest::validate::validate` checks filesystem globs and HTTP declarations early. +4. `wasm::set_custom_section` writes `act:component`, optional `act:skill`, and metadata sections back into the component. +5. `validate::run` can later verify the section and the exported `act:core/tool-provider` interface. + +The rest of the docs break those stages down in detail. Start with [Component References](/docs/component-references) if you care about resolution and caching, [Runtime Policies](/docs/runtime-policies) if you care about sandboxing, or [Build Pipeline](/docs/component-packaging) if you care about authoring components. diff --git a/docs/codedocs/component-host-lifecycle.md b/docs/codedocs/component-host-lifecycle.md new file mode 100644 index 0000000..d1ee6f4 --- /dev/null +++ b/docs/codedocs/component-host-lifecycle.md @@ -0,0 +1,129 @@ +--- +title: "Component Host Lifecycle" +description: "Follow a component from bytes on disk into a Wasmtime store, actor loop, and the HTTP or MCP transport adapters." +--- + +The host lifecycle is the path from `ComponentRef` to a running ACT tool provider. It matters because most user-visible behavior in `act` is a thin wrapper over the same runtime actor: direct CLI calls, ACT-HTTP requests, and MCP JSON-RPC messages all converge on `ComponentRequest`. + +## What the Concept Is + +The runtime layer in `act-cli/src/runtime/mod.rs` is responsible for: + +- Creating a Wasmtime engine with component-model and async support. +- Loading the `.wasm` component from disk. +- Building a linker that wires both WASI and policy-aware overrides. +- Creating a store whose `HostState` contains filesystem and HTTP policy state. +- Instantiating the `ActWorld` binding generated from `wit/world.wit`. +- Spawning a Tokio actor that serializes requests into the component instance. + +## How It Relates to Other Concepts + +- It consumes [Component References](/docs/component-references) because it only accepts a local file path. +- It embeds [Runtime Policies](/docs/runtime-policies) in `HostState` before any guest code runs. +- It is exposed externally by the ACT-HTTP and MCP transports covered in the API reference and guides. + +## Internal Logic + +`prepare_component` in `act-cli/src/main.rs` is the orchestration point: + +1. Resolve CLI and config options into `ResolvedOpts`. +2. Resolve the component reference to a local path. +3. Read raw bytes and parse `ComponentInfo` from `act:component`. +4. Derive preopens with `fs_policy::derive_preopens`, then optionally remap them with `apply_mount_root`. +5. Create `Metadata` from merged JSON metadata. +6. Call `create_engine`, `load_component`, `create_linker`, and `instantiate_component`. +7. Create a `ComponentHandle` with `spawn_component_actor`. + +The actor loop is central. It accepts four message variants: + +```rust +pub enum ComponentRequest { + GetMetadataSchema { ... }, + ListTools { ... }, + CallTool { ... }, + CallToolStreaming { ... }, +} +``` + +`CallTool` collects tool events into a `Vec`, while `CallToolStreaming` forwards them through an `mpsc::Sender`. The same ACT tool provider call powers both. + +```mermaid +sequenceDiagram + participant Main as prepare_component + participant Runtime as runtime/mod.rs + participant Store as Store + participant Actor as spawn_component_actor + participant Client as CLI/HTTP/MCP + + Main->>Runtime: create_engine() + Main->>Runtime: load_component(engine, path) + Main->>Runtime: create_linker(engine) + Main->>Runtime: instantiate_component(...) + Runtime-->>Main: (ActWorld, Store) + Main->>Actor: spawn_component_actor(instance, store) + Client->>Actor: ComponentRequest + Actor->>Store: run_concurrent(...) + Store-->>Actor: Tool result or schema + Actor-->>Client: buffered result or stream events +``` + +## Basic Usage + +Start the ACT-HTTP server: + +```bash +act run --http --listen 3000 ./component.wasm +``` + +Internally, `cmd_run` parses the port as `[::1]:3000`, prepares the component, wraps `AppState` in `Arc`, and serves an Axum router from `http::create_router`. + +Call a tool directly: + +```bash +act call ./component.wasm summarize --args '{"text":"hello"}' +``` + +`cmd_call` converts the JSON arguments to CBOR with `act_types::cbor::json_to_cbor`, sends `ComponentRequest::CallTool`, and then prints each returned content part. + +## Advanced and Edge-case Usage + +Run the same component as an MCP server: + +```bash +act run --mcp ./component.wasm +``` + +`cmd_run` uses the same `prepare_component` path, but instead of creating an HTTP router it passes `ComponentInfo`, `ComponentHandle`, and `Metadata` to `mcp::run_stdio`. That loop reads line-delimited JSON-RPC messages, handles `initialize`, `tools/list`, and `tools/call`, and writes one JSON line per response. + +Inspect a component without instantiating it: + +```bash +act info ./component.wasm +``` + +If `--tools` is omitted, `cmd_info` never calls `prepare_component`. It only reads the custom section with `read_component_info`, which is significantly lighter and is useful when the runtime environment for the component is unavailable. + +`act run` requires an explicit transport choice. In `cmd_run`, `--mcp` and `--http` are mutually exclusive, and if neither `--http` nor `--listen` is provided, the command errors instead of guessing a default transport. + + + + +`spawn_component_actor` keeps ownership of `Store` inside one Tokio task. That makes the runtime easier to reason about because every guest call crosses one channel boundary and one `run_concurrent` call. + +The trade-off is that extremely high request concurrency is serialized at the actor boundary, so the host favors correctness and consistent state over maximal throughput. + +For CLI, local automation, and tool-serving workloads, that trade-off is sensible because predictability matters more than squeezing parallelism out of one Wasmtime store. + + + + +The runtime offers both `CallTool` and `CallToolStreaming` because some consumers need a completed result, while ACT-HTTP with `text/event-stream` wants progressive output. The buffered path is simpler and easier to map to stdout or JSON. + +The streaming path reduces latency for long-running tools but adds more coordination: the actor must forward `SseEvent::Stream` items and send a terminal `Done` or `Error`. + +The source keeps both paths in the same actor so they share tool invocation logic even though the delivery strategy differs. + + + + +For transport-specific adapters, continue to [API Reference: transports](/docs/api-reference/transports). diff --git a/docs/codedocs/component-packaging.md b/docs/codedocs/component-packaging.md new file mode 100644 index 0000000..f993748 --- /dev/null +++ b/docs/codedocs/component-packaging.md @@ -0,0 +1,120 @@ +--- +title: "Component Packaging" +description: "Understand how act-build resolves metadata, validates declarations, and writes ACT custom sections back into a compiled WASM component." +--- + +`act-build` is the authoring half of the repository. Where `act` is concerned with runtime behavior, `act-build` is concerned with making a compiled WASM component self-describing. It exists so hosts can rely on embedded metadata instead of out-of-band packaging conventions. + +## What the Concept Is + +The build pipeline centers on three custom sections: + +- `act:component` for serialized `ComponentInfo`. +- `act:skill` for an optional tar archive of a `skill/` directory. +- Standard `version` and `description` sections for convenient fallback metadata. + +`pack::run` in `act-build/src/pack.rs` orchestrates the pipeline. `validate::run` in `act-build/src/validate.rs` verifies that a component has the right metadata and exports the expected `act:core/tool-provider` interface. + +## How It Relates to Other Concepts + +- The resulting `act:component` section is what powers [Runtime Policies](/docs/runtime-policies), because capability declarations live there. +- `act info` and `runtime::read_component_info` depend on the same section to avoid a full instantiation when metadata alone is enough. +- `act skill` depends on `act:skill` being embedded by this build pipeline. + +## Internal Logic + +The metadata resolution order in `act-build/src/manifest/mod.rs` is: + +1. Base metadata from `Cargo.toml`, `pyproject.toml`, or `package.json`. +2. Inline ACT patch from `[package.metadata.act]`, `[tool.act]`, or `act`. +3. Final override from `act.toml`. + +That merged `ComponentInfo` is validated, serialized to CBOR, and then inserted into the component with `wasm::set_custom_section`. `pack::run` also calls `skill::pack_skill_dir`, which requires a `skill/SKILL.md` file if the directory exists. + +```mermaid +flowchart TD + A[Compiled component.wasm] --> B[find_project_dir] + B --> C[manifest::resolve] + C --> D[manifest::validate::validate] + D --> E[serialize ComponentInfo to CBOR] + E --> F[wasm::set_custom_section act:component] + F --> G[write version section] + G --> H[write description section] + H --> I{skill/ exists?} + I -->|yes| J[skill::pack_skill_dir] + J --> K[write act:skill] + I -->|no| L[skip] + K --> M[write file back] + L --> M +``` + +## Basic Usage + +Pack a component after compilation: + +```bash +act-build pack ./target/wasm32-wasip2/release/my_component.wasm +``` + +With a Rust project, `manifest::resolve` first tries `cargo metadata` so workspace-inherited package fields still resolve correctly. If `cargo metadata` is unavailable, it falls back to raw TOML parsing in `manifest/cargo.rs`. + +Validate a packed component: + +```bash +act-build validate ./target/wasm32-wasip2/release/my_component.wasm +``` + +`validate::run` reads the `act:component` custom section, decodes it as `act_types::ComponentInfo`, checks that `std.name` and `std.version` are present, and scans the component export section for a name containing `act:core/tool-provider`. + +## Advanced and Edge-case Usage + +Use `act.toml` to override language-native metadata: + +```toml +[std] +name = "search-component" +version = "1.2.3" +description = "Search tools for internal docs" + +[std.capabilities."wasi:http"] +allow = [{ host = "api.example.com", scheme = "https" }] +``` + +That file is applied last, so it can refine or replace fields discovered from `Cargo.toml`, `pyproject.toml`, or `package.json`. + +Package a skill with the component: + +```text +my-project/ + skill/ + SKILL.md + references/ + usage.md +``` + +When `pack` runs, `skill::pack_skill_dir` archives the entire `skill/` directory and embeds it as `act:skill`. Later, `act skill ./component.wasm` can extract that archive into `.agents/skills//`. + +The inline metadata key names in the current source are `act` for JavaScript, `[tool.act]` for Python, and `[package.metadata.act]` for Rust. If you follow older examples that use `actComponent` or `[package.metadata.act-component]`, the code in `act-build/src/manifest/*.rs` will not pick them up. + + + + +The build pipeline serializes `ComponentInfo` to JSON, applies RFC 7396 merge-patch, and then deserializes the result. That choice makes precedence rules easy to explain and keeps overrides expressive without a bespoke domain-specific language. + +The trade-off is that patches are structural, not semantic: replacing an object or array is blunt and requires care in `act.toml`. + +In practice that is acceptable because the metadata shape is small and the value of a familiar merge model outweighs the need for fine-grained custom merge rules. + + + + +`manifest::validate::validate` catches broken filesystem globs and invalid HTTP schemes before the `.wasm` file is rewritten. That pushes packaging mistakes left and keeps runtime failures focused on execution and policy, not on malformed author metadata. + +The downside is a stricter build pipeline that may reject components authors consider "mostly correct." + +This is a good trade because hosts need the metadata to be trustworthy, and invalid declarations are cheaper to fix in CI than during production execution. + + + + +The API details for this pipeline are split between [Manifest Resolution](/docs/api-reference/manifest-resolution) and [Build Pipeline](/docs/api-reference/build-pipeline). diff --git a/docs/codedocs/component-references.md b/docs/codedocs/component-references.md new file mode 100644 index 0000000..69e8e08 --- /dev/null +++ b/docs/codedocs/component-references.md @@ -0,0 +1,115 @@ +--- +title: "Component References" +description: "Learn how act recognizes local paths, HTTP URLs, OCI references, and unresolved names, then turns them into cached component files." +--- + +Component references are the first abstraction every `act` command relies on. The host accepts one string argument, but `resolve.rs` interprets that string as a local path, HTTP URL, OCI reference, or future logical name. This layer exists so `run`, `call`, `info`, `skill`, and `pull` can all share a single resolution path instead of each reimplementing download logic. + +## What the Concept Solves + +Without a reference abstraction, the CLI would need separate commands for local files, registries, and URLs. `ComponentRef` in `act-cli/src/resolve.rs` centralizes that decision. It also ensures remote downloads are normalized to a local cache entry before the runtime ever sees them, which simplifies component loading and avoids transport-specific branches in `main.rs`. + +## How It Relates to Other Concepts + +- It runs before [Component Host Lifecycle](/docs/component-host-lifecycle), because the runtime only knows how to load a local path. +- It interacts with [Runtime Policies](/docs/runtime-policies) indirectly, because policy decisions happen after the component bytes are available and the `act:component` manifest can be read. +- It also supports [Component Packaging](/docs/component-packaging) workflows, because `act-build validate` expects a local `.wasm` file that may have been obtained with `act pull`. + +## How It Works Internally + +`ComponentRef` is defined as: + +```rust +pub enum ComponentRef { + Local(PathBuf), + Http(Url), + Oci(String), + Name(String), +} +``` + +The `FromStr` implementation in `act-cli/src/resolve.rs` uses three heuristics in order: + +1. Parse as a URL. `http` and `https` become `Http`, while `oci://...` becomes `Oci`. +2. Match an OCI regex. The regex requires a dotted registry host or `localhost` plus a path. +3. Treat strings containing path separators, a `.wasm` suffix, or a leading `.` as `Local`. +4. Fall back to `Name`, which currently errors because centralized registry lookup is not implemented. + +`resolve(component_ref, fresh)` then turns the enum into a local `PathBuf`. Local paths are checked with `tokio::fs::try_exists`. HTTP references stream through `reqwest::get`, while OCI references use `oci-client` to fetch the manifest and stream the first layer blob into the cache. Both remote branches use a SHA-256-derived filename under `~/.cache/act/components`. + +```mermaid +flowchart TD + A[Raw CLI string] --> B{URL parse?} + B -->|http/https| C[Http] + B -->|oci://| D[Oci] + B -->|no| E{OCI regex match?} + E -->|yes| D + E -->|no| F{Looks like path?} + F -->|yes| G[Local] + F -->|no| H[Name] + C --> I[Cache by SHA-256] + D --> I + G --> J[Use path directly] + H --> K[Error: lookup not implemented] +``` + +## Basic Usage + +Use an OCI component directly: + +```bash +act info --tools ghcr.io/actpkg/sqlite:0.1.0 +``` + +That call parses the value as `ComponentRef::Oci`, downloads the layer if needed, caches it, and then hands the cached file to `runtime::read_component_info`. + +Use a local artifact during development: + +```bash +act run --http ./target/wasm32-wasip2/release/my_component.wasm +``` + +That path becomes `ComponentRef::Local`, so no cache lookup or download is needed. + +## Advanced and Edge-case Usage + +Force a fresh download with `pull`: + +```bash +act pull -O ghcr.io/actpkg/sqlite:0.1.0 +``` + +`cmd_pull` in `act-cli/src/main.rs` always calls `resolve(&reference, true)`, which bypasses the cache and redownloads the remote artifact before copying it to the target path. + +Extract a skill from an HTTP-hosted component: + +```bash +act skill https://example.com/components/search.wasm --output ./.agents/skills/search +``` + +The `skill` command still uses the same resolver. Once the bytes are local, `cmd_skill` scans top-level custom sections until it finds `act:skill` and unpacks the tar archive to the output directory. + +A bare component name like `sqlite` currently parses successfully but still fails at resolution time because `ComponentRef::Name` is reserved for a future registry lookup path. Use a local path, HTTP URL, or OCI reference today. + + + + +The runtime only needs a local path once resolution is complete, so normalizing remote inputs to disk keeps the Wasmtime bootstrap path simple. It also means `info`, `call`, `run`, and `skill` all benefit from the same cache key and download behavior without duplicating transport-specific setup. + +The trade-off is that a remote reference is not fully ephemeral: it leaves a cache entry in `~/.cache/act/components`, which is desirable for repeatability but something operators should understand when debugging version drift. + +The explicit `fresh` flag in `resolve` gives `pull` a way to bypass that cache when the user wants a deliberate re-download. + + + + +The source chooses convenience over strictness because the common path should be one positional argument that developers can paste from a README or a registry listing. `FromStr` makes the ergonomic path work for most inputs, including `oci://` prefixed values and plain registry references. + +The downside is that borderline strings can be surprising, especially bare names that are accepted syntactically but not implemented semantically. + +If you need deterministic behavior in automation, use values that are unambiguous, such as `https://...`, `ghcr.io/...`, or an explicit filesystem path. + + + + +For the exact public API of this layer, continue to [API Reference: resolve](/docs/api-reference/resolve). diff --git a/docs/codedocs/guides/configuring-policies-and-profiles.md b/docs/codedocs/guides/configuring-policies-and-profiles.md new file mode 100644 index 0000000..0be85a4 --- /dev/null +++ b/docs/codedocs/guides/configuring-policies-and-profiles.md @@ -0,0 +1,86 @@ +--- +title: "Configuring Policies and Profiles" +description: "Create repeatable runtime policy profiles and understand when CLI overrides replace config-file settings." +--- + +This guide focuses on the configuration merge path implemented in `act-cli/src/config.rs`. The goal is to get deterministic runtime behavior without rewriting a long command line every time you call a component. + + + +### Create a config file + +By default, `load_config` looks for `~/.config/act/config.toml`. Create that file with one or more reusable profiles: + +```toml +log-level = "info" + +[profile.sqlite.metadata] +database_path = "/workspace/data/app.db" + +[profile.sqlite.policy.filesystem] +mode = "allowlist" +allow = ["/workspace/data/**"] + +[profile.sqlite.policy.http] +mode = "deny" + +[profile.fetcher.policy.http] +mode = "allowlist" +allow = [ + { host = "example.com", scheme = "https" }, + { host = "api.example.com", scheme = "https", methods = ["GET", "POST"] } +] +``` + +Profiles are keyed by name in `ConfigFile.profile`, and `get_profile` returns one by exact string match. + + +### Run a component with a profile + +Use the profile name on any command that accepts `CommonOpts`: + +```bash +act call ./sqlite_component.wasm query \ + --profile sqlite \ + --args '{"sql":"SELECT name FROM sqlite_master"}' +``` + +The host will merge profile metadata into the outgoing `Metadata` value and will resolve filesystem and HTTP policy from the profile first, then from top-level config if the profile lacks a policy section. + + +### Override the profile intentionally + +CLI overrides win. If you pass any filesystem override flag, `resolve_fs_config` stops using the config file and builds a fresh `FsConfig` from CLI state only. + +```bash +act call ./sqlite_component.wasm query \ + --profile sqlite \ + --fs-policy allowlist \ + --fs-allow '/workspace/data/**' \ + --fs-deny '/workspace/data/private/**' \ + --args '{"sql":"SELECT 1"}' +``` + +That behavior is deliberate. The code in `resolve_fs_config` and `resolve_http_config` treats CLI flags as a full override layer, not as an additive patch on top of profile values. + + +### Add metadata per invocation + +Profile metadata and CLI metadata are merged as JSON objects, with CLI keys winning on collisions: + +```bash +act call ./service_component.wasm invoke \ + --profile fetcher \ + --metadata '{"tenant":"staging","timeout_ms":5000}' \ + --args '{"operation":"status"}' +``` + +`resolve_metadata` ignores non-object JSON values. If you pass a string or array to `--metadata`, the merge result becomes empty rather than partially applied. + + + +Real-world pattern: keep broad defaults in the config file, then use CLI overrides only when you need a temporary narrowing or a one-off open policy for debugging. That keeps operator intent visible and makes it clear when a command is deviating from the normal policy envelope. + +The main pitfall is assuming CLI overrides merge with profile rules. They do not. Once `CliPolicyOverrides::any_fs_override()` or `any_http_override()` is true, the corresponding config side is rebuilt from CLI values. If you need the profile's allowlist plus one extra CLI allow rule, you currently have to restate the profile rules on the command line or move that broader rule into the profile itself. + +For the exact types and functions involved, see [API Reference: config](/docs/api-reference/config). diff --git a/docs/codedocs/guides/packing-and-validating-components.md b/docs/codedocs/guides/packing-and-validating-components.md new file mode 100644 index 0000000..b662be4 --- /dev/null +++ b/docs/codedocs/guides/packing-and-validating-components.md @@ -0,0 +1,85 @@ +--- +title: "Packing and Validating Components" +description: "Turn a compiled component into a self-describing ACT artifact with embedded metadata and optional skills." +--- + +This guide walks through a typical `act-build` workflow. The target audience is a component author or build engineer who wants hosts to discover metadata, capability declarations, and skills directly from the `.wasm` artifact. + + + +### Put metadata in the project manifest + +For a Rust project, start with `Cargo.toml` and add an inline ACT section: + +```toml +[package] +name = "docs-search" +version = "0.1.0" +description = "Search tools for docs" +edition = "2024" + +[package.metadata.act.std.capabilities."wasi:http"] +allow = [{ host = "api.example.com", scheme = "https" }] + +[package.metadata.act.std.capabilities."wasi:filesystem"] +allow = [{ path = "/workspace/cache/**", mode = "rw" }] +``` + +The manifest reader in `act-build/src/manifest/cargo.rs` extracts the package fields into `ComponentInfo.std.*` and passes the inline `act` object back as a merge patch. + + +### Add a final override with act.toml when needed + +Use `act.toml` for deployment-specific or workspace-external metadata: + +```toml +[std] +name = "docs-search" +version = "0.1.0" +description = "Hosted search for docs and changelogs" + +[extra] +"std:skill" = "Use this component for search-oriented workflows." +``` + +`manifest::resolve` applies `act.toml` after the language-specific manifest, so this is the highest-priority override layer. + + +### Embed an optional skill + +If the component should ship with an agent skill, add a `skill/` directory: + +```text +skill/ + SKILL.md + references/ + usage.md +``` + +Then run: + +```bash +act-build pack ./target/wasm32-wasip2/release/docs_search.wasm +``` + +`skill::pack_skill_dir` requires `skill/SKILL.md` to exist. If the directory exists without that file, `pack` fails instead of embedding a partial archive. + + +### Validate the result + +Run validation before publishing: + +```bash +act-build validate ./target/wasm32-wasip2/release/docs_search.wasm +act info --tools ./target/wasm32-wasip2/release/docs_search.wasm +``` + +`validate::run` checks for `act:component`, decodes it as CBOR, verifies `std.name` and `std.version`, and confirms the component exports `act:core/tool-provider`. The follow-up `act info` call is useful because it exercises the runtime-side metadata reader that other hosts will rely on. + + + +Problem solved: the final `.wasm` file becomes the delivery artifact, not a loose collection of separate metadata files. That is important for registries and OCI publishing, because hosts pulling the component later only need the binary. + +One practical caution: `pack` rewrites the target file in place. If your build pipeline produces immutable artifacts or content-addressed filenames, copy the component before packing it or write the pack step into the artifact pipeline before digest calculation. Also note that `validate` does not confirm that runtime behavior matches the declared capability set. It confirms packaging integrity, not semantic correctness of the component's implementation. + +Continue with [Component Packaging](/docs/component-packaging) for the conceptual model or [API Reference: build pipeline](/docs/api-reference/build-pipeline) for the low-level functions. diff --git a/docs/codedocs/guides/serving-components.md b/docs/codedocs/guides/serving-components.md new file mode 100644 index 0000000..a98d480 --- /dev/null +++ b/docs/codedocs/guides/serving-components.md @@ -0,0 +1,95 @@ +--- +title: "Serving Components" +description: "Host one ACT component over HTTP or MCP and understand the request path from client input to the runtime actor." +--- + +This guide shows the two serving modes exposed by `act run`: ACT-HTTP over a socket and MCP over stdio. Both are thin wrappers around the same prepared component and `ComponentHandle`, so the practical choice is about client compatibility, not about runtime capability. + + + +### Inspect the component first + +Use `info --tools` before you expose a component. It tells you whether the component has a metadata schema, what tool names exist, and whether the transport choice needs extra policy grants. + +```bash +act info --tools ./target/wasm32-wasip2/release/my_component.wasm +``` + +If the output lists tools but no metadata schema, the server can often start without `--metadata`. If the component declares filesystem or HTTP capabilities, decide those policy flags before you move on. + + +### Serve over ACT-HTTP + +Start the server on a predictable local port: + +```bash +act run \ + --http \ + --listen 3000 \ + --fs-policy allowlist \ + --fs-allow '/workspace/data/**' \ + ./target/wasm32-wasip2/release/my_component.wasm +``` + +Then call it from another terminal: + +```bash +curl -s http://[::1]:3000/info +curl -s -X POST http://[::1]:3000/tools/summarize \ + -H 'content-type: application/json' \ + -d '{"arguments":{"text":"ACT keeps one actor per component"}}' +``` + +The first request maps to `get_info` in `act-cli/src/http.rs`. The second becomes a `ComponentRequest::CallTool` and returns buffered JSON unless the client asks for `text/event-stream`. + + +### Serve over MCP stdio + +Use MCP when the client expects JSON-RPC over stdin and stdout: + +```bash +act run --mcp ./target/wasm32-wasip2/release/my_component.wasm +``` + +A minimal `initialize` request looks like this: + +```json +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} +``` + +`mcp::run_stdio` in `act-cli/src/mcp.rs` reads one line at a time, dispatches `initialize`, `tools/list`, and `tools/call`, and emits one JSON line per response. Unknown `notifications/*` methods are ignored, which matches MCP expectations. + + +### Enable streaming when the tool emits incremental events + +For ACT-HTTP, request SSE explicitly: + +```bash +curl -N -X POST http://[::1]:3000/tools/generate \ + -H 'accept: text/event-stream' \ + -H 'content-type: application/json' \ + -d '{"arguments":{"prompt":"Write a release note"}}' +``` + +The HTTP layer switches to `call_tool_sse`, which creates a `ComponentRequest::CallToolStreaming`. The runtime forwards `ToolEvent::Content` items as SSE `content` events and finishes with `done`. + + + +```mermaid +sequenceDiagram + participant Client + participant HTTP as http.rs or mcp.rs + participant Actor as ComponentHandle + participant Guest as ACT component + + Client->>HTTP: request or JSON-RPC message + HTTP->>Actor: ComponentRequest + Actor->>Guest: call_list_tools / call_call_tool + Guest-->>Actor: events + Actor-->>HTTP: buffered result or stream items + HTTP-->>Client: JSON, SSE, or JSON-RPC response +``` + +Problem solved: one component can serve web clients and agent clients without duplicating runtime setup. The important operational detail is that both transports still depend on the same policy model. If a served tool needs outbound HTTP or filesystem access, set the policy flags when starting `act run`, not later at request time. + +For deeper details, see [Component Host Lifecycle](/docs/component-host-lifecycle) and [API Reference: transports](/docs/api-reference/transports). diff --git a/docs/codedocs/index.md b/docs/codedocs/index.md new file mode 100644 index 0000000..79b766c --- /dev/null +++ b/docs/codedocs/index.md @@ -0,0 +1,102 @@ +--- +title: "Getting Started" +description: "Use /actcore/act-cli to resolve, inspect, host, call, package, and validate ACT WebAssembly components." +--- + +/actcore/act-cli hosts ACT WebAssembly components with `act` and post-processes them with `act-build`. + +## The Problem + +- Shipping tool components as raw `.wasm` files is not enough if developers still need bespoke loaders, transport adapters, and caching. +- Sandboxing is easy to get wrong. Filesystem and outbound HTTP access need a deny-by-default model with explicit, auditable grants. +- Component metadata tends to drift across `Cargo.toml`, `pyproject.toml`, `package.json`, and ad hoc build scripts. +- The same component often needs to be used three ways: as a one-off CLI call, as an HTTP server, and as an MCP server. + +## The Solution + +`act` turns a local path, HTTP URL, or OCI reference into a runnable ACT host, while `act-build` embeds normalized metadata and optional skills directly into the compiled component. + +```bash +act info --tools ghcr.io/actpkg/sqlite:0.1.0 +act call --fs-policy open ghcr.io/actpkg/sqlite:0.1.0 query \ + --args '{"sql":"SELECT sqlite_version()"}' +act-build pack ./target/wasm32-wasip2/release/my_component.wasm +``` + +The host path is implemented in `act-cli/src/main.rs`, `act-cli/src/resolve.rs`, and `act-cli/src/runtime/mod.rs`. The build path lives in `act-build/src/manifest/mod.rs` and `act-build/src/pack.rs`. + +## Installation + +" "bun"]}> + + +```bash +npm install -g @actcore/act @actcore/act-build +``` + + + + +```bash +pnpm add -g @actcore/act @actcore/act-build +``` + + + + +```bash +yarn global add @actcore/act @actcore/act-build +``` + + + + +```bash +bun add -g @actcore/act @actcore/act-build +``` + + + + +Cargo, PyPI, Docker, and GitHub Releases are also supported distribution paths according to the workspace manifests in `Cargo.toml`, `act-cli/pyproject.toml`, and `act-build/pyproject.toml`. + +## Quick Start + +The smallest working flow is: inspect a component, then call a tool directly. + +```bash +act info --tools ghcr.io/actpkg/sqlite:0.1.0 +act call --fs-policy open ghcr.io/actpkg/sqlite:0.1.0 query \ + --args '{"sql":"SELECT sqlite_version()"}' +``` + +Expected output: + +```text +# sqlite v0.1.0 +... +## query +... +3.46.0 +``` + +What happens internally: + +1. `act info` resolves the OCI reference into the cache with `resolve::resolve`. +2. `runtime::read_component_info` reads the `act:component` custom section without instantiating the component. +3. `act call` instantiates the component, converts JSON arguments to CBOR, and sends a `ComponentRequest::CallTool` message to the actor loop. + +## Key Features + +- One CLI for `run`, `call`, `info`, `skill`, and `pull`. +- Deny-by-default filesystem and HTTP policy resolution with profile support. +- ACT-HTTP and MCP transports backed by the same component actor. +- OCI, HTTP, and local path resolution with on-disk caching. +- Build-time metadata merge-patching from Rust, Python, JavaScript, or `act.toml`. +- Optional `act:skill` packaging alongside standard WASM custom sections. + + + See how resolution, policy, runtime, and transports fit together. + Learn the concepts you need before extending or deploying the tools. + Inspect every public Rust type and function exported by the source modules. + diff --git a/docs/codedocs/runtime-policies.md b/docs/codedocs/runtime-policies.md new file mode 100644 index 0000000..ad3fb97 --- /dev/null +++ b/docs/codedocs/runtime-policies.md @@ -0,0 +1,150 @@ +--- +title: "Runtime Policies" +description: "See how act merges config, profiles, CLI overrides, and declared capabilities into effective filesystem and HTTP policy." +--- + +Runtime policy is the core safety mechanism in `act`. The host never instantiates a component with unrestricted ambient access unless the user asks for it and the component has declared the capability in its embedded metadata. This concept exists to make the host useful in automation and agent environments where a silent filesystem or network escape is unacceptable. + +## What the Concept Is + +There are two policy families: + +- Filesystem policy, represented by `FsConfig` and enforced through `FsMatcher` plus a custom `wasi:filesystem` host implementation. +- HTTP policy, represented by `HttpConfig` and enforced through `PolicyHttpHooks`, `ActHttpClient`, redirect checks, and DNS filtering. + +Both start with the same enum: + +```rust +pub enum PolicyMode { + Deny, + Allowlist, + Open, +} +``` + +`config.rs` resolves user intent. `runtime/effective.rs` then intersects that intent with the component's declared capabilities from the `act:component` section. The result is a ceiling model: the effective policy can only stay the same or get narrower. + +## How It Relates to Other Concepts + +- It depends on [Component Packaging](/docs/component-packaging), because the capability declaration comes from build-time metadata. +- It feeds directly into [Component Host Lifecycle](/docs/component-host-lifecycle), because `create_store` computes effective policy before building `HostState`. +- It constrains [Component References](/docs/component-references) only after the bytes are resolved and the manifest can be read. + +## Internals Walkthrough + +`resolve_opts` in `act-cli/src/main.rs` does four important things: + +1. Loads `ConfigFile` with `load_config`. +2. Selects an optional profile with `get_profile`. +3. Applies CLI overrides through `resolve_fs_config` and `resolve_http_config`. +4. Merges profile metadata and CLI metadata with `resolve_metadata`. + +`create_store` in `act-cli/src/runtime/mod.rs` then calls: + +```rust +let effective_fs = crate::runtime::effective::effective_fs(fs, &info.std.capabilities).config; +let effective_http = + crate::runtime::effective::effective_http(http, &info.std.capabilities).config; +``` + +The filesystem side uses `FsMatcher::compile` from `runtime/fs_matcher.rs` plus `PolicyFilesystemCtxView` from `runtime/fs_policy.rs`. Paths are canonicalized, checked against allow and deny globs, and then recorded per descriptor. The HTTP side uses `PolicyHttpHooks::decide_uri` from `runtime/http_policy.rs` and a reqwest DNS resolver in `runtime/http_client.rs` to enforce host, CIDR, scheme, method, and redirect decisions. + +```mermaid +flowchart TD + A[Config file] --> D[resolve_fs_config / resolve_http_config] + B[Profile] --> D + C[CLI flags] --> D + D --> E[User policy] + F[Component capabilities] --> G[effective_fs / effective_http] + E --> G + G --> H[Effective policy] + H --> I[FsMatcher + PolicyFilesystem] + H --> J[PolicyHttpHooks + ActHttpClient] +``` + +## Basic Usage + +Use a named profile for repeated local development: + +```toml +# ~/.config/act/config.toml +log-level = "debug" + +[profile.local.policy.filesystem] +mode = "allowlist" +allow = ["/workspace/**"] + +[profile.local.policy.http] +mode = "allowlist" +allow = [{ host = "api.example.com", scheme = "https" }] + +[profile.local.metadata] +database_path = "/workspace/tmp/app.db" +``` + +```bash +act call ./component.wasm query --profile local --args '{"sql":"SELECT 1"}' +``` + +Open HTTP access for a single command while keeping filesystem closed: + +```bash +act call \ + --http-policy open \ + --fs-policy deny \ + ./component.wasm fetch \ + --args '{"url":"https://example.com"}' +``` + +## Advanced and Edge-case Usage + +Use a deny carve-out inside an allowlist filesystem policy: + +```bash +act call \ + --fs-policy allowlist \ + --fs-allow '/workspace/**' \ + --fs-deny '/workspace/**/.env' \ + ./component.wasm inspect +``` + +That maps to `FsConfig { mode: Allowlist, allow, deny }`, and `FsMatcher::decide` checks deny globs before allow globs. + +Use host and CIDR rules together for HTTP: + +```bash +act call \ + --http-policy allowlist \ + --http-allow example.com \ + --http-deny 0.0.0.0/0 \ + --http-deny ::/0 \ + ./component.wasm fetch \ + --args '{"url":"https://example.com"}' +``` + +This intentionally fails. The integration test in `act-cli/tests/http_policy_e2e.rs` demonstrates that the DNS resolver removes all resolved IPs, which the client surfaces as a DNS-related failure. + +The older `--allow-dir` style flags shown in the repo skill markdown are not the current interface. The integration tests in `act-cli/tests/config_integration.rs` explicitly verify that legacy `--allow-dir` is rejected and that the supported flags are `--fs-policy`, `--fs-allow`, `--fs-deny`, `--http-policy`, `--http-allow`, and `--http-deny`. + + + + +The source defaults filesystem policy to `Deny` and HTTP policy to the default `HttpConfig`, which also resolves to deny behavior unless explicit allow rules or `open` mode are provided. That makes the secure path the accidental path, which is the right choice for agent-style automation. + +The trade-off is friction during local development: components that touch the network or filesystem will fail until you add grants. + +The repository chooses that friction intentionally because debugging a denied operation is cheaper than auditing a silent escape after the fact. + + + + +`effective_fs` and `effective_http` in `runtime/effective.rs` do not trust user policy alone. If a component never declares `wasi:http`, then even `--http-policy open` collapses to `Deny` for that invocation. + +This prevents host-side configuration drift from accidentally broadening a component past what its package metadata said it needed. + +The cost is that poorly declared components are frustrating to use until they are rebuilt with correct metadata, but that pressure is exactly what keeps the build and runtime models aligned. + + + + +The next page, [Component Host Lifecycle](/docs/component-host-lifecycle), shows where those effective policies are injected into Wasmtime.