libnoa 0.1.1

AI-native distributed version control system with per-agent workspace isolation, JSONL append-only logs, snapshot-based history, and full git protocol compatibility
Documentation
# Conception du stockage d'objets

## Aperçu

noa utilise un modèle de stockage adressé par contenu inspiré de Git mais avec une
architecture backend enfichable. Les objets sont adressés par un hachage SHA-256
et stockés sous forme de blobs opaques.

## Types d'objets

### Blob

Contenu brut d'un fichier. Identifié par `SHA256(contenu)`.

```rust
pub struct BlobId(pub String); // SHA-256 encodé en hexadécimal
```

Pas de compression delta. Chaque contenu unique produit exactement un blob.
Le contenu dupliqué est automatiquement dédupliqué par le hachage.

### Arbre (Tree)

Liste de répertoire. Associe des chemins à des entrées enfants (blobs ou sous-arbres).

```rust
pub struct TreeEntry {
    pub name: String,
    pub kind: TreeEntryKind, // Blob ou Tree
    pub hash: String,        // SHA-256 de l'enfant
}

pub struct TreeId(pub String); // SHA-256(msgpack(entries))
```

Les arbres sont sérialisés en MessagePack pour la compacité et la désérialisation rapide.

## Définition du trait

```rust
#[async_trait]
pub trait ObjectStore: Send + Sync {
    async fn put_blob(&self, data: &[u8]) -> Result<BlobId>;
    async fn get_blob(&self, id: &BlobId) -> Result<Vec<u8>>;
    async fn put_tree(&self, entries: Vec<TreeEntry>) -> Result<TreeId>;
    async fn get_tree(&self, id: &TreeId) -> Result<Vec<TreeEntry>>;
}
```

## Backends

### RedbObjectStore (Local)

Utilise le magasin clé-valeur embarqué [redb](https://github.com/cberner/redb).

- Deux tables : `blobs` (clé : octets de hachage, valeur : octets de contenu) et
  `trees` (clé : octets de hachage, valeur : entrées msgpack)
- Lectures zéro-copie via fichiers mappés en mémoire
- Transactions ACID avec récupération automatique après crash
- Écrivain unique, plusieurs lecteurs via MVCC
- Aucun démon externe requis

### MinioObjectStore (Distant)

Utilise une API compatible S3 via `aws-sdk-s3`.

- Adressage par chemin : `<bucket>/blobs/<hash>`, `<bucket>/trees/<hash>`
- Prend en charge tout backend compatible S3 (MinIO, AWS S3, GCS, etc.)
- Tentatives automatiques avec backoff exponentiel
- Adapté aux déploiements distribués

## Décisions de conception

### Pourquoi SHA-256 plutôt que SHA-1 ?

Git utilise SHA-1, qui est cryptographiquement cassé (attaque SHAttered, 2017).
SHA-256 est résistant aux collisions et largement disponible.

### Pourquoi pas de compression delta ?

1. **Simplicité** : La compression delta (fichiers pack de Git) ajoute une complexité
   significative (correspondance par fenêtre glissante, packs minces, chaînes de delta).
2. **Performance d'écriture** : Les écritures directes de blob sont O(1). La compression
   delta nécessite la lecture des objets existants.
3. **Charge de travail des agents IA** : Les agents régénèrent fréquemment des fichiers
   entiers. Les anciennes versions sont éphémères — les chaînes de delta seraient courtes
   et nombreuses.
4. **Délégation au backend** : S3/MinIO gèrent la déduplication au niveau du stockage.

### Pourquoi MessagePack pour les arbres ?

- 30-50% plus petit que JSON pour les données à forte composante binaire
- Schéma flexible (pas besoin de définitions protobuf)
- Support de l'écosystème Rust via `rmp-serde`
- Désérialisation rapide

### Pourquoi redb plutôt que SQLite ?

- **Sécurité de type** : redb utilise les génériques Rust pour les définitions de tables
- **Performance** : redb est optimisé pour les charges de travail Rust (lectures zéro-copie)
- **Simplicité** : Dépendance unique, pas de liaison avec une bibliothèque C
- **Sécurité anti-crash** : Le journal de pré-écriture de redb est plus simple que le mode WAL de SQLite

Compromis : redb a une communauté plus petite et moins d'outils que SQLite.
Pour le cas d'usage de noa (stockage binaire embarqué), le compromis est favorable.