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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["crates/capture-ipc", "crates/gateway", "crates/prompt-ir"]
members = ["crates/capture-ipc", "crates/gateway", "crates/prompt-ir", "crates/proxy-recovery"]
resolver = "3"

[workspace.package]
Expand All @@ -18,3 +18,4 @@ serde_json = "1.0"
tokio = { version = "1.45", features = ["io-util", "macros", "net", "rt-multi-thread", "signal", "sync", "time"] }
tokio-stream = "0.1"
url = "2.5"
windows-sys = { version = "0.61", features = ["Win32_Storage_FileSystem"] }
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Rust crate 位于 `crates/prompt-ir`,公开 JSON Schema 位于 `schemas/prompt

捕获 sidecar 位于 `sidecars/mitmproxy`,跨进程契约位于 `crates/capture-ipc`,公开 CaptureEnvelope Schema 位于 `schemas/capture-envelope/v0.1.schema.json`。sidecar 的安装、测试与打包命令见 [`sidecars/mitmproxy/README.md`](./sidecars/mitmproxy/README.md)。

系统代理事务与独立恢复 watchdog 的平台无关核心位于 `crates/proxy-recovery`;当前使用文件 backend 做强杀故障注入,真实 Windows/macOS backend 尚在开发。

启动 Gateway Spike:

```powershell
Expand Down
13 changes: 13 additions & 0 deletions crates/proxy-recovery/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "codeischeap-proxy-recovery"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
description = "Transactional system proxy recovery core for CodeIsCheap"

[dependencies]
serde.workspace = true
serde_json.workspace = true

[target.'cfg(windows)'.dependencies]
windows-sys.workspace = true
13 changes: 13 additions & 0 deletions crates/proxy-recovery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Proxy recovery core

This crate owns the transaction state machine used before CodeIsCheap changes system proxy settings.

1. Read the exact original settings.
2. Persist an armed recovery journal.
3. Spawn an independent watchdog and wait for its `ready` handshake.
4. Apply the desired proxy settings.
5. On normal shutdown, restore and disarm the watchdog.
6. On owner-process death, pipe EOF makes the watchdog restore its in-memory snapshot.
7. On the next startup, an armed journal provides a second recovery path.

The included file backend exists only for deterministic crash injection. Production Windows and macOS backends, private journal-directory permissions, and platform notifications are separate work; `SPIKE-003` remains in progress until those backends pass real setting/restore experiments.
42 changes: 42 additions & 0 deletions crates/proxy-recovery/src/bin/proxy-recovery-spike.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::env;
use std::fs;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;

use codeischeap_proxy_recovery::{FileProxyBackend, ProxySession, ProxySettings};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut arguments = env::args_os().skip(1);
if arguments.next().as_deref() != Some("hold".as_ref()) {
return Err("usage: proxy-recovery-spike hold <state> <journal> <ready> <watchdog>".into());
}
let state = next_path(&mut arguments, "state")?;
let journal = next_path(&mut arguments, "journal")?;
let ready = next_path(&mut arguments, "ready")?;
let watchdog = next_path(&mut arguments, "watchdog")?;
if arguments.next().is_some() {
return Err("unexpected proxy-recovery-spike argument".into());
}

let desired = ProxySettings::Manual {
http_proxy: "http://127.0.0.1:3210".to_owned(),
https_proxy: "http://127.0.0.1:3210".to_owned(),
bypass: vec!["localhost".to_owned(), "127.0.0.1".to_owned()],
};
let _session = ProxySession::begin(FileProxyBackend::new(state), desired, journal, watchdog)?;
fs::write(ready, b"ready\n")?;
loop {
thread::sleep(Duration::from_secs(60));
}
}

fn next_path(
arguments: &mut impl Iterator<Item = std::ffi::OsString>,
name: &str,
) -> Result<PathBuf, Box<dyn std::error::Error>> {
arguments
.next()
.map(PathBuf::from)
.ok_or_else(|| format!("missing {name} path").into())
}
20 changes: 20 additions & 0 deletions crates/proxy-recovery/src/bin/proxy-watchdog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::env;
use std::path::PathBuf;

use codeischeap_proxy_recovery::run_watchdog;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut arguments = env::args_os().skip(1);
if arguments.next().as_deref() != Some("--journal".as_ref()) {
return Err("usage: proxy-watchdog --journal <path>".into());
}
let journal = arguments
.next()
.map(PathBuf::from)
.ok_or("missing journal path")?;
if arguments.next().is_some() {
return Err("unexpected proxy-watchdog argument".into());
}
run_watchdog(&journal)?;
Ok(())
}
Loading
Loading