The public marketing website of codebar Solutions AG — a small software company based in the Basel region, Switzerland. Laravel, Blade and Tailwind, bilingual (DE/EN), content managed as version-controlled files rather than through an admin panel.
- Content architecture
- Localization
- SEO
- LLM usage analytics
- Local development
- Testing & code quality
- Deployment
- Key packages
Almost everything editorial — pages, news articles, team members, services, products, technologies, network partners, the AI model catalogue — lives as YAML or Markdown files under database/files/, not as data entered through a CMS. The files are the source of truth; the database is a rebuildable cache of them.
database/files/
├── ai_models/ one YAML file per model
├── networks/ one YAML file per partner
├── news/{locale}/ one Markdown file per article, per language
├── pages/ one YAML file per page (SEO metadata: title, description, robots, image)
├── products/{locale}/
├── services/{locale}/
├── team/ one YAML file per person
└── technologies/{locale}/
Each content type has a matching import command that reads its files and upserts the database, and is safe to run repeatedly:
| Command | Reads |
|---|---|
php artisan pages:import |
database/files/pages/*.yaml |
php artisan news:import |
database/files/news/{locale}/*.md |
php artisan team:import |
database/files/team/*.yaml |
php artisan jobs:import |
database/files/jobs/*.yaml |
php artisan services:import |
database/files/services/{locale}/*.md |
php artisan products:import |
database/files/products/{locale}/*.md |
php artisan technologies:import |
database/files/technologies/{locale}/*.md |
php artisan networks:import |
database/files/networks/*.yaml |
php artisan ai-models:import |
database/files/ai_models/*.yaml |
php artisan sync:repositories |
live GitHub repositories (open-source content) |
database/seeders/DatabaseSeeder.php calls every one of these on db:seed — including in production. That is intentional: these seeders don't generate test fixtures, they publish the real content the same way running the command by hand would. The one exception is AiModelDailyUsagesTableSeeder, which loads a static local-dev fixture rather than real usage data (see LLM usage analytics).
A file that disappears from database/files/ removes the matching row on the next import — the importers are the single source of truth in both directions.
The site ships in German (de_CH) and English (en_CH) under distinct, fully translated URL prefixes rather than a shared /en/… segment — e.g. /dienstleistungen and /services, /aktuelles and /news, /ueber-uns and /about-us. Every localized route pair cross-references the other via hreflang, including on detail pages where the slug itself is translated (routes/web.php, resources/views/layouts/_partials/_seo.blade.php).
- Structured data — a single
@graphJSON-LD payload (App\Seo\SchemaGraph/App\Seo\SchemaNodes) built fromconfig/company.php, the one source of truth for the company's name, addresses, phone andsameAsprofiles. Every page shipsOrganization,WebSite,WebPageandBreadcrumbListnodes; content pages addService,Person,BlogPosting, etc. - Sitemap —
App\Sitemap\SitemapBuilder+App\Http\Controllers\Sitemap\SitemapControllerbuild/sitemap.xmlfrom the same models the site renders, cached 24h viaCache::remember. - Response cache —
spatie/laravel-responsecachecaches full HTTP responses for up to 7 days. Invalidation is event-driven, not scheduled:App\Observers\SitemapCacheObserverdrops the sitemap's data cache and the content observers drop both the listing caches and the rendered pages on every relevant model save. Clearing goes throughApp\Support\ResponseCacheFlusher, which collapses the flush to once per import — the response cache has no per-URL invalidation, so an import that saves 50 rows would otherwise clear the whole site 50 times. - Social images — article heroes that are local SVG placeholders (no real photography yet) are not usable as
og:image— social crawlers don't render SVG.App\Support\NewsImage::ogImage()falls back to a same-named.pngrendered from the SVG when one exists, or the site's default share image otherwise. - Tests —
tests/Feature/Seo/asserts on the actual rendered JSON-LD and meta tags (not just "does it look right"), andtests/lighthouse/audits real Lighthouse scores against the built (npm run build) output, not the Vite dev server.
/ki (/ai) publishes aggregate usage figures for the AI models codebar runs internally via a self-hosted LiteLLM proxy. php artisan llm:fetch-analytics pulls per-day usage from the proxy's /spend/logs endpoint and stores it in ai_model_daily_usages; it's scheduled hourly but only backfills the last 3 days by default — use --full (syncs from 2026-01-01) or --from/--to to backfill a specific range. Spend figures are stored but must never be displayed publicly. The dashboard intentionally shows monthly/yearly aggregates only, never a daily breakdown.
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed # imports real content — see "Content architecture" above
npm install
npm run build # or `npm run dev` for the Vite dev serverIf you use Herd:
herd link
herd secure
herd openIf you use Valet:
valet link
valet secure
valet openSet
valetTls: 'your-domain.test'belowrefresh: trueinvite.config.jsif you usevalet secure/herd secure.
QUEUE_CONNECTION=database, so a worker has to be running for anything queued to happen — the network profile link, the LLM usage sync. Deliberately not sync: running the mail job inside the request makes the intentionally constant response of network.request.store observable by its timing.
php artisan queue:work # network profile mails, LLM usage sync
php artisan schedule:work # hourly LLM sync, health checksWithout the scheduler, health:check never runs and /health reports stale results. A dispatched job with no worker is a silent black hole — JobsCheck exists to catch exactly that, and reports through /health.
./vendor/bin/pest # test suite (Pest)
./vendor/bin/phpstan analyse # static analysis (Larastan, see phpstan.neon.dist)
./vendor/bin/pint # code style
./vendor/bin/pint --blade # blade formatting (beta)The site runs on Laravel Cloud, behind Cloudflare. Required environment variables for security headers:
CSP_ENABLED=true— enables Content-Security-Policy enforcement via Spatie CSP middlewareFPH_ENABLED=true— enables Permissions-Policy headersHEALTH_NOTIFICATION_EMAIL— where a failing health check reports to; unset means no notification is sent
HSTS, COOP, CORP, X-Content-Type-Options, Referrer-Policy and X-Frame-Options are applied automatically by the SecurityHeaders middleware on every web response.
/health returns the Spatie Health JSON result set. It is rate limited and never response-cached; it needs schedule:work running to hold current results.
Lighthouse note: deprecated-API warnings for /cdn-cgi/challenge-platform/scripts/jsd/main.js come from Cloudflare's bot protection, injected at the edge — not from application code. Run Lighthouse in incognito without extensions for an accurate score, and prefer tests/lighthouse/ (which audits the built output) over ad-hoc runs against the dev server.
- Content & storage —
spatie/laravel-translatable,codebar-ag/laravel-flysystem-cloudinary(editorial images),league/flysystem-aws-s3-v3(DigitalOcean Spaces for other assets),symfony/yaml - SEO —
spatie/laravel-sitemap,spatie/laravel-responsecache - Security & health —
spatie/laravel-csp,spatie/laravel-honeypot(network request form),spatie/laravel-permission,spatie/laravel-health,spatie/security-advisories-health-check,mazedlx/laravel-feature-policy - Ops —
laravel/nightwatch(observability),symfony/postmark-mailer - Analytics — Fathom (privacy-friendly, no cookie banner)
The application code is proprietary to codebar Solutions AG. The underlying Laravel framework is open-source software licensed under the MIT license.