Repository navigation
Add trusted in-process dynamic modules - #5
Conversation
Add the `tinybus-module` crate as a workspace member and introduce the `modules` feature flag for loading in-process cdylib modules. This change lays the groundwork for trusted, in-process module support while keeping the module-side C ABI versioned in lockstep with the host to prevent definition drift. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
This change introduces the tinybus-module crate with its Cargo.toml and lib.rs files, providing the foundational structure for the module system. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Changed the tinybus dependency in tinybus-module from a workspace reference with default-features disabled to an explicit path and version specification. This ensures the dependency resolves correctly when the crate is used outside the workspace context. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new example demonstrating how to use the tinybus library with a clock module, providing users with a practical reference for implementing time-based functionality in their applications. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The lock file is updated to include the new `tinybus-module` crate, which is now a dependency of the main `tinybus` package. This addition enables the module system functionality within the project. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ailures Introduce a new error variant to represent when a dynamic module is rejected by a fixed admission rule, along with a constructor that sanitizes the artifact path to a safe basename. This allows the host to report refusal reasons without leaking untrusted path or descriptor bytes. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Added a new `sanitize_untrusted` function that filters untrusted input to only allow ASCII alphanumeric characters and a small set of safe punctuation, while capping the result at 32 characters. This provides a reusable utility for producing log-safe strings from potentially malicious or malformed data. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add the transport.rs file to the module directory, which was previously untracked and missing from the crate. This resolves a build error caused by the absent source file. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
When a module file is present in the filesystem but not tracked by the module registry, the loader now treats it as a valid untracked module rather than failing. This allows external tools to drop module files into the monitored directory without requiring prior registration. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The module host file was previously untracked, causing it to be ignored during builds and runtime operations. This change adds the file to version control and ensures it is properly included in the module system. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…t style Reformatted multi-line conditional expressions and error function calls across several modules to use a consistent brace style, improving readability without changing any behavior. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test module covering module validation scenarios, including rejection of modules compiled with panic=abort, handling of mismatched rustc versions in permissive versus strict mode, and an integration test for loading a real cdylib and making a remote procedure call. These tests improve coverage of the module host's validation logic and ensure that the host correctly enforces safety constraints. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Moved the `Connection` import to maintain alphabetical ordering among the crate imports in the test module, keeping the codebase consistent with the project's import style conventions. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test for connecting to the clock proxy was failing intermittently because it tried to create the proxy before the clock service had registered its name. Added a polling loop with a two-second timeout that waits until the clock name appears in the list of available names before proceeding. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a `ModuleControl` trait that exposes the host's module management operations through a weak pointer, allowing the broker to list, load, stop, enable, and rescan modules without holding a strong reference to the host. Add an `enabled` field to `ModuleInfo` so that modules can be marked as disabled during discovery, and track registered directories to support future rescanning. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The strict flag in ModuleHost is changed from a plain bool to an AtomicBool so it can be read without a full lock, and the strict setter no longer requires mutable access to the inner Arc. A new stop_sync method is extracted from the async close implementation to allow synchronous callers to shut down the transport without needing an async context. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a full implementation of the ModuleControl trait on ModuleHostInner, providing methods to list, load, stop, enable, and rescan modules. This enables external control of module lifecycle through a consistent interface, replacing ad-hoc access to the inner state. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a `modules` field to the Broker that holds a weak reference to a ModuleControl, along with a setter and a dispatch method that handles module-related RPC calls. This allows the broker to forward requests such as ListModules, LoadModule, and StopModule to the module host when the "modules" feature is enabled, without affecting the core broker logic when the feature is absent. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The `host` module was previously private, which prevented other modules within the crate from accessing it when the `modules` feature is enabled. Changing its visibility to `pub(crate)` allows internal crate access while keeping it hidden from external consumers. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat chained method calls in the broker and module host to use consistent indentation, placing each method call on its own line. This improves readability and aligns with the project's coding style without changing any behavior. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…modules Add a new `modules` subcommand to the tinybus CLI that provides operations for managing trusted in-process modules, including listing, showing details, scanning, loading, stopping, enabling, disabling, and running diagnostics. This enables operators to inspect and control the module lifecycle at runtime. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The combined match arm for Enable and Disable commands was split into two distinct arms to improve code clarity and remove the conditional logic that determined the enabled state. Each command now directly passes the appropriate boolean value to the EnableModule call. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…n signature Reordered the printed fields in the module listing to show the manifest after the rustc version, and collapsed the `run_modules` function signature onto a single line for improved readability. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…send The host_send callback is now wrapped in catch_unwind to prevent panics from crossing the FFI boundary, returning TB_BACKPRESSURE on unwind. The inner send path was changed from try_send to blocking_send so that backpressure is applied only to the calling module, ensuring reliable completion without adding a new callback to the frozen v1 ABI. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a custom panic hook to the module runtime that captures the panic location, logs it via the host's log interface, and then triggers a host fault. This ensures that when a module panics, the host receives a structured diagnostic message containing the file, line, and column of the panic, while deliberately omitting the panic payload to avoid leaking sensitive data. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The module_export macro now accepts optional provides, requires, optional, and lazy parameters, allowing modules to declare their interface dependencies and capabilities at compile time. A static OnceLock caches the serialized manifest bytes for the process lifetime, replacing the previous hardcoded empty manifest. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Collapse the unsafe block in the `log` method into a single line to improve readability without changing any behaviour. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add the trusted in-process module ABI to the architecture documentation, update the roadmap to reflect that module loading is now the M3 milestone, and extend the wire protocol with module management methods. The module system lets the broker load selected integrations as cdylib libraries behind a non-default feature flag, with a versioned C-shaped ABI that rejects incompatible artifacts before initialization. The protocol gains Announce, GetManifest, ListPeers, and module control methods while keeping the wire format at version 1 since old peers can still parse every message. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new CI job that builds and tests the module loader on all three platforms, and extend the existing portability job to test modules without the socket transport. This ensures the in-process module system, which is portable across Unix and Windows, is exercised in CI alongside the existing Unix-only socket adapter. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The LoadModule RPC now accepts an optional second argument containing a JSON configuration object that is passed to the module's setup function. This allows modules to receive initialization parameters without requiring separate configuration files or environment variables. The change also extends the host ABI with a config field that provides the serialized configuration bytes to the module during initialization. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The module vtable is now wrapped in a SendModuleVtable newtype that implements Send, matching the existing pattern used for the host vtable. This ensures the vtable can be safely transferred across async task boundaries during module initialization. Additionally, a redundant mut qualifier is removed from a lock guard and a clone is added to the message delivery path to avoid a move-related ownership issue. Auto-committed-on: dragonfly
Move the construction of SendHostVtable outside of the closure passed to spawn_blocking to ensure the host vtable pointer is captured by value before the blocking task begins, preventing a potential use-after-free if the original host reference becomes invalid during the timeout. Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
…ions Replaced repeated string comparisons against member names with a single match to an enum, improving readability and reducing the risk of typos. The StopModule path now returns an explicit error instead of relying on an unreachable branch, making the control flow clearer. Auto-committed-on: dragonfly
The `matches!` macro was consuming the `old` variable by value, which prevented the state from being used after the check. By borrowing with `&old`, the state is now inspected without moving it, allowing subsequent operations to proceed correctly. Auto-committed-on: dragonfly
The unreachable panic was removed because the preceding match arms already cover all valid module members, making the fallback case dead code that would never be reached in practice. Auto-committed-on: dragonfly
Updated the module documentation guide to specify that design references should be a focused README when the rationale exceeds what source rustdoc can convey, otherwise the module-level rustdoc itself suffices. This clarifies the documentation strategy and removes ambiguity about when to create separate README files. Auto-committed-on: dragonfly
Add two new tests: one verifying that an invalid manifest declaration returns a null slice instead of panicking, and another confirming that the module refusal error sanitizes untrusted reason strings by stripping spaces and path separators. Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
…nsistency Reformat the large match expression in the broker's dispatch method to reduce nesting depth, and collapse several multi-line function signatures and call sites across the module host, transport, and test files to fit within the project's preferred line length. No behaviour is changed. Auto-committed-on: dragonfly
The error message for module refusal now sanitizes each word individually instead of treating the entire reason as a single token, so that spaces in the original reason are preserved and each word is independently cleaned of untrusted content. Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
… disabled When the `modules` feature is not enabled, the `module_states` variable is unused because the loop that consumes it is conditionally compiled out. Adding a `let _ = module_states` assignment under the `not(feature = "modules")` guard silences the compiler warning without changing runtime behaviour. Auto-committed-on: dragonfly
Adds assertions to the existing test for stopping one module to confirm that attempting to re-enable a stopped module produces a ModuleUnavailable error and that the module remains in the Stopped state. Auto-committed-on: dragonfly
When the module transport fails to drain its queue within the deadline, the transport now marks the context as faulted, notifies waiters, and clears the inbound channel. This ensures that any pending or future operations on a stalled transport are properly terminated rather than left in an inconsistent state. Auto-committed-on: dragonfly
…ect-only error handling Replace two instances of `map_err` that only performed a side effect and re-raised the same error with `inspect_err`, which is the idiomatic way to observe an error without transforming it. This removes the unnecessary error re-wrapping and makes the intent clearer. Auto-committed-on: dragonfly
The CI workflow now copies test modules to a private directory with restricted permissions to simulate real-world deployment conditions. On the host side, the module validation function uses `symlink_metadata` on Windows instead of `metadata` to correctly handle files that may be accessed through directory-level access control, and defers the file open call until after the metadata check to avoid permission errors on locked directories. Auto-committed-on: dragonfly
Replace the legacy `New-Object` calls with the modern PowerShell class constructor syntax for `DirectorySecurity` and `FileSystemAccessRule` in the CI workflow. This improves consistency with current PowerShell practices and avoids potential compatibility issues with newer PowerShell versions. Auto-committed-on: dragonfly
…ile test The test for skipping non-regular files was creating a temporary directory in the current working directory, which could fail in environments where the current directory is not writable. The change now creates the temporary directory relative to the module artifact's parent path, ensuring the test works reliably regardless of the working directory. Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
Reformatted a long line in the test helper to improve code readability without changing any behaviour. Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/tinybus/src/bin/tinybus.rs (1)
330-367: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy liftAuthorization Bypass (CWE-862): Missing Authorization
Reachability: External
Reachability path
● Entry crates/tinybus/src/module/host_test.rs:289 stopping_one_module_leaves_the_other_serving │ ▼ ● Sink crates/tinybus/src/bin/tinybus.rsAdd caller authorization before module-control RPCs.
module_methoddispatches calls toai.tinyhumans.tinybus.Busdirectly intoModuleControlwithout any caller, peer, or policy check.LoadModule/RescanModulescan load and initialize acdylibin the host process, andStopModule/EnableModulecan change module lifecycle independently of the CLI. Artifact validation only authorizes the module path/content, not the RPC caller. Add a non-bypassable check for these members beforemodule_method; if deployment expects all socket peers to be trusted, prove socket permissions make this true.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/tinybus/src/bin/tinybus.rs` around lines 330 - 367, Add a mandatory caller/peer authorization check before the module_method dispatch for LoadModule, RescanModules, StopModule, and EnableModule, using the existing policy or peer-identity mechanism rather than trusting artifact validation or CLI origin. Reject unauthorized requests before invoking ModuleControl, and ensure there is no bypass path; if socket peers are intended to be universally trusted, enforce and document verifiable socket permissions instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@crates/tinybus/src/bin/tinybus.rs`:
- Around line 330-367: Add a mandatory caller/peer authorization check before
the module_method dispatch for LoadModule, RescanModules, StopModule, and
EnableModule, using the existing policy or peer-identity mechanism rather than
trusting artifact validation or CLI origin. Reject unauthorized requests before
invoking ModuleControl, and ensure there is no bypass path; if socket peers are
intended to be universally trusted, enforce and document verifiable socket
permissions instead.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 181835b0-4f80-4a0d-b78a-fcf1c1ad222b
📒 Files selected for processing (24)
.github/workflows/ci.ymlCargo.tomlREADME.mdROADMAP.mdcrates/tinybus-module/Cargo.tomlcrates/tinybus-module/src/lib.rscrates/tinybus/build.rscrates/tinybus/examples/module_clock_two.rscrates/tinybus/examples/module_wrong_target.rscrates/tinybus/src/bin/tinybus.rscrates/tinybus/src/broker.rscrates/tinybus/src/connection.rscrates/tinybus/src/error.rscrates/tinybus/src/module/abi.rscrates/tinybus/src/module/hash.rscrates/tinybus/src/module/host.rscrates/tinybus/src/module/host_test.rscrates/tinybus/src/module/loader.rscrates/tinybus/src/module/resolve.rscrates/tinybus/src/module/transport.rsdocs/modules/README.mddocs/modules/module/README.mddocs/modules/module/abi.mddocs/protocol.md
🚧 Files skipped from review as they are similar to previous changes (17)
- Cargo.toml
- docs/modules/README.md
- crates/tinybus-module/Cargo.toml
- crates/tinybus/src/module/hash.rs
- crates/tinybus/build.rs
- crates/tinybus/examples/module_wrong_target.rs
- docs/modules/module/abi.md
- docs/modules/module/README.md
- docs/protocol.md
- README.md
- crates/tinybus/examples/module_clock_two.rs
- crates/tinybus/src/module/abi.rs
- .github/workflows/ci.yml
- crates/tinybus/src/module/loader.rs
- crates/tinybus/src/broker.rs
- crates/tinybus/src/module/host.rs
- crates/tinybus-module/src/lib.rs
The CI workflow now creates a directory with restricted permissions and copies two module artifacts into it, exposing the test to a scenario where some modules are loadable and others are not. The host test is updated to use this pre-prepared directory when the environment variable is set, falling back to the previous temporary-directory approach otherwise. This allows the integration test to verify that a single refused module does not prevent the remaining modules in the same directory from loading. Auto-committed-on: dragonfly
Summary
ModuleHost,LoadModule, the CLI, andmodule_export! { config = Type }Security boundary
The
modulesfeature remains off by default. Loaded modules are explicitly treated as trusted code inside the host address space. Admission checks cover directory ownership/modes or Windows ACLs, parent components on Unix,O_NOFOLLOW, regular files, platform extensions, a 512 MiB cap, and an authoritative SHA-256modules.tomlallowlist when present. The ABI hard gate runs before the manifest function or init entrypoint.Libraries are opened locally/eagerly and are never unloaded. The security-boundary amendment and operational constraints are documented in this change.
Validation
cargo fmt --all -- --checkcargo clippy --locked --all-targets --all-features -- -D warningscargo test --locked --all-featurescargo check --locked --no-default-featurescargo check --locked --no-default-features --features modulescargo check --locked --target x86_64-pc-windows-gnu --no-default-features --features modules,macrosCloses #4
Summary by CodeRabbit
New Features
Documentation