use thiserror::Error;
pub trait RequestSigner: Send + Sync {
fn sign(&self, req: SignInput<'_>) -> Result<SignOutput, SignError>;
fn algorithm_version(&self) -> u8;
}
pub struct SignInput<'a> {
pub method: &'a str,
pub path: &'a str,
pub body: &'a [u8],
pub oauth_token: &'a str,
pub user_id: &'a str,
pub timestamp_unix: u64,
pub nonce: [u8; 16],
}
#[derive(Debug)]
pub struct SignOutput {
pub headers: Vec<(&'static str, String)>,
}
#[derive(Debug, Error)]
pub enum SignError {
#[error("signer unavailable in this build")]
Unavailable,
#[error("signing-key derivation failed: {0}")]
Derive(String),
}
pub struct UnavailableSigner;
impl RequestSigner for UnavailableSigner {
fn sign(&self, _req: SignInput<'_>) -> Result<SignOutput, SignError> {
Err(SignError::Unavailable)
}
fn algorithm_version(&self) -> u8 {
0
}
}
#[cfg(not(feature = "codingplan-crypto"))]
static UNAVAILABLE_SIGNER: UnavailableSigner = UnavailableSigner;
#[cfg(not(feature = "codingplan-crypto"))]
pub fn signer() -> &'static dyn RequestSigner {
&UNAVAILABLE_SIGNER
}
#[cfg(feature = "codingplan-crypto")]
struct RealSigner;
#[cfg(feature = "codingplan-crypto")]
impl RequestSigner for RealSigner {
fn sign(&self, req: SignInput<'_>) -> Result<SignOutput, SignError> {
Ok(SignOutput {
headers: atomcode_codingplan_crypto::sign_v1(
req.method,
req.path,
req.body,
req.oauth_token,
req.user_id,
req.timestamp_unix,
&req.nonce,
env!("CARGO_PKG_VERSION"),
),
})
}
fn algorithm_version(&self) -> u8 {
atomcode_codingplan_crypto::ALGORITHM_VERSION
}
}
#[cfg(feature = "codingplan-crypto")]
static REAL_SIGNER: RealSigner = RealSigner;
#[cfg(feature = "codingplan-crypto")]
pub fn signer() -> &'static dyn RequestSigner {
&REAL_SIGNER
}
#[cfg(feature = "codingplan-crypto")]
pub fn signer_available() -> bool {
true
}
#[cfg(not(feature = "codingplan-crypto"))]
pub fn signer_available() -> bool {
false
}
pub fn is_atomgit_gateway(base_url: &str) -> bool {
let url = match url::Url::parse(base_url) {
Ok(u) => u,
Err(_) => return false,
};
match url.scheme() {
"https" | "http" => {}
_ => return false,
}
matches!(
url.host_str(),
Some("llm-api.atomgit.com") | Some("api-ai.gitcode.com")
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unavailable_signer_returns_unavailable_error() {
let s = UnavailableSigner;
let input = SignInput {
method: "POST",
path: "/v1/chat/completions",
body: b"{}",
oauth_token: "any-token",
user_id: "user-1",
timestamp_unix: 1_700_000_000,
nonce: [0u8; 16],
};
let err = s.sign(input).expect_err("UnavailableSigner must error");
assert!(matches!(err, SignError::Unavailable));
}
#[test]
fn unavailable_signer_reports_algorithm_version_zero() {
let s = UnavailableSigner;
assert_eq!(s.algorithm_version(), 0);
}
#[cfg(not(feature = "codingplan-crypto"))]
#[test]
fn signer_available_reports_false_in_open_source_build() {
assert!(!signer_available());
}
#[cfg(feature = "codingplan-crypto")]
#[test]
fn signer_available_reports_true_in_official_build() {
assert!(signer_available());
}
#[cfg(not(feature = "codingplan-crypto"))]
#[test]
fn default_signer_in_open_source_build_is_unavailable() {
let input = SignInput {
method: "POST",
path: "/v1/chat/completions",
body: b"{}",
oauth_token: "any-token",
user_id: "user-1",
timestamp_unix: 1_700_000_000,
nonce: [0u8; 16],
};
let err = signer().sign(input).expect_err("open-source must error");
assert!(matches!(err, SignError::Unavailable));
}
#[test]
fn is_atomgit_gateway_matches_official_host() {
assert!(is_atomgit_gateway("https://pre-llm-api-cce.atomgit.com/v1"));
assert!(is_atomgit_gateway("https://pre-llm-api-cce.atomgit.com/v1/chat/completions"));
}
#[test]
fn is_atomgit_gateway_matches_legacy_codingplan_host() {
assert!(is_atomgit_gateway("https://api-ai.gitcode.com/v1"));
assert!(is_atomgit_gateway("https://api-ai.gitcode.com/v1/chat/completions"));
}
#[test]
fn is_atomgit_gateway_rejects_third_party_hosts() {
assert!(!is_atomgit_gateway("https://api.anthropic.com"));
assert!(!is_atomgit_gateway("https://api.openai.com/v1"));
assert!(!is_atomgit_gateway("http://localhost:11434"));
}
#[test]
fn is_atomgit_gateway_rejects_subdomains_and_lookalikes() {
assert!(!is_atomgit_gateway("https://pre-llm-api-cce.atomgit.com.evil.example"));
assert!(!is_atomgit_gateway("https://evil.pre-llm-api-cce.atomgit.com"));
assert!(!is_atomgit_gateway("https://atomgit.com"));
}
#[test]
fn is_atomgit_gateway_rejects_malformed_input() {
assert!(!is_atomgit_gateway(""));
assert!(!is_atomgit_gateway("not a url"));
assert!(!is_atomgit_gateway("ftp://pre-llm-api-cce.atomgit.com"));
}
}