Association CapHumain
An institutional website and a full content back-office for a Malian youth NGO, so the team publishes its own news, projects and impact figures without a developer.
Full-stack developer and designer (solo)
Association CapHumain (ACH) is a Malian NGO run by young volunteers, active since 2017 around education, social solidarity, citizenship, leadership and the environment. After eight years of fieldwork visible only through a Facebook page, the association needed a real online presence it could keep up to date on its own. The project is a one-page public site backed by a full administration area: every text, figure, photo, news post and activity report is editable by the ACH team. It also includes a privacy-friendly visitor dashboard built in-house, with no third-party tracking service. The site is in production at caphumain.org and maintained day to day by a member of the association trained on the back-office.
The problem
Eight years of activities, more than 2,000 people reached, sixteen field projects: all of it lived only in internal reports and a Facebook page. Partners and institutions had no serious page to look up, and the association depended on outside help for every change to its communication. Nothing let the team show its track record or keep it current without a technical contact.
The approach
I turned the association's 2017-2025 activity report into a clear content model (strategic areas, projects, news, impact figures, team) and built the site as a Laravel application with server-rendered pages, no JavaScript framework. Every block on the public page is wired to an administration area so the team edits content directly, and a member was trained to run it. The association has no server of its own, so the site runs on shared hosting I lend them for now, which pointed the whole stack toward something deliberately lightweight.
The result
- ACH has a public site it fully controls: the team publishes news, projects and reports with no developer involved.
- The eight-year track record (16 projects, impact figures, five areas of work) is now presented in one place, usable with partners and institutions.
- Visitor statistics are available to the association directly in the back-office, without Google Analytics or any external service.
- In production at caphumain.org, maintained day to day by a trained member of the association.
Key features
- Full content editing: every text, figure and image on the public page is editable from the admin area.
- News and project management with photo galleries and downloadable PDF activity reports.
- Contact and "get involved" forms that land in an inbox inside the back-office, with reply drafts.
- Privacy-friendly visitor dashboard: visits, unique visitors, trends, top pages, traffic sources and device split, with no third-party tracker.
- Anonymous visit tracking: IP addresses are hashed, bots are filtered out, data older than one year is deleted automatically.
- Animated logo on load, with a static fallback for visitors who prefer reduced motion; the association can replace the logo itself.
- Multiple admin accounts with safeguards: no account can delete itself or the last remaining account.
- Legal notice and privacy policy pages, plus SEO basics (sitemap, editable meta content).
Engineering — Architecture, decisions and trade-offs
Architecture
The application is a single Laravel 13 codebase rendering Blade templates on the server, with no SPA and no API layer. The public site is one page assembled from a content model (strategic areas, impact figures, projects, news, team, resources, partners) plus a key/value settings table. A single class, SiteContent, is the source of truth for every editable text and image block and its default value, so adding an editable block is one declaration rather than a change scattered across views. The back-office is one CRUD controller per resource on a shared shape, behind session auth under an /admin prefix. Visitor analytics are computed from a visits table populated by a middleware that only runs on public page loads; a stats controller aggregates it into the dashboard. File uploads (images, PDF reports, custom logo) go through one shared trait that enforces validation and storage rules in a single place. Front-end assets are built with Vite and Tailwind CSS v4. Tests run on in-memory SQLite while production uses MySQL, and GitHub Actions runs the full PHPUnit suite on every push. Deployment targets shared hosting: SSH plus git pull, document root pointed at public/, and a daily cron for data retention.
Engineering challenges
- Shared hosting only: no Node runtime, no queue workers, no VPS. The stack had to stay Blade-only with assets pre-built, and background work (visit retention) runs as a plain daily cron through Laravel's scheduler.
- Making the whole public page editable without an unmanageable sprawl of settings. Solved with
SiteContentas a single declarative registry: each block declares key, label, type and default, and admin values override the default only when non-empty. - Building useful analytics with no third-party service and no personal data at rest. Visits are recorded with a salted SHA-256 hash of the IP, bots are filtered by user-agent, viewing duration is sent on page unload via
sendBeacon, and rows older than a year are pruned automatically. - Stored-XSS risk on image uploads: an SVG file can carry a script. Every upload field shares one trait that restricts types to raster formats (jpeg / png / webp), and tests assert SVG uploads are rejected.
Technical decisions
- Chose server-rendered Blade over a JavaScript SPA because the client team maintains the site on shared hosting with no build step in production, and a single Laravel deploy is simpler to hand over.
- Chose an in-house visit counter over Google Analytics or Plausible for cost and deployment simplicity: no external account, no script to embed, and the stats live in the same database and admin as the rest of the site.
- Chose MySQL over the initial SQLite because the production host provides managed MySQL, which removes file-permission and locking concerns on shared hosting.
- Chose to model content in the database and build a full editor rather than hardcode the 2017-2025 report in templates, because the association needed to keep publishing on its own after handover.
Stack
- Laravel 13
- PHP 8.3
- Blade
- Tailwind CSS v4
- Vite
- MySQL
- PHPUnit
- GitHub Actions
- Hébergement mutualisé (Apache)
Full-stack developer and designer (solo) — Association CapHumain (ACH), a Malian youth NGO (pro bono) (2026)