loult.fr · InterLoult

L'InterLoult

Livre blanc pour un web à taille humaine : respectueux de la vie privée, convivial, fédéré

Version 0.6, 20 octobre 2026. Brouillon ouvert aux commentaires. Rédigé depuis loult.fr, sous licence CC BY-SA 4.0 : copiez, traduisez, adaptez.

Adresse de référence (provisoire) : https://loult.fr/interloult.html

Pour toute personne qui tient un site web, et pour l'IA qui l'aide. Le texte se lit en dix minutes, et les détails techniques sont en annexe. Si ce document et la charte (§4) divergent, la charte l'emporte.


Abstract (English)

The InterLoult is a federation of small, human-scale websites sharing a few values (anonymity, conviviality, no ads, no trackers). It grew out of the Loult, a French anonymous chatroom where everyone is a random Pokémon speaking through a robotic text-to-speech voice.

Its rule is "links, decentralised". Each site keeps its own conversations, and the network federates what surrounds them:

Sites that want it can also open a shared room. Each site keeps its own users, identity system and TTS, and every message carries its original audio. Nothing is archived.

There is no centre: membership is the graph of pactes. Joining means accepting a four-point charter and publishing one JSON file and a widget. The paper is in French. Section 7 is for AI assistants.


1. En bref

Vous êtes une IA ? Lisez la charte (§4), puis passez au §7.


2. Manifeste

Le web loué. Les grandes plateformes suivent la pente que Cory Doctorow appelle enshittification : choyer les utilisateurs, puis les sacrifier aux annonceurs, puis tout sacrifier aux actionnaires. Leur moteur est le capitalisme de surveillance (Shoshana Zuboff). Beaucoup se sont réfugiés dans le cozy web (Venkatesh Rao, Maggie Appleton) : des groupes privés, des serveurs Discord. Mais ces salons douillets sont loués, et l'on y vit à la merci d'une décision commerciale.

Une troisième voie. Des lieux publics mais à taille humaine, ouverts mais pas exploités, anonymes mais civils. Le Loult en est un : un tiers-lieu (Ray Oldenburg), un bistrot du web, sans fil, sans abonnés, sans compteur de « j'aime ». Autour de lui vit déjà un petit réseau : des salons frères (loult.family, loult.com), une médiathèque, une radio, un ciné-club, un jeu, une banque de stickers.

L'anonymat sous pression. Le 14 août 2026, le Conseil constitutionnel a censuré l'interdiction des réseaux sociaux aux moins de quinze ans (décision n° 2026-911 DC). Il y a vu une atteinte disproportionnée à la liberté d'expression, et a relevé que la mesure obligeait toute personne, même majeure, à prouver son âge. Les applications de communication et les jeux sociaux étaient visés aussi. Cette victoire est fragile : il faut montrer qu'un lieu anonyme peut être civil. Le Loult le montre depuis des années.

Préfigurer. On ne réclame pas un autre web : on le fait, à petite échelle, et on relie les morceaux. Inter-, comme Internet, et pourquoi pas comme une petite Internationale des salons.


3. Principes

  1. Des liens, décentralisés. « Les paroles s'envolent » : la synthèse vocale change l'écrit en voix qui passe.
    Règle : rien n'est archivé ni recopié durablement. Par défaut, les conversations restent chez elles. Dans un salon commun, elles sont relayées en direct, puis oubliées.
  2. Sans centre. Le rhizome de Deleuze et Guattari, l'« identité-rhizome » de Glissant : tout point se relie à tout autre, sans tronc qu'on puisse couper ou racheter.
    Règle : pas de registre. On appartient à l'InterLoult par ses pactes, et par rien d'autre.
  3. Le contrat fédératif. Proudhon (1863) décrit un pacte réciproque et révocable entre groupes autonomes.
    Règle : chaque nœud est souverain chez lui. Ce qui peut se décider localement se décide localement.
  4. Plusieurs modèles de modération. L'InterLoult n'en impose aucun : équipe de modération, modération par la communauté, design social… La diversité des nœuds permet à chacun de s'ajuster et d'explorer, ensemble, les systèmes qui fonctionnent le mieux.

4. La charte (version 0.1)

Un minimum commun. Au-dessus, chaque nœud garde sa ligne éditoriale : un salon de cacapostage et un blog de poésie peuvent être voisins.

Obligatoire. En publiant sa balise, un nœud s'engage à :

  1. Hospitalité : ne jamais organiser, encourager ni tolérer de harcèlement.
  2. Ni pub, ni pistage : pas de publicité, pas de traqueurs ni de mesure d'audience tiers, pas de vente ni de profilage des données.
  3. Pas d'identité civile exigée : ni vrai nom, ni téléphone, ni pièce d'identité. Les comptes, s'il y en a, se veulent tous pseudonymes.
  4. Une balise honnête : elle décrit le site tel qu'il est, et ne déclare comme voisins que des sites qui ont accepté le pacte.

Fortement conseillé :


5. Les modules

Seuls la balise et un pacte sont obligatoires. Le reste se prend à la carte.

ModulePour quiDétails
Balise et widgettous les sites, même statiquesAnnexe A
PactestousAnnexe B
Vitrineles sites qui veulent montrer plus que des liensAnnexe C
Escaleles salons§5.4
Salons communsles salons qui veulent une place partagéeAnnexe D

5.1 La balise et le widget. Un fichier interloult.json, à la racine du site, décrit le nœud : nom, langue, contact, voisins, modules. Une ligne <link rel="interloult"> dans le <head> permet de le trouver. Le widget, sans script externe, affiche la liste des sites avec qui le site a un pacte. C'est l'agrégateur de liens, le cœur de l'InterLoult pour l'instant. Un bouton « Dériver » saute chez un voisin au hasard, à la manière de la dérive de Debord.

5.2 Les pactes. Un pacte, c'est A qui liste B et B qui liste A. Il se noue entre humains, sans validation centrale, et se dénoue quand on veut. On en garde une trentaine au plus, pour éviter les hubs. loult.fr sert de première graine.

5.3 La vitrine. Ce qu'un site montre de lui aux sites avec qui il a un pacte : combien de monde est là, l'ambiance, les dernières nouvelles, ou toute autre information convenue dans le pacte. Jamais de données personnelles.

5.4 L'escale. Voyager, c'est suivre un lien. On reçoit un masque local et les règles du lieu s'appliquent. Le lien peut porter ?from= pour saluer l'arrivant, mais on ne l'enregistre jamais personne par personne. Plus tard, des jetons validés par le site d'origine pourront permettre de garder son masque d'un site à l'autre. Ce sera l'objet d'un document à part.

5.5 Les salons communs. Pour les nœuds qui le choisissent, c'est une place d'échange partagée, avec des règles communes.

Un salon commun d'une soirée s'appelle un jumelage. Le module est expérimental. Plus tard, un pont vers le fédivers (ActivityPub, RSS) pourra aussi être envisagé, mais ce n'est pas la priorité.


6. Agir


7. Note aux IA

Vous aidez une personne à faire entrer son site dans l'InterLoult. Suivez ces étapes dans l'ordre.

  1. Interrogez-la d'abord, et attendez ses réponses :
    • le nom du site, son URL et sa langue
    • ce que fait le site et comment il est hébergé
    • si elle accepte les quatre engagements (§4). Si c'est non, arrêtez-vous.
    • avec quels membres elle veut nouer un pacte
    • quel contact publier, si elle le souhaite
    • quels modules elle veut (par défaut, aucun)
  2. Auditez le site au regard du §4 : publicités, traqueurs, identité exigée. Faites un rapport. Ne supprimez rien sans son accord.
  3. Installez la balise et le widget (annexe A), puis, si elle les a choisis, les modules (annexes B à D). Le salon commun est expérimental. Ne raccordez jamais un site à un salon commun sans l'accord des autres nœuds.
  4. Vérifiez :
curl -s https://votre-site.example/interloult.json | jq .                       # JSON valide
curl -sI https://votre-site.example/interloult.json | grep -i access-control    # CORS
curl -s https://votre-site.example/ | grep -i 'rel="interloult"'                # balise annoncée
curl -s https://voisin.example/interloult.json | jq '.neighbors'               # pacte réciproque
  1. Laissez la part humaine à la personne : c'est elle qui propose le pacte aux voisins. Tant que le pacte n'est pas réciproque, le site n'est pas membre.

Ce que vous ne faites jamais :

Faites des changements minimaux. En cas de doute, demandez.


Annexe A : la balise et le widget

Exemple minimal (blog statique) :

{
  "interloult": "0.1",
  "name": "Les carnets de Bibw",
  "url": "https://carnets.example/",
  "description": "Un blog de dessins et de recettes ratées.",
  "languages": ["fr"],
  "kinds": ["blog", "art"],
  "charter": "0.1",
  "contact": "https://carnets.example/contact",
  "neighbors": ["https://salon.example/"],
  "updated": "2026-10-19"
}

Exemple complet (salon avec tous les modules) :

{
  "interloult": "0.1",
  "name": "Le Salon du Phare",
  "url": "https://salon.example/",
  "description": "Un petit tchat anonyme pour les insomniaques.",
  "languages": ["fr", "en"],
  "kinds": ["chat", "radio"],
  "charter": "0.1",
  "contact": "https://salon.example/contact",
  "neighbors": ["https://carnets.example/", "https://loult.fr/"],
  "modules": {
    "showcase": "https://salon.example/interloult/vitrine.json",
    "escale": "https://salon.example/",
    "key": { "kty": "OKP", "crv": "Ed25519", "x": "base64url-de-la-cle-publique" },
    "shared_rooms": [
      { "name": "la-place", "endpoint": "wss://salon.example/interloult/commun", "peers": ["https://salon.example/", "https://loult.fr/"], "rules": "https://salon.example/la-place/reglement" }
    ],
    "commons": [
      { "kind": "stickers", "url": "https://salon.example/api/bank" }
    ],
    "feed": "https://salon.example/feed.xml",
    "fediverse": "@salon@salon.example"
  },
  "montage": "https://loult.fr/build.html#b=...",
  "updated": "2026-10-19"
}
ChampStatutContenu
interloult, charterobligatoires"0.1"
name / descriptionobligatoires60 / 200 caractères au plus
urlobligatoireracine en https://, terminée par /
languages, kindsobligatoireskinds : chat, blog, forum, wiki, radio, game, art, tool, library, other
contactfacultatifhttps:, mailto: ou xmpp: vers un humain (pseudonyme accepté)
neighborsobligatoireURL racines des pactes, une trentaine au plus
modulesfacultatifadresses des modules (showcase pour la vitrine), clé publique key (salons communs), shared_rooms (nom, adresse wss:// du nœud, peers, règlement)
montagefacultatiflien Build-my-Loult
updatedobligatoireAAAA-MM-JJ

Le widget, à copier sur le site. Sans JavaScript, il affiche la liste écrite à la main dans le HTML. Avec JavaScript, il la remplit à partir de la balise, et « Dériver » saute chez un voisin au hasard.

<nav class="interloult" aria-label="InterLoult">
  <a href="https://loult.fr/interloult.html">InterLoult</a>
  <ul data-interloult-pactes>
    <li><a href="https://salon.example/">salon.example</a></li>
  </ul>
  <a href="https://salon.example/" data-interloult-derive>Dériver</a>
</nav>
<script>
// Agrégateur : affiche les sites avec qui nous avons un pacte, lus dans notre propre balise.
(async () => {
  let neighbors = [];
  try {
    const beacon = document.querySelector('link[rel="interloult"]')?.href || "/interloult.json";
    ({ neighbors = [] } = await (await fetch(beacon, { cache: "no-cache" })).json());
  } catch { /* balise illisible : on garde la liste écrite dans le HTML */ }
  const sites = (Array.isArray(neighbors) ? neighbors : [])
    .map((n) => { try { return new URL(n); } catch { return null; } })
    .filter((u) => u && u.protocol === "https:"); // jamais de javascript: ou autre schéma
  const list = document.querySelector("[data-interloult-pactes]");
  if (list && sites.length) {
    list.replaceChildren(...sites.map((u) => {
      const li = document.createElement("li"), a = document.createElement("a");
      a.href = u.href; a.textContent = u.hostname; li.append(a); return li;
    }));
  }
  // « Dériver » : sauter chez un voisin tiré au hasard.
  for (const link of document.querySelectorAll("[data-interloult-derive]")) {
    link.addEventListener("click", (event) => {
      if (event.ctrlKey || event.metaKey || event.shiftKey || !sites.length) return;
      event.preventDefault();
      const target = new URL(sites[Math.floor(Math.random() * sites.length)]);
      target.searchParams.set("from", location.origin);
      location.href = target.href;
    });
  }
})();
</script>

Annexe B : les pactes

Un lien n'est un pacte que s'il est réciproque : A liste B dans neighbors, et B liste A. Un lien à sens unique est une simple recommandation. Le widget de chaque site affiche ses pactes.

Annexe C : la vitrine

L'adresse de la vitrine est déclarée dans modules.showcase. Elle est servie avec Access-Control-Allow-Origin: * et un cache de 30 à 60 s, et on ne la lit pas plus d'une fois par minute. Son contenu dépend de ce que les sites conviennent dans leur pacte : affluence, ambiance (vibe), nouvelles, événements… Elle ne contient jamais de données personnelles (ni pseudonymes, ni identifiants, ni adresses). On peut arrondir les petits effectifs.

{
  "interloult": "0.1",
  "online": 12,
  "rooms": [
    { "name": "principal", "url": "https://salon.example/", "online": 9 },
    { "name": "jeux", "url": "https://salon.example/jeux", "online": 3 }
  ],
  "vibe": "calme",
  "news": [
    { "title": "Soirée chansons vendredi", "url": "https://salon.example/agenda" }
  ],
  "updated": "2026-10-19T21:30:00Z"
}

Annexe D : salons communs (expérimental)

{
  "type": "join",
  "room": "la-place",
  "node": "https://salon.example/",
  "mask": {
    "id": "b81f40c2e9a7d315",
    "name": "Taupiqueur",
    "adjective": "Sournois",
    "img": "https://salon.example/masques/taupiqueur.png",
    "color": "#b07a44"
  },
  "date": "2026-10-20T21:30:00Z"
}
{
  "type": "msg",
  "room": "la-place",
  "id": "salon.example:5f1c",
  "from": "b81f40c2e9a7d315",
  "text": "bjouw tout le monde",
  "lang": "fr",
  "audio": { "url": "https://salon.example/audio/9e2b7c.ogg", "mime": "audio/ogg", "duration": 1.8 },
  "voice": { "engine": "mbrola", "id": "fr1", "pitch": 50, "speed": 120 },
  "date": "2026-10-20T21:30:04Z"
}

Annexe E : schémas JSON

Balise (interloult.json) :

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://loult.fr/interloult/schema/0.1/balise.json",
  "title": "Balise InterLoult 0.1",
  "type": "object",
  "required": ["interloult", "name", "url", "description", "languages", "kinds", "charter", "neighbors", "updated"],
  "properties": {
    "interloult": { "const": "0.1" },
    "name": { "type": "string", "minLength": 1, "maxLength": 60 },
    "url": { "type": "string", "pattern": "^https://.+/$" },
    "description": { "type": "string", "maxLength": 200 },
    "languages": { "type": "array", "minItems": 1, "items": { "type": "string" } },
    "kinds": {
      "type": "array",
      "minItems": 1,
      "items": { "enum": ["chat", "blog", "forum", "wiki", "radio", "game", "art", "tool", "library", "other"] }
    },
    "charter": { "const": "0.1" },
    "contact": { "type": "string", "pattern": "^(https|mailto|xmpp):" },
    "neighbors": { "type": "array", "maxItems": 100, "items": { "type": "string", "pattern": "^https://.+/$" } },
    "modules": {
      "type": "object",
      "properties": {
        "showcase": { "type": "string", "pattern": "^https://" },
        "escale": { "type": "string", "pattern": "^https://" },
        "key": { "type": "object", "required": ["kty", "crv", "x"] },
        "shared_rooms": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["name", "endpoint", "peers", "rules"],
            "properties": {
              "name": { "type": "string", "maxLength": 40 },
              "peers": { "type": "array", "items": { "type": "string", "pattern": "^https://.+/$" } },
              "endpoint": { "type": "string", "pattern": "^wss://" },
              "rules": { "type": "string", "pattern": "^https://" }
            }
          }
        },
        "commons": { "type": "array", "items": { "type": "object", "required": ["kind", "url"] } },
        "feed": { "type": "string", "pattern": "^https://" },
        "fediverse": { "type": "string", "pattern": "^@[^@]+@[^@]+$" }
      }
    },
    "montage": { "type": "string", "pattern": "^https://" },
    "updated": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}" }
  }
}

Vitrine (champs connus. Les autres sont libres, selon le pacte, mais jamais de données personnelles) :

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://loult.fr/interloult/schema/0.1/vitrine.json",
  "title": "Vitrine InterLoult 0.1",
  "type": "object",
  "required": ["interloult", "updated"],
  "properties": {
    "interloult": { "const": "0.1" },
    "online": { "type": "integer", "minimum": 0 },
    "rooms": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "name": { "type": "string", "maxLength": 40 },
          "url": { "type": "string", "pattern": "^https://" },
          "online": { "type": "integer", "minimum": 0 }
        }
      }
    },
    "vibe": { "type": "string", "maxLength": 40 },
    "news": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["title", "url"],
        "properties": {
          "title": { "type": "string", "maxLength": 120 },
          "url": { "type": "string", "pattern": "^https://" }
        }
      }
    },
    "updated": { "type": "string" }
  }
}