Le signal, pas le bruit. Une app de veille qui agrège et filtre l'actu tech chaque jour, pré-triée par heuristique avant tout appel LLM, pour ne garder que ce qui compte.
En ligne : signal.deuwi.xyz · Conception : SPEC.md · Sources : SOURCES.md
Cloudflare Workers + Hono + D1 + Cron + Assets. Dashboard vanilla JS (zéro build). Budget mini. Interface éditoriale « périodique imprimé », bilingue FR/EN et mode clair/sombre (préférence système par défaut, choix mémorisé).
Le flux principal (dev) : veille perso large, filtrable par catégories
(langages, IA outillage, web, cloud, backend, sécu, devops, archi), avec
favoris ★. Chaque entrée a un résumé dépliable et ses liens de référence.
L'idée : ne pas rouvrir dix onglets chaque matin pour se faire une idée de ce qui bouge. Une passe quotidienne (cron, 07:00 UTC) ingère, dédup, filtre et catégorise, sans bouton d'exécution public :
ingest (RSS/Atom + Hacker News + recherche Brave + France Travail)
→ dédup (hash URL normalisée + titre)
→ pré-filtre heuristique 0 token (fraîcheur · exclusions · pertinence) + catégorisation
La config du pré-filtre (fraîcheur, exclusions, mots-clés, catégories) est éditable dans l'onglet Réglages et appliquée à la passe suivante.
La même passe quotidienne enchaîne ensuite, pour l'Atelier, la lecture Notion (exclusion) puis la curation Haiku — voir ci-dessous.
Un second flux (deuwi) sert à ma propre production de contenu : curation
plus sélective, chaque fiche associant fait sourcé, angle de post,
chapitre de référence et profil ciblé, en FR et EN. Il alimente directement
mon écriture LinkedIn et s'exporte en CSV ou vers Notion. Pas conçu
pour un usage tiers, mais rien n'est caché : le code et la logique sont dans
ce même repo.
Ce flux intègre aussi des chiffres marché francophones sourcés (France Travail, passe mensuelle) : volume d'offres actives et indice de tension de recrutement (1–5) par métier dev (codes ROME éditables en config).
- Lecture (exclusion) — la passe lit la propriété
Source(URL) de la base pour ne pas reproposer ce qui y est déjà. - Écriture (à la demande) — bouton « Créer dans Notion » sur une fiche →
crée la page (colonnes Fait / Angle / Chapitre / Profil / Chiffres /
Source). Protégé par
ADMIN_TOKEN(voir Sécurité). La copie CSV reste dispo en repli.
Contrainte base Notion : une propriété Source de type URL. Partage
la base avec l'intégration (⋯ → Connections) sinon l'API renvoie 404. Sans
token Notion, l'app tourne sans exclusion ni écriture, la partie « La
sélection » n'est pas affectée.
- Node 20+
- Compte Cloudflare +
wrangler(npx wrangler login)
npm install
npx wrangler d1 create veille # colle le database_id dans wrangler.jsonc
npm run db:migrate:local # schéma + seed sources
npm run dev # http://localhost:8787Secrets locaux dans .dev.vars (gitignoré, voir
.dev.vars.example) : ANTHROPIC_API_KEY (curation),
NOTION_TOKEN + NOTION_DB_ID (Notion), ADMIN_TOKEN (protège les
écritures), BRAVE_API_KEY (sources recherche), FT_CLIENT_ID +
FT_CLIENT_SECRET (France Travail). Tous optionnels sauf ANTHROPIC : sans
Brave ou France Travail, ces sources sont simplement ignorées.
Déclencher une passe en dev (l'endpoint est protégé — en-tête
X-Admin-Token) :
curl -X POST "http://localhost:8787/api/daily?force=1" -H "x-admin-token: <ADMIN_TOKEN>"
# → {started:true}, tourne en tâche de fondnpm run db:migrate # migrations sur la base distante
npm run deploy # déploie le Worker + assets
npx wrangler secret put ANTHROPIC_API_KEY
npx wrangler secret put NOTION_TOKEN
npx wrangler secret put NOTION_DB_ID
npx wrangler secret put ADMIN_TOKEN
npx wrangler secret put BRAVE_API_KEY # optionnel — sources recherche
npx wrangler secret put FT_CLIENT_ID # optionnel — France Travail
npx wrangler secret put FT_CLIENT_SECRETCron quotidien (07:00 UTC) et custom domain signal.deuwi.xyz configurés
dans wrangler.jsonc.
Repo public. Aucun secret dans le dépôt. .dev.vars est gitignoré ; voir
.dev.vars.example. Secrets de prod via wrangler secret put.
L'app est déployée publiquement. La séparation est simple :
- Lecture = publique (aucune auth) :
GET /api/items,/api/stats,/api/sources,/api/drafts,/api/config, et le dashboard. - Écriture = protégée par un secret partagé
ADMIN_TOKEN: toute route qui modifie l'état ou dépense des ressources.
| Route | Méthode | Auth | Effet protégé |
|---|---|---|---|
/api/drafts/:id/notion |
POST | ✅ | crée une page Notion |
/api/config |
PUT / DELETE | ✅ | modifie/réinitialise les filtres (regex → ReDoS possible) |
/api/daily |
POST | ✅ | déclenche ingestion + curation Haiku (coût $) |
/api/items/:id/flag |
POST | ✅ | écrit en base (lu/favori) |
/api/backfill-en |
POST | ✅ | traduit en EN les fiches (coût $) |
| lectures + dashboard | GET | ❌ | — |
Comment ça marche (src/auth.ts) :
- Le client envoie l'en-tête
X-Admin-Token: <valeur>sur chaque écriture. - Le serveur compare à
ADMIN_TOKEN(secret Worker) en temps constant (empreintes SHA-256, pas de fuite de longueur). - Absent/mauvais →
401.ADMIN_TOKENnon configuré →503.
Côté dashboard : la clé est saisie une fois (popup), gardée en
localStorage, renvoyée sur chaque action d'écriture. Un 401 la purge et
redemande.
ADMIN_TOKEN est une chaîne aléatoire que tu définis (ex. openssl rand -hex 24) — la même valeur va dans le secret Worker et dans le dashboard ; ce
n'est pas un token fourni par un service. NOTION_TOKEN (intégration
Notion, pour écrire les pages) et ADMIN_TOKEN (ta clé d'accès aux
écritures) sont distincts.
Défenses complémentaires : requêtes SQL paramétrées ; processEntities:false
sur le parseur XML (pas de XXE) ; bornes sur la config (longueur des motifs,
nb de règles, fraîcheur 1-365) contre le ReDoS ; safeUrl côté client
bloque les href javascript:/data: ; erreurs internes loggées serveur,
message générique au client.
Rate-limiting : règle WAF Cloudflare au niveau de la zone —
100 requêtes / 10 s par IP sur /api/*, action Block. Elle s'applique à
l'edge, avant le Worker (protège aussi le quota Workers et le coût Haiku).
Configurée hors dépôt (dashboard Cloudflare → Security rules → Rate limiting).
- ✅ Ingestion (RSS/Atom + Hacker News), dédup, pré-filtre heuristique 0 token, catégorisation
- ✅ Curation Deuwi : fetch source + Haiku (score, chapitre, profil, vérif chiffres) + draft angle
- ✅ Fiches bilingues FR/EN (générées à la curation)
- ✅ Notion : exclusion (lecture) + création de page (écriture protégée) + copie CSV
- ✅ Passe quotidienne (cron) + garde-fou 1×/20h
- ✅ Config éditable (Réglages), liens de référence, résumé dépliable
- ✅ Interface « Le Signal » (bilingue, mode clair/sombre) déployée sur signal.deuwi.xyz
- ✅ Rate-limiting (règle WAF Cloudflare : 100 req/10 s par IP sur
/api/*, à l'edge) - ✅ Sources
searchvia Brave (Cursor, Codex, Free-Work, APEC) - ✅ France Travail — volume d'offres dev par code ROME (API Offres, passe mensuelle)
- ✅ France Travail — tension de recrutement par métier (API Marché du travail, indicateur PERSP_2, niveau 1–5, France entière, passe mensuelle)
MIT — voir LICENSE.