tanshu d04314ab1c Deploy with rootless podman + Quadlet on emily
Retargets the build and the playbook off rootful Docker on monoco, following the
conventions already in place on emily's other services: Quadlet units in
~/.config/containers/systemd, per-service data in ~/podman-data, and a shared
Caddy that every service registers itself with — a network drop-in plus a
marked block in its Caddyfile. Nothing is published on the host; Caddy reaches
the containers by name over mozimo_net.

- Makefile: podman build + push instead of docker buildx, PLATFORMS=linux/arm64
  (both hosts are aarch64). podman has no built-in QEMU the way buildx's builder
  image does, so a foreign-platform failure now points at qemu-user-static.
- deploy.sh: pass COMMIT= to make, which the Makefile actually reads; it was
  passing TAG=, so the tag silently came from `git rev-parse` instead.
- ansible: hosts emily, become false, XDG_RUNTIME_DIR for rootless podman and
  systemctl --user. Docker modules become Quadlet templates with user-service
  handlers, and the network role now ships mozimo.network plus the Caddy drop-in
  rather than calling docker_network.
- UserNS=keep-id maps the container's uid 1000 (node) and root (vince) onto the
  host user, so PocketBase and Vince write host-owned files instead of
  subuid-owned ones; the play asserts the pinned uid still matches the host.
- ansible/README.md: the unit layout, the service-registration convention, and
  the monoco -> emily migration (PocketBase 23 MB + Vince 119 MB, DNS cutover,
  rollback).

Verified by a check-mode rehearsal against emily (13 ok / 8 changed / 0 failed)
and a disposable Quadlet unit on that host proving the uid mapping, the
EnvironmentFile and the isolated network. The real build and cutover have not
been run.
2026-10-16 10:11:39 +05:30
2026-09-06 06:38:04 +00:00
2026-09-06 06:38:04 +00:00
2026-09-06 16:03:13 +00:00
2026-09-06 16:03:13 +00:00
2025-07-30 17:39:51 +05:30
2026-09-06 06:38:04 +00:00
2026-09-06 06:38:04 +00:00
2026-10-13 12:26:03 +00:00
2026-09-06 16:03:13 +00:00
2026-09-06 06:38:04 +00:00
2026-09-06 06:38:04 +00:00
2026-09-06 06:38:04 +00:00
2026-09-06 06:38:04 +00:00
2026-09-06 16:03:13 +00:00

Mozimo — The Art of Chocolate, Made in India

A full luxury-editorial redesign of mozimo.in, implementing the proposed "Enter the world of Mozimo" flow: ivory, deep cacao, champagne gold, restrained puce — editorial, European, slightly mysterious.

Built with TanStack Start (React 19 + Vite), Tailwind CSS 4, and zero animation dependencies (hand-rolled CSS + IntersectionObserver).

Design language

  • Palette — ivory #faf6ee, deep cacao #140a06, champagne gold #c2a06e, brand puce #703133
  • Type — Fraunces (display serif), Renner* (brand geometric sans), Samantha Signature (script accents)
  • Motifs — arched image masks, paper-grain texture, champagne hairlines, marquee tickers, cursor-trailing index previews, staggered line reveals

Site map

Route Contents
/ Announcement bar → hero ("Chocolate, perfected.") → brand statement → Shop by Experience → The Mozimo Edit → Signatures → Made by Hand (film) → Ingredient Story → The Art of Giving → Seasonal Edit → Experiences → Journal → Press → Instagram
/chocolate Collection index (Bars → Gifting) with 3-image hover carousels + The Pantry
/collections/[handle] A Shopify collection as a first-party page (also powers /gifting CTAs)
/products/[handle] Product page — gallery, price, description, buy (Shopify checkout)
/gifting Who is it for (price-wise tiles) → hampers → Seasonal Edit
/experiences Chef's Table, Tastings, Private Events, Collaborations, Pop-ups, Mozimo at Home
/bespoke Made for Your Moment — corporate, weddings, collaborations, events, custom packaging + bulk/bespoke enquiry form
/contact Contact form + atelier details
/our-world Story · Chocolate · Craft · Ingredients · People (+ /about redirects here)
/journal (+3 essays) The Mozimo Journal — The Art of Tempering; Why Dark Chocolate Tastes Different; The Story Behind Our Signature Box

Headless commerce & CMS

The site renders commerce itself; Shopify runs headless behind it.

  • Shopify Storefront API (src/lib/shopify.ts) — products, prices, collections. Server-side, cached ~60s; auth via a static SHOPIFY_STOREFRONT_ACCESS_TOKEN or, with SHOPIFY_CLIENT_ID + SHOPIFY_CLIENT_SECRET, the client auto-mints a storefront token (client-credentials → Admin REST) and self-heals if it is ever revoked. Checkout stays on Shopify's hosted page until Phase 2 (in-container cart, unauthenticated_write_checkouts scope already granted).
  • PocketBase (src/lib/cms.ts, running in the same container on :8090, published as cms.mozimo.in) — editorial layer: gifting tabs, /chocolate rows + carousel images, gifting page copy. Reads cached ~30s.
  • Fallbacks — if either backend is down the site serves the bundled defaults from src/data/catalog.ts; it never blanks.

Content editing (no deploys): PocketBase admin at https://cms.mozimo.in/_/ — edit gifting_tabs (tiles, blurbs, product handles per tier, ordering), collection_rows (row order, names, the 3 carousel images per collection), gifting_page (hero copy). Changes go live within the cache TTL.

First-run seeding: scripts/pb-seed.mjs (runs automatically at container start; also npm run seed) creates the collections and inserts the shipped content into an empty database. Idempotent — it never overwrites edits.

Develop

npm run dev       # vite dev server (port 3000), loads .env when present
npm run pb        # one-time download + run PocketBase locally (:8090)
npm run seed      # create PB schema + seed default content
npm run build     # production build to dist/
npm start         # serve the production build (node server.mjs)
npm run preview   # serve the production build via vite preview
npm run lint      # eslint (currently broken: leftover Next.js config)

Copy .env.example to .env and fill in the Shopify Storefront token and PocketBase superuser credentials (.env* is gitignored).

Containers

The image is built with podman (podman build reads the same Dockerfile):

podman build -t mozimo-web .
podman run -p 3000:3000 -p 8090:8090 -v mozimo_pb_data:/app/pb_data mozimo-web

Production is rootless podman + Quadlet on the host emily: systemd user services generated from .container units, fronted by that host's shared Caddy. ./deploy.sh builds the pushed commit with make build-production and runs ansible/playbook.yml. See ansible/README.md for the unit layout and the monoco → emily migration.

Three-stage build (deps → builder → runner) on pinned node:26-trixie-slim: dependencies install with npm ci and BuildKit cache mounts, the final image runs as the unprivileged node user, ships production dependencies, the build output, PocketBase v0.40.2 (multi-arch) and entrypoint.sh which starts both processes, ensures the PocketBase superuser matches PB_SUPERUSER_EMAIL / PB_SUPERUSER_PASSWORD on every boot, and seeds the CMS schema + defaults. Volumes: /app/pb_data holds the CMS database — back it up. Ports: 3000 web, 8090 PocketBase (both reached by Caddy over the podman network, not published on the host; see ansible/).

S
Description
No description provided
Readme
113 MiB
0 Stars 1 Watchers 0 Forks
Languages
JavaScript 97.2%
TypeScript 2.5%
Shell 0.1%
CSS 0.1%