Source for mobiera.io, the Mobiera SAS website: operator-grade software for mobile networks (Aircast, AI One) and verifiable trust on Verana (trust services, integrator certification for Latin America).
Built with Next.js (App Router, standalone output), React 19,
Tailwind CSS v4 and TypeScript, on the same stack as
2060.io-website. No database, no
accounts. Delivered as a container image (mobiera/website on Docker Hub).
The copy is specified in spec/: one Markdown file per page under
spec/pages/, the style rules in spec/STYLE.md, every fact the site may
state in spec/facts.yaml. Pages under app/ implement those files; change
the spec first, then the page.
News items are Markdown files in content/news/, named
YYYY-MM-DD-slug.md with front matter (title, date, tags, summary).
They render at /news/<slug> and in the RSS feed at /news/feed.xml.
Design: direction A ("Signal") from spec/design/. Tokens live in
app/globals.css; the logo is the existing Mobiera mark and wordmark.
English at the bare paths, Latin American Spanish under /es. proxy.ts
redirects a first visit whose Accept-Language prefers any Spanish variant to
/es; the NEXT_LOCALE cookie set by the language switch wins afterwards.
Both languages are prerendered, with hreflang alternates and a sitemap entry
each.
- Copy lives in
messages/<locale>/<page>.json, one file per page or shared area, same key tree in both languages. English is the source; a key missing in Spanish falls back to English at build time. Keys are typed from the English files (global.d.ts), so a typo failstsc. - News:
content/news/<date>-<name>.es.mdbeside the English file. Without it the English article is shown on/eswith an "English only" note. - Contact form and acknowledgement email follow the visitor's language
(
messages/*/contact.json,messages/*/email.json). - The SVG illustrations keep English labels for now.
- Glossary and register:
spec/STYLE.md, section "Spanish (es-419)".
nvm use # Node 22
npm install
cp .env.example .env.local # optional: SMTP for the contact form
npm run dev # http://localhost:3000Scripts: npm run build, npm start, npm run typecheck, npm test.
/contact posts to app/api/contact/route.ts, which validates the
submission (honeypot, time-to-submit, rate limit, consent) and emails it over
SMTP with Nodemailer, the same way 2060.io-website does. A submission dropped
by the honeypot or the timing check still answers success to the browser, but
the pod log says why ([contact] dropped: ...); a delivered inquiry logs
[contact] <topic> inquiry emailed. Recipients come from
CONTACT_TO with per-topic overrides (CONTACT_TO_CAREERS, ...). Career
applications attach the PDF the candidate uploads. With MAIL_HOST unset the
form accepts and logs submissions without delivering them.
Google mailbox and app password, recipients per topic, GitHub variables and the runtime secret: see docs/configuration.md.
GitHub Actions build and push mobiera/website to Docker Hub: dev on every
push to main, and main, latest, vX.Y.Z, vX.Y when release-please cuts
a release from conventional commits (secrets DOCKER_HUB_LOGIN,
DOCKER_HUB_PWD). The "Deploy to Kubernetes (OVH)" workflow rolls any tag out
to the OVH cluster's web namespace with the Helm chart in charts/ (nginx
ingress, cert-manager TLS), and every release deploys itself. Secrets and the cut-over from GitHub Pages:
docs/configuration.md.
Old URLs from the Hugo site redirect (app/lib/redirects.ts); the PHP form
handlers and the mock login return 410 (proxy.ts). /images/* keeps the old
paths for the mark-only logos and hosted images linked from outside the site.