Second Brain documentation

ADR 0105 – Favori « Envoyer vers l'inbox »

Accepté (2026-10-17, vague 3 du document d'idées)

← Toutes les décisions

ADR 0105 – Favori « Envoyer vers l’inbox »

Statut : Accepté (2026-10-17, vague 3 du document d’idées)

Capturer une page web depuis un ordinateur demandait de copier son adresse puis d’ouvrir la capture. Sur mobile, le partage vers l’application installée existe (ADR 0034) ; sur ordinateur, il manquait l’équivalent : un favori qui envoie la page ouverte.

Décision

  • Un favori javascript: sans secret ni appel inter-sites. Il lit location.href, document.title et la sélection (bornés à 2 000, 300 et 2 000 caractères), puis ouvre une petite fenêtre de l’application : /capture/bookmarklet?url=…&title=…&text=…. Si le navigateur bloque la fenêtre, la page courante est remplacée par celle de l’application. Aucune requête n’est faite vers l’API depuis le site visité : pas de CORS à ouvrir, pas de jeton dans le favori.
  • La page de l’application confirme, puis écrit. BookmarkletPage (features/capture/) montre le titre, l’adresse et le passage, et ne crée le nœud d’inbox qu’au clic « Ajouter à l’inbox », par POST /api/nodes (inbox: true) avec la session et le jeton CSRF habituels : aucune nouvelle route serveur. Tout site peut ouvrir cette page avec des paramètres de son choix, d’où la confirmation (même raison que pour share_target, ADR 0034).
  • Contrôles des paramètres (core/capture/bookmarklet.ts) : seule une adresse http/https sans identifiants est retenue (jamais javascript:, data:, file:), caractères de contrôle retirés, longueurs bornées. Sans adresse valide, la page affiche l’explication et rien n’est écrit.
  • Réutilisation de l’existant. La note se compose comme un partage (sharedNote, ADR 0034) : titre de la page (sinon l’adresse), corps = passage cité en Markdown (> …) puis adresse. Le titre vient du navigateur : le service de titre des liens (ADR 0075, appel sortant du serveur) n’est pas sollicité. Le favori ne passe pas par le share_target de la PWA, qui exige l’application installée et un POST géré par le service worker.
  • Installation. La page /capture/bookmarklet explique et montre le favori à glisser (lien javascript: marqué sûr pour Angular, uniquement celui que nous produisons), avec le code à copier si le glisser-déposer n’est pas possible. Accès par la palette (« Aller à : Favori… ») et une carte de la page Données. Le favori est lié à l’origine de l’application au moment de l’installation.

Alternatives écartées

  • fetch du favori vers /api/nodes : exigerait CORS avec cookies depuis n’importe quel site, un jeton CSRF lisible par la page visitée et un secret dans le favori. Rejeté.
  • Jeton d’API dédié dans le favori : un secret à garder, révoquer et faire tourner, pour un gain faible.
  • Écriture automatique à l’ouverture de la fenêtre : n’importe quel site pourrait remplir l’inbox à l’insu de l’utilisateur.
  • Extension de navigateur : une autre chaîne de publication, hors périmètre.

Limites connues

  • Un site dont la politique de sécurité (CSP) interdit les favoris javascript: ne peut pas être capturé ainsi ; la page le dit et renvoie vers le partage ou la capture d’une adresse.
  • Il faut être connecté dans ce navigateur ; sans session, l’écriture échoue et l’erreur est affichée dans la fenêtre.
  • La sélection est le texte brut (pas la mise en forme), limité à 2 000 caractères.

Corrections de la revue

  • Le code du favori borne chaque valeur sans couper une paire de substitution : un émoji à la limite de longueur faisait lever URIError à encodeURIComponent (le demi-caractère final est retiré après chaque slice). Test avec un émoji à la limite.
  • La page ouverte par le favori met window.opener = null : le site d’origine ne garde aucun lien vers la fenêtre de l’application.
  • Un clic (ou Entrée) sur le bouton d’installation, au lieu de le glisser, affiche « glissez ce bouton dans la barre de favoris ».