• HTML 80.3%
  • Astro 14.3%
  • TypeScript 2.3%
  • CSS 1.8%
  • JavaScript 0.5%
  • Other 0.7%
Find a file
morgan 08b06b88ca
All checks were successful
Build and push / changes (push) Successful in 3s
Build and push / check (push) Successful in 24s
Build and push / build (push) Successful in 1m15s
Build and push / notify-komodo (push) Successful in 0s
content: fiche « La sédentarité » (Mouvement, reviewed)
2026-07-10 23:56:55 +02:00
.claude tooling: les relecteurs de gate ne modifient jamais la fiche (évite une collision avec l'iconographe) 2026-07-10 15:37:11 +02:00
.forgejo/workflows chore: keep compose.yaml at repo root (Komodo deploy path) 2026-06-21 14:18:59 +02:00
.githooks tooling: hook pre-push qui lance la gate CI (just check) 2026-06-26 13:09:05 +02:00
docker feat(visuels): build-time diagram/chart/math/figure pipeline + template 2026-06-24 18:27:20 +02:00
docs content: fiche « La sédentarité » (Mouvement, reviewed) 2026-07-10 23:56:55 +02:00
web content: fiche « La sédentarité » (Mouvement, reviewed) 2026-07-10 23:56:55 +02:00
.gitignore chore: ignorer waitroom/ (feuilles en attente, non versionnées) 2026-07-10 13:42:59 +02:00
compose.yaml chore: keep compose.yaml at repo root (Komodo deploy path) 2026-06-21 14:18:59 +02:00
justfile tooling: hook pre-push qui lance la gate CI (just check) 2026-06-26 13:09:05 +02:00
LICENSE Initial commit 2026-06-21 12:23:59 +02:00
README.md docs(recherche): add a clean fiche template, move research docs under docs/recherche/ 2026-06-24 13:40:24 +02:00
Welcome to Life.html wip(custom-layout): migrate off Starlight — custom Astro Page layout, native i18n, articles collection, home FR/EN; ticker/seal/footer/search fixes 2026-06-22 17:04:16 +02:00

Bienvenue dans la vie

A free, volunteer, French knowledge hub for young adults: the plain, sourced explanations of adult-life essentials that nobody hands you.

« La vie ne livre pas de mode d'emploi. Nous, si. »

Three pillars, Le Corps (body), L'Argent (money), Le Monde (world). Each topic is a fiche: a short, calibrated, sourced synthesis of one question or mental model.

Look and language. French only, served at /. Single light "riso zine" theme: warm paper, Bricolage / Hanken / Spline type, orange and teal accents. The repository, image and Komodo stack keep the slug welcome-to-life; documentation and tooling are in English.

Editorial source of truth (read before writing content):

  • Brand brief: docs/brand.md. Idea, audience, pillars, voice.
  • Editorial line: docs/editorial.md. Epistemics, tone, fiche architecture, vocabulary.
  • Research project: docs/recherche/. The Claude Project setup (brief.md) and the fiche template (fiche-modele.md) that turn a topic into a sourced knowledge sheet before a fiche is written.

Contents

Stack

Concern Choice
Framework Astro, plain, custom full-page layout (no Starlight)
Runtime Bun
Fonts Self-hosted (Bricolage Grotesque display, Hanken Grotesk body, Spline Sans Mono labels)
Search Pagefind, standalone post-build index over dist
Output Static HTML
Runtime img Rootless nginx-unprivileged
CI/CD Forgejo Actions, container registry, Komodo
Proxy Pangolin (TLS and routing, external pangolin network)

Project layout

.
├── web/                       # the Astro site (all app code)
│   ├── astro.config.mjs       # plain Astro, no Starlight, FR-only (served at /)
│   ├── src/
│   │   ├── content.config.ts  # `articles` collection + extended trust schema
│   │   ├── content/articles/  # the fiches (FR), entry id = pillar/slug
│   │   ├── layouts/           # Page (site shell), Prose (styleguide), Legal
│   │   ├── pages/             # home, [pillar], [pillar]/[slug], notre-methode,
│   │   │                      #   recherche, mentions-legales, confidentialite, template
│   │   ├── components/        # nav, hero, seal, ticker, pillar grid, ArticleView, sources
│   │   ├── lib/               # pillars.ts, schema.ts, trust.ts (+ unit tests)
│   │   └── styles/theme.css   # riso design tokens, fonts, per-pillar accents
│   └── scripts/make-stubs.ts  # generator for placeholder fiches
├── docs/
│   ├── brand.md               # brand brief (idea, audience, voice)
│   ├── editorial.md           # editorial line (epistemics, tone, fiche architecture)
│   ├── recherche/             # research project: brief.md, fiche-modele.md, sheets
│   └── superpowers/           # historical design specs and plans (archive)
├── docker/
│   ├── Dockerfile             # multi-stage: Bun build, then rootless nginx
│   ├── Dockerfile.dockerignore
│   └── nginx.conf             # security headers, CSP, healthz, caching
├── compose.yaml              # deploy unit (kept at root; Komodo expects it here)
├── justfile                  # dev / build / docker recipes
└── .forgejo/workflows/build.yml   # CI pipeline

Develop

Easiest path: just (run just with no argument to list every recipe).

just install       # install dependencies
just dev           # dev server (http://localhost:4321)
just build         # static build (web/dist)
just lint          # format check + types + the no-em-dash house rule (read-only)
just check         # lint + tests (local gate)
just clean         # remove dist/.astro/node_modules

just docker-build  # build the image
just docker-run    # build + run the image detached (http://localhost:8080)
just docker-logs   # follow the running container's logs
just docker-clean  # remove the local container + image

Without just, the equivalent commands live in the justfile (all cd web && bun ...).

Content and trust model

The reader-facing unit is a fiche: a short, calibrated synthesis of one question or mental model. Fiches live in web/src/content/articles/{corps,argent,monde}/. The Astro collection is named articles internally; the word "article" never appears on screen. Each entry id is pillar/slug, rendered by the [pillar]/[slug] route through ArticleView.

Because the content is AI-assisted, credibility is an explicit, visible system, and the trust signals are deliberately modest (the schema lives in src/lib/schema.ts, the logic in src/lib/trust.ts, both unit-tested):

  • Status (status: draft | reviewed). Drafts show an « En rédaction » pill. There is no "human-reviewed" claim: it would promise more than a solo, non-expert process can back.
  • Freshness (lastReviewed). Shown as « Mis à jour le ... ». A fiche can age, and the date says when it was last touched.
  • Evidence strength (confiance: solide | debattu | incertain). A coloured badge for the central claim. It is the author's read of the evidence, not an expert validation.
  • Sources (typed study | official | press | book, Zod-validated). Rendered as a Sources block. No source is ever invented.

The « Comment on vérifie » page (/notre-methode/) states the method and its honest limits in public. docs/editorial.md holds the full editorial line, and docs/recherche/ the workflow that turns a topic into vetted sources before a fiche is written.

How deployment works

  git push (main)
        │
        ▼
┌──────────────────────────────────────────────────────────────┐
│ Forgejo Actions  (.forgejo/workflows/build.yml)              │
│                                                              │
│  changes ──► check ──► build ──► notify-komodo               │
│  (diff)     (lint/    (buildx    (signed webhook)            │
│             test)      push)                                 │
└───────┬───────────────────┬──────────────────┬──────────────┘
        │                   │                  │
        │            push image to       POST signed webhook
        │            container registry   (HMAC-SHA256)
        │                   │                  │
        ▼                   ▼                  ▼
   (skips build      git.libresoftware    Pangolin ──► Komodo
    if only           .cloud/morgan/       (Basic auth)  │
    compose.yaml      welcome-to-life                    │ pull :latest
    changed)          :latest + :<sha>                   ▼ + recreate
                                                    compose.yaml
                                                    (rootless nginx,
                                                     pangolin network)
  1. Push to main triggers the pipeline.
  2. CI runs checks, builds the image, and pushes it to the Forgejo container registry.
  3. CI notifies Komodo with a signed HTTP webhook.
  4. Komodo pulls the new :latest image and recreates the container from compose.yaml.
  5. Pangolin (reverse proxy on the external pangolin network) terminates TLS and routes public traffic to the container's port 8080.

CI pipeline

.forgejo/workflows/build.yml runs on push to main (and workflow_dispatch). It is path-aware, to avoid rebuilding when only the deploy unit changes:

Job Runs when Does
changes always Diffs the push; sets code (web/ docker/ workflows) and compose.
check code == true bun install, bun test, bun run format:check, bun astro check.
build code == true Buildx build of docker/Dockerfile, push :latest + :<short-sha>, registry build cache.
notify-komodo build succeeded or compose-only change Sends the signed redeploy webhook (see below).

A compose-only change (editing compose.yaml) skips check and build and still triggers notify-komodo, so deploy-config tweaks redeploy without an image rebuild.

Komodo redeploy (webhook)

The notify-komodo job POSTs a small JSON body to a Komodo listener that sits behind Pangolin. Two layers of authentication are involved:

curl -u "$KOMODO_BASIC_AUTH"  \                         # 1. Pangolin Basic auth
     -H "X-Hub-Signature-256: sha256=<HMAC>"  \         # 2. Komodo HMAC check
     -H "Content-Type: application/json"  \
     -d '{"ref":"refs/heads/main","trigger":"forgejo-actions"}'  \
     "$KOMODO_WEBHOOK_URL"                               # URL ends in /main
  1. Pangolin Basic auth. Pangolin gates the webhook URL behind HTTP Basic auth. The user:password is sent via curl -u. With Pangolin's "Extended compatibility" enabled, it forwards the Authorization header to Komodo, which ignores it.
  2. Komodo HMAC verification. Komodo runs a GitHub-style listener. It reads ref from the body, strips refs/heads/, and compares it against the trailing URL segment (/main). The request must carry X-Hub-Signature-256: sha256=<hex>, where <hex> is the HMAC-SHA256 of the exact request body keyed with the listener's secret. The CI computes it with openssl dgst -sha256 -hmac "$KOMODO_WEBHOOK_SECRET".

On a valid call, Komodo's Stack for this repo pulls the new :latest image and recreates the service defined in compose.yaml.

Komodo side (configured once, outside this repo): a Stack pointing at this repo's compose.yaml, a webhook listener whose secret matches KOMODO_WEBHOOK_SECRET, and a URL ending in /main. The container joins the external pangolin network so Pangolin can route to it.

Configuration: repo variables and secrets

Set these in Forgejo: Repository, Settings, Actions, Variables / Secrets. Variables are plain text; secrets are masked in logs.

Name Kind Used by Purpose
SITE_URL Variable build Canonical site URL baked into the build (site: in astro.config.mjs, for sitemap, canonical, SEO).
KOMODO_WEBHOOK_URL Variable notify-komodo Komodo listener endpoint (behind Pangolin). Must end in /main.
REGISTRY_TOKEN Secret build Forgejo token for ${{ github.actor }} to log in and push packages to git.libresoftware.cloud. Package-write.
KOMODO_WEBHOOK_SECRET Secret notify-komodo HMAC-SHA256 key; must match the secret configured on the Komodo listener.
KOMODO_BASIC_AUTH Secret notify-komodo user:password for Pangolin's HTTP Basic auth in front of the Komodo URL.

Provided automatically by Forgejo Actions (no setup): github.actor (the pusher, used as the registry username), github.sha, github.ref, github.repository, github.server_url.

Runner requirements

The workflow targets runs-on: docker (a Forgejo Actions runner labelled docker). The build job uses Buildx against a Docker-in-Docker service reachable at tcp://docker:2376 with TLS certs under /certs/client, so make sure the runner provides a DinD service (the standard Forgejo docker-runner-with-DinD setup).

Run it locally with Docker

compose.yaml targets production (it pulls the registry image and joins the external pangolin network), so for a local smoke test, build and run the image directly:

just docker-run        # builds docker/Dockerfile, runs detached on http://localhost:8080
curl -fsS http://localhost:8080/healthz   # health check, prints "ok"
just docker-clean      # remove the local container and image