Changelog

Technical changes across Sodimo's infrastructure

Repo
Category
Period
Search

Changelog site scaffolded

The internal changelog site is live in draft form, temporarily hosted on GitHub Pages at sodimo-changelog.pages.dev. Cloudflare Pages and the changelog.sodimo.eu subdomain follow once the domain is transferred from Strato.

Added
  • Static site generated from the Omarchy-style template with Sodimo colours applied
  • Release feed aggregates GitHub releases across every repo registered in config/repos.toml
  • Pagefind full-text search across releases and the internal manual
  • Filters by repo, category, and date
  • Cloudflare Access gate planned for sodimo.eu emails plus thomas@leger.run once the domain flips
Changed
  • All release tags move to calver YYYY-MM-DD-<slug> — semver is not a legible format for Michel Fattal reading reports on his phone

Template repo for every Sodimo repository

Every new Sodimo repo is forked from this template. It bakes in the release cadence, the architectural descriptor, and the commit conventions so a new repo reaches production readiness in minutes rather than hours.

Added
  • REPO.md architectural descriptor with frontmatter fields: name, slug, chapter, audience, vendor_class, upstream, sanitization_posture, status, depends_on, consumed_by
  • CLAUDE.md with the release convention, the definition of "deployed", and the Sodimo stack split
  • COMMIT_MESSAGE.md with conventional-commit conventions
  • GitHub Actions: release-please.yml for calver releases, pr-feed.yml appending to .changelog/prs.jsonl on merge, deploy-docs.yml triggering the changelog rebuild on release, and ci.yml
  • Vendor taxonomy labels: vendored-upstream, hard-fork, soft-fork-inspired, contrib-upstream, home-built
  • Issue and PR templates, CODEOWNERS, .gitignore, and tasks/ scaffold
Changed
  • Release convention fixed to calver YYYY-MM-DD-<slug> — board-legible, avoids semver churn on client repos that do not export APIs

Sodiwin data contract locked with Florian

The nightly Sodiwin dump is fully specified. Florian accepted SFTP to the Synology NAS, atomic writes, the dated filename convention, and the failure-email contract. Column widening on clients, suppliers and articles is in a second round. No code has landed — the repo will be scaffolded once the first dump arrives.

Added
  • 20 canonical tables specified (7 référentiels, 2 stock/lots, 9 document twins, 2 finances)
  • SFTP destination: 192.168.0.17, user sodiwin-dump, chroot /volume1/sodiwin-dumps/, schedule 02:30 Paris
  • Filename convention: YYYY-MM-DD_<slug>.csv per slug directory, atomic rename from .csv.partial
  • File format: Sodiwin-native CSV — ; separator, ISO-8859-1/Windows-1252, DD/MM/YYYY dates, , decimal, CRLF
  • Temporal windows negotiated: référentiels, stock, lots, devis, BL, encours, balance, objectifs and recettes as nightly full snapshot; base-ventes, base-commandes-ventes, base-achats and base-reglements on a 1-year rolling window; base-echeances restricted to unsettled invoices only
  • One-time historical dump agreed — all data since ERP origin through Dec 31 2025
  • Failure email contract: 3 retries at 60s intervals, then admin@sodimo.eu with fallback to paul.ghafary@sodimo.eu
Changed
  • Historical window on transactional tables reduced from 18 months (Thomas's ask) to 1 year rolling — Florian offered 3 years, settled on 1 year plus one-time historical backfill
  • Previous day's file deletion delegated to the Sodimo side after nightly ingest
Removed
  • Dynamic rupture columns dropped from base-articles (rup1/rup2/rup3/sous_rupt/librup1..3) — generated at report time in Sodiwin, not stored

Hybrid Cloudflare plus on-prem architecture locked

The architecture split for the engagement is fixed. Neither pure-Cloudflare (Monday plan, rejected for email sovereignty and LLM economics) nor pure on-prem (rejected for public surface cost). Cloudflare owns DNS, R2, D1, MCP Worker, Pages and Access. A single Framework Desktop owns SMTP, IMAP, FTP, LLM inference, Whisper and the Piler archive. Tailscale binds the box, the NAS, the laptops and Rani's phone.

Added
  • Framework Desktop AMD Strix Halo 395+ with 128GB unified memory, ordered by Paul (€3,583 HT), ETA approximately 1 week
  • Four R2 buckets planned: sodimo-archive, sodimo-backup, sodimo-dashboards, sodimo-public with a 90d Standard → IA lifecycle
  • Bitwarden Teams EU adopted for credentials, 4 seats, €16/month, admin on admin@sodimo.eu, 2FA backup codes and LUKS passphrase in Paul's fire-safe
  • Second-node decision deferred to T+90 dashboard review — one Framework plus Synology backup plus 2-hour RTO bare-metal restore is sufficient
  • Four domains inventoried (sodimo.eu, yallafood.eu, cavisteduliban.fr, sodimonet.fr) and Strato mailbox weights captured across 33 mailboxes totalling ~45.5 GB
Changed
  • LLM plan: cloud-only Opus (€1,640–2,500/month) → local Gemma 4 plus Qwen 3.5 MoE on the Framework box (~€210/month electricity), payback month 2
  • Email plan: Strato (33 mailboxes, ~45.5 GB) → Postfix, Dovecot, rspamd and Piler on the Framework box — sovereignty and deletion guarantees Strato cannot give

v2 brand system — Levantine Modernism

The v2 brand system replaces the Olive/Leaf v1 that shipped with the legacy WordPress site. Cedar, sumac, parchment, ink. Fraunces, Inter, JetBrains Mono. Four P0 accessibility fixes cleared against measured contrast ratios. Review score 7.80/10 against v1 at 5.27/10.

Added
  • Cedar primary #1F3B2D, sumac accent #C85A3A, parchment canvas #FAF6EF, warm-black ink #1B1913
  • Saffron reserved as signal colour — focus rings and warning badges only
  • Composite type tokens (type.h1 bundles family, weight, size, line-height, tracking) so templates reference one token per heading level
  • Dual spacing scales: static space.100..900 plus fluid space-fluid.100..800, 1:1 name-aligned
  • Warm-tinted elevation using cedar-900 alpha rather than neutral grey, 5 shadow tiers plus focus ring
  • prefers-reduced-motion collapses every duration token to 0ms at :root
  • VOICE.md covering forbidden vocabulary, three writing tests, tone dial, sample headlines in FR and EN
  • Six-file SVG logo set at assets/logo-v2/ (light, dark, icon, favicon, wordmark-dark, wordmark-light)
  • Two dark-mode variants shipped side by side: cedar-deepening default and warm-charcoal alternate
Changed
  • Primary colour #4b5759#1F3B2D — the old value read as hesitant B2B
  • Accent #a2c32a → #5d9934 gradient removed entirely, replaced by solid sumac for print compatibility
  • Canvas #f7f7f5#FAF6EF — warm parchment harmonises with the food and wine subject
  • Typography Montserrat → Fraunces + Inter + JetBrains Mono — Montserrat is universal, Fraunces has a voice
  • Button radius 999px pill → 8px rectangle, pill reserved for badges and tags only
  • Status colours reuse brand primitives — no foreign traffic-light red or green
  • Focus ring remapped from saffron on parchment (1.95:1, fails WCAG 1.4.11) to a surface-aware pair: sumac-500 on light (5.63:1), saffron-400 on dark (11.3:1)
  • --color-text-subtle remapped from ink-300 (2.79:1, fails AA-Large) to ink-400 (5.13:1 AA)
Fixed
  • Warning badge tokenised as --color-warning-bg: saffron-50 + --color-warning-ink: saffron-700 — the prior ad-hoc pairing was 2.95:1 (AA fail)
  • Contrast claim corrected at index.html:824 — white on sumac-400 is 4.22:1 (AA-Large only), not the self-reported 4.6:1

sodimo.eu rebuilt on Cloudflare Pages

The public site is off WordPress and onto Cloudflare Pages, served from a single build.mjs ESM script with no framework, no CMS, no runtime dependencies. Three sibling domains (yallafood.eu, cavisteduliban.fr, sodimonet.fr) will move to the same pattern.

Added
  • 20 HTML pages (10 FR at root, 10 EN under /en/, full hreflang coverage)
  • Pages Functions POST /api/contact (contact form, Turnstile support, MailChannels delivery, Slack webhook fan-out) and POST /api/newsletter (Workers KV persistence)
  • Strict security headers: CSP, HSTS, X-Frame-Options DENY, interest-cohort=(), immutable asset caching
  • 301 redirects for every legacy WordPress path, xmlrpc.php returns 410 to deter probes
  • GDPR-compliant cookie notice, no analytics, no advertising cookies, explicit consent checkbox on the contact form
  • 21 brand tiles on the homepage anchor-linked to nos-marques.html feature sections
Changed
  • Hosting: legacy WordPress (over €100/month) → Cloudflare Pages (free tier)
  • Build: WordPress/PHP → single build.mjs, only devDependency is wrangler@^3
Removed
  • WordPress install, wp-admin, wp-content, RSS feeds

PNG logos replaced with SVG set

Four legacy PNGs (sodimo-logo-dark.png, sodimo-logo-light.png, sodimo-logo-footer.png, sodimo-favicon.png) are replaced by a six-file SVG set. Web, email and print now share a single source of truth.

Added
  • logo-light.svg, logo-dark.svg, icon.svg, favicon.svg, wordmark-dark.svg, wordmark-light.svg
  • SVG generation pipeline documented in HANDOFF.md — potrace for the icon, vtracer for the wordmark, svgo multipass
Removed
  • All PNG logo files
Fixed
  • Logo-dark rule updated for photography backgrounds — a 35% or darker veil is required before logo-dark.svg is legible over imagery

Network topology mapped; working path established

Sodimo's two-VLAN network is now documented: 192.168.0.x for data, 192.168.1.x for VoIP. Thomas's desk wall ports are both wrong — one lands on VoIP, the other is dead. Until the panel repatch, the work path is the sodimo_wifi SSID, which binds to the data VLAN and gives access to the Synology SAUVE_DISK at 192.168.0.17.

Added
  • Device inventory: TP-Link ER706W router, TP-Link OC300 controller, Synology DS218j (DSM 6.2), Sodiwin server at 192.168.0.88 (Fujitsu, Windows Server, IIS 10.0, Pervasive SQL on ports 1583/3351), Canon iR-ADV printer, 12 Dahua IP cameras
  • Second Fujitsu Windows host at 192.168.0.108 noted, purpose TBD
  • VoIP VLAN inventory: Sagemcom router, Zyxel PoE switch, Yealink DECT — no NAS access
  • Guest SSID on 192.168.200.x flagged as client-isolated and unusable for work
Changed
  • Working path confirmed as sodimo_wifi until the data-port repatch to NETGEAR clears the wall-port problem

Search uses Pagefind — available after npm run build.