geth/crates/geth-control/src/lib.rs

349 lines
7.7 KiB
Rust
Raw Normal View History

2026-05-16 16:32:03 +02:00
use geth_auth::{AuthExplanation, AuthOp};
2026-05-16 21:13:33 +02:00
use geth_db::DbResource;
2026-05-16 22:15:18 +02:00
use geth_document::DocumentResource;
use geth_keychain::KeychainOp;
2026-05-16 21:52:35 +02:00
use geth_kv::{KvEntry, KvResource};
2026-05-15 15:08:20 +02:00
use geth_resource::ResourceDescriptor;
use geth_secrets::{BearerAccess, ResourceMasterSecret};
use geth_ssh_identity::{
SshCertApproval, SshCertRequest, SshCertificateRecord, SshRevocationEntry,
};
2026-05-15 15:08:20 +02:00
use geth_types::BlobHash;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum ControlRequest {
Status,
NodeId,
ResourceList,
ResourceCreate {
kind: String,
name: String,
},
CasAdd {
path: PathBuf,
},
CasGet {
hash: BlobHash,
out: PathBuf,
},
CasHash {
path: PathBuf,
},
CasHas {
hash: BlobHash,
},
2026-05-16 16:36:35 +02:00
CasPin {
hash: BlobHash,
},
CasUnpin {
hash: BlobHash,
},
2026-05-16 21:10:25 +02:00
CasCleanup {
dry_run: bool,
},
2026-05-15 15:08:20 +02:00
CasList,
KeychainInit {
admin_key_path: Option<PathBuf>,
},
2026-05-15 15:08:20 +02:00
KeychainStatus,
2026-05-16 22:18:49 +02:00
SecretStatus,
SecretCreate {
resource: String,
},
SecretRotate {
resource: String,
},
SecretBearerCreate {
resource: String,
capabilities: Vec<String>,
expires_at_ms: Option<i64>,
},
SecretBearerList,
SecretBearerRevoke {
resource: String,
secret: String,
},
2026-05-15 15:08:20 +02:00
AuthExplain {
subject: String,
resource: String,
capability: String,
},
2026-05-16 16:32:03 +02:00
AuthGrant {
subject: String,
resource: String,
capability: String,
grant_id: Option<String>,
},
AuthRevoke {
resource: String,
grant_id: String,
},
SshCertRequest {
public_key_path: PathBuf,
cert_kind: String,
principals: Vec<String>,
requested_validity: Option<String>,
renewal_of: Option<String>,
reason: Option<String>,
},
SshCertRequests,
SshCertApprove {
request_id: String,
ca_key_path: PathBuf,
valid_for: Option<String>,
serial: Option<u64>,
out: Option<PathBuf>,
},
SshCertImport {
request_id: String,
cert_path: PathBuf,
},
SshCertList,
SshRevocationAdd {
kind: String,
target: String,
reason: Option<String>,
},
SshRevocationList,
SshRevocationExport {
out: PathBuf,
},
2026-05-16 21:13:33 +02:00
DbAdd {
name: String,
path: PathBuf,
},
DbStatus {
name: String,
},
2026-05-16 21:52:35 +02:00
KvCreate {
name: String,
},
KvSet {
name: String,
key: String,
value: String,
},
KvGet {
name: String,
key: String,
},
2026-05-16 22:15:18 +02:00
DocumentCreate {
name: String,
},
DocumentStatus {
name: String,
},
2026-05-15 15:08:20 +02:00
ModuleStub {
module: String,
command: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum ControlResponse {
Status(StatusResponse),
NodeId(NodeIdResponse),
ResourceList {
resources: Vec<ResourceDescriptor>,
},
ResourceCreated {
resource: ResourceDescriptor,
},
CasAdded {
hash: BlobHash,
size_bytes: u64,
},
CasGot {
hash: BlobHash,
out: PathBuf,
size_bytes: u64,
},
CasHash {
hash: BlobHash,
},
CasHas {
hash: BlobHash,
present: bool,
},
2026-05-16 16:36:35 +02:00
CasPinned {
hash: BlobHash,
pinned: bool,
},
2026-05-16 21:10:25 +02:00
CasCleanup {
removed: Vec<BlobHash>,
retained_pinned: Vec<BlobHash>,
dry_run: bool,
},
2026-05-15 15:08:20 +02:00
CasList {
blobs: Vec<CasBlob>,
},
KeychainStatus(KeychainStatusResponse),
KeychainInitialized {
ops: Vec<KeychainOp>,
},
2026-05-16 22:18:49 +02:00
SecretStatus {
secrets: Vec<ResourceMasterSecret>,
},
SecretCreated {
secret: ResourceMasterSecret,
},
SecretBearerCreated {
access: BearerAccess,
},
SecretBearerList {
access: Vec<BearerAccess>,
},
SecretBearerRevoked {
resource: String,
secret: String,
},
2026-05-15 15:08:20 +02:00
AuthExplain(AuthExplanation),
2026-05-16 16:32:03 +02:00
AuthOpRecorded {
op: AuthOp,
},
SshCertRequested {
request: SshCertRequest,
},
SshCertRequests {
requests: Vec<SshCertRequest>,
},
SshCertApproved {
approval: SshCertApproval,
},
SshCertImported {
certificate: SshCertificateRecord,
},
SshCertList {
requests: Vec<SshCertRequest>,
certificates: Vec<SshCertificateRecord>,
},
SshRevocationAdded {
revocation: SshRevocationEntry,
},
SshRevocationList {
revocations: Vec<SshRevocationEntry>,
},
SshRevocationExported {
out: PathBuf,
count: usize,
},
2026-05-16 21:13:33 +02:00
DbAdded {
db: DbResource,
},
DbStatus {
db: DbResource,
},
2026-05-16 21:52:35 +02:00
KvCreated {
kv: KvResource,
},
KvSet {
entry: KvEntry,
},
KvGet {
entry: Option<KvEntry>,
},
2026-05-16 22:15:18 +02:00
DocumentCreated {
document: DocumentResource,
},
DocumentStatus {
document: DocumentResource,
},
2026-05-15 15:08:20 +02:00
NotImplemented {
module: String,
command: String,
},
Error {
message: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusResponse {
pub home: PathBuf,
pub socket: PathBuf,
pub agent_id: String,
pub node_id: String,
2026-05-16 01:54:00 +02:00
pub iroh_enabled: bool,
pub endpoint_id: Option<String>,
2026-05-16 03:17:45 +02:00
pub iroh_relay_mode: String,
2026-05-16 14:33:45 +02:00
pub iroh_local_discovery: bool,
2026-05-15 15:08:20 +02:00
pub iroh: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct NodeIdResponse {
pub agent_id: String,
pub node_id: String,
pub endpoint_id: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeychainStatusResponse {
pub initialized: bool,
pub admin_keys: usize,
pub users: usize,
pub devices: usize,
pub nodes: usize,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CasBlob {
pub hash: BlobHash,
pub size_bytes: u64,
2026-05-16 16:36:35 +02:00
pub pinned: bool,
2026-05-15 15:08:20 +02:00
}
#[derive(Debug, thiserror::Error)]
pub enum ControlError {
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
}
pub fn encode_request(request: &ControlRequest) -> Result<String, ControlError> {
let mut line = serde_json::to_string(request)?;
line.push('\n');
Ok(line)
}
pub fn decode_request(line: &str) -> Result<ControlRequest, ControlError> {
serde_json::from_str(line).map_err(ControlError::from)
}
pub fn encode_response(response: &ControlResponse) -> Result<String, ControlError> {
let mut line = serde_json::to_string(response)?;
line.push('\n');
Ok(line)
}
pub fn decode_response(line: &str) -> Result<ControlResponse, ControlError> {
serde_json::from_str(line).map_err(ControlError::from)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn control_request_response_serialization_roundtrip() {
let request = ControlRequest::CasHas {
hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
};
assert_eq!(
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
request
);
let response = ControlResponse::CasHas {
hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
present: true,
};
assert_eq!(
decode_response(&encode_response(&response).expect("encode")).expect("decode"),
response
);
}
}