Wire keychain status to local keychain ops

This commit is contained in:
Eric Wendland 2026-05-16 16:34:20 +02:00
commit 99f6dee26f
12 changed files with 191 additions and 20 deletions

View file

@ -16,6 +16,7 @@ geth-config = { path = "../geth-config" }
geth-control = { path = "../geth-control" }
geth-crypto = { path = "../geth-crypto" }
geth-iroh = { path = "../geth-iroh" }
geth-keychain = { path = "../geth-keychain" }
geth-resource = { path = "../geth-resource" }
geth-ssh-identity = { path = "../geth-ssh-identity" }
geth-store = { path = "../geth-store" }

View file

@ -9,6 +9,7 @@ use geth_control::{
};
use geth_crypto::AgentKey;
use geth_iroh::{EndpointStatus, GethIrohConfig, GethIrohEndpoint, GethRelayMode};
use geth_keychain::{KeychainOp, KeychainOpKind};
use geth_resource::ResourceDescriptor;
use geth_ssh_identity::{
SshCertApproval, SshCertKind, SshCertRequest, SshCertRequestStatus, SshCertificateRecord,
@ -16,12 +17,12 @@ use geth_ssh_identity::{
certificate_id, revocation_id, ssh_public_key_fingerprint,
};
use geth_store::{
Store, StoredAuthOp, StoredResource, StoredSshCertRequest, StoredSshCertificate,
StoredSshRevocation,
Store, StoredAuthOp, StoredKeychainOp, StoredResource, StoredSshCertRequest,
StoredSshCertificate, StoredSshRevocation,
};
use geth_types::{
AuthOpId, Capability, NodeId, PrincipalId, ResourceId, ResourceKind, ResourceName, SshCertId,
SshCertRequestId, UnixMillis,
AuthOpId, Capability, KeyId, NodeId, PrincipalId, ResourceId, ResourceKind, ResourceName,
SshCertId, SshCertRequestId, UnixMillis,
};
use std::path::Path;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
@ -237,13 +238,40 @@ pub fn handle_request(
.collect(),
})
}
ControlRequest::KeychainInit { admin_key_path } => {
let mut ops = Vec::new();
let created_at = UnixMillis(geth_store::now_ms());
let init = KeychainOp {
id: generated_keychain_op_id("keychain-init", "local", created_at),
created_at,
kind: KeychainOpKind::KeychainInit,
};
store_keychain_op(&store, &init)?;
ops.push(init);
if let Some(admin_key_path) = admin_key_path {
let public_key = std::fs::read_to_string(admin_key_path)?;
let created_at = UnixMillis(geth_store::now_ms());
let admin_key = KeyId::new(ssh_public_key_fingerprint(&public_key));
let op = KeychainOp {
id: generated_keychain_op_id("admin-key-add", admin_key.as_str(), created_at),
created_at,
kind: KeychainOpKind::AdminKeyAdd { key: admin_key },
};
store_keychain_op(&store, &op)?;
ops.push(op);
}
Ok(ControlResponse::KeychainInitialized { ops })
}
ControlRequest::KeychainStatus => {
let view = geth_keychain::reduce_keychain_ops(&load_keychain_ops(&store)?);
Ok(ControlResponse::KeychainStatus(KeychainStatusResponse {
initialized: false,
admin_keys: 0,
users: 0,
devices: 0,
nodes: 1,
initialized: view.initialized,
admin_keys: view.admin_keys.len(),
users: view.users.len(),
devices: view.devices.len(),
nodes: view.nodes.len(),
}))
}
ControlRequest::AuthExplain {
@ -518,6 +546,23 @@ fn load_auth_ops_for_resource(store: &Store, resource: &str) -> Result<Vec<AuthO
.collect()
}
fn store_keychain_op(store: &Store, op: &KeychainOp) -> Result<(), NodeError> {
store.insert_keychain_op(&StoredKeychainOp {
op_id: op.id.to_string(),
op_json: serde_json::to_string(op)?,
created_at_ms: op.created_at.0,
})?;
Ok(())
}
fn load_keychain_ops(store: &Store) -> Result<Vec<KeychainOp>, NodeError> {
store
.list_keychain_ops()?
.into_iter()
.map(|stored| serde_json::from_str(&stored.op_json).map_err(NodeError::from))
.collect()
}
fn generated_grant_id(subject: &str, resource: &str, capability: &str) -> String {
format!(
"grant:{}",
@ -539,6 +584,13 @@ fn generated_auth_op_id(
))
}
fn generated_keychain_op_id(kind: &str, stable_id: &str, created_at: UnixMillis) -> AuthOpId {
AuthOpId::new(format!(
"keychain-op:{}",
geth_crypto::blake3_hex(format!("{}\0{kind}\0{stable_id}", created_at.0).as_bytes())
))
}
fn stable_node_id(agent_id: &str) -> String {
format!("node:{agent_id}")
}