Depuis des années, mes photos vivaient dans une arborescence de dossiers datés, sauvegardée à la main sur un disque externe que j’oubliais de brancher. Fragment est né du besoin de fiabiliser tout ça : un service Go qui pousse mes clichés sur du stockage objet compatible S3, les dédoublonne au passage, et les rend consultables sans exhumer un client lourd.

Vue d'ensemble du tableau de bord Fragment
Boîtier
Fujifilm X-T30 II
Objectif
XF 27mm f/2.8
Focale
27 mm
Ouverture
ƒ/2.8
Vitesse
1/125 s
ISO
400
Le tableau de bord de Fragment, généré à la volée depuis les métadonnées S3.

Pourquoi S3 plutôt qu’un NAS

Le stockage objet règle trois problèmes d’un coup : la durabilité (réplication côté fournisseur), l’adressage stable (chaque photo a une clé immuable) et le coût marginal quasi nul pour de l’archive froide. Je vise un backend agnostique — ça tourne aussi bien sur MinIO auto-hébergé que sur Backblaze B2 ou AWS S3.

// Store dépose un objet et renvoie sa clé de contenu (content-addressed).
func (s *Store) Put(ctx context.Context, r io.Reader) (string, error) {
    h := sha256.New()
    buf := new(bytes.Buffer)
    if _, err := io.Copy(io.MultiWriter(h, buf), r); err != nil {
        return "", fmt.Errorf("hashing photo: %w", err)
    }
    key := "blobs/" + hex.EncodeToString(h.Sum(nil))
    _, err := s.client.PutObject(ctx, s.bucket, key, buf, int64(buf.Len()),
        minio.PutObjectOptions{ContentType: "image/jpeg"})
    return key, err
}

Déduplication par le contenu

Chaque photo est adressée par le SHA-256 de son contenu. Importer deux fois la même image ne coûte rien : la clé est identique, l’objet n’est écrit qu’une fois. Les métadonnées (EXIF, tags manuels, date) vivent dans un index séparé qui pointe vers ces blobs.

Le contenu est immuable, l’organisation ne l’est pas. Je peux reclasser mille photos sans déplacer un seul octet de pixel.

Classement

Le classement s’appuie sur l’EXIF extrait à l’import (boîtier, objectif, date) plus des tags que j’ajoute à la main. Voici un échantillon de la bibliothèque telle que Fragment la présente :

Prochaines étapes

  • Un mode « galeries publiques » qui exporte un sous-ensemble en statique — comme ce site.
  • La recherche par similarité visuelle (embeddings) pour retrouver « la photo avec le vélo rouge ».
  • Le chiffrement côté client avant l’envoi, pour les archives sensibles.

Le code n’est pas encore public, mais l’architecture se stabilise. J’en reparlerai.