fix(runner): initialize Rustls crypto provider (#13023)
## Thinking Path > - Paperclip runs AI agents through local and remote execution adapters. > - The native runner daemon uses Rustls for secure transport. > - The production dependency graph enables the `ring` and `aws-lc-rs` crypto backends. > - Rustls cannot select a default backend when both backends are active. > - The runner daemon did not select a backend before it built a TLS client configuration. > - This pull request installs the workspace-selected `ring` provider during process startup. > - The benefit is that the runner can start reliably with the production feature graph. ## Linked Issues or Issue Description **What happened?** `paperclip-runnerd` exited with code 101 before it opened a provider session. Rustls reported that it could not select a process-level `CryptoProvider` because the binary included two crypto backends. **Expected behavior** The runner daemon must select its configured crypto provider before it creates a TLS client configuration. The daemon must start and open the provider session. **Steps to reproduce** 1. Build `paperclip-runnerd` with the locked production dependency graph. 2. Start the daemon through the local loopback transport. 3. Observe the Rustls provider-selection panic before this change. **Paperclip version or commit** `d8b958053` on `master`. **Deployment mode** Self-hosted server. **Installation method** Built from source. **Agent adapter(s) involved** Codex through `paperclip_runner`. **Operating system** Linux 7.0.0-1010-aws on aarch64. ## What Changed - Install the Rustls `ring` provider before runner setup reaches TLS initialization. - Accept an existing process-level provider as an initialized state. - Add a focused regression test for TLS builder creation and repeated initialization. ## Verification - `cargo test --manifest-path packages/paperclip-runner/runner/Cargo.toml --locked -p paperclip-runner-core --bin paperclip-runnerd startup_installs_a_crypto_provider_before_tls_initialization` - `cargo test --manifest-path packages/paperclip-runner/runner/Cargo.toml --locked -p paperclip-runner-core --test local_runner runnerd_startup_reports_build_metadata_without_panicking` - `cargo test --manifest-path packages/paperclip-runner/runner/Cargo.toml --locked -p paperclip-runner-core --test local_runner happy_path_emits_one_result_and_one_terminal` - `cargo fmt --manifest-path packages/paperclip-runner/runner/Cargo.toml --all -- --check` - Built the debug daemon and ran `paperclip-runnerd --build-metadata` successfully. - `pnpm -r typecheck` - `pnpm build` ## Risks - Low risk. The change selects the crypto provider that the workspace already declares. - A host process can install a provider first. The runner accepts that initialized state. - No database, API, UI, telemetry, observability, or run-log contract changes. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex with model `gpt-5`. The context-window size is not exposed. The model used reasoning, repository tools, code execution, and test execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
d8b9580531
commit
ff24578765
|
|
@ -104,6 +104,14 @@ fn install_diagnostic_panic_hook(directory: Option<PathBuf>) {
|
|||
}));
|
||||
}
|
||||
|
||||
fn install_crypto_provider() {
|
||||
// The production dependency graph enables both rustls crypto backends.
|
||||
// Select the backend declared by this workspace before any TLS builder
|
||||
// asks rustls for the process-level default. An embedding process may have
|
||||
// already selected a provider, which is also a valid initialized state.
|
||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||
}
|
||||
|
||||
fn build_metadata() -> serde_json::Value {
|
||||
json!({
|
||||
"schema": RUNNERD_BUILD_METADATA_SCHEMA,
|
||||
|
|
@ -359,6 +367,28 @@ fn run(args: &[String]) -> Result<(), LocalRunnerError> {
|
|||
})
|
||||
}
|
||||
|
||||
fn run_main(args: Vec<String>) -> ExitCode {
|
||||
let diagnostics_directory = diagnostic_directory(&args);
|
||||
install_diagnostic_panic_hook(diagnostics_directory.clone());
|
||||
install_crypto_provider();
|
||||
match run(&args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
let message = format!("paperclip-runnerd: {error}");
|
||||
if let Some(directory) = diagnostics_directory {
|
||||
if let Err(persist_error) = persist_runner_diagnostic(&directory, &message) {
|
||||
eprintln!(
|
||||
"paperclip-runnerd: failed to persist bounded diagnostic: {persist_error}"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
eprintln!("{message}");
|
||||
}
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -374,6 +404,17 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_installs_a_crypto_provider_before_tls_initialization() {
|
||||
let _ = run_main(vec!["--build-metadata".to_owned()]);
|
||||
assert!(rustls::crypto::CryptoProvider::get_default().is_some());
|
||||
|
||||
// Startup is process-global. A repeated startup call must remain
|
||||
// safe when a provider was selected earlier in the process lifetime.
|
||||
let _ = run_main(vec!["--build-metadata".to_owned()]);
|
||||
let _ = rustls::ClientConfig::builder();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn persistent_diagnostic_is_private_bounded_and_redacted() {
|
||||
let unique = format!(
|
||||
|
|
@ -422,23 +463,5 @@ mod tests {
|
|||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||
let diagnostics_directory = diagnostic_directory(&args);
|
||||
install_diagnostic_panic_hook(diagnostics_directory.clone());
|
||||
match run(&args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
let message = format!("paperclip-runnerd: {error}");
|
||||
if let Some(directory) = diagnostics_directory {
|
||||
if let Err(persist_error) = persist_runner_diagnostic(&directory, &message) {
|
||||
eprintln!(
|
||||
"paperclip-runnerd: failed to persist bounded diagnostic: {persist_error}"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
eprintln!("{message}");
|
||||
}
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
run_main(std::env::args().skip(1).collect())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,19 @@ fn event_type(message: &Value) -> Option<&str> {
|
|||
.flatten()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runnerd_startup_reports_build_metadata_without_panicking() {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_paperclip-runnerd"))
|
||||
.arg("--build-metadata")
|
||||
.output()
|
||||
.expect("runner daemon should start");
|
||||
|
||||
assert!(output.status.success());
|
||||
let metadata: Value =
|
||||
serde_json::from_slice(&output.stdout).expect("build metadata should be valid JSON");
|
||||
assert_eq!(metadata["binaryName"], "paperclip-runnerd");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn happy_path_emits_one_result_and_one_terminal() {
|
||||
let commands = [
|
||||
|
|
|
|||
Loading…
Reference in New Issue