23 KiB
Architecture de fondation Knowledge
Décision structurante
Knowledge est un workspace modulaire multi-app. Il possède trois applications Actix Web déployables séparément pour PGR, HOWTOs et snippets, et des bibliothèques communes pour les responsabilités réellement partagées.
Cette décision ne crée pas une architecture de microservices distribués : les trois launchers restent des couches de composition minces, peuvent utiliser le même PostgreSQL et partagent les mêmes contrats de domaine, Git, stockage et synchronisation lorsque leur ownership est commun.
Le principe directeur reste :
Git est le backend du contenu ; une forge est une capacité optionnelle.
Workspace Rust matérialisé
alpha.2 instancie le skeleton PGR 002-RUST_CARGO_WORKSPACE. alpha.3 matérialise les trois premières frontières passives :
knowledge-domain-lib famille/source de contenu
knowledge-git-api GitRepository provider-neutral
knowledge-sync-api RepositoryEventSource et signal de réconciliation
alpha.5 complète la fondation avec :
knowledge-web-lib composition Actix/Maud et assets Vite
knowledge-pgr-server launcher PGR
knowledge-howtos-server launcher HOWTOs
knowledge-snippets-server launcher snippets
Les deux crates -api sont justifiées par des implémentations réellement remplaçables ou multiples. Elles restent sans runtime, sans logging propre et sans dépendance à Actix, Gitea ou PostgreSQL.
Les tests unitaires Rust sont désormais placés hors de src/ sous unit_tests/, conformément à la convention locale Knowledge sélectionnée sur l'option permise par PGR.
Le workspace est une application déployable à train de release unique. Knowledge choisit néanmoins de conserver Cargo.lock comme état généré local et ignoré par Git ; les manifests Cargo portent le contrat versionné des dépendances. Les artefacts Cargo sont dirigés hors du dépôt vers ../builds/sasedev-knowledge/target/.
Vue de responsabilités
pgr.sasedev.com howtos.sasedev.com snippets.sasedev.com
| | |
knowledge-pgr-server knowledge-howtos-server knowledge-snippets-server
\________________________|________________________/
|
v
application use cases
|
+----------------------+-----------------------+
| | |
v v v
content/domain contracts GitRepository RepositoryEventSource
| | |
| v v
| Git local/remotes explicit/poll/hook/webhook
|
+---------------------> GitForge (optionnel)
| Gitea/autre forge
|
+---------------------> PostgreSQL partagé
| état/index/relations/jobs
|
+---------------------> projections/cache locales
lecture/recherche/rendu
La dépendance pointe vers les contrats métier. Les détails Actix, processus Git, API Gitea ou SQL ne remontent pas dans le domaine.
GitRepository ne porte dans la stable 0.0.1 que les opérations génériques alors nécessaires : rafraîchir la vue locale d'une source et résoudre une référence opaque vers une révision Git autoritaire. 0.0.2 n'étend ce contrat que lorsqu'un besoin de snapshot réellement implémenté le démontre. Le contrat ne suppose ni API de forge, ni type de remote particulier, ni format SHA figé.
RepositoryEventSource ne produit qu'un RepositorySyncSignal associé à un source_id. La cause reste volontairement grossière (Explicit, Periodic, ExternalSignal) afin que hook Git, webhook de forge ou watcher ne deviennent pas des concepts autoritaires du domaine.
1. Applications publiques
Les trois façades publiques sont des unités de déploiement distinctes :
knowledge-pgr-server -> famille PGR
knowledge-howtos-server -> famille HOWTO
knowledge-snippets-server -> famille snippet
Chaque serveur :
- compose les mêmes bibliothèques communes lorsque la responsabilité est réellement partagée ;
- possède sa configuration de source(s) Git et de publication ;
- peut être déployé et dimensionné indépendamment ;
- ne porte pas de logique métier dupliquée dans
main.rs; - peut utiliser le même PostgreSQL logique que les deux autres applications.
La topologie d'administration reste DEFER. Une future application d'administration séparée n'est créée que si son boundary de déploiement devient réellement utile.
2. Application et domaine
Le domaine porte les concepts durables : famille de contenu, source de contenu, révision, proposition, validation, publication, type documentaire et références entre contenus.
La couche application orchestre les cas d'usage tels que synchroniser une source, indexer une révision, ouvrir/revoir une proposition et publier une révision autorisée.
Les modèles HTTP, SQL et payloads Gitea ne sont pas des modèles domaine par défaut.
Le domaine ne suppose pas qu'une famille possède exactement un repository. Il manipule des identités de source configurables afin de permettre plusieurs repositories PGR/HOWTO/snippet à l'avenir sans refonte du modèle.
3. Git générique
Le contrat GitRepository reste provider-neutral. 0.0.2-alpha.1 sélectionne comme première implémentation un processus git contrôlé et 0.0.2-alpha.2 matérialise son premier owner runtime :
knowledge-git-lib
Cette sélection est une décision locale Knowledge, pas un couplage du contrat au CLI Git. Une autre implémentation pourra remplacer le backend ultérieurement si des preuves de maintenance, performance ou portabilité le justifient.
Configuration physique
knowledge-domain-lib continue de posséder SourceId et ContentSource. Les URLs, chemins locaux, exécutable Git et répertoires de travail ne remontent pas dans le domaine.
knowledge-git-lib possède la configuration physique :
source_id -> local path ou remote Git générique
source_id -> référence cible configurée
backend -> racine d'état Git contrôlée
La composition runtime pourra charger ces valeurs depuis la configuration de déploiement. La persistance logique future dans PostgreSQL reste différée.
Matérialisation locale
Les données générées résident hors du dépôt applicatif et hors des dépôts éditoriaux :
<git-state-root>/
hooks-disabled/
repositories/
<source-key>.git/
snapshots/
<source-key>/
<resolved-revision>/
Le backend reçoit explicitement <git-state-root> ; il n'utilise aucun défaut implicite sous le workspace. Une variable de déploiement telle que KNOWLEDGE_GIT_STATE_ROOT pourra alimenter la composition, mais la librairie reçoit un chemin et ne lit pas elle-même l'environnement.
source-key est une clé sûre dérivée par l'implémentation. En alpha.2, elle encode de manière déterministe les octets UTF-8 du SourceId sous une forme hexadécimale préfixée ; un SourceId ou une URL remote ne sont jamais interpolés directement comme chemin de filesystem.
Pendant 0.0.2, une racine d'état appartient exclusivement à une instance backend/processus. Le partage simultané d'une même racine entre plusieurs processus reste DEFER jusqu'à matérialisation d'un verrouillage inter-processus.
Mirror et refresh
Une source locale ou remote converge vers un mirror Git contrôlé. alpha.2 implémente l'initialisation et l'inspection bare minimale ; refresh, résolution et snapshot restent dans les tranches suivantes.
L'initialisation utilise conceptuellement :
git clone --mirror [--no-local pour une source locale] -- <source> <mirror>
Pour une source locale, --no-local évite de créer une dépendance par hardlinks/optimisation locale sur le repository éditorial.
Le refresh réutilise la configuration du remote du mirror et met les refs à jour avec pruning. L'opération doit être idempotente et ne dépend d'aucune API de forge.
Résolution et snapshot
Une référence cible n'est jamais considérée comme identité autoritaire avant résolution. Le backend exige qu'elle se résolve vers un commit réel, selon la sémantique :
git --git-dir=<mirror> rev-parse --verify --end-of-options <reference>^{commit}
La valeur obtenue devient GitRevision.
Le snapshot de lecture est ensuite créé comme worktree détaché adressé par cette révision. Un snapshot déjà présent n'est réutilisé qu'après vérification de son HEAD et d'un état propre ; une divergence produit une erreur récupérable plutôt qu'un nettoyage destructif silencieux.
Les hooks Git sont désactivés pour les opérations de checkout/worktree appartenant au backend. Les commandes sont exécutées directement sans shell, avec prompts interactifs désactivés et sans journaliser de credential.
Scope futur du contrat Git
Les opérations génériques à plus long terme peuvent inclure status/diff, branch, commit, merge/rebase et push. Elles ne sont pas ajoutées en 0.0.2 : la release n'étend le contrat que pour la matérialisation de lecture réellement nécessaire.
La baseline conserve au moins trois sources indépendantes (pgr, howtos, snippets) mais le mapping reste configurable et extensible ; aucun match codé en dur n'impose exactement trois repositories.
4. Forge optionnelle
Le contrat conceptuel GitForge porte uniquement les capacités qui ne sont pas des opérations Git fondamentales :
pull/merge requests
issues
permissions forge
releases forge
webhooks et métadonnées spécifiques
L'absence d'un adapter forge ne doit pas empêcher la synchronisation d'un dépôt local/générique, la lecture publique ou le workflow interne de proposition.
Gitea est le premier candidat d'intégration mais n'est pas figé comme dépendance du domaine.
5. Événements et synchronisation
Le contrat RepositoryEventSource produit un signal de synchronisation, pas une vérité de contenu.
Sources conceptuelles :
sync explicite
polling
hook Git local/serveur
webhook de forge
watcher filesystem éventuel
Toutes les sources doivent converger vers le même cas d'usage :
signal(source_id)
-> refresh du mirror si nécessaire
-> résolution de la révision cible Git
-> comparaison avec previous_revision
-> snapshot/worktree de cette révision si changement
-> futurs parsing + validation + projection
Un webhook peut être perdu ou dupliqué. Le commit Git résolu reste l'identité autoritaire.
Ownership de 0.0.2
La synchronisation partagée est une responsabilité de couche application, distincte du backend Git et des producteurs de signaux. La candidate existante knowledge-app-lib devient l'owner naturel si l'orchestration réelle confirme qu'une crate partagée est nécessaire ; elle n'est pas créée en alpha.1 comme placeholder.
Sans PostgreSQL dans cette release, la dernière révision connue n'est pas stockée dans un fichier ad hoc caché. Le cas d'usage reçoit previous_revision de l'appelant et retourne explicitement la révision observée ainsi qu'un résultat Changed ou Unchanged. La future persistance pourra remplacer cet état appelant sans modifier la frontière Git.
La cause Periodic reste supportée par knowledge-sync-api, mais 0.0.2-alpha.1 ne désigne pas encore de scheduler concret. Un scheduler n'est matérialisé que si son lifecycle/runtime est démontré ; lorsqu'il existe, il appelle exactement le même cas d'usage que la voie explicite.
La synchronisation peut rester composée par chacun des trois serveurs au début. Un worker réseau séparé n'est créé que si un owner/lifecycle distinct devient nécessaire.
6. Propositions et revues
Le workflow exact reste à formaliser avant implémentation, mais le contrat minimal conserve :
auteur
famille et source cible
révision Git de base
patch ou contenu proposé
statut
historique de revue
résultat de validation
révision/branche résultante si acceptée
Une proposition ne modifie jamais directement la révision publiée.
Si la base n'est plus courante, le workflow doit pouvoir signaler un conflit/rebase plutôt que forcer un écrasement.
Une pull request de forge MAY refléter ou accélérer ce workflow ; elle n'en est pas le modèle autoritaire obligatoire.
7. Parsing et validation des contenus
Le contrat conceptuel ContentRepository expose du contenu normalisé au métier sans révéler le provider Git ou forge.
Le format final des HOWTOs/snippets reste DEFER. Le parseur devra néanmoins pouvoir produire au minimum :
identité logique
famille de contenu
source Git
révision source
métadonnées normalisées
corps rendu/parsé
références sortantes
résultat de validation
Les validations normatives PGR restent propres au corpus PGR. Knowledge ne doit pas inventer une règle PGR pour simplifier son parseur.
8. PostgreSQL partagé, index, recherche et cache
La baseline retient une base PostgreSQL logique partagée par les trois applications.
Ce choix est justifié par l'état transversal : identités, sessions, propositions, synchronisation, relations entre familles, index et recherche. Il évite trois bases divergentes pour des concepts qui appartiennent à la même plateforme.
PostgreSQL MAY contenir des documents/index/métadonnées dérivés et assurer une première capacité de recherche. Aucun moteur de recherche externe n'est sélectionné en 0.0.1.
Les enregistrements dérivés doivent porter au minimum l'identité de famille/source et la révision Git afin de savoir exactement quel contenu ils représentent.
Une séparation physique de bases ou de schémas par application reste possible ultérieurement si une contrainte de sécurité, disponibilité, charge ou ownership la justifie. Elle n'est pas la baseline de fondation.
9. Rendu et publication
Chaque application publique sélectionne et publie uniquement les familles/sources qui lui sont autorisées.
Pour PGR :
- le canal stable provient d'un tag stable vérifié par le workflow PGR ;
- un canal
devMAY exister mais doit être visuellement et sémantiquement distinct.
Pour HOWTOs/snippets :
- la cadence MAY être continue et plus légère ;
- le workflow SemVer/delta/history PGR n'est pas imposé artificiellement.
Actix Web et Maud sont des choix de composition/rendu locaux. Vite gère les assets/interactions frontend lorsqu'il est matérialisé.
La baseline alpha.4 retient un package de laboratoire partagé Web/knowledge-template-lab/ pour les assets et templates statiques de référence communs. Cette décision évite trois toolchains identiques ; une divergence réelle pourra introduire ultérieurement des entrées ou packages supplémentaires.
Pipeline Web de référence et rendu runtime
Le frontend Web n’est pas une SPA concurrente du SSR Maud. Il possède deux responsabilités distinctes :
Vite + TypeScript + Sass + Bootstrap
-> assets construits et templates statiques de référence
-> typecheck/build/preview/smoke indépendants du backend
Actix Web + Maud
-> markup runtime des trois applications
-> consommation des mêmes contrats d’assets
Les templates statiques de référence permettent de travailler rapidement sur le HTML, le responsive, les classes Bootstrap, les comportements TypeScript et les styles Sass sans démarrer les serveurs Rust. Ils sont ensuite adaptés en composants/templates Maud pour le rendu runtime. Cette adaptation ne doit pas créer deux produits UI indépendants : la tranche Web devra définir les invariants vérifiables qui empêchent la dérive entre référence statique et rendu Maud.
Le build production Vite doit exposer un manifest permettant au backend de convertir une clé d’entrée stable en URL d’asset hashé. Le corpus actix-auth-app montre un pattern utile : manifest Vite activé, entrées TypeScript/Sass explicites, loader Rust et sélection dev-server versus manifest en fonction du mode. Knowledge retient le contrat d’intégration, pas l’implémentation historique telle quelle.
En particulier, Knowledge ne doit pas hériter mécaniquement :
- d’un origin Vite
localhost:5173codé en dur ; - d’un préfixe
/resuniversel ; - d’un
panic!lorsque le manifest est absent ou invalide ; - d’une arborescence d’assets spécifique au corpus ;
- de dépendances TypeScript
@types/*non justifiées par les packages réellement consommés.
alpha.4 possède le frontend indépendant et la production du manifest. La clé stable src/main.ts constitue l'entrée d'asset commune de départ ; son entrée de manifest doit référencer les sorties JavaScript et CSS générées.
alpha.5 matérialise knowledge-web-lib comme owner partagé de l'intégration Actix/Maud. La crate choisit entre un dev server configuré par KNOWLEDGE_VITE_DEV_ORIGIN et un manifest configuré par KNOWLEDGE_VITE_MANIFEST_PATH. En production elle dérive le répertoire assets/ depuis le manifest et le sert sous le préfixe /assets, tandis que KNOWLEDGE_ASSET_BASE_PATH garde le préfixe d'URL configurable. Les erreurs de configuration/manifest restent récupérables et remontent avant le démarrage du listener.
Les trois packages knowledge-pgr-server, knowledge-howtos-server et knowledge-snippets-server sont des launchers minces : chacun sélectionne ServerSite et le nom de sa variable de bind, puis délègue au runtime commun. Ils ne possèdent aucune logique de rendu ou de manifest dupliquée.
Réconciliation 0.0.1-beta.1
La fondation matérialisée est cohérente avec les objectifs 0.0.1 :
- trois launchers séparés, sans duplication de runtime ;
- Git encore contractuel et provider-neutral, sans adapter de forge ;
- synchronisation encore contractuelle, sans worker artificiel ;
- frontend de référence indépendant et rendu runtime Maud effectivement relié au manifest Vite ;
- aucune persistance PostgreSQL, auth complète, UI admin complète, forge spécifique ou format final HOWTO/snippet introduit par anticipation.
0.0.2-alpha.1 conserve cette réconciliation comme baseline : la release courante ouvre uniquement le chemin Git générique de lecture et la synchronisation réelle ; les autres axes restent différés.
10. Administration
L'administration Web est une interface vers les mêmes cas d'usage que l'édition locale Git, pas une deuxième source de vérité.
Chaque application doit pouvoir protéger sa propre surface d'administration métier :
knowledge-pgr-server -> administration PGR
knowledge-howtos-server -> administration HOWTOs
knowledge-snippets-server -> administration snippets
Les chemins HTTP exacts restent DEFER. Les exemples /admin, /adminapi, /login ou autres observés dans les corpus historiques ne sont pas adoptés comme convention Knowledge.
Une quatrième application d'administration MAY être créée ultérieurement si une vraie responsabilité transversale apparaît. Elle pourra agréger comptes, sources, jobs, propositions ou autres vues communes, mais MUST NOT remplacer les contrôles d'autorisation propres aux trois applications.
Une UI complète reste hors scope 0.0.1.
11. Authentification et autorisation
Le domaine doit distinguer identité, authentification, rôle/capacité et action autorisée. L'identité et les sessions peuvent être transversales via PostgreSQL partagé ; l'autorisation métier reste vérifiée côté serveur dans chaque application.
Le fournisseur d'identité, les URLs de login, le modèle détaillé de rôles/capabilities, OAuth2/OIDC et MFA restent différés.
Les permissions forge ne sont pas supposées équivalentes aux permissions applicatives.
Le partage d'une base PostgreSQL facilite une identité et des autorisations transversales aux trois façades sans imposer trois silos de comptes.
12. Frontières PGR / HOWTO / snippet / project-local
Les quatre familles gardent une sémantique propre :
| Famille | Rôle | Force |
|---|---|---|
| PGR | règle/contrat de gouvernance | normative dans son scope |
| HOWTO | procédure, diagnostic, explication, anti-pattern | guidance |
| snippet | exemple court, commande, configuration, fragment | contextualisée |
| project-local | décision concrètement instanciée | locale au projet |
Les métadonnées peuvent relier ces familles sans promotion automatique de force normative.
Une pratique répétée dans HOWTO/snippet peut devenir un candidat d'étude PGR ; elle n'est normative qu'après le workflow PGR correspondant.
13. Git versus PostgreSQL
Autoritaire dans Git
- corps du contenu publié ;
- métadonnées éditoriales versionnées avec ce contenu ;
- historique de révisions Git ;
- refs/tags/branches autorisées par la politique de chaque source.
État applicatif PostgreSQL partagé
- utilisateurs/sessions ;
- propositions et revue ;
- commentaires/audit applicatif ;
- configuration logique persistante des sources lorsque PostgreSQL sera matérialisé ;
- mapping source/famille/application ;
- dernière révision synchronisée/indexée par source lorsque la persistance sera matérialisée ;
- index et recherche dérivés ;
- relations calculées entre familles ;
- jobs de synchronisation/publication ;
- cache/projections reconstructibles.
Interdit comme raccourci architectural
Une modification du contenu publié ne doit pas exister uniquement dans PostgreSQL après acceptation. L'acceptation doit produire une révision Git avant qu'elle devienne contenu publié.
Topologie de dépôts retenue
sasedev-knowledge application, workspace et gouvernance de la plateforme
pgr source Git normative PGR existante
howtos source Git procédurale séparée
snippets source Git courte/contextualisée séparée
Les dépôts de contenu ne sont pas fusionnés dans PGR et ne sont pas requis comme submodules Git du dépôt applicatif. Leur localisation et leurs credentials sont de la configuration de déploiement.
Pendant 0.0.2, cette configuration de déploiement est adaptée en configuration physique possédée par knowledge-git-lib. La racine de mirrors/worktrees reste un état généré reconstructible et ne remplace pas le futur stockage logique PostgreSQL.