Implement embedded HTTPS Git synchronization

This commit is contained in:
Hermes Agent
2026-08-09 23:54:10 +00:00
parent 410007c012
commit 75ce19da00
14 changed files with 4811 additions and 15 deletions

View File

@@ -11,8 +11,8 @@ use std::{
use cap_std::{ambient_authority, fs::Dir};
use pgp::{
composed::{
Deserializable, DetachedSignature, Esk, Message, MessageBuilder, PublicOrSecret,
SignedPublicKey, SignedPublicSubKey, SignedSecretKey, SubpacketConfig,
ArmorOptions, Deserializable, DetachedSignature, Esk, Message, MessageBuilder,
PublicOrSecret, SignedPublicKey, SignedPublicSubKey, SignedSecretKey, SubpacketConfig,
},
crypto::{hash::HashAlgorithm, sym::SymmetricKeyAlgorithm},
packet::{SignatureType, Subpacket, SubpacketData},
@@ -492,6 +492,18 @@ impl KeyStore {
Ok(DetachedSignatureBytes(bytes))
}
/// Encode a detached signature as the ASCII armor required by Git's
/// `gpgsig` commit header.
pub fn armor_signature(
&self,
signature: &DetachedSignatureBytes,
) -> Result<Vec<u8>, CryptoError> {
DetachedSignature::from_bytes(Cursor::new(signature.as_bytes()))
.map_err(|_| CryptoError::SigningFailed)?
.to_armored_bytes(ArmorOptions::default())
.map_err(|_| CryptoError::SigningFailed)
}
/// Verify a detached `.gpg-id.sig` against an explicit set of allowed primary identities.
pub fn verify(
&self,

2464
crates/storage/src/git.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ pub mod command;
pub mod config;
pub mod crypto;
pub mod generate;
pub mod git;
pub mod mutation;
pub mod read;
pub mod recipient;

View File

@@ -24,6 +24,7 @@ pub struct TreeCommit {
action: MutationAction,
source: String,
destination: Option<String>,
changed_paths: Vec<std::path::PathBuf>,
message: String,
}
@@ -37,6 +38,9 @@ impl TreeCommit {
pub fn destination(&self) -> Option<&str> {
self.destination.as_deref()
}
pub fn changed_paths(&self) -> &[std::path::PathBuf] {
&self.changed_paths
}
pub fn message(&self) -> &str {
&self.message
}
@@ -148,6 +152,16 @@ impl<'a> TreeMutator<'a> {
action: MutationAction::Remove,
source: display.clone(),
destination: None,
changed_paths: entries
.iter()
.map(|entry| entry.source.encrypted_relative_path())
.chain(policies.iter().flat_map(|policy| {
[
policy.directory.as_path().join(".gpg-id"),
policy.directory.as_path().join(".gpg-id.sig"),
]
}))
.collect(),
message: format!("Remove {display} from store."),
};
if let Err(error) = committer.commit(&change) {
@@ -452,10 +466,34 @@ impl<'a> TreeMutator<'a> {
MutationAction::Copy
};
let verb = if moving { "Rename" } else { "Copy" };
let mut changed_paths = entries
.iter()
.map(|entry| entry.destination.encrypted_relative_path())
.chain(policies.iter().flat_map(|policy| {
[
policy.destination.as_path().join(".gpg-id"),
policy.destination.as_path().join(".gpg-id.sig"),
]
}))
.collect::<Vec<_>>();
if moving {
changed_paths.extend(
source_entries
.iter()
.map(|entry| entry.source.encrypted_relative_path()),
);
changed_paths.extend(source_policies.iter().flat_map(|policy| {
[
policy.directory.as_path().join(".gpg-id"),
policy.directory.as_path().join(".gpg-id.sig"),
]
}));
}
let change = TreeCommit {
action,
source: source.to_owned(),
destination: Some(destination.to_owned()),
changed_paths,
message: format!("{verb} {source} to {destination}."),
};
if let Err(error) = committer.commit(&change) {

View File

@@ -45,7 +45,7 @@ impl EntryPath {
DirectoryPath(self.0.parent().map_or_else(PathBuf::new, Path::to_path_buf))
}
fn encrypted_relative_path(&self) -> PathBuf {
pub fn encrypted_relative_path(&self) -> PathBuf {
let mut path = self.0.clone();
let mut file_name = path
.file_name()