From 2ea473d3bd2d8bd8323c31f2355b5d427852e164 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 18 Apr 2020 17:17:54 +0200 Subject: Rework website structure --- .gitignore | 1 + .webpull | 1 - render.js | 62 +++++- src/Association/AG1.md | 49 +++++ src/Association/AG2.md | 136 ++++++++++++ src/Association/Statuts.md | 126 +++++++++++ src/Association/index.md | 54 +++++ src/Documentation/Association/AG1/index.md | 49 ----- src/Documentation/Association/AG2/index.md | 136 ------------ src/Documentation/Association/Statuts/index.md | 126 ----------- src/Documentation/Association/index.md | 11 - .../Technique/Infra/Internet/index.md | 5 - .../Technique/Logiciels/Bottin/index.md | 1 - .../Technique/Logiciels/Diplonat/index.md | 1 - .../Technique/Logiciels/Garage/index.md | 241 --------------------- .../Technique/Logiciels/Guichet/index.md | 1 - .../Technique/Services/Jitsi/exemple_offre.txt | 118 ---------- .../Technique/Services/Jitsi/index.md | 94 -------- .../Jitsi/livebox_parefeu_personnalise.png | Bin 84154 -> 0 bytes src/Documentation/Technique/index.md | 22 -- src/Documentation/_markdown.pug | 14 -- src/Documentation/index.md | 54 ----- "src/Technique/D\303\251veloppement/Bottin.md" | 1 + "src/Technique/D\303\251veloppement/Diplonat.md" | 1 + "src/Technique/D\303\251veloppement/Garage.md" | 241 +++++++++++++++++++++ "src/Technique/D\303\251veloppement/Guichet.md" | 1 + src/Technique/Infra/Internet.md | 5 + src/Technique/Operations/Assets/exemple_offre.txt | 118 ++++++++++ .../Assets/livebox_parefeu_personnalise.png | Bin 0 -> 84154 bytes src/Technique/Operations/Jitsi.md | 94 ++++++++ src/Technique/index.md | 22 ++ src/_layout.pug | 13 +- src/_markdown.pug | 7 + src/_mixin/menu.pug | 10 +- src/css/main.css | 2 +- src/index.pug | 102 ++++----- 36 files changed, 982 insertions(+), 937 deletions(-) create mode 100644 src/Association/AG1.md create mode 100644 src/Association/AG2.md create mode 100644 src/Association/Statuts.md create mode 100644 src/Association/index.md delete mode 100644 src/Documentation/Association/AG1/index.md delete mode 100644 src/Documentation/Association/AG2/index.md delete mode 100644 src/Documentation/Association/Statuts/index.md delete mode 100644 src/Documentation/Association/index.md delete mode 100644 src/Documentation/Technique/Infra/Internet/index.md delete mode 100644 src/Documentation/Technique/Logiciels/Bottin/index.md delete mode 100644 src/Documentation/Technique/Logiciels/Diplonat/index.md delete mode 100644 src/Documentation/Technique/Logiciels/Garage/index.md delete mode 100644 src/Documentation/Technique/Logiciels/Guichet/index.md delete mode 100644 src/Documentation/Technique/Services/Jitsi/exemple_offre.txt delete mode 100644 src/Documentation/Technique/Services/Jitsi/index.md delete mode 100644 src/Documentation/Technique/Services/Jitsi/livebox_parefeu_personnalise.png delete mode 100644 src/Documentation/Technique/index.md delete mode 100644 src/Documentation/_markdown.pug delete mode 100644 src/Documentation/index.md create mode 100644 "src/Technique/D\303\251veloppement/Bottin.md" create mode 100644 "src/Technique/D\303\251veloppement/Diplonat.md" create mode 100644 "src/Technique/D\303\251veloppement/Garage.md" create mode 100644 "src/Technique/D\303\251veloppement/Guichet.md" create mode 100644 src/Technique/Infra/Internet.md create mode 100644 src/Technique/Operations/Assets/exemple_offre.txt create mode 100644 src/Technique/Operations/Assets/livebox_parefeu_personnalise.png create mode 100644 src/Technique/Operations/Jitsi.md create mode 100644 src/Technique/index.md create mode 100644 src/_markdown.pug diff --git a/.gitignore b/.gitignore index e5322a6..09c4aa8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ static/ node_modules/ +*.swp diff --git a/.webpull b/.webpull index 2d25330..4df1cf1 100755 --- a/.webpull +++ b/.webpull @@ -1,4 +1,3 @@ #!/bin/bash -rm -rf static/ npm install node render.js diff --git a/render.js b/render.js index 8fb7545..352fe7f 100644 --- a/render.js +++ b/render.js @@ -10,7 +10,7 @@ const log = process.env.VERBOSE ? console.log : unit const walk = async (path, filename) => { log('[walk]', path) const type = await fs.lstat(path) - if (type.isFile()) return {type: 'file', path: path, name: filename || path} + if (type.isFile()) return {type: 'file', path: path, name: filename || path, tags:[]} if (!type.isDirectory()) return null const files = await fs.readdir(path) @@ -18,6 +18,7 @@ const walk = async (path, filename) => { type: 'folder', path: path, name: filename || path, + tags: [], children: await Promise.all(files.map(file => walk(`${path}/${file}`, file))) } } @@ -32,6 +33,7 @@ const is_static = suffixl(...ext_static) const is_md = suffixl(...ext_md) const is_pug = suffixl(...ext_pug) const is_templated = f => is_md(f) /* || is_rst(f) */ +const is_document = f => is_templated(f) || is_pug(f) const prefix = file => ext => file.substring(0, ext.length) == ext ? ext : null const prefixl = (...l) => file => l.find(prefix(file)) @@ -54,12 +56,41 @@ const propagate_md_layout = (tree, markdown_template) => { const elagate = tree => { if (tree.type != 'folder') return tree - const lh = e => log('[elegate]', e.path) && false + const lh = e => log('[elagate]', e.path) && false tree.children = tree.children.filter(e => !(e.name[0] == '_') || lh(e)) tree.children.forEach(elagate) return tree } +const tag_document = tree => { + if (tree.type == 'file' && is_document(tree.name)) { + tree.tags.push('document_leaf', 'document') + log('[tag_document]', tree.path, 'document_leaf') + } else if (tree.type == 'folder') { + tree.children.forEach(tag_document) + if(tree.children.some(c => c.tags.includes('document'))) { + tree.tags.push('document_branch', 'document') + log('[tag_document]', tree.path, 'document_branch') + } + } + return tree +} + +const reference_index = indexes => tree => { + if (tree.type != 'folder') return tree; + + const index = tree.children.find(e => indexes.includes(e.name)) + if (index) { + tree.index = index + tree.tags.push('has_index') + log('[reference_index]', tree.path, index.name) + index.tags.push('is_index') + } + tree.children.forEach(reference_index(indexes)) + + return tree; +} + const propagate_nice_name = prefix => tree => { const without_prefix = tree.path.substring(prefix.length) const splitted = without_prefix.split('/').filter(v => v.length > 0) @@ -90,10 +121,12 @@ const prepare_copy = (old_prefix, new_prefix, exts) => tree => { const prepare_pug = (old_prefix, new_prefix) => tree => { if (tree.type == 'file' && is_pug(tree.name)) { + tree.old_url = tree.url + tree.url = rm_prefix(old_prefix)(rm_suffix(...ext_pug)(tree.path)) + '.html' tree.generate = { cmd: 'pug', src: tree.path, - out: new_prefix + rm_prefix(old_prefix)(rm_suffix(...ext_pug)(tree.path)) + '.html' + out: new_prefix + tree.url } log('[prepare_pug]',tree.generate.src,'->',tree.generate.out) } @@ -106,11 +139,13 @@ const prepare_pug = (old_prefix, new_prefix) => tree => { const prepare_md = (old_prefix, new_prefix) => tree => { if (tree.type == 'file' && is_md(tree.name)) { + tree.old_url = tree.url + tree.url = rm_prefix(old_prefix)(rm_suffix(...ext_md)(tree.path)) + '.html' tree.generate = { cmd: 'pug', src: tree.template.path, markdown: tree.path, - out: new_prefix + rm_prefix(old_prefix)(rm_suffix(...ext_md)(tree.path)) + '.html' + out: new_prefix + tree.url } log('[prepare_md]',tree.generate.markdown,'+',tree.generate.src,'->',tree.generate.out) } @@ -170,16 +205,35 @@ const do_pug = (prt, root) => async tree => { return tree } +const rm_tree = t => { + if (t.type == 'file') { + log('[do_clean] file', t.path) + return fs.unlink(t.path) + } + + return Promise + .all(t.children.map(rm_tree)) + .then(_ => { + log('[do_clean] path', t.path) + return fs.rmdir(t.path) + }) +} + +const do_clean = path => tree => walk(path).then(rm_tree).then(_ => tree) const conf = { src: './src', dest: './static'} walk(conf.src) .then(propagate_md_layout) .then(elagate) + .then(tag_document) + .then(reference_index(['index.md', 'index.pug'])) .then(propagate_nice_name(conf.src)) .then(prepare_copy(conf.src, conf.dest)) .then(prepare_pug(conf.src, conf.dest)) .then(prepare_md(conf.src, conf.dest)) .then(prepare_folder(conf.src, conf.dest)) + //.then(v => {log(v) ; return v}) + .then(do_clean(conf.dest)) .then(do_folder) .then(do_copy) .then(do_pug()) diff --git a/src/Association/AG1.md b/src/Association/AG1.md new file mode 100644 index 0000000..9d89540 --- /dev/null +++ b/src/Association/AG1.md @@ -0,0 +1,49 @@ +Le 13 janvier 2020 à 19 heures, les fondateurs de l'association +Deuxfleurs se sont réunis en assemblée générale constitutive au 24 rue +des Tanneurs à Rennes. Sont présents Adrien, Alex, Anaïs, Axelle, +Louison, Maximilien, Quentin, Rémi et Vincent. + +L'assemblée générale désigne Adrien en qualité de président de +séance et Quentin en qualité de secrétaire de séance. Le +président de séance met à la disposition des présents le projet de +statuts de l'association et l'état des actes passés pour le compte de +l'association en formation. + +Puis il rappelle que l'assemblée générale constitutive est appelée à +statuer sur l'ordre du jour suivant : + +- présentation du projet de constitution de l'association ; +- présentation du projet de statuts ; +- adoption des statuts ; +- désignation des premiers membres du conseil ; +- pouvoirs en vue des formalités de déclaration et publication. + +Enfin, le président de séance expose les motifs du projet de création de +l'association et commente le projet de statuts. Il ouvre la discussion. +Un débat s'instaure entre les membres de l'assemblée. + +Après quoi, personne ne demandant plus la parole, le président met +successivement aux voix les délibérations suivantes. + +### 1e délibération + +L'assemblée générale adopte les statuts dont le projet lui a été soumis. +Cette délibération est adoptée à l'unanimité. + +### 2e délibération + +L'assemblée générale constitutive désigne en qualité de premiers membres +du conseil d'administration : + +- Adrien +- Alex +- Maximilien +- Quentin +- Vincent + +Conformément aux statuts, cette désignation est faite pour une durée +expirant lors de l'assemblée générale qui sera appelée à statuer sur les +comptes de l'exercice clos le 13 janvier 2021. Les membres du conseil +ainsi désignés acceptent leurs fonctions + +Nom, prénom et signature du président et du secrétaire de séance diff --git a/src/Association/AG2.md b/src/Association/AG2.md new file mode 100644 index 0000000..4a27429 --- /dev/null +++ b/src/Association/AG2.md @@ -0,0 +1,136 @@ +AG2 +=== + +Présent : Quentin, Alex, Maximilien, Vincent +Remote : Simon +Invité : Tom + +> Maximilien prends des notes. + +https://p.adnab.me/pad/#/2/pad/edit/VOqs46ZeH7iR2EnL63xeXxHP/ + +Depuis l'AG 1 +------------- + +Quentin (anime la réunion) : + +- Migration DNS (depuis Cloudflare vers Online) +- Ajout domaine deuxfleurs.org (acheté par Maximilien) +- Quentin explique la partie technique +- Alex explique les avancé sur la partie LDAP/authentification basé sur consul (bottin + guichet) +- Ajout de l'invitation dans guichet (lien à usage unique) +- Nettoyage des comptes LDAP +- Mettre une étiquette deuxfleurs sur la boite de Quentin +- Discussion avec Jaxom & Almet sur l'hébergement +- Le site web c'est important, tout le monde en parle +- Refondre la partie graphique pour la rendre plus attrayante et moins RFC-like +- Alex s'est lancé dans du dev de bridge matrix qui fonctionne pour mattermost et XMPP +- Le bridge mattermost focntionne pas mal + +Ce que l'on n'a pas encore fait +------------------------------- + +Banque : la moitié des quotisation part dans une banque +Décision de faire un pot commun ? +Continuer sans ? (mais c'est dans les status) + +> Vote : trésorerie en liquide jusqu'à 200€ +> Sinon on dépense ou bien on ouvre un compte + +- 200€ : contre 0, neutre 0, unanimité pour +- Fonctionner en pot commun : contre 0, neutre 0, unanimité pour + +**Motion voté.** + +### Gestion de la compta + +Quentin a un compte courant vide. Mais à son avis pas une bonne idéee. +Gestion de la compta sur un logiciel (lequel ?) +Trésorier ? + +Alex a trouvé une boite. + +> Vote : Alex est le gardien de la boite qui contient les cotisations dans la limite de 200€ +> Contre : 0, Neutre 0, Uninimité + +Pour le choix du logiciel, Maximilien enverra un mail avec des solutions. L'idée de base est de mettre le fichier dans un repo git (facilement backupé et consultables), avec des commit signés. + +Pour les présents, les quotisations sont payable à la fin de l'AG. + +### Charte + +Trouver pour la prochaine AG (voir avant) une base. Maximilien doit envoyer des idées sur la base de ce qui est fait en conférence. Quentin envoie des idée pour les projets Open-Source. + +### Site web + +Intégrer la documentation au site web, afin qu'elle soit consultable et plus transparent par rapport aux infrastructure. + +Outil pour build du Markdown avec un blog statique. +Utiliser les outils de templating des trucs web. + +Quentin fera une proposition. +Simon : Les gens qui font des choses se doivent de les documenter. + +À qui s'adresse la documentation + +- tout ce qui tourne autours de l'administration +- de l'accessibilité +- la partie technique + +Répliquer le gitea d'Adrien (Maximilien va leur faire sur le siens). + +### Lieux de réunion + +Vincent propose le salon de thé (on peut commander un café), mais on est trop bruyant ? + +Pas de souci tant que l'on rentre dans un salon de chez quelqu'un (jusqu'à 10-12 personnes) + +Les objectifs +------------- + +Quentin : but original du CHATON, documenter l'auto-hébergement distribué, fournir des services que tu gères toi-même, sans manipulation ni tracking + +Trois niveaux : + +- petits services +- backups et disaster-recovery +- CHATON (candidature chez framasoft et référencement) : l'objectif est-il d'obtenir le label ou bien simplement de s'inspirer de leur idéal ? + +> Simon : pour la partie non technique, sauf si cela présente un effort technique trop important. +> Quentin : leur cahier des charges n'est pas aberrant et pourrait être un guide sur le développement de l'infra + +**TODO** : faire un document de travail (Quentin a fait une milestone dans le gitea) +- géo-distribué (résilient à la perte d'une machine/d'un site - penser datacenter) + +**TARGET** soumettre une candidature _CHATON_ dans 6 mois + +Pour la géo-distribution, Quentin préconise le backend S3-compatible +Approche totalement différente des ressources. + +Débat à suivre. + +### Recommendation + +Quentin : si jamais on embarque des gens et que l'on leur fait faux bond, on dessert la cause de l'hébergement participatif. + +Alex : il est de la responsabilité des personnes qui crée un compte de s'informer des limites + +Simon : par cooptation : chacun vois midi à sa porte. + +> Vote : Maximilie propose l'ajout d'un avertissement sur le formulaire d'inscription de guichet. La rédaction du bloc de text est laissé à Maximilien, et soumise à l'approbation du prochain conseil d'afministration. +> Contre : 0, Neutre 0, Uninimité + +Repasser sur le document de travail +----------------------------------- + +Alex : piorité de faire le site web et d'avoir une solution facilement éditable pour les PV d'AG & co (pas tout le temps dépendre des pads) + +Quentin : les PV en PDF sont stockés dans un repos + +Retex de Toms +------------- + +Toms est intéressé pour rejoindre l'association. +Pas assez de vison pour savoir si c'est réalisable. + +Quentin montre la nouvelle maquette du site web. diff --git a/src/Association/Statuts.md b/src/Association/Statuts.md new file mode 100644 index 0000000..caa3978 --- /dev/null +++ b/src/Association/Statuts.md @@ -0,0 +1,126 @@ +### Article 1. Constitution et dénomination + +Il est fondé entre les adhérents aux présents statuts une association +régie par la loi 1901, ayant pour titre Deuxfleurs. + +### Article 2. Buts + +Cette association a pour but de défendre et promouvoir les libertés +individuelles et collectives à travers la mise en place d'infrastuctures +numériques libres. + +### Article 3. Siège social + +Le siège social est fixé au 10A, Allée de Lanvaux, 35700 Rennes. Il +pourra être transféré suite à un vote par l'assemblée générale. + +### Article 4. Durée de l'association + +L'association perdure tant qu'elle possède au moins un membre, ou +jusqu'à sa dissolution décidée en assemblée générale. + + + +### Article 5. Admission et adhésion + +Pour faire partie de l'association, il faut être coopté par un membre de +l'association, adhérer aux présents statuts et s'acquitter de la +cotisation annuelle dont le montant est de 10 euros. + +### Article 6. Composition de l'association + +L'association se compose exclusivement de membres admis selon les dispositions +de l'[Article 5](#article-admission) et à jour de leur cotisation. Tout membre +actif possède une voix lors des votes en assemblée générale. Est considéré +actif tout membre présent à l'assemblée générale (physiquement, par +visioconférence ou par procuration écrite donnée à un autre membre de +l'association). + +### Article 7. Perte de la qualité de membre + +La qualité de membre se perd par : + +- la démission, +- le non-renouvelement de la cotisation dans un délai de deux mois + après le 1er Janvier de l'année courante, +- le décès, +- la radiation prononcée aux deux tiers des votes exprimés, lors d'un + vote extraordinaire ou de l'assemblée générale. + + + +### Article 8. L'assemblée générale + +L'assemblée générale ordinaire se réunit au moins une fois par an, convoquée +par le conseil d'administration. L'assemblée générale extraordinaire est +convoquée par le conseil d'administration, à la demande de celui-ci ou à la +demande du quart au moins des membres de l'association. + +L'assemblée générale (ordinaire ou extraordinaire) comprend tous les +membres de l'association à jour de leur cotisation. Quinze jours au +moins avant la date fixée, les membres de l'association sont convoqués +via la liste de diffusion de l'association et l'ordre du jour est +inscrit sur les convocations. + +Le conseil d'administration anime l'assemblée générale. L'assemblée +générale, après avoir délibéré, se prononce sur le rapport moral et/ou +d'activités. Le conseil d'administration rend compte de l'exercice +financier clos et soumet le bilan de l'exercice clos à l'approbation de +l'assemblée dans un délai de six mois après la clôture des comptes. +L'assemblée générale délibère sur les orientations à venir et se +prononce sur le budget prévisionnel de l'année en cours. + +Elle pourvoit, au scrutin secret, à la nomination ou au renouvellement +des membres du conseil d'administration via un scrutin de Condorcet +Randomisé. Elle fixe le montant de la cotisation annuelle. Les décisions +de l'assemblée sont prises à la majorité des membres présents ou +représentés. Chaque membre présent ne peut détenir plus d'une +procuration. + +### Article 9. Membres mineurs + +Les mineurs peuvent adhérer à l'association sous réserve d'un accord +tacite ou d'une autorisation écrite de leurs parents ou tuteurs légaux. +Ils sont membres à part entière de l'association. Seuls les membres âgés +de 16 ans au moins au jour d'une élection sont autorisés à y voter, +notamment au cours d'une assemblée générale. Pour les autres, leur droit +de vote est transmis à leur représentant légal. + +### Article 10. Le conseil d'administration + +L'association est administrée par un conseil d'administration composé de +3 à 6 membres, élus pour 1 an dans les conditions fixées à +l'[Article 8](#article-ag). Tous les membres de l'association à jour de +leur cotisation sont éligibles. En cas de vacance de poste, le conseil +d'administration peut pourvoir provisoirement au remplacement de ses +membres. Ce remplacement est obligatoire quand le conseil +d'administration compte moins de 3 membres. Il est procédé à leur +remplacement définitif à la plus prochaine assemblée générale. Les +pouvoirs des membres ainsi élus prennent fin à l'époque où devrait +normalement expirer le mandat des membres remplacés. + +Le conseil d'administration met en œuvre les décisions de l'assemblée +générale, organise et anime la vie de l'association, dans le cadre fixé +par les statuts. Chacun de ses membres peut être habilité par le conseil +à remplir toutes les formalités de déclaration et de publication +prescrites par la législation et tout autre acte nécessaire au +fonctionnement de l'association et décidé par le conseil +d'administration. Tous les membres du conseil d'administration sont +responsables des engagements contractés par l'association. Tout contrat +ou convention passé entre l'association d'une part, et un membre du +conseil d'administration, son conjoint ou un proche, d'autre part, est +soumis pour autorisation au conseil d'administration et présenté pour +information à la plus prochaine assemblée générale. Le conseil +d'administration se réunit au moins 4 fois par an et toutes les fois +qu'il est convoqué par le tiers de ses membres. La présence de la moitié +au moins des membres du conseil est nécessaire pour que le conseil +d'administration puisse délibérer valablement. Les décisions sont prises +au consensus et, à défaut, à la majorité des voix des présents. Le vote +par procuration n'est pas autorisé. + +### Article 11. Modification des statuts de l'association + +Sur demande d'un tiers des membres actifs, ou sur demande du conseil +d'administration, des amendements aux statuts de l'association peuvent +être discutés et soumis au vote lors d'une assemblée générale, selon les +modalités de l'[Article 8](#article-ag). diff --git a/src/Association/index.md b/src/Association/index.md new file mode 100644 index 0000000..57ee2cc --- /dev/null +++ b/src/Association/index.md @@ -0,0 +1,54 @@ +### Notre raison d'être + +Aujourd'hui, de grandes entreprises conçoivent des services numériques qui ont +pour objectif de +maximiser le temps +que nous passons dessus, de +collecter et recouper des données +à notre insu pour nous influencer, de +limiter nos possibilités d'expression +au delà du cadre légal et de +créer de nouveaux monopoles. +Ces effets nous montrent que la technologie n'est pas +neutre et a un réel impact sur nos vies. En choisissant et en hébergeant nos +propres outils de communication, sans but lucratif ni hégémonique, nous +espérons nous affranchir de ces nuisances et préserver nos libertés. + +Pour en savoir plus, rendez-vous sur +La Quadrature du Net +et allez lire le manifeste des CHATONS. + + +### Nos objectifs + +#### Des utilisateurs impliqués + +Que ce soit à l'école, par l'expérimentation, via un forum d'échange, lors d'un +atelier, via une publicité à la télévision, un tutoriel, lors d'une discussion +avec un ami, il y toujours une phase d'apprentissage en informatique. +Malheureusement, dans ces conditions, dur de lutter pour des services libres +face à la puissance de frappe d'une entreprise et des logiciels ayant une base +d'utilisateurs immense. Nous pensons donc qu'une personne souhaitant s'héberger +chez un hébergeur indépendant a besoin d'un accompagnement. C'est pourquoi les +inscriptions se font par cooptation. La cooptation permet aussi un lien de +confiance et ainsi de se prémunir de bon nombres d'attaques que subissent les +hébergeurs. + +

En savoir plus sur l'association

+ +#### Une architecture résiliente + +Les sites webs, les réseaux sociaux, les emails ne peuvent fonctionner que +grâce à des ordinateurs qui restent allumés 24/24h et qui n'attendent que vous. +Cependant, ces derniers sont faillibles. Une coupure d'électricité, un disque +dur cassé, une mise à jour ratée, un bug dans le logiciel, les raisons ne +manquent pas. Heureusement, il est possible de masquer ces pannes avec du +logiciel astucieusement conçu. C'est pourquoi vous avez l'impression que Google +est toujours disponible, que Dropbox ne perd pas vos données, etc. La gestion +de ces pannes, c'est aussi ce qui rend la vie compliquée aux hébergeurs +indépendants. Entre incompréhension des utilisateurs quand un service est hors +ligne et sueurs froides pour les administrateurs, ça n'a rien de marrant. Et +c'est très chronophage. Notre objectif est donc de construire des solutions +d'hébergements qui peuvent résister à ces pannes. + +

En savoir plus sur l'aspect technique

diff --git a/src/Documentation/Association/AG1/index.md b/src/Documentation/Association/AG1/index.md deleted file mode 100644 index 9d89540..0000000 --- a/src/Documentation/Association/AG1/index.md +++ /dev/null @@ -1,49 +0,0 @@ -Le 13 janvier 2020 à 19 heures, les fondateurs de l'association -Deuxfleurs se sont réunis en assemblée générale constitutive au 24 rue -des Tanneurs à Rennes. Sont présents Adrien, Alex, Anaïs, Axelle, -Louison, Maximilien, Quentin, Rémi et Vincent. - -L'assemblée générale désigne Adrien en qualité de président de -séance et Quentin en qualité de secrétaire de séance. Le -président de séance met à la disposition des présents le projet de -statuts de l'association et l'état des actes passés pour le compte de -l'association en formation. - -Puis il rappelle que l'assemblée générale constitutive est appelée à -statuer sur l'ordre du jour suivant : - -- présentation du projet de constitution de l'association ; -- présentation du projet de statuts ; -- adoption des statuts ; -- désignation des premiers membres du conseil ; -- pouvoirs en vue des formalités de déclaration et publication. - -Enfin, le président de séance expose les motifs du projet de création de -l'association et commente le projet de statuts. Il ouvre la discussion. -Un débat s'instaure entre les membres de l'assemblée. - -Après quoi, personne ne demandant plus la parole, le président met -successivement aux voix les délibérations suivantes. - -### 1e délibération - -L'assemblée générale adopte les statuts dont le projet lui a été soumis. -Cette délibération est adoptée à l'unanimité. - -### 2e délibération - -L'assemblée générale constitutive désigne en qualité de premiers membres -du conseil d'administration : - -- Adrien -- Alex -- Maximilien -- Quentin -- Vincent - -Conformément aux statuts, cette désignation est faite pour une durée -expirant lors de l'assemblée générale qui sera appelée à statuer sur les -comptes de l'exercice clos le 13 janvier 2021. Les membres du conseil -ainsi désignés acceptent leurs fonctions - -Nom, prénom et signature du président et du secrétaire de séance diff --git a/src/Documentation/Association/AG2/index.md b/src/Documentation/Association/AG2/index.md deleted file mode 100644 index 4a27429..0000000 --- a/src/Documentation/Association/AG2/index.md +++ /dev/null @@ -1,136 +0,0 @@ -AG2 -=== - -Présent : Quentin, Alex, Maximilien, Vincent -Remote : Simon -Invité : Tom - -> Maximilien prends des notes. - -https://p.adnab.me/pad/#/2/pad/edit/VOqs46ZeH7iR2EnL63xeXxHP/ - -Depuis l'AG 1 -------------- - -Quentin (anime la réunion) : - -- Migration DNS (depuis Cloudflare vers Online) -- Ajout domaine deuxfleurs.org (acheté par Maximilien) -- Quentin explique la partie technique -- Alex explique les avancé sur la partie LDAP/authentification basé sur consul (bottin + guichet) -- Ajout de l'invitation dans guichet (lien à usage unique) -- Nettoyage des comptes LDAP -- Mettre une étiquette deuxfleurs sur la boite de Quentin -- Discussion avec Jaxom & Almet sur l'hébergement -- Le site web c'est important, tout le monde en parle -- Refondre la partie graphique pour la rendre plus attrayante et moins RFC-like -- Alex s'est lancé dans du dev de bridge matrix qui fonctionne pour mattermost et XMPP -- Le bridge mattermost focntionne pas mal - -Ce que l'on n'a pas encore fait -------------------------------- - -Banque : la moitié des quotisation part dans une banque -Décision de faire un pot commun ? -Continuer sans ? (mais c'est dans les status) - -> Vote : trésorerie en liquide jusqu'à 200€ -> Sinon on dépense ou bien on ouvre un compte - -- 200€ : contre 0, neutre 0, unanimité pour -- Fonctionner en pot commun : contre 0, neutre 0, unanimité pour - -**Motion voté.** - -### Gestion de la compta - -Quentin a un compte courant vide. Mais à son avis pas une bonne idéee. -Gestion de la compta sur un logiciel (lequel ?) -Trésorier ? - -Alex a trouvé une boite. - -> Vote : Alex est le gardien de la boite qui contient les cotisations dans la limite de 200€ -> Contre : 0, Neutre 0, Uninimité - -Pour le choix du logiciel, Maximilien enverra un mail avec des solutions. L'idée de base est de mettre le fichier dans un repo git (facilement backupé et consultables), avec des commit signés. - -Pour les présents, les quotisations sont payable à la fin de l'AG. - -### Charte - -Trouver pour la prochaine AG (voir avant) une base. Maximilien doit envoyer des idées sur la base de ce qui est fait en conférence. Quentin envoie des idée pour les projets Open-Source. - -### Site web - -Intégrer la documentation au site web, afin qu'elle soit consultable et plus transparent par rapport aux infrastructure. - -Outil pour build du Markdown avec un blog statique. -Utiliser les outils de templating des trucs web. - -Quentin fera une proposition. -Simon : Les gens qui font des choses se doivent de les documenter. - -À qui s'adresse la documentation - -- tout ce qui tourne autours de l'administration -- de l'accessibilité -- la partie technique - -Répliquer le gitea d'Adrien (Maximilien va leur faire sur le siens). - -### Lieux de réunion - -Vincent propose le salon de thé (on peut commander un café), mais on est trop bruyant ? - -Pas de souci tant que l'on rentre dans un salon de chez quelqu'un (jusqu'à 10-12 personnes) - -Les objectifs -------------- - -Quentin : but original du CHATON, documenter l'auto-hébergement distribué, fournir des services que tu gères toi-même, sans manipulation ni tracking - -Trois niveaux : - -- petits services -- backups et disaster-recovery -- CHATON (candidature chez framasoft et référencement) : l'objectif est-il d'obtenir le label ou bien simplement de s'inspirer de leur idéal ? - -> Simon : pour la partie non technique, sauf si cela présente un effort technique trop important. -> Quentin : leur cahier des charges n'est pas aberrant et pourrait être un guide sur le développement de l'infra - -**TODO** : faire un document de travail (Quentin a fait une milestone dans le gitea) -- géo-distribué (résilient à la perte d'une machine/d'un site - penser datacenter) - -**TARGET** soumettre une candidature _CHATON_ dans 6 mois - -Pour la géo-distribution, Quentin préconise le backend S3-compatible -Approche totalement différente des ressources. - -Débat à suivre. - -### Recommendation - -Quentin : si jamais on embarque des gens et que l'on leur fait faux bond, on dessert la cause de l'hébergement participatif. - -Alex : il est de la responsabilité des personnes qui crée un compte de s'informer des limites - -Simon : par cooptation : chacun vois midi à sa porte. - -> Vote : Maximilie propose l'ajout d'un avertissement sur le formulaire d'inscription de guichet. La rédaction du bloc de text est laissé à Maximilien, et soumise à l'approbation du prochain conseil d'afministration. -> Contre : 0, Neutre 0, Uninimité - -Repasser sur le document de travail ------------------------------------ - -Alex : piorité de faire le site web et d'avoir une solution facilement éditable pour les PV d'AG & co (pas tout le temps dépendre des pads) - -Quentin : les PV en PDF sont stockés dans un repos - -Retex de Toms -------------- - -Toms est intéressé pour rejoindre l'association. -Pas assez de vison pour savoir si c'est réalisable. - -Quentin montre la nouvelle maquette du site web. diff --git a/src/Documentation/Association/Statuts/index.md b/src/Documentation/Association/Statuts/index.md deleted file mode 100644 index caa3978..0000000 --- a/src/Documentation/Association/Statuts/index.md +++ /dev/null @@ -1,126 +0,0 @@ -### Article 1. Constitution et dénomination - -Il est fondé entre les adhérents aux présents statuts une association -régie par la loi 1901, ayant pour titre Deuxfleurs. - -### Article 2. Buts - -Cette association a pour but de défendre et promouvoir les libertés -individuelles et collectives à travers la mise en place d'infrastuctures -numériques libres. - -### Article 3. Siège social - -Le siège social est fixé au 10A, Allée de Lanvaux, 35700 Rennes. Il -pourra être transféré suite à un vote par l'assemblée générale. - -### Article 4. Durée de l'association - -L'association perdure tant qu'elle possède au moins un membre, ou -jusqu'à sa dissolution décidée en assemblée générale. - - - -### Article 5. Admission et adhésion - -Pour faire partie de l'association, il faut être coopté par un membre de -l'association, adhérer aux présents statuts et s'acquitter de la -cotisation annuelle dont le montant est de 10 euros. - -### Article 6. Composition de l'association - -L'association se compose exclusivement de membres admis selon les dispositions -de l'[Article 5](#article-admission) et à jour de leur cotisation. Tout membre -actif possède une voix lors des votes en assemblée générale. Est considéré -actif tout membre présent à l'assemblée générale (physiquement, par -visioconférence ou par procuration écrite donnée à un autre membre de -l'association). - -### Article 7. Perte de la qualité de membre - -La qualité de membre se perd par : - -- la démission, -- le non-renouvelement de la cotisation dans un délai de deux mois - après le 1er Janvier de l'année courante, -- le décès, -- la radiation prononcée aux deux tiers des votes exprimés, lors d'un - vote extraordinaire ou de l'assemblée générale. - - - -### Article 8. L'assemblée générale - -L'assemblée générale ordinaire se réunit au moins une fois par an, convoquée -par le conseil d'administration. L'assemblée générale extraordinaire est -convoquée par le conseil d'administration, à la demande de celui-ci ou à la -demande du quart au moins des membres de l'association. - -L'assemblée générale (ordinaire ou extraordinaire) comprend tous les -membres de l'association à jour de leur cotisation. Quinze jours au -moins avant la date fixée, les membres de l'association sont convoqués -via la liste de diffusion de l'association et l'ordre du jour est -inscrit sur les convocations. - -Le conseil d'administration anime l'assemblée générale. L'assemblée -générale, après avoir délibéré, se prononce sur le rapport moral et/ou -d'activités. Le conseil d'administration rend compte de l'exercice -financier clos et soumet le bilan de l'exercice clos à l'approbation de -l'assemblée dans un délai de six mois après la clôture des comptes. -L'assemblée générale délibère sur les orientations à venir et se -prononce sur le budget prévisionnel de l'année en cours. - -Elle pourvoit, au scrutin secret, à la nomination ou au renouvellement -des membres du conseil d'administration via un scrutin de Condorcet -Randomisé. Elle fixe le montant de la cotisation annuelle. Les décisions -de l'assemblée sont prises à la majorité des membres présents ou -représentés. Chaque membre présent ne peut détenir plus d'une -procuration. - -### Article 9. Membres mineurs - -Les mineurs peuvent adhérer à l'association sous réserve d'un accord -tacite ou d'une autorisation écrite de leurs parents ou tuteurs légaux. -Ils sont membres à part entière de l'association. Seuls les membres âgés -de 16 ans au moins au jour d'une élection sont autorisés à y voter, -notamment au cours d'une assemblée générale. Pour les autres, leur droit -de vote est transmis à leur représentant légal. - -### Article 10. Le conseil d'administration - -L'association est administrée par un conseil d'administration composé de -3 à 6 membres, élus pour 1 an dans les conditions fixées à -l'[Article 8](#article-ag). Tous les membres de l'association à jour de -leur cotisation sont éligibles. En cas de vacance de poste, le conseil -d'administration peut pourvoir provisoirement au remplacement de ses -membres. Ce remplacement est obligatoire quand le conseil -d'administration compte moins de 3 membres. Il est procédé à leur -remplacement définitif à la plus prochaine assemblée générale. Les -pouvoirs des membres ainsi élus prennent fin à l'époque où devrait -normalement expirer le mandat des membres remplacés. - -Le conseil d'administration met en œuvre les décisions de l'assemblée -générale, organise et anime la vie de l'association, dans le cadre fixé -par les statuts. Chacun de ses membres peut être habilité par le conseil -à remplir toutes les formalités de déclaration et de publication -prescrites par la législation et tout autre acte nécessaire au -fonctionnement de l'association et décidé par le conseil -d'administration. Tous les membres du conseil d'administration sont -responsables des engagements contractés par l'association. Tout contrat -ou convention passé entre l'association d'une part, et un membre du -conseil d'administration, son conjoint ou un proche, d'autre part, est -soumis pour autorisation au conseil d'administration et présenté pour -information à la plus prochaine assemblée générale. Le conseil -d'administration se réunit au moins 4 fois par an et toutes les fois -qu'il est convoqué par le tiers de ses membres. La présence de la moitié -au moins des membres du conseil est nécessaire pour que le conseil -d'administration puisse délibérer valablement. Les décisions sont prises -au consensus et, à défaut, à la majorité des voix des présents. Le vote -par procuration n'est pas autorisé. - -### Article 11. Modification des statuts de l'association - -Sur demande d'un tiers des membres actifs, ou sur demande du conseil -d'administration, des amendements aux statuts de l'association peuvent -être discutés et soumis au vote lors d'une assemblée générale, selon les -modalités de l'[Article 8](#article-ag). diff --git a/src/Documentation/Association/index.md b/src/Documentation/Association/index.md deleted file mode 100644 index 3821b1d..0000000 --- a/src/Documentation/Association/index.md +++ /dev/null @@ -1,11 +0,0 @@ -### Statuts de l'association - -Depuis janvier 2020, Deuxfleurs est constitué en association loi 1901. -Vous trouverez [ici](/Documentation/Association/Statuts/) les statuts de l'association. - -### Comptes rendus d'AG - -Les comptes-rendus d'AG sont disponibles aux liens suivants: - - - [13 janvier 2020 (AG constitutive)](/Documentation/Association/AG1/) - diff --git a/src/Documentation/Technique/Infra/Internet/index.md b/src/Documentation/Technique/Infra/Internet/index.md deleted file mode 100644 index b7ba6b4..0000000 --- a/src/Documentation/Technique/Infra/Internet/index.md +++ /dev/null @@ -1,5 +0,0 @@ -## Problèmes de connexion - -Actuellement les serveurs sont hébergés derrière une connexion Free qui a des problèmes en soirée. - -Plus d'informations ici : https://www.aduf.org/viewtopic.php?t=286599&start=0 diff --git a/src/Documentation/Technique/Logiciels/Bottin/index.md b/src/Documentation/Technique/Logiciels/Bottin/index.md deleted file mode 100644 index 0cf67ed..0000000 --- a/src/Documentation/Technique/Logiciels/Bottin/index.md +++ /dev/null @@ -1 +0,0 @@ -Le code de Bottin se trouve ici : https://git.deuxfleurs.fr/Deuxfleurs/bottin diff --git a/src/Documentation/Technique/Logiciels/Diplonat/index.md b/src/Documentation/Technique/Logiciels/Diplonat/index.md deleted file mode 100644 index 47bb620..0000000 --- a/src/Documentation/Technique/Logiciels/Diplonat/index.md +++ /dev/null @@ -1 +0,0 @@ -Le code est ici : https://git.deuxfleurs.fr/Deuxfleurs/diplonat diff --git a/src/Documentation/Technique/Logiciels/Garage/index.md b/src/Documentation/Technique/Logiciels/Garage/index.md deleted file mode 100644 index 16fa635..0000000 --- a/src/Documentation/Technique/Logiciels/Garage/index.md +++ /dev/null @@ -1,241 +0,0 @@ -# Garage: a S3-like object storage - -Store pile of bytes in your garage. - - -## Context - -Data storage is critical: it can lead to data loss if done badly and/or on hardware failure. -Filesystems + RAID can help on a single machine but a machine failure can put the whole storage offline. -Moreover, it put a hard limit on scalability. Often this limit can be pushed back far away by buying expensive machines. -But here we consider non specialized off the shelf machines that can be as low powered and subject to failures as a raspberry pi. - -Distributed storage may help to solve both availability and scalability problems on these machines. -Many solutions were proposed, they can be categorized as block storage, file storage and object storage depending on the abstraction they provide. - -## Related work - -Block storage is the most low level one, it's like exposing your raw hard drive over the network. -It requires very low latencies and stable network, that are often dedicated. -However it provides disk devices that can be manipulated by the operating system with the less constraints: it can be partitioned with any filesystem, meaning that it supports even the most exotic features. -We can cite [iSCSI](https://en.wikipedia.org/wiki/ISCSI) or [Fibre Channel](https://en.wikipedia.org/wiki/Fibre_Channel). -Openstack Cinder proxy previous solution to provide an uniform API. - -File storage provides a higher abstraction, they are one filesystem among others, which means they don't necessarily have all the exotic features of every filesystem. -Often, they relax some POSIX constraints while many applications will still be compatible without any modification. -As an example, we are able to run MariaDB (very slowly) over GlusterFS... -We can also mention CephFS (read [RADOS](https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf) whitepaper), Lustre, LizardFS, MooseFS, etc. -OpenStack Manila proxy previous solutions to provide an uniform API. - -Finally object storages provide the highest level abstraction. -They are the testimony that the POSIX filesystem API is not adapted to distributed filesystems. -Especially, the strong concistency has been dropped in favor of eventual consistency which is way more convenient and powerful in presence of high latencies and unreliability. -We often read about S3 that pioneered the concept that it's a filesystem for the WAN. -Applications must be adapted to work for the desired object storage service. -Today, the S3 HTTP REST API acts as a standard in the industry. -However, Amazon S3 source code is not open but alternatives were proposed. -We identified Minio, Pithos, Swift and Ceph. -Minio/Ceph enforces a total order, so properties similar to a (relaxed) filesystem. -Swift and Pithos are probably the most similar to AWS S3 with their consistent hashing ring. - -There was many attempts in research too. I am only thinking to [LBFS](https://pdos.csail.mit.edu/papers/lbfs:sosp01/lbfs.pdf) that was used as a basis for Seafile. - -- Cassandra (ScyllaDB) for metadata -- Own system using consistent hashing for data chunks - - -**Quentin:** - -- pas d'erasure coding mais des checksums à côté des fichiers (ou dans les meta données) -- 2 ou 3 copies, configurable, potentiellement on a per bucket or per file basis -- on ne setup pas à la main en effet, je pensais au système qui scan sa partition de stockage et qui fait stockage géré = min(stockage de la partition - stockage que je ne gère pas, stockage alloué) -- La DHT/Ring à la dynamo, on doit pouvoir repomper un millier de truc sur leur papier. Surtout que je me le suis déja cogné deux fois. Le nombre d'entrées que tu mets est un multiple de ton stockage. eg: 500 Go, on fait des tranches de 10 Go --> 50 entrées dans le ring. -- un protocole de maintenance pompé sur le papier dynamo avec de l'anti entropy qui vérifie les blobs et leurs checksums (en plus de la vérification réalisée à la lecture) -- une interface web qui te donne en presque direct la santé de ton cluster (noeuds en vie, états de la réplication des données, problèmes de checksums) - -**Other ideas:** - -- split objects in constant size blocks or use the SeaFile strategy for better de-duplication? (Content Defined Chunking, Rabin's algorithm etc) - -_Remark 1_ I really like the Rabin fingerprinting approach however deduplication means we need to implement reference counting. How do we implement it? If we suppose a CRDT counter, if we do +1, +1, -1 but counter is registered as +1, -1, +1, we are at zero at one point and lost ou chunk. ---> we need to be careful in our implementation if we want to play. - -_Remark 2_ Seafile idea has been stolen from this article: https://pdos.csail.mit.edu/papers/lbfs:sosp01/lbfs.pdf - -#### Random notes - ---> we should not talk about block. It is the abstraction that manipulate your FS to interact with your hard drive. "Chunk" is probably more appropriate. Block storage are a class of distributed storage where you expose the abstraction of your hard drive over the network, mainly SATA over ethernet, thinking to SCSI, FiberChannel, and so on - -### Questions à résoudre - - - 1. est-ce que cassandra support de mettre certaines tables sur un SSD et d'autres sur un disque rotatif ? - 2. est-ce que cassandra/scylladb a un format de table on disk qui ne s'écroule pas complètement losque tu as des gros blobs ? (les devs de sqlite ont écrit tout un article pour dire que même avec leur lib qui est quand même sacrément optimisés, ils considèrent qu'à partir de je crois 4ko c'est plus efficace de mettre les blobs dans des fichiers séparés) - https://www.sqlite.org/intern-v-extern-blob.html - 3. Quelle taille de blocs ? L'idée c'est qu'on a quand même des liens en WAN avec des débits pas forcéments incroyables. Et ça serait bien que le temps de répliquer un bloc soit de l'ordre de la seconde maxi. En cas de retry, pour pouvoir mieux monitorer la progression, etc. Exoscale utilise 16Mo. LX propose 1Mo. - - - - - -#### Modules - -- `membership/`: configuration, membership management (gossip of node's presence and status), ring generation --> what about Serf (used by Consul/Nomad) : https://www.serf.io/? Seems a huge library with many features so maybe overkill/hard to integrate -- `metadata/`: metadata management -- `blocks/`: block management, writing, GC and rebalancing -- `internal/`: server to server communication (HTTP server and client that reuses connections, TLS if we want, etc) -- `api/`: S3 API -- `web/`: web management interface - - -#### Metadata tables - -**Objects:** - -- *Hash key:* Bucket name (string) -- *Sort key:* Object key (string) -- *Sort key:* Version timestamp (int) -- *Sort key:* Version UUID (string) -- Complete: bool -- Inline: bool, true for objects < threshold (say 1024) -- Object size (int) -- Mime type (string) -- Data for inlined objects (blob) -- Hash of first block otherwise (string) - -*Having only a hash key on the bucket name will lead to storing all file entries of this table for a specific bucket on a single node. At the same time, it is the only way I see to rapidly being able to list all bucket entries...* - -**Blocks:** - -- *Hash key:* Version UUID (string) -- *Sort key:* Offset of block in total file (int) -- Hash of data block (string) - -A version is defined by the existence of at least one entry in the blocks table for a certain version UUID. -We must keep the following invariant: if a version exists in the blocks table, it has to be referenced in the objects table. -We explicitly manage concurrent versions of an object: the version timestamp and version UUID columns are index columns, thus we may have several concurrent versions of an object. -Important: before deleting an older version from the objects table, we must make sure that we did a successfull delete of the blocks of that version from the blocks table. - -Thus, the workflow for reading an object is as follows: - -1. Check permissions (LDAP) -2. Read entry in object table. If data is inline, we have its data, stop here. - -> if several versions, take newest one and launch deletion of old ones in background -3. Read first block from cluster. If size <= 1 block, stop here. -4. Simultaneously with previous step, if size > 1 block: query the Blocks table for the IDs of the next blocks -5. Read subsequent blocks from cluster - -Workflow for PUT: - -1. Check write permission (LDAP) -2. Select a new version UUID -3. Write a preliminary entry for the new version in the objects table with complete = false -4. Send blocks to cluster and write entries in the blocks table -5. Update the version with complete = true and all of the accurate information (size, etc) -6. Return success to the user -7. Launch a background job to check and delete older versions - - -Workflow for DELETE: - -1. Check write permission (LDAP) -2. Get current version (or versions) in object table -3. Do the deletion of those versions NOT IN A BACKGROUND JOB THIS TIME -4. Return succes to the user if we were able to delete blocks from the blocks table and entries from the object table - -To delete a version: - -1. List the blocks from Cassandra -2. For each block, delete it from cluster. Don't care if some deletions fail, we can do GC. -3. Delete all of the blocks from the blocks table -4. Finally, delete the version from the objects table - - -Known issue: if someone is reading from a version that we want to delete and the object is big, the read might be interrupted. I think it is ok to leave it like this, we just cut the connection if data disappears during a read. - -("Soit P un problème, on s'en fout est une solution à ce problème") - - -#### Block storage on disk - -**Blocks themselves:** - -- file path = /blobs/(first 3 hex digits of hash)/(rest of hash) - -**Reverse index for GC & other block-level metadata:** - -- file path = /meta/(first 3 hex digits of hash)/(rest of hash) -- map block hash -> set of version UUIDs where it is referenced - -Usefull metadata: - -- list of versions that reference this block in the Casandra table, so that we can do GC by checking in Cassandra that the lines still exist -- list of other nodes that we know have acknowledged a write of this block, usefull in the rebalancing algorithm - -Write strategy: have a single thread that does all write IO so that it is serialized (or have several threads that manage independent parts of the hash space). When writing a blob, write it to a temporary file, close, then rename so that a concurrent read gets a consistent result (either not found or found with whole content). - -Read strategy: the only read operation is get(hash) that returns either the data or not found (can do a corruption check as well and return corrupted state if it is the case). Can be done concurrently with writes. - -**Internal API:** - -- get(block hash) -> ok+data/not found/corrupted -- put(block hash & data, version uuid + offset) -> ok/error -- put with no data(block hash, version uuid + offset) -> ok/not found plz send data/error -- delete(block hash, version uuid + offset) -> ok/error - -GC: when last ref is deleted, delete block. -Long GC procedure: check in Cassandra that version UUIDs still exist and references this block. - -Rebalancing: takes as argument the list of newly added nodes. - -- List all blocks that we have. For each block: -- If it hits a newly introduced node, send it to them. - Use put with no data first to check if it has to be sent to them already or not. - Use a random listing order to avoid race conditions (they do no harm but we might have two nodes sending the same thing at the same time thus wasting time). -- If it doesn't hit us anymore, delete it and its reference list. - -Only one balancing can be running at a same time. It can be restarted at the beginning with new parameters. - - -#### Membership management - -Two sets of nodes: - -- set of nodes from which a ping was recently received, with status: number of stored blocks, request counters, error counters, GC%, rebalancing% - (eviction from this set after say 30 seconds without ping) -- set of nodes that are part of the system, explicitly modified by the operator using the web UI (persisted to disk), - is a CRDT using a version number for the value of the whole set - -Thus, three states for nodes: - -- healthy: in both sets -- missing: not pingable but part of desired cluster -- unused/draining: currently present but not part of the desired cluster, empty = if contains nothing, draining = if still contains some blocks - -Membership messages between nodes: - -- ping with current state + hash of current membership info -> reply with same info -- send&get back membership info (the ids of nodes that are in the two sets): used when no local membership change in a long time and membership info hash discrepancy detected with first message (passive membership fixing with full CRDT gossip) -- inform of newly pingable node(s) -> no result, when receive new info repeat to all (reliable broadcast) -- inform of operator membership change -> no result, when receive new info repeat to all (reliable broadcast) - -Ring: generated from the desired set of nodes, however when doing read/writes on the ring, skip nodes that are known to be not pingable. -The tokens are generated in a deterministic fashion from node IDs (hash of node id + token number from 1 to K). -Number K of tokens per node: decided by the operator & stored in the operator's list of nodes CRDT. Default value proposal: with node status information also broadcast disk total size and free space, and propose a default number of tokens equal to 80%Free space / 10Gb. (this is all user interface) - - -#### Constants - -- Block size: around 1MB ? --> Exoscale use 16MB chunks -- Number of tokens in the hash ring: one every 10Gb of allocated storage -- Threshold for storing data directly in Cassandra objects table: 1kb bytes (maybe up to 4kb?) -- Ping timeout (time after which a node is registered as unresponsive/missing): 30 seconds -- Ping interval: 10 seconds -- ?? - - -#### Links - - - CDC: - - Erasure coding: - - [Openstack Storage Concepts](https://docs.openstack.org/arch-design/design-storage/design-storage-concepts.html) - - [RADOS](https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf) - diff --git a/src/Documentation/Technique/Logiciels/Guichet/index.md b/src/Documentation/Technique/Logiciels/Guichet/index.md deleted file mode 100644 index 3e67c44..0000000 --- a/src/Documentation/Technique/Logiciels/Guichet/index.md +++ /dev/null @@ -1 +0,0 @@ -Le code est ici : https://git.deuxfleurs.fr/Deuxfleurs/guichet diff --git a/src/Documentation/Technique/Services/Jitsi/exemple_offre.txt b/src/Documentation/Technique/Services/Jitsi/exemple_offre.txt deleted file mode 100644 index 3af76ec..0000000 --- a/src/Documentation/Technique/Services/Jitsi/exemple_offre.txt +++ /dev/null @@ -1,118 +0,0 @@ -type: offer, sdp: v=0 -o=- 1923518516 2 IN IP4 127.0.0.1 -s=- -t=0 0 -a=msid-semantic: WMS 48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 -a=group:BUNDLE audio video data -m=audio 10000 RTP/SAVPF 111 103 104 126 -c=IN IP4 82.253.205.190 -a=rtpmap:111 opus/48000/2 -a=rtpmap:103 ISAC/16000 -a=rtpmap:104 ISAC/32000 -a=rtpmap:126 telephone-event/8000 -a=fmtp:111 minptime=10;useinbandfec=1 -a=rtcp:9 IN IP4 0.0.0.0 -a=rtcp-fb:111 transport-cc -a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level -a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 -a=setup:actpass -a=mid:audio -a=sendrecv -a=ice-ufrag:97nc61e52b11gu -a=ice-pwd:k2df7f8vgknj27sctj550cl2u -a=fingerprint:sha-1 FC:4F:0B:F5:34:07:D8:09:47:D2:3C:FE:D1:8E:05:4B:05:10:CD:A1 -a=candidate:1 1 ssltcp 2130706431 172.17.0.1 8080 typ host generation 0 -a=candidate:2 1 ssltcp 2130706431 192.168.1.4 8080 typ host generation 0 -a=candidate:4 1 udp 2113932031 172.17.0.1 10000 typ host generation 0 -a=candidate:5 1 udp 2113932031 192.168.1.4 10000 typ host generation 0 -a=candidate:3 1 ssltcp 1677724415 82.253.205.190 8080 typ srflx raddr 192.168.1.4 rport 8080 generation 0 -a=candidate:6 1 udp 1677724415 82.253.205.190 10000 typ srflx raddr 192.168.1.4 rport 10000 generation 0 -a=ssrc:3265394670 cname:mixed -a=ssrc:3265394670 msid:mixedmslabel mixedlabelaudio0 -a=ssrc:3265394670 mslabel:mixedmslabel -a=ssrc:3265394670 label:mixedlabelaudio0 -a=ssrc:3761143749 cname:sMYSy0kNyRU3eK0c-1 -a=ssrc:3761143749 msid:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 8a034425-b6b5-4928-ab5f-9ca0ec4168c4-1 -a=ssrc:3761143749 mslabel:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 -a=ssrc:3761143749 label:8a034425-b6b5-4928-ab5f-9ca0ec4168c4-1 -a=ssrc:3240916804 cname:75Ayhq4Cuv7k5JAP-1 -a=ssrc:3240916804 msid:27755a82-e9e7-4cc4-bdb3-354a06b3f32a-1 45de0b7f-8590-4232-9bde-77d55a7366b5-1 -a=rtcp-mux -m=video 10000 RTP/SAVPF 100 107 101 96 97 99 -c=IN IP4 82.253.205.190 -a=rtpmap:100 VP8/90000 -a=rtpmap:107 H264/90000 -a=rtpmap:101 VP9/90000 -a=rtpmap:96 rtx/90000 -a=rtpmap:97 rtx/90000 -a=rtpmap:99 rtx/90000 -a=fmtp:100 x-google-start-bitrate=800 -a=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f;x-google-start-bitrate=800 -a=fmtp:101 x-google-start-bitrate=800 -a=fmtp:96 apt=100 -a=fmtp:97 apt=101 -a=fmtp:99 apt=107 -a=rtcp:9 IN IP4 0.0.0.0 -a=rtcp-fb:100 ccm fir -a=rtcp-fb:100 nack -a=rtcp-fb:100 nack pli -a=rtcp-fb:100 transport-cc -a=rtcp-fb:107 ccm fir -a=rtcp-fb:107 nack -a=rtcp-fb:107 nack pli -a=rtcp-fb:107 transport-cc -a=rtcp-fb:101 ccm fir -a=rtcp-fb:101 nack -a=rtcp-fb:101 nack pli -a=rtcp-fb:101 transport-cc -a=rtcp-fb:96 ccm fir -a=rtcp-fb:96 nack -a=rtcp-fb:96 nack pli -a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time -a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 -a=setup:actpass -a=mid:video -a=sendrecv -a=ice-ufrag:97nc61e52b11gu -a=ice-pwd:k2df7f8vgknj27sctj550cl2u -a=fingerprint:sha-1 FC:4F:0B:F5:34:07:D8:09:47:D2:3C:FE:D1:8E:05:4B:05:10:CD:A1 -a=candidate:1 1 ssltcp 2130706431 172.17.0.1 8080 typ host generation 0 -a=candidate:2 1 ssltcp 2130706431 192.168.1.4 8080 typ host generation 0 -a=candidate:4 1 udp 2113932031 172.17.0.1 10000 typ host generation 0 -a=candidate:5 1 udp 2113932031 192.168.1.4 10000 typ host generation 0 -a=candidate:3 1 ssltcp 1677724415 82.253.205.190 8080 typ srflx raddr 192.168.1.4 rport 8080 generation 0 -a=candidate:6 1 udp 1677724415 82.253.205.190 10000 typ srflx raddr 192.168.1.4 rport 10000 generation 0 -a=ssrc:3339205972 cname:75Ayhq4Cuv7k5JAP-1 -a=ssrc:3339205972 msid:27755a82-e9e7-4cc4-bdb3-354a06b3f32a-1 4de277cb-7421-402a-bbb1-2090dab4540e-1 -a=ssrc:3560865865 cname:sMYSy0kNyRU3eK0c-1 -a=ssrc:3560865865 msid:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 -a=ssrc:3560865865 mslabel:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 -a=ssrc:3560865865 label:21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 -a=ssrc:1942865873 cname:mixed -a=ssrc:1942865873 msid:mixedmslabel mixedlabelvideo0 -a=ssrc:1942865873 mslabel:mixedmslabel -a=ssrc:1942865873 label:mixedlabelvideo0 -a=ssrc:3656552182 cname:sMYSy0kNyRU3eK0c-1 -a=ssrc:3656552182 msid:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 -a=ssrc:3656552182 mslabel:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 -a=ssrc:3656552182 label:21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 -a=ssrc:4136660991 cname:75Ayhq4Cuv7k5JAP-1 -a=ssrc:4136660991 msid:27755a82-e9e7-4cc4-bdb3-354a06b3f32a-1 4de277cb-7421-402a-bbb1-2090dab4540e-1 -a=ssrc-group:FID 3560865865 3656552182 -a=ssrc-group:FID 3339205972 4136660991 -a=rtcp-mux -a=x-google-flag:conference -m=application 10000 DTLS/SCTP 5000 -c=IN IP4 82.253.205.190 -a=setup:actpass -a=mid:data -a=ice-ufrag:97nc61e52b11gu -a=ice-pwd:k2df7f8vgknj27sctj550cl2u -a=fingerprint:sha-1 FC:4F:0B:F5:34:07:D8:09:47:D2:3C:FE:D1:8E:05:4B:05:10:CD:A1 -a=candidate:1 1 ssltcp 2130706431 172.17.0.1 8080 typ host generation 0 -a=candidate:2 1 ssltcp 2130706431 192.168.1.4 8080 typ host generation 0 -a=candidate:4 1 udp 2113932031 172.17.0.1 10000 typ host generation 0 -a=candidate:5 1 udp 2113932031 192.168.1.4 10000 typ host generation 0 -a=candidate:3 1 ssltcp 1677724415 82.253.205.190 8080 typ srflx raddr 192.168.1.4 rport 8080 generation 0 -a=candidate:6 1 udp 1677724415 82.253.205.190 10000 typ srflx raddr 192.168.1.4 rport 10000 generation 0 -a=sctpmap:5000 webrtc-datachannel 1024 diff --git a/src/Documentation/Technique/Services/Jitsi/index.md b/src/Documentation/Technique/Services/Jitsi/index.md deleted file mode 100644 index dee7b53..0000000 --- a/src/Documentation/Technique/Services/Jitsi/index.md +++ /dev/null @@ -1,94 +0,0 @@ -## 2020-04-02 Campagne de debug Jitsi - -Contact: Quentin - -### Description du problème - -Les conversations à 3+ donc relayées par le serveur ne fonctionnent pas bien. -Louison m'a rapporté que ça avait marché pour lui (3 utilisateurs avec un Webkit). -Mais moi ça a échoué hier soir (01/04/2020) avec des participants sous Firefox. -Le bug est toujours le même : on entend 2 personnes sur 3 ou on voit 2 personnes sur 3, on recharge la page et c'est quelqu'un d'autre pour qui ça ne fonctionne plus. Souvent c'est que dans un sens. -À chaque fois en passant sur Facebook Messenger, le problème est résolu instantanément. -Par contre Facebook Messenger impose Google Chrome/Chromium pour les visio de groupe (et ne supporte donc pas Firefox). - -D'où mes 2 suspicions : - - - Firefox a un bug quelconque dans sa pile WebRTC déclenché par le mode conversation de groupe - - Jitsi a un problème avec les déconnexions/changement de connexion/petit hoquets du réseau et n'arrive pas à se reconnecter. Ça pourrait être rendu pire à certain moment de la journée comme le soir où le réseau est plus sollicité. Et ce serait provoqué lors du reload on repasse de 3 à 2, en P2P donc puis de nouveau de 2 à 3. - -### Approfondissement - -Avant d'aller plus loin, nous avons voulu prendre le temps d'identifier précisément les problèmes d'expérience utilisateurs et leur corrélation avec la plateforme de l'utilisateur (navigateur, OS). - -Pour celà, nous avons suivi deux approches : - 1. Mener nos propres tests - 2. Chercher d'autres retours - -#### Mener nos propres tests - -Nous avons effectué deux appels : un avec Firefox seulement et un avec Chrome/Chromium seulement. -Merci à Alex, Adrien et Maximilien pour leur participation. - -Voilà les conclusions que nous avons tirées de nos tests : - - - L'appel avec Firefox a déclenché le bug immédiatement, peu importe la version de Firefox ou de l'OS (firefox stable/nightly, fedora stable/beta, etc.) - - Le passage de tout le monde sous Chrome/Chromium a permis d'avoir une conversation stable. - - Adrien avait sa Livebox avec pare-feu configuré en mode "élevé" et a du ajouter dans sa liste blanche les ports utilisés par Jitsi (`4443/tcp` et `10000/udp` au moment du test, seul un des deux a besoin d'être accessible) - -Nous avons donc demandé à Adrien quels étaient les ports ouverts par défaut dans le mode élevé de sa box : - -![Livebox Parefeu Personnalisé](livebox_parefeu_personnalise.png) - -Nous avons dans un premier temps retenu le port `995/tcp` pour Jitsi, le port UDP ne pouvant être changé (limitation de Jitsi). -Cependant, pour des raisons de sécurité, les navigateurs ne peuvent pas utiliser les ports en dessous de `1024/*`, à l'exception des ports `80/tcp` et `443/tcp` comme l'indique ;'issue [#3583](https://bugs.chromium.org/p/webrtc/issues/detail?id=3583) de Chromium. - -La capture n'indique pas de port TCP supérieur à 1024, nous ne pouvons donc pas résoudre ce problème de notre côté, car à l'heure actuelle, nos ports `80/tcp` et `443/tcp` sont utilisés et nous n'avons qu'une seule IP publique. -Les utilisateurs activant le parefeu en mode élevé devront ajouter une exception dans leur parefeu eux-mêmes. - -#### Chercher d'autres retours - -[Tedomum](https://tedomum.net/) a eu connaissance de problèmes similaires. -Ils ont également identifié Firefox et assurent qu'avec l'application Android ça marche bien. -Ils me confirment que le problème vient bien du logiciel et non pas de notre déploiement. -Ils m'ont pointé entre autre vers cette issue github [#4758](https://github.com/jitsi/jitsi-meet/issues/4758) -Apparemment une issue est dédiée en particulier au problème que nous rencontrons de déconnexion partielle d'un participant, mais nous ne l'avons pas encore retrouvée. - -### Correctifs - - 1. Notre instance Jitsi a été reconfigurée pour refuser Firefox. Suivre l'avancée du développement de Jitsi pour Firefox - * [#4758](https://github.com/jitsi/jitsi-meet/issues/4758) - * [#5439](https://github.com/jitsi/jitsi-meet/issues/5439) - * _À compléter_ - 2. Pour relayer la vidéo à travers la plupart des parefeux, notre `videobridge` Jitsi devait écouter sur le port `443/tcp`. Hors, ce port est déjà utilisé par notre frontal HTTPS. À défaut, Jitsi est maintenant configuré avec `8080/tcp` et `10000/udp` (contre `4443/tcp` et `10000/udp` avant). - * Un déploiement en IPv6 pourrait résoudre le problème pour une partie des utilisateurs - * Avoir un cluster géo-distribué avec plusieurs IPv4 pourrait également résoudre le problème - * Avoir un frontend layer 4 (niveau TCP) qui trouve une signature pour router du DTLS vers videobridge et du TLS vers traefik - -### À propos du control/data plane de Jitsi - -Notre videobridge écoute donc sur les ports `8080/tcp` et `10000/udp`. - -WebRTC fonctionne en deux étapes : - - Des offres doivent être échangées via un serveur de signaling quelconque - - Ensuite, ces offres servent à connecter des clients directement via un protocole TCP ou UDP avec un thin layer propre à WebRTC - -#### Le control plane de Jitsi : Prosody sur HTTPS via Bosh/XMPP - -Le serveur de signaling Jitsi n'est autre que le serveur de chat prosody. -Pour ça, prosody est exposé à travers HTTP grâce au protocole BOSH (XMPP overs HTTPS). -Une fois l'offre reçue ([exemple](exemple_offre.txt)), elle est enregistrée dans le navigateur à l'aide de `setRemoteDescription` pour initialiser le data plane. -On peut débugger le signaling WebRTC sous Chromium avec [chrome://webrtc-internarls](chrome://webrtc-internals/). - -Quand plus de deux participants sont connectés dans la conversation, Jitsi n'envoie pas les offres de chaque participant aux autres participants. À la place, elle envoie qu'une seule offre, celle de son VideoBridge. - -Ainsi, le VideoBridge est une sorte de client WebRTC particulier, qui récolte et redispatche à travers WebRTC tous les flux video/audio. - -#### Le data plane de Jitsi : videobridge sur DTLS/SCTP via WebRTC - -WebRTC utilise deux formats de paquets selon [Mozilla Developer Network|RTCDataChannel|DataFormat](https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel#Data_format) : - - `UDP/DTLS/SCTP` - - `TCP/DTLS/SCTP` - -On a donc un format de données arbitraire encapsulé dans du SCTP lui même encapsulé dans du DTLS. -DTLS est prévu pour les datagrammes à l'origine, car WebRTC est prévu d'abord pour fonctionner sous UDP. -Le TCP est là en mode dégradé, en secours, il sert juste de tunnel pour relayer des datagrammes. diff --git a/src/Documentation/Technique/Services/Jitsi/livebox_parefeu_personnalise.png b/src/Documentation/Technique/Services/Jitsi/livebox_parefeu_personnalise.png deleted file mode 100644 index 16c922a..0000000 Binary files a/src/Documentation/Technique/Services/Jitsi/livebox_parefeu_personnalise.png and /dev/null differ diff --git a/src/Documentation/Technique/index.md b/src/Documentation/Technique/index.md deleted file mode 100644 index 8aea90f..0000000 --- a/src/Documentation/Technique/index.md +++ /dev/null @@ -1,22 +0,0 @@ -Deuxfleurs utilise les composants suivants dans son infrastructure: - -- Ansible (configuration des noeuds) -- Docker (conteneurs) -- Nomad (orchestration des conteneurs) -- Consul (stockage clef/valeur distribué, découverte de services) -- Glusterfs (système de fichiers distribué) -- Stolon (système de réplication pour PostgreSQL) - -Les services proposés sont les suivants: - -- Chat via Matrix (Synapse, Riot) -- Email (Postfix, Dovecot, SoGo) -- Stockage (Seafile) - -Par ailleurs, nous avons développés nous-même un certain nombre d'outils pour compléter la stack: - -- [Bottin](https://bottin.eu), un serveur LDAP (gestion des comptes utilisateurs) basé sur le stockage clef/valeur de Consul -- [Guichet](https://git.deuxfleurs.fr/Deuxfleurs/Guichet/), une interface web de gestion des utilisateurs -- [Easybridge](https://git.deuxfleurs.fr/lx/Easybridge/), un bridge entre Matrix et d'autres réseaux - -Le code de l'infrastructure [est publiquement disponible](https://git.deuxfleurs.fr/Deuxfleurs/deuxfleurs.fr/). diff --git a/src/Documentation/_markdown.pug b/src/Documentation/_markdown.pug deleted file mode 100644 index b5dcdf4..0000000 --- a/src/Documentation/_markdown.pug +++ /dev/null @@ -1,14 +0,0 @@ -extends ../_layout.pug - -prepend root - - title = element.nice_path[element.nice_path.length - 1] - -block content - .container.spacing - nav - strong - a(href="/Documentation") Documentation - +menu(root.children.find(e => e.nice_name == "Documentation")) - - main.spacing - != markdown diff --git a/src/Documentation/index.md b/src/Documentation/index.md deleted file mode 100644 index 57ee2cc..0000000 --- a/src/Documentation/index.md +++ /dev/null @@ -1,54 +0,0 @@ -### Notre raison d'être - -Aujourd'hui, de grandes entreprises conçoivent des services numériques qui ont -pour objectif de -maximiser le temps -que nous passons dessus, de -collecter et recouper des données -à notre insu pour nous influencer, de -limiter nos possibilités d'expression -au delà du cadre légal et de -créer de nouveaux monopoles. -Ces effets nous montrent que la technologie n'est pas -neutre et a un réel impact sur nos vies. En choisissant et en hébergeant nos -propres outils de communication, sans but lucratif ni hégémonique, nous -espérons nous affranchir de ces nuisances et préserver nos libertés. - -Pour en savoir plus, rendez-vous sur -La Quadrature du Net -et allez lire le manifeste des CHATONS. - - -### Nos objectifs - -#### Des utilisateurs impliqués - -Que ce soit à l'école, par l'expérimentation, via un forum d'échange, lors d'un -atelier, via une publicité à la télévision, un tutoriel, lors d'une discussion -avec un ami, il y toujours une phase d'apprentissage en informatique. -Malheureusement, dans ces conditions, dur de lutter pour des services libres -face à la puissance de frappe d'une entreprise et des logiciels ayant une base -d'utilisateurs immense. Nous pensons donc qu'une personne souhaitant s'héberger -chez un hébergeur indépendant a besoin d'un accompagnement. C'est pourquoi les -inscriptions se font par cooptation. La cooptation permet aussi un lien de -confiance et ainsi de se prémunir de bon nombres d'attaques que subissent les -hébergeurs. - -

En savoir plus sur l'association

- -#### Une architecture résiliente - -Les sites webs, les réseaux sociaux, les emails ne peuvent fonctionner que -grâce à des ordinateurs qui restent allumés 24/24h et qui n'attendent que vous. -Cependant, ces derniers sont faillibles. Une coupure d'électricité, un disque -dur cassé, une mise à jour ratée, un bug dans le logiciel, les raisons ne -manquent pas. Heureusement, il est possible de masquer ces pannes avec du -logiciel astucieusement conçu. C'est pourquoi vous avez l'impression que Google -est toujours disponible, que Dropbox ne perd pas vos données, etc. La gestion -de ces pannes, c'est aussi ce qui rend la vie compliquée aux hébergeurs -indépendants. Entre incompréhension des utilisateurs quand un service est hors -ligne et sueurs froides pour les administrateurs, ça n'a rien de marrant. Et -c'est très chronophage. Notre objectif est donc de construire des solutions -d'hébergements qui peuvent résister à ces pannes. - -

En savoir plus sur l'aspect technique

diff --git "a/src/Technique/D\303\251veloppement/Bottin.md" "b/src/Technique/D\303\251veloppement/Bottin.md" new file mode 100644 index 0000000..0cf67ed --- /dev/null +++ "b/src/Technique/D\303\251veloppement/Bottin.md" @@ -0,0 +1 @@ +Le code de Bottin se trouve ici : https://git.deuxfleurs.fr/Deuxfleurs/bottin diff --git "a/src/Technique/D\303\251veloppement/Diplonat.md" "b/src/Technique/D\303\251veloppement/Diplonat.md" new file mode 100644 index 0000000..47bb620 --- /dev/null +++ "b/src/Technique/D\303\251veloppement/Diplonat.md" @@ -0,0 +1 @@ +Le code est ici : https://git.deuxfleurs.fr/Deuxfleurs/diplonat diff --git "a/src/Technique/D\303\251veloppement/Garage.md" "b/src/Technique/D\303\251veloppement/Garage.md" new file mode 100644 index 0000000..16fa635 --- /dev/null +++ "b/src/Technique/D\303\251veloppement/Garage.md" @@ -0,0 +1,241 @@ +# Garage: a S3-like object storage + +Store pile of bytes in your garage. + + +## Context + +Data storage is critical: it can lead to data loss if done badly and/or on hardware failure. +Filesystems + RAID can help on a single machine but a machine failure can put the whole storage offline. +Moreover, it put a hard limit on scalability. Often this limit can be pushed back far away by buying expensive machines. +But here we consider non specialized off the shelf machines that can be as low powered and subject to failures as a raspberry pi. + +Distributed storage may help to solve both availability and scalability problems on these machines. +Many solutions were proposed, they can be categorized as block storage, file storage and object storage depending on the abstraction they provide. + +## Related work + +Block storage is the most low level one, it's like exposing your raw hard drive over the network. +It requires very low latencies and stable network, that are often dedicated. +However it provides disk devices that can be manipulated by the operating system with the less constraints: it can be partitioned with any filesystem, meaning that it supports even the most exotic features. +We can cite [iSCSI](https://en.wikipedia.org/wiki/ISCSI) or [Fibre Channel](https://en.wikipedia.org/wiki/Fibre_Channel). +Openstack Cinder proxy previous solution to provide an uniform API. + +File storage provides a higher abstraction, they are one filesystem among others, which means they don't necessarily have all the exotic features of every filesystem. +Often, they relax some POSIX constraints while many applications will still be compatible without any modification. +As an example, we are able to run MariaDB (very slowly) over GlusterFS... +We can also mention CephFS (read [RADOS](https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf) whitepaper), Lustre, LizardFS, MooseFS, etc. +OpenStack Manila proxy previous solutions to provide an uniform API. + +Finally object storages provide the highest level abstraction. +They are the testimony that the POSIX filesystem API is not adapted to distributed filesystems. +Especially, the strong concistency has been dropped in favor of eventual consistency which is way more convenient and powerful in presence of high latencies and unreliability. +We often read about S3 that pioneered the concept that it's a filesystem for the WAN. +Applications must be adapted to work for the desired object storage service. +Today, the S3 HTTP REST API acts as a standard in the industry. +However, Amazon S3 source code is not open but alternatives were proposed. +We identified Minio, Pithos, Swift and Ceph. +Minio/Ceph enforces a total order, so properties similar to a (relaxed) filesystem. +Swift and Pithos are probably the most similar to AWS S3 with their consistent hashing ring. + +There was many attempts in research too. I am only thinking to [LBFS](https://pdos.csail.mit.edu/papers/lbfs:sosp01/lbfs.pdf) that was used as a basis for Seafile. + +- Cassandra (ScyllaDB) for metadata +- Own system using consistent hashing for data chunks + + +**Quentin:** + +- pas d'erasure coding mais des checksums à côté des fichiers (ou dans les meta données) +- 2 ou 3 copies, configurable, potentiellement on a per bucket or per file basis +- on ne setup pas à la main en effet, je pensais au système qui scan sa partition de stockage et qui fait stockage géré = min(stockage de la partition - stockage que je ne gère pas, stockage alloué) +- La DHT/Ring à la dynamo, on doit pouvoir repomper un millier de truc sur leur papier. Surtout que je me le suis déja cogné deux fois. Le nombre d'entrées que tu mets est un multiple de ton stockage. eg: 500 Go, on fait des tranches de 10 Go --> 50 entrées dans le ring. +- un protocole de maintenance pompé sur le papier dynamo avec de l'anti entropy qui vérifie les blobs et leurs checksums (en plus de la vérification réalisée à la lecture) +- une interface web qui te donne en presque direct la santé de ton cluster (noeuds en vie, états de la réplication des données, problèmes de checksums) + +**Other ideas:** + +- split objects in constant size blocks or use the SeaFile strategy for better de-duplication? (Content Defined Chunking, Rabin's algorithm etc) + +_Remark 1_ I really like the Rabin fingerprinting approach however deduplication means we need to implement reference counting. How do we implement it? If we suppose a CRDT counter, if we do +1, +1, -1 but counter is registered as +1, -1, +1, we are at zero at one point and lost ou chunk. ---> we need to be careful in our implementation if we want to play. + +_Remark 2_ Seafile idea has been stolen from this article: https://pdos.csail.mit.edu/papers/lbfs:sosp01/lbfs.pdf + +#### Random notes + +--> we should not talk about block. It is the abstraction that manipulate your FS to interact with your hard drive. "Chunk" is probably more appropriate. Block storage are a class of distributed storage where you expose the abstraction of your hard drive over the network, mainly SATA over ethernet, thinking to SCSI, FiberChannel, and so on + +### Questions à résoudre + + + 1. est-ce que cassandra support de mettre certaines tables sur un SSD et d'autres sur un disque rotatif ? + 2. est-ce que cassandra/scylladb a un format de table on disk qui ne s'écroule pas complètement losque tu as des gros blobs ? (les devs de sqlite ont écrit tout un article pour dire que même avec leur lib qui est quand même sacrément optimisés, ils considèrent qu'à partir de je crois 4ko c'est plus efficace de mettre les blobs dans des fichiers séparés) - https://www.sqlite.org/intern-v-extern-blob.html + 3. Quelle taille de blocs ? L'idée c'est qu'on a quand même des liens en WAN avec des débits pas forcéments incroyables. Et ça serait bien que le temps de répliquer un bloc soit de l'ordre de la seconde maxi. En cas de retry, pour pouvoir mieux monitorer la progression, etc. Exoscale utilise 16Mo. LX propose 1Mo. + + + + + +#### Modules + +- `membership/`: configuration, membership management (gossip of node's presence and status), ring generation --> what about Serf (used by Consul/Nomad) : https://www.serf.io/? Seems a huge library with many features so maybe overkill/hard to integrate +- `metadata/`: metadata management +- `blocks/`: block management, writing, GC and rebalancing +- `internal/`: server to server communication (HTTP server and client that reuses connections, TLS if we want, etc) +- `api/`: S3 API +- `web/`: web management interface + + +#### Metadata tables + +**Objects:** + +- *Hash key:* Bucket name (string) +- *Sort key:* Object key (string) +- *Sort key:* Version timestamp (int) +- *Sort key:* Version UUID (string) +- Complete: bool +- Inline: bool, true for objects < threshold (say 1024) +- Object size (int) +- Mime type (string) +- Data for inlined objects (blob) +- Hash of first block otherwise (string) + +*Having only a hash key on the bucket name will lead to storing all file entries of this table for a specific bucket on a single node. At the same time, it is the only way I see to rapidly being able to list all bucket entries...* + +**Blocks:** + +- *Hash key:* Version UUID (string) +- *Sort key:* Offset of block in total file (int) +- Hash of data block (string) + +A version is defined by the existence of at least one entry in the blocks table for a certain version UUID. +We must keep the following invariant: if a version exists in the blocks table, it has to be referenced in the objects table. +We explicitly manage concurrent versions of an object: the version timestamp and version UUID columns are index columns, thus we may have several concurrent versions of an object. +Important: before deleting an older version from the objects table, we must make sure that we did a successfull delete of the blocks of that version from the blocks table. + +Thus, the workflow for reading an object is as follows: + +1. Check permissions (LDAP) +2. Read entry in object table. If data is inline, we have its data, stop here. + -> if several versions, take newest one and launch deletion of old ones in background +3. Read first block from cluster. If size <= 1 block, stop here. +4. Simultaneously with previous step, if size > 1 block: query the Blocks table for the IDs of the next blocks +5. Read subsequent blocks from cluster + +Workflow for PUT: + +1. Check write permission (LDAP) +2. Select a new version UUID +3. Write a preliminary entry for the new version in the objects table with complete = false +4. Send blocks to cluster and write entries in the blocks table +5. Update the version with complete = true and all of the accurate information (size, etc) +6. Return success to the user +7. Launch a background job to check and delete older versions + + +Workflow for DELETE: + +1. Check write permission (LDAP) +2. Get current version (or versions) in object table +3. Do the deletion of those versions NOT IN A BACKGROUND JOB THIS TIME +4. Return succes to the user if we were able to delete blocks from the blocks table and entries from the object table + +To delete a version: + +1. List the blocks from Cassandra +2. For each block, delete it from cluster. Don't care if some deletions fail, we can do GC. +3. Delete all of the blocks from the blocks table +4. Finally, delete the version from the objects table + + +Known issue: if someone is reading from a version that we want to delete and the object is big, the read might be interrupted. I think it is ok to leave it like this, we just cut the connection if data disappears during a read. + +("Soit P un problème, on s'en fout est une solution à ce problème") + + +#### Block storage on disk + +**Blocks themselves:** + +- file path = /blobs/(first 3 hex digits of hash)/(rest of hash) + +**Reverse index for GC & other block-level metadata:** + +- file path = /meta/(first 3 hex digits of hash)/(rest of hash) +- map block hash -> set of version UUIDs where it is referenced + +Usefull metadata: + +- list of versions that reference this block in the Casandra table, so that we can do GC by checking in Cassandra that the lines still exist +- list of other nodes that we know have acknowledged a write of this block, usefull in the rebalancing algorithm + +Write strategy: have a single thread that does all write IO so that it is serialized (or have several threads that manage independent parts of the hash space). When writing a blob, write it to a temporary file, close, then rename so that a concurrent read gets a consistent result (either not found or found with whole content). + +Read strategy: the only read operation is get(hash) that returns either the data or not found (can do a corruption check as well and return corrupted state if it is the case). Can be done concurrently with writes. + +**Internal API:** + +- get(block hash) -> ok+data/not found/corrupted +- put(block hash & data, version uuid + offset) -> ok/error +- put with no data(block hash, version uuid + offset) -> ok/not found plz send data/error +- delete(block hash, version uuid + offset) -> ok/error + +GC: when last ref is deleted, delete block. +Long GC procedure: check in Cassandra that version UUIDs still exist and references this block. + +Rebalancing: takes as argument the list of newly added nodes. + +- List all blocks that we have. For each block: +- If it hits a newly introduced node, send it to them. + Use put with no data first to check if it has to be sent to them already or not. + Use a random listing order to avoid race conditions (they do no harm but we might have two nodes sending the same thing at the same time thus wasting time). +- If it doesn't hit us anymore, delete it and its reference list. + +Only one balancing can be running at a same time. It can be restarted at the beginning with new parameters. + + +#### Membership management + +Two sets of nodes: + +- set of nodes from which a ping was recently received, with status: number of stored blocks, request counters, error counters, GC%, rebalancing% + (eviction from this set after say 30 seconds without ping) +- set of nodes that are part of the system, explicitly modified by the operator using the web UI (persisted to disk), + is a CRDT using a version number for the value of the whole set + +Thus, three states for nodes: + +- healthy: in both sets +- missing: not pingable but part of desired cluster +- unused/draining: currently present but not part of the desired cluster, empty = if contains nothing, draining = if still contains some blocks + +Membership messages between nodes: + +- ping with current state + hash of current membership info -> reply with same info +- send&get back membership info (the ids of nodes that are in the two sets): used when no local membership change in a long time and membership info hash discrepancy detected with first message (passive membership fixing with full CRDT gossip) +- inform of newly pingable node(s) -> no result, when receive new info repeat to all (reliable broadcast) +- inform of operator membership change -> no result, when receive new info repeat to all (reliable broadcast) + +Ring: generated from the desired set of nodes, however when doing read/writes on the ring, skip nodes that are known to be not pingable. +The tokens are generated in a deterministic fashion from node IDs (hash of node id + token number from 1 to K). +Number K of tokens per node: decided by the operator & stored in the operator's list of nodes CRDT. Default value proposal: with node status information also broadcast disk total size and free space, and propose a default number of tokens equal to 80%Free space / 10Gb. (this is all user interface) + + +#### Constants + +- Block size: around 1MB ? --> Exoscale use 16MB chunks +- Number of tokens in the hash ring: one every 10Gb of allocated storage +- Threshold for storing data directly in Cassandra objects table: 1kb bytes (maybe up to 4kb?) +- Ping timeout (time after which a node is registered as unresponsive/missing): 30 seconds +- Ping interval: 10 seconds +- ?? + + +#### Links + + - CDC: + - Erasure coding: + - [Openstack Storage Concepts](https://docs.openstack.org/arch-design/design-storage/design-storage-concepts.html) + - [RADOS](https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf) + diff --git "a/src/Technique/D\303\251veloppement/Guichet.md" "b/src/Technique/D\303\251veloppement/Guichet.md" new file mode 100644 index 0000000..3e67c44 --- /dev/null +++ "b/src/Technique/D\303\251veloppement/Guichet.md" @@ -0,0 +1 @@ +Le code est ici : https://git.deuxfleurs.fr/Deuxfleurs/guichet diff --git a/src/Technique/Infra/Internet.md b/src/Technique/Infra/Internet.md new file mode 100644 index 0000000..b7ba6b4 --- /dev/null +++ b/src/Technique/Infra/Internet.md @@ -0,0 +1,5 @@ +## Problèmes de connexion + +Actuellement les serveurs sont hébergés derrière une connexion Free qui a des problèmes en soirée. + +Plus d'informations ici : https://www.aduf.org/viewtopic.php?t=286599&start=0 diff --git a/src/Technique/Operations/Assets/exemple_offre.txt b/src/Technique/Operations/Assets/exemple_offre.txt new file mode 100644 index 0000000..3af76ec --- /dev/null +++ b/src/Technique/Operations/Assets/exemple_offre.txt @@ -0,0 +1,118 @@ +type: offer, sdp: v=0 +o=- 1923518516 2 IN IP4 127.0.0.1 +s=- +t=0 0 +a=msid-semantic: WMS 48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 +a=group:BUNDLE audio video data +m=audio 10000 RTP/SAVPF 111 103 104 126 +c=IN IP4 82.253.205.190 +a=rtpmap:111 opus/48000/2 +a=rtpmap:103 ISAC/16000 +a=rtpmap:104 ISAC/32000 +a=rtpmap:126 telephone-event/8000 +a=fmtp:111 minptime=10;useinbandfec=1 +a=rtcp:9 IN IP4 0.0.0.0 +a=rtcp-fb:111 transport-cc +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 +a=setup:actpass +a=mid:audio +a=sendrecv +a=ice-ufrag:97nc61e52b11gu +a=ice-pwd:k2df7f8vgknj27sctj550cl2u +a=fingerprint:sha-1 FC:4F:0B:F5:34:07:D8:09:47:D2:3C:FE:D1:8E:05:4B:05:10:CD:A1 +a=candidate:1 1 ssltcp 2130706431 172.17.0.1 8080 typ host generation 0 +a=candidate:2 1 ssltcp 2130706431 192.168.1.4 8080 typ host generation 0 +a=candidate:4 1 udp 2113932031 172.17.0.1 10000 typ host generation 0 +a=candidate:5 1 udp 2113932031 192.168.1.4 10000 typ host generation 0 +a=candidate:3 1 ssltcp 1677724415 82.253.205.190 8080 typ srflx raddr 192.168.1.4 rport 8080 generation 0 +a=candidate:6 1 udp 1677724415 82.253.205.190 10000 typ srflx raddr 192.168.1.4 rport 10000 generation 0 +a=ssrc:3265394670 cname:mixed +a=ssrc:3265394670 msid:mixedmslabel mixedlabelaudio0 +a=ssrc:3265394670 mslabel:mixedmslabel +a=ssrc:3265394670 label:mixedlabelaudio0 +a=ssrc:3761143749 cname:sMYSy0kNyRU3eK0c-1 +a=ssrc:3761143749 msid:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 8a034425-b6b5-4928-ab5f-9ca0ec4168c4-1 +a=ssrc:3761143749 mslabel:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 +a=ssrc:3761143749 label:8a034425-b6b5-4928-ab5f-9ca0ec4168c4-1 +a=ssrc:3240916804 cname:75Ayhq4Cuv7k5JAP-1 +a=ssrc:3240916804 msid:27755a82-e9e7-4cc4-bdb3-354a06b3f32a-1 45de0b7f-8590-4232-9bde-77d55a7366b5-1 +a=rtcp-mux +m=video 10000 RTP/SAVPF 100 107 101 96 97 99 +c=IN IP4 82.253.205.190 +a=rtpmap:100 VP8/90000 +a=rtpmap:107 H264/90000 +a=rtpmap:101 VP9/90000 +a=rtpmap:96 rtx/90000 +a=rtpmap:97 rtx/90000 +a=rtpmap:99 rtx/90000 +a=fmtp:100 x-google-start-bitrate=800 +a=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f;x-google-start-bitrate=800 +a=fmtp:101 x-google-start-bitrate=800 +a=fmtp:96 apt=100 +a=fmtp:97 apt=101 +a=fmtp:99 apt=107 +a=rtcp:9 IN IP4 0.0.0.0 +a=rtcp-fb:100 ccm fir +a=rtcp-fb:100 nack +a=rtcp-fb:100 nack pli +a=rtcp-fb:100 transport-cc +a=rtcp-fb:107 ccm fir +a=rtcp-fb:107 nack +a=rtcp-fb:107 nack pli +a=rtcp-fb:107 transport-cc +a=rtcp-fb:101 ccm fir +a=rtcp-fb:101 nack +a=rtcp-fb:101 nack pli +a=rtcp-fb:101 transport-cc +a=rtcp-fb:96 ccm fir +a=rtcp-fb:96 nack +a=rtcp-fb:96 nack pli +a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time +a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 +a=setup:actpass +a=mid:video +a=sendrecv +a=ice-ufrag:97nc61e52b11gu +a=ice-pwd:k2df7f8vgknj27sctj550cl2u +a=fingerprint:sha-1 FC:4F:0B:F5:34:07:D8:09:47:D2:3C:FE:D1:8E:05:4B:05:10:CD:A1 +a=candidate:1 1 ssltcp 2130706431 172.17.0.1 8080 typ host generation 0 +a=candidate:2 1 ssltcp 2130706431 192.168.1.4 8080 typ host generation 0 +a=candidate:4 1 udp 2113932031 172.17.0.1 10000 typ host generation 0 +a=candidate:5 1 udp 2113932031 192.168.1.4 10000 typ host generation 0 +a=candidate:3 1 ssltcp 1677724415 82.253.205.190 8080 typ srflx raddr 192.168.1.4 rport 8080 generation 0 +a=candidate:6 1 udp 1677724415 82.253.205.190 10000 typ srflx raddr 192.168.1.4 rport 10000 generation 0 +a=ssrc:3339205972 cname:75Ayhq4Cuv7k5JAP-1 +a=ssrc:3339205972 msid:27755a82-e9e7-4cc4-bdb3-354a06b3f32a-1 4de277cb-7421-402a-bbb1-2090dab4540e-1 +a=ssrc:3560865865 cname:sMYSy0kNyRU3eK0c-1 +a=ssrc:3560865865 msid:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 +a=ssrc:3560865865 mslabel:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 +a=ssrc:3560865865 label:21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 +a=ssrc:1942865873 cname:mixed +a=ssrc:1942865873 msid:mixedmslabel mixedlabelvideo0 +a=ssrc:1942865873 mslabel:mixedmslabel +a=ssrc:1942865873 label:mixedlabelvideo0 +a=ssrc:3656552182 cname:sMYSy0kNyRU3eK0c-1 +a=ssrc:3656552182 msid:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 +a=ssrc:3656552182 mslabel:48d3ae09-99f6-4a73-bad1-1b0963eaf3cc-1 +a=ssrc:3656552182 label:21a02fe8-c9f4-49fe-aaef-4c4ad48a3516-1 +a=ssrc:4136660991 cname:75Ayhq4Cuv7k5JAP-1 +a=ssrc:4136660991 msid:27755a82-e9e7-4cc4-bdb3-354a06b3f32a-1 4de277cb-7421-402a-bbb1-2090dab4540e-1 +a=ssrc-group:FID 3560865865 3656552182 +a=ssrc-group:FID 3339205972 4136660991 +a=rtcp-mux +a=x-google-flag:conference +m=application 10000 DTLS/SCTP 5000 +c=IN IP4 82.253.205.190 +a=setup:actpass +a=mid:data +a=ice-ufrag:97nc61e52b11gu +a=ice-pwd:k2df7f8vgknj27sctj550cl2u +a=fingerprint:sha-1 FC:4F:0B:F5:34:07:D8:09:47:D2:3C:FE:D1:8E:05:4B:05:10:CD:A1 +a=candidate:1 1 ssltcp 2130706431 172.17.0.1 8080 typ host generation 0 +a=candidate:2 1 ssltcp 2130706431 192.168.1.4 8080 typ host generation 0 +a=candidate:4 1 udp 2113932031 172.17.0.1 10000 typ host generation 0 +a=candidate:5 1 udp 2113932031 192.168.1.4 10000 typ host generation 0 +a=candidate:3 1 ssltcp 1677724415 82.253.205.190 8080 typ srflx raddr 192.168.1.4 rport 8080 generation 0 +a=candidate:6 1 udp 1677724415 82.253.205.190 10000 typ srflx raddr 192.168.1.4 rport 10000 generation 0 +a=sctpmap:5000 webrtc-datachannel 1024 diff --git a/src/Technique/Operations/Assets/livebox_parefeu_personnalise.png b/src/Technique/Operations/Assets/livebox_parefeu_personnalise.png new file mode 100644 index 0000000..16c922a Binary files /dev/null and b/src/Technique/Operations/Assets/livebox_parefeu_personnalise.png differ diff --git a/src/Technique/Operations/Jitsi.md b/src/Technique/Operations/Jitsi.md new file mode 100644 index 0000000..8e87d8f --- /dev/null +++ b/src/Technique/Operations/Jitsi.md @@ -0,0 +1,94 @@ +## 2020-04-02 Campagne de debug Jitsi + +Contact: Quentin + +### Description du problème + +Les conversations à 3+ donc relayées par le serveur ne fonctionnent pas bien. +Louison m'a rapporté que ça avait marché pour lui (3 utilisateurs avec un Webkit). +Mais moi ça a échoué hier soir (01/04/2020) avec des participants sous Firefox. +Le bug est toujours le même : on entend 2 personnes sur 3 ou on voit 2 personnes sur 3, on recharge la page et c'est quelqu'un d'autre pour qui ça ne fonctionne plus. Souvent c'est que dans un sens. +À chaque fois en passant sur Facebook Messenger, le problème est résolu instantanément. +Par contre Facebook Messenger impose Google Chrome/Chromium pour les visio de groupe (et ne supporte donc pas Firefox). + +D'où mes 2 suspicions : + + - Firefox a un bug quelconque dans sa pile WebRTC déclenché par le mode conversation de groupe + - Jitsi a un problème avec les déconnexions/changement de connexion/petit hoquets du réseau et n'arrive pas à se reconnecter. Ça pourrait être rendu pire à certain moment de la journée comme le soir où le réseau est plus sollicité. Et ce serait provoqué lors du reload on repasse de 3 à 2, en P2P donc puis de nouveau de 2 à 3. + +### Approfondissement + +Avant d'aller plus loin, nous avons voulu prendre le temps d'identifier précisément les problèmes d'expérience utilisateurs et leur corrélation avec la plateforme de l'utilisateur (navigateur, OS). + +Pour celà, nous avons suivi deux approches : + 1. Mener nos propres tests + 2. Chercher d'autres retours + +#### Mener nos propres tests + +Nous avons effectué deux appels : un avec Firefox seulement et un avec Chrome/Chromium seulement. +Merci à Alex, Adrien et Maximilien pour leur participation. + +Voilà les conclusions que nous avons tirées de nos tests : + + - L'appel avec Firefox a déclenché le bug immédiatement, peu importe la version de Firefox ou de l'OS (firefox stable/nightly, fedora stable/beta, etc.) + - Le passage de tout le monde sous Chrome/Chromium a permis d'avoir une conversation stable. + - Adrien avait sa Livebox avec pare-feu configuré en mode "élevé" et a du ajouter dans sa liste blanche les ports utilisés par Jitsi (`4443/tcp` et `10000/udp` au moment du test, seul un des deux a besoin d'être accessible) + +Nous avons donc demandé à Adrien quels étaient les ports ouverts par défaut dans le mode élevé de sa box : + +![Livebox Parefeu Personnalisé](Assets/livebox_parefeu_personnalise.png) + +Nous avons dans un premier temps retenu le port `995/tcp` pour Jitsi, le port UDP ne pouvant être changé (limitation de Jitsi). +Cependant, pour des raisons de sécurité, les navigateurs ne peuvent pas utiliser les ports en dessous de `1024/*`, à l'exception des ports `80/tcp` et `443/tcp` comme l'indique ;'issue [#3583](https://bugs.chromium.org/p/webrtc/issues/detail?id=3583) de Chromium. + +La capture n'indique pas de port TCP supérieur à 1024, nous ne pouvons donc pas résoudre ce problème de notre côté, car à l'heure actuelle, nos ports `80/tcp` et `443/tcp` sont utilisés et nous n'avons qu'une seule IP publique. +Les utilisateurs activant le parefeu en mode élevé devront ajouter une exception dans leur parefeu eux-mêmes. + +#### Chercher d'autres retours + +[Tedomum](https://tedomum.net/) a eu connaissance de problèmes similaires. +Ils ont également identifié Firefox et assurent qu'avec l'application Android ça marche bien. +Ils me confirment que le problème vient bien du logiciel et non pas de notre déploiement. +Ils m'ont pointé entre autre vers cette issue github [#4758](https://github.com/jitsi/jitsi-meet/issues/4758) +Apparemment une issue est dédiée en particulier au problème que nous rencontrons de déconnexion partielle d'un participant, mais nous ne l'avons pas encore retrouvée. + +### Correctifs + + 1. Notre instance Jitsi a été reconfigurée pour refuser Firefox. Suivre l'avancée du développement de Jitsi pour Firefox + * [#4758](https://github.com/jitsi/jitsi-meet/issues/4758) + * [#5439](https://github.com/jitsi/jitsi-meet/issues/5439) + * _À compléter_ + 2. Pour relayer la vidéo à travers la plupart des parefeux, notre `videobridge` Jitsi devait écouter sur le port `443/tcp`. Hors, ce port est déjà utilisé par notre frontal HTTPS. À défaut, Jitsi est maintenant configuré avec `8080/tcp` et `10000/udp` (contre `4443/tcp` et `10000/udp` avant). + * Un déploiement en IPv6 pourrait résoudre le problème pour une partie des utilisateurs + * Avoir un cluster géo-distribué avec plusieurs IPv4 pourrait également résoudre le problème + * Avoir un frontend layer 4 (niveau TCP) qui trouve une signature pour router du DTLS vers videobridge et du TLS vers traefik + +### À propos du control/data plane de Jitsi + +Notre videobridge écoute donc sur les ports `8080/tcp` et `10000/udp`. + +WebRTC fonctionne en deux étapes : + - Des offres doivent être échangées via un serveur de signaling quelconque + - Ensuite, ces offres servent à connecter des clients directement via un protocole TCP ou UDP avec un thin layer propre à WebRTC + +#### Le control plane de Jitsi : Prosody sur HTTPS via Bosh/XMPP + +Le serveur de signaling Jitsi n'est autre que le serveur de chat prosody. +Pour ça, prosody est exposé à travers HTTP grâce au protocole BOSH (XMPP overs HTTPS). +Une fois l'offre reçue ([exemple](Assets/exemple_offre.txt)), elle est enregistrée dans le navigateur à l'aide de `setRemoteDescription` pour initialiser le data plane. +On peut débugger le signaling WebRTC sous Chromium avec [chrome://webrtc-internarls](chrome://webrtc-internals/). + +Quand plus de deux participants sont connectés dans la conversation, Jitsi n'envoie pas les offres de chaque participant aux autres participants. À la place, elle envoie qu'une seule offre, celle de son VideoBridge. + +Ainsi, le VideoBridge est une sorte de client WebRTC particulier, qui récolte et redispatche à travers WebRTC tous les flux video/audio. + +#### Le data plane de Jitsi : videobridge sur DTLS/SCTP via WebRTC + +WebRTC utilise deux formats de paquets selon [Mozilla Developer Network|RTCDataChannel|DataFormat](https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel#Data_format) : + - `UDP/DTLS/SCTP` + - `TCP/DTLS/SCTP` + +On a donc un format de données arbitraire encapsulé dans du SCTP lui même encapsulé dans du DTLS. +DTLS est prévu pour les datagrammes à l'origine, car WebRTC est prévu d'abord pour fonctionner sous UDP. +Le TCP est là en mode dégradé, en secours, il sert juste de tunnel pour relayer des datagrammes. diff --git a/src/Technique/index.md b/src/Technique/index.md new file mode 100644 index 0000000..8aea90f --- /dev/null +++ b/src/Technique/index.md @@ -0,0 +1,22 @@ +Deuxfleurs utilise les composants suivants dans son infrastructure: + +- Ansible (configuration des noeuds) +- Docker (conteneurs) +- Nomad (orchestration des conteneurs) +- Consul (stockage clef/valeur distribué, découverte de services) +- Glusterfs (système de fichiers distribué) +- Stolon (système de réplication pour PostgreSQL) + +Les services proposés sont les suivants: + +- Chat via Matrix (Synapse, Riot) +- Email (Postfix, Dovecot, SoGo) +- Stockage (Seafile) + +Par ailleurs, nous avons développés nous-même un certain nombre d'outils pour compléter la stack: + +- [Bottin](https://bottin.eu), un serveur LDAP (gestion des comptes utilisateurs) basé sur le stockage clef/valeur de Consul +- [Guichet](https://git.deuxfleurs.fr/Deuxfleurs/Guichet/), une interface web de gestion des utilisateurs +- [Easybridge](https://git.deuxfleurs.fr/lx/Easybridge/), un bridge entre Matrix et d'autres réseaux + +Le code de l'infrastructure [est publiquement disponible](https://git.deuxfleurs.fr/Deuxfleurs/deuxfleurs.fr/). diff --git a/src/_layout.pug b/src/_layout.pug index 0a7f697..9bd186b 100644 --- a/src/_layout.pug +++ b/src/_layout.pug @@ -16,9 +16,12 @@ block root .menu-item a(href='https://guichet.deuxfleurs.fr') compte span  |  - .menu-item - a(href='/Documentation') doc - span  |  h1 #{title} - main - block content + block main + main + .container.spacing + nav + strong + a(href="/") Accueil + +menu(root) + block content diff --git a/src/_markdown.pug b/src/_markdown.pug new file mode 100644 index 0000000..b92c4e9 --- /dev/null +++ b/src/_markdown.pug @@ -0,0 +1,7 @@ +extends ./_layout.pug + +prepend root + - title = element.nice_path[element.nice_path.length - 1] + +block content + != markdown diff --git a/src/_mixin/menu.pug b/src/_mixin/menu.pug index 09794c6..462d06f 100644 --- a/src/_mixin/menu.pug +++ b/src/_mixin/menu.pug @@ -1,7 +1,13 @@ mixin menu(o) ul each val in o.children - - if (val.type == 'folder') + - if (val.type == 'folder' && val.tags.includes('document')) li - a(href=val.url)= val.nice_name + - if (val.tags.includes('has_index')) + a(href=val.url)= val.nice_name + - else + span= val.nice_name +menu(val) + - else if (val.type == 'file' && val.tags.includes('document') && !val.tags.includes('is_index')) + li + a(href=val.url)= val.nice_name diff --git a/src/css/main.css b/src/css/main.css index 37e3b4d..3cee741 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -67,7 +67,7 @@ header > .container > .menu-item > img { vertical-align: -9px; } -header > .container > .menu-item > a,span { +header > .container > .menu-item > a, header > .container > .menu-item > span { font-size: 30px; color: white; text-decoration: none; diff --git a/src/index.pug b/src/index.pug index 00a8afc..a02ebb9 100644 --- a/src/index.pug +++ b/src/index.pug @@ -4,55 +4,55 @@ prepend root - title = "deuxfleurs" block content - .container.spacing - .chapeau ⇨ protège votre vie privée - .chapeau ⇨ défend vos libertés et vos droits - .chapeau ⇨ ne vous manipule pas - .chapeau ⇨ promeut la sobriété numérique - - section.spacing - h2 nos services permettent de - .list - a.service-box.spacing(href='https://riot.deuxfleurs.fr') - div(style='font-size: 80px; height: 120px') 💬 - h3 discuter - a.service-box.spacing(href='https://jitsi.deuxfleurs.fr') - div(style='font-size: 80px; height: 120px') 📞 - h3 s'appeler - a.service-box.spacing(href='https://cloud.deuxfleurs.fr') - div(style='font-size: 80px; height: 120px') 🔒 - h3 sauvegarder vos documents - a.service-box.spacing(href='https://sogo.deuxfleurs.fr') - div(style='font-size: 80px; height: 120px') 📨 - h3 envoyer des emails - a.service-box.spacing(href='https://p.adnab.me') - div(style='font-size: 80px; height: 120px') 📄 - h3 collaborer - a.service-box.spacing(href='documentation.html#site') - div(style='font-size: 80px; height: 120px') 🌐 - h3 créer votre site - a.service-box.spacing(href='https://git.deuxfleurs.fr') - div(style='font-size: 80px; height: 120px') 💻 - h3 coder - br - - p.spacing ⚠️ Vous devez être membre pour utiliser ces services.  - a(href="#nous-rejoindre") Nous rejoindre. - - section.spacing - h2 internet est politique - :markdown-it(linkify) - L'IETF, l'organisme en charge de la standardisation d'internet, reconnait que les choix technologiques ont un impact sur les droits de l'homme [[RFC8280]](https://trac.tools.ietf.org/html/rfc8280). - - section.spacing - h2 notre réponse - em à venir - - section.spacing - h2(id="nous-rejoindre") nous rejoindre - p.spacing Nous fonctionnons actuellement selon un mode de cooptation qui nous permet d'une part de mieux contrôler l'utilisation des ressources et éviter les abus, et d'autre part de créer et garder un contact humain avec nos utilisateurs. - p.spacing - | Si vous connaissez un membre de l'association, contactez le directement pour qu'il vous créer un compte. - br - | Sinon, vous pouvez nous écrire à coucoudeuxfleurs.fr. + .chapeau ⇨ protège votre vie privée + .chapeau ⇨ défend vos libertés et vos droits + .chapeau ⇨ ne vous manipule pas + .chapeau ⇨ promeut la sobriété numérique + + + section.spacing + h2 nos services permettent de + .list + a.service-box.spacing(href='https://riot.deuxfleurs.fr') + div(style='font-size: 80px; height: 120px') 💬 + h3 discuter + a.service-box.spacing(href='https://jitsi.deuxfleurs.fr') + div(style='font-size: 80px; height: 120px') 📞 + h3 s'appeler + a.service-box.spacing(href='https://cloud.deuxfleurs.fr') + div(style='font-size: 80px; height: 120px') 🔒 + h3 sauvegarder vos documents + a.service-box.spacing(href='https://sogo.deuxfleurs.fr') + div(style='font-size: 80px; height: 120px') 📨 + h3 envoyer des emails + a.service-box.spacing(href='https://p.adnab.me') + div(style='font-size: 80px; height: 120px') 📄 + h3 collaborer + a.service-box.spacing(href='documentation.html#site') + div(style='font-size: 80px; height: 120px') 🌐 + h3 créer votre site + a.service-box.spacing(href='https://git.deuxfleurs.fr') + div(style='font-size: 80px; height: 120px') 💻 + h3 coder + br + + p.spacing ⚠️ Vous devez être membre pour utiliser ces services.  + a(href="#nous-rejoindre") Nous rejoindre. + + section.spacing + h2 internet est politique + :markdown-it(linkify) + L'IETF, l'organisme en charge de la standardisation d'internet, reconnait que les choix technologiques ont un impact sur les droits de l'homme [[RFC8280]](https://trac.tools.ietf.org/html/rfc8280). + + section.spacing + h2 notre réponse + em à venir + + section.spacing + h2(id="nous-rejoindre") nous rejoindre + p.spacing Nous fonctionnons actuellement selon un mode de cooptation qui nous permet d'une part de mieux contrôler l'utilisation des ressources et éviter les abus, et d'autre part de créer et garder un contact humain avec nos utilisateurs. + p.spacing + | Si vous connaissez un membre de l'association, contactez le directement pour qu'il vous créer un compte. + br + | Sinon, vous pouvez nous écrire à coucoudeuxfleurs.fr. -- cgit v1.2.3