- HTML 80.9%
- Astro 14%
- TypeScript 2.1%
- CSS 1.8%
- JavaScript 0.5%
- Other 0.7%
Fiche 03 du parcours Le Corps (La maintenance). Architecture en cycles (visuel hypnogramme SVG inline), les 4 voleurs de récupération, 7-9 h + régularité comme levier n°1, ce qui abîme/améliore une nuit sans gadget, frontière médicale (quand consulter), 3 notes débattues (glymphatique, avant minuit, courbe en U). Passée par la gate secrétaire de rédaction (R1 PASS). Feuille rangée sous docs/recherche/corps/. |
||
|---|---|---|
| .claude/skills/secretaire-redaction | ||
| .forgejo/workflows | ||
| docker | ||
| docs | ||
| web | ||
| .gitignore | ||
| compose.yaml | ||
| justfile | ||
| LICENSE | ||
| README.md | ||
| Welcome to Life.html | ||
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 slugwelcome-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
- Project layout
- Develop
- Content and trust model
- How deployment works
- CI pipeline
- Komodo redeploy (webhook)
- Configuration: repo variables and secrets
- Runner requirements
- Run it locally with Docker
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)
- Push to
maintriggers the pipeline. - CI runs checks, builds the image, and pushes it to the Forgejo container registry.
- CI notifies Komodo with a signed HTTP webhook.
- Komodo pulls the new
:latestimage and recreates the container fromcompose.yaml. - Pangolin (reverse proxy on the external
pangolinnetwork) terminates TLS and routes public traffic to the container's port8080.
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
- Pangolin Basic auth. Pangolin gates the webhook URL behind HTTP Basic
auth. The
user:passwordis sent viacurl -u. With Pangolin's "Extended compatibility" enabled, it forwards theAuthorizationheader to Komodo, which ignores it. - Komodo HMAC verification. Komodo runs a GitHub-style listener. It reads
reffrom the body, stripsrefs/heads/, and compares it against the trailing URL segment (/main). The request must carryX-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 withopenssl 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 matchesKOMODO_WEBHOOK_SECRET, and a URL ending in/main. The container joins the externalpangolinnetwork 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