use crate::agent::document::JACSDocument;
use crate::error::JacsError;
use crate::schema::utils::ValueExt;
use crate::storage::MultiStorage;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentInfo {
pub agent_id: String,
pub name: String,
pub public_key_path: String,
pub config_path: String,
#[serde(default)]
pub version: String,
#[serde(default)]
pub algorithm: String,
#[serde(default)]
pub private_key_path: String,
#[serde(default)]
pub data_directory: String,
#[serde(default)]
pub key_directory: String,
#[serde(default)]
pub domain: String,
#[serde(default)]
pub dns_record: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedDocument {
pub raw: String,
pub document_id: String,
pub agent_id: String,
pub timestamp: String,
}
impl SignedDocument {
pub(crate) fn from_jacs_document(
jacs_doc: JACSDocument,
serialized_kind: &str,
) -> Result<Self, JacsError> {
let raw = serde_json::to_string(&jacs_doc.value).map_err(|e| JacsError::Internal {
message: format!("Failed to serialize {}: {}", serialized_kind, e),
})?;
let timestamp = jacs_doc
.value
.get_path_str_or(&["jacsSignature", "date"], "");
let agent_id = jacs_doc
.value
.get_path_str_or(&["jacsSignature", "agentID"], "");
Ok(Self {
raw,
document_id: jacs_doc.id,
agent_id,
timestamp,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationResult {
pub valid: bool,
pub data: Value,
pub signer_id: String,
pub signer_name: Option<String>,
pub timestamp: String,
pub attachments: Vec<Attachment>,
pub errors: Vec<String>,
}
impl VerificationResult {
#[must_use]
pub fn failure(error: String) -> Self {
Self {
valid: false,
data: json!(null),
signer_id: String::new(),
signer_name: None,
timestamp: String::new(),
attachments: vec![],
errors: vec![error],
}
}
#[must_use]
pub fn success(data: Value, signer_id: String, timestamp: String) -> Self {
Self {
valid: true,
data,
signer_id,
signer_name: None,
timestamp,
attachments: vec![],
errors: vec![],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attachment {
pub filename: String,
pub mime_type: String,
#[serde(with = "base64_bytes")]
pub content: Vec<u8>,
pub hash: String,
pub embedded: bool,
}
mod base64_bytes {
use base64::{Engine as _, engine::general_purpose::STANDARD};
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&STANDARD.encode(bytes))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
STANDARD.decode(&s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetupInstructions {
pub dns_record_bind: String,
pub dns_record_value: String,
pub dns_owner: String,
pub provider_commands: std::collections::HashMap<String, String>,
pub dnssec_instructions: std::collections::HashMap<String, String>,
pub tld_requirement: String,
pub well_known_json: String,
pub summary: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateAgentParams {
pub name: String,
#[serde(default)]
pub password: String,
#[serde(default = "default_algorithm")]
pub algorithm: String,
#[serde(default = "default_data_directory")]
pub data_directory: String,
#[serde(default = "default_key_directory")]
pub key_directory: String,
#[serde(default = "default_config_path")]
pub config_path: String,
#[serde(default = "default_agent_type")]
pub agent_type: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub domain: String,
#[serde(default = "default_storage")]
pub default_storage: String,
#[serde(skip)]
pub storage: Option<MultiStorage>,
}
fn default_algorithm() -> String {
"pq2025".to_string()
}
fn default_data_directory() -> String {
"./jacs_data".to_string()
}
fn default_key_directory() -> String {
"./jacs_keys".to_string()
}
fn default_config_path() -> String {
"./jacs.config.json".to_string()
}
fn default_agent_type() -> String {
"ai".to_string()
}
fn default_storage() -> String {
"fs".to_string()
}
impl Default for CreateAgentParams {
fn default() -> Self {
Self {
name: String::new(),
password: String::new(),
algorithm: default_algorithm(),
data_directory: default_data_directory(),
key_directory: default_key_directory(),
config_path: default_config_path(),
agent_type: default_agent_type(),
description: String::new(),
domain: String::new(),
default_storage: default_storage(),
storage: None,
}
}
}
impl CreateAgentParams {
pub fn builder() -> CreateAgentParamsBuilder {
CreateAgentParamsBuilder::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct CreateAgentParamsBuilder {
params: CreateAgentParams,
}
impl CreateAgentParamsBuilder {
pub fn name(mut self, name: &str) -> Self {
self.params.name = name.to_string();
self
}
pub fn password(mut self, password: &str) -> Self {
self.params.password = password.to_string();
self
}
pub fn algorithm(mut self, algorithm: &str) -> Self {
self.params.algorithm = algorithm.to_string();
self
}
pub fn data_directory(mut self, dir: &str) -> Self {
self.params.data_directory = dir.to_string();
self
}
pub fn key_directory(mut self, dir: &str) -> Self {
self.params.key_directory = dir.to_string();
self
}
pub fn config_path(mut self, path: &str) -> Self {
self.params.config_path = path.to_string();
self
}
pub fn agent_type(mut self, agent_type: &str) -> Self {
self.params.agent_type = agent_type.to_string();
self
}
pub fn description(mut self, desc: &str) -> Self {
self.params.description = desc.to_string();
self
}
pub fn domain(mut self, domain: &str) -> Self {
self.params.domain = domain.to_string();
self
}
pub fn default_storage(mut self, storage_type: &str) -> Self {
self.params.default_storage = storage_type.to_string();
self
}
pub fn storage(mut self, storage: MultiStorage) -> Self {
self.params.storage = Some(storage);
self
}
pub fn build(self) -> CreateAgentParams {
self.params
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignerStatus {
pub agent_id: String,
pub signed: bool,
pub signed_at: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgreementStatus {
pub complete: bool,
pub signers: Vec<SignerStatus>,
pub pending: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RotationResult {
pub jacs_id: String,
pub old_version: String,
pub new_version: String,
pub new_public_key_pem: String,
pub new_public_key_hash: String,
pub signed_agent_json: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrateResult {
pub jacs_id: String,
pub old_version: String,
pub new_version: String,
pub patched_fields: Vec<String>,
}