{
  "header.eyebrow": "LABEL section=portfolio",
  "header.title": "docker-compose up projects --build --force-recreate",
  "header.description": "services:\n  portfolio:\n    image: travaux:curated\n    volumes:\n      - ./projects:/app/showcase:ro\n    labels:\n      - 'filter.enabled=true'\n      - 'stack.visible=true'",
  "header.cta_works.top": "COPY ./explore",
  "header.cta_works.bottom": "ENTRYPOINT [\"journey\"]",
  "filter.category": "docker ps --filter label=category",
  "filter.tags": "docker ps --filter label=tag --format '{{.Labels}}'",
  "filter.stack": "docker images --filter reference='stack/*'",
  "filter.reset": "docker system prune -af --volumes # reset: nuclear option",
  "filter.search_tag": "docker search tags/ --limit 100",
  "filter.search_stack": "docker search stack/ --no-trunc",
  "filter.none_tag": "Error: No such image: tag:notfound",
  "filter.none_stack": "Error response from daemon: stack not found",
  "filter.selected": "STATUS: attached",
  "filter.all": "docker ps -a # show everything. even the dead.",
  "filter.personal": "docker network inspect personal --format '{{.Containers}}'",
  "filter.open_source": "docker pull ghcr.io/opensource:latest --platform linux/generosity",
  "filter.client": "docker secret create client-work /dev/null # client work is classified",
  "status.done": "STATUS: exited (0) # clean exit. rare.",
  "status.in_progress": "STATUS: running (healthy) # or at least it claims to be",
  "status.archived": "STATUS: exited (137) OOMKilled # archived by the kernel",
  "omnicard.shortDescription": "docker run -d --name omnicard -p 8080:8080 card-game:strategic --restart unless-stopped",
  "omnicard.longDescription": "services:\n  omnicard:\n    build:\n      context: ./omnicard\n      dockerfile: Dockerfile.card-engine\n      args:\n        - RULES_ENGINE=custom\n        - COMPLEXITY=yes\n    depends_on:\n      game-logic:\n        condition: service_healthy\n      realtime-sync:\n        condition: service_started\n      state-manager:\n        condition: service_healthy\n    # 47 containers for a card game\n    # 'microservices' they said\n    # 'it'll be simpler' they said\n    # each card effect is its own container\n    # deploying a new card requires a helm chart update",
  "omnicard.highlights.0": "FROM rules-engine:modular AS card-logic\n# multi-stage build for game rules. 12 stages.",
  "omnicard.highlights.1": "docker events --filter 'type=container' --format '{{.Status}}'\n# game events ARE container events. effect triggers = container starts.",
  "omnicard.highlights.2": "docker swarm init --advertise-addr 0.0.0.0\n# multiplayer via docker swarm. each player is a node.",
  "omnicard.lessonsLearned.0": "# Learned: container orchestration is basically game state management",
  "omnicard.lessonsLearned.1": "# Learned: OOMKill during a boss fight is technically a game mechanic",
  "omnicard.lessonsLearned.2": "# Learned: 'one container per card' doesn't scale past 40 cards",
  "pvzf-translation-fr.shortDescription": "docker exec -it pvzf-translation sh -c 'wc -l /app/translations/fr/*.json'",
  "pvzf-translation-fr.longDescription": "services:\n  translation-lead:\n    image: pvzf-translator:fr-lead\n    environment:\n      - ROLE=lead\n      - LANGUAGE=fr\n      - QUALITY_BAR=high\n    volumes:\n      - translations-fr:/app/output\n      - terminology-db:/app/terms:ro\n    # the terminology database is a 47MB JSON file\n    # mounted read-only because last time someone\n    # wrote to it, 'bonjour' became 'bonjourno'\n    # (not even Italian. just wrong.)\n    deploy:\n      replicas: 1  # there can be only one lead",
  "pvzf-translation-fr.highlights.0": "LABEL role=lead maintainer=charles # leadership via Docker labels",
  "pvzf-translation-fr.highlights.1": "docker diff translation-container\n# tracking every file change for terminology consistency",
  "pvzf-translation-fr.highlights.2": "docker volume create --name contribution-tracker\n# each contribution is a volume mount. disk is cheap. right?",
  "pvzf-translation-fr.lessonsLearned.0": "# Learned: docker volumes don't have spell-check",
  "pvzf-translation-fr.lessonsLearned.1": "# Learned: replicas=1 means 'single point of failure'. also known as 'the lead'.",
  "pvzf-translation-fr.lessonsLearned.2": "# Learned: mounting terminology as read-only was the best decision in the project's history",
  "portfolio.shortDescription": "FROM angular:20-ssr AS portfolio\nLABEL description=\"immersive portfolio experience\" version=\"over-engineered\"",
  "portfolio.longDescription": "# This portfolio's docker-compose.yml:\nservices:\n  angular-ssr:\n    build: .\n    ports: ['4200:4200']\n  gsap-animations:\n    image: gsap:latest\n    # animations run in their own container\n    # because isolation matters\n  tailwind-compiler:\n    image: tailwind:4\n    volumes: ['./styles:/app/styles']\n    # tailwind in a container. watches files.\n    # rebuilds on change. adds 30s to boot.\n  audio-service:\n    image: bloodborne-ost:ambient\n    volumes: ['audio-state:/app/state']\n    # the audio runs in a container\n    # volume persistence for... volume level\n    # yes that's a volume for volume\n  seo-injector:\n    image: meta-tags:dynamic\n    depends_on: [angular-ssr, content-api]\n# Total: 9 containers for a portfolio\n# docker stats shows 14GB RAM usage\n# 'but it's containerized' I whisper, crying",
  "portfolio.highlights.0": "FROM node:20-alpine AS build\nRUN npm ci && npm run build:ssr\n# 'npm ci' takes 4 minutes. the alpine image didn't help.",
  "portfolio.highlights.1": "docker buildx build --platform linux/amd64,linux/arm64\n# multi-arch portfolio. in case someone views it on a Raspberry Pi.",
  "portfolio.highlights.2": "docker run --gpus all gsap-renderer:latest\n# GPU passthrough for CSS animations. necessary? no. cool? yes.",
  "portfolio.highlights.3": "HEALTHCHECK CMD curl -f http://localhost:4200 && lighthouse http://localhost:4200 --output json\n# healthcheck runs Lighthouse. every 30 seconds. the CPU weeps.",
  "portfolio.highlights.4": "LABEL design.system='custom' design.font='not-comic-sans' design.quality='high'\n# design system defined as Docker labels. the metadata IS the documentation.",
  "portfolio.lessonsLearned.0": "# Learned: 9 containers for a portfolio is 8 too many",
  "portfolio.lessonsLearned.1": "# Learned: GPU passthrough for GSAP is technically impressive and morally questionable",
  "portfolio.lessonsLearned.2": "# Learned: 'volume for volume' is the worst pun in DevOps history",
  "portfolio.lessonsLearned.3": "# Learned: multi-arch builds add 20 minutes for 0 users on ARM",
  "lis-web.shortDescription": "docker run -d --name lis-web -p 80:80 --restart always professional-site:production",
  "lis-web.longDescription": "services:\n  lis-web:\n    image: professional-site:production\n    environment:\n      - CLIENT_SATISFACTION=pending\n      - SCOPE_CREEP=enabled\n      - BUDGET=it-depends\n    deploy:\n      resources:\n        limits:\n          cpus: '0.1'\n          memory: 128M\n    # limited resources because shared hosting\n    # the container simulates shared hosting constraints\n    # for authenticity\n    # and also because the client won't pay for more",
  "lis-web.highlights.0": "EXPOSE 80\n# just port 80. no HTTPS. the client said 'the padlock is optional'.",
  "lis-web.highlights.1": "LABEL alignment='business-technical' # alignment tracked via label. always 'pending'.",
  "lis-web.highlights.2": "ENV PROJECT_TYPE=real # as opposed to our other projects which are... imaginary?",
  "lis-web.lessonsLearned.0": "# Learned: client says 'simple site'. docker-compose.yml: 200 lines.",
  "lis-web.lessonsLearned.1": "# Learned: scope_creep should not be an environment variable. it should be a feature flag. that's always ON.",
  "lis-web.lessonsLearned.2": "# Learned: 128M memory limit teaches you what 'optimization' really means",
  "dev-mates.shortDescription": "docker build -t dev-mates:corporate --label 'credibility=maximum' .",
  "dev-mates.longDescription": "# Dev-Mates Docker history:\n# v1: docker run nginx -v ./index.html:/usr/share/nginx/html/\n#     (entire site: 1 HTML file mounted into nginx)\n# v2: added PHP-FPM container for contact form\n#     (2 containers for a contact form. progress.)\n# v3: migrated to WordPress\n#     (docker-compose.yml: wordpress + mysql + phpmyadmin + redis + mailhog)\n#     (5 containers for a company website)\n# v4: ditched WordPress\n#     (docker rmi wordpress --force. cathartic.)\n# v5: Angular build in multi-stage Dockerfile\n#     (3 stages, 1 output, 0 regrets)\n# Current: 1 container. We went full circle.",
  "dev-mates.highlights.0": "LABEL business.type=legit business.trust=100 business.please=hire-us",
  "dev-mates.highlights.1": "docker inspect dev-mates --format '{{.Config.Labels.coherence}}'\n# coherence: tracked as a Docker label. value: 'mostly'.",
  "dev-mates.highlights.2": "EXPOSE 443\n# HTTPS enabled. we're credible now.",
  "dev-mates.lessonsLearned.0": "# Learned: 5 containers for a company website is called 'enterprise architecture'",
  "dev-mates.lessonsLearned.1": "# Learned: docker rmi wordpress --force is the most satisfying command in Docker",
  "dev-mates.lessonsLearned.2": "# Learned: going from 5 containers to 1 is called 'maturity'",
  "pvz-fuzion-console-manager.shortDescription": "docker run --rm pvzf-checker:latest --version latest --lang fr",
  "pvz-fuzion-console-manager.longDescription": "# Translation checker as a Docker container:\n# Step 1: docker build -t checker . (2 minutes)\n# Step 2: docker run checker (3 seconds)\n# Step 3: docker rmi checker (because disk)\n# \n# The actual check: diff two JSON files.\n# The Docker image: 800MB.\n# Contains: Node.js, Python, Ruby, and Java.\n# Uses: none of them. It's a bash script.\n# The bash script: 4 lines.\n# We dockerized a 4-line bash script.\n# It has a Kubernetes Helm chart.\n# For a 4-line bash script.\n# We are engineers.",
  "pvz-fuzion-console-manager.highlights.0": "docker run --rm checker diff /app/en.json /app/fr.json\n# shelling out to diff inside a 800MB container",
  "pvz-fuzion-console-manager.highlights.1": "docker logs checker --since 1h | grep MISSING\n# quality control via grep on container logs",
  "pvz-fuzion-console-manager.highlights.2": "docker stats checker --no-stream\n# community impact measured in CPU usage",
  "pvz-fuzion-console-manager.lessonsLearned.0": "# Learned: not everything needs to be a container. but here we are.",
  "pvz-fuzion-console-manager.lessonsLearned.1": "# Learned: 800MB for a bash script is 'production-ready'",
  "pvz-fuzion-console-manager.lessonsLearned.2": "# Learned: a Helm chart for a 4-line script is 'Kubernetes-native'",
  "shreksophone.shortDescription": "docker run --rm -it --privileged --device /dev/snd shreksophone:latest",
  "shreksophone.longDescription": "# Shreksophone: the only container that matters.\n#\n# Dockerfile:\n# FROM alpine:latest\n# RUN apk add mpv\n# COPY shrek_sax.mp4 /app/\n# ENTRYPOINT [\"mpv\", \"--fs\", \"--loop\", \"/app/shrek_sax.mp4\"]\n#\n# That's it. That's the entire application.\n# 4 lines. Perfect. No vulnerabilities.\n# (Trivy scan: 0 CVEs. The cleanest image in the registry.)\n# \n# It requires --privileged for fullscreen.\n# It requires --device /dev/snd for audio.\n# It requires /dev/video0 for... actually no.\n# It just needs your faith.\n#\n# docker push shreksophone:latest has 47,000 pulls.\n# It's our most popular image.\n# The portfolio image has 3 pulls. All from CI.",
  "shreksophone.highlights.0": "FROM alpine:latest\n# minimal base image. maximum Shrek.",
  "shreksophone.highlights.1": "ENTRYPOINT [\"mpv\", \"--fs\", \"--loop\"]\n# fullscreen, infinite loop. like Shrek intended.",
  "shreksophone.highlights.2": "docker scan shreksophone:latest\n# 0 vulnerabilities. Shrek protects this image.",
  "shreksophone.lessonsLearned.0": "# Learned: the simplest Dockerfile is the best Dockerfile",
  "shreksophone.lessonsLearned.1": "# Learned: 47,000 pulls > any enterprise project on my registry",
  "shreksophone.lessonsLearned.2": "# Learned: --privileged flag for Shrek is always justified",
  "glossairequest.shortDescription": "docker run -d --name quiz-engine -e JWT_SECRET=changeme quiz:pedagogique",
  "glossairequest.longDescription": "services:\n  quiz-frontend:\n    image: angular:quiz-ui\n    ports: ['4200:4200']\n  quiz-backend:\n    image: aspnet:quiz-api\n    ports: ['5000:5000']\n    environment:\n      - JWT_SECRET=changeme  # still says changeme in prod\n      - DB_PASSWORD=also-changeme  # also still changeme\n    depends_on:\n      postgres:\n        condition: service_healthy\n  postgres:\n    image: postgres:16\n    environment:\n      - POSTGRES_PASSWORD=changeme  # sensing a pattern?\n    volumes:\n      - quiz-data:/var/lib/postgresql/data\n  # Every env var says 'changeme'\n  # None were changed\n  # The real quiz was the security holes we made along the way",
  "glossairequest.highlights.0": "ENV JWT_SECRET=changeme # 'temporary' since deployment day",
  "glossairequest.highlights.1": "docker volume create quiz-scores\n# scores in a volume. persistent. immutable. unchangeable. (we lost the volume name once. scores: gone.)",
  "glossairequest.highlights.2": "docker network create --internal quiz-network\n# internal network between front and back. security! (the DB port is still exposed. oops.)",
  "glossairequest.lessonsLearned.0": "# Learned: 'changeme' in prod is not a password strategy. it's a prayer.",
  "glossairequest.lessonsLearned.1": "# Learned: docker volume names should be documented somewhere other than 'it's the default one'",
  "glossairequest.lessonsLearned.2": "# Learned: --internal network means nothing if you EXPOSE the DB port",
  "league-of-data-base.shortDescription": "docker pull league-data:multilang --platform linux/amd64",
  "league-of-data-base.longDescription": "services:\n  riot-api-fetcher:\n    image: api-fetcher:latest\n    environment:\n      - RIOT_API_KEY=RGAPI-xxxxxxxx  # rotates every 24h. we forget to update it every 24h.\n    deploy:\n      restart_policy:\n        condition: on-failure\n        delay: 60s  # rate limited? just wait.\n        max_attempts: 9999  # never give up. never surrender.\n  data-storage:\n    image: postgres:16\n    volumes:\n      - champion-data:/app/data  # 47GB of champion splash arts\n      - item-data:/app/items     # 12GB of item icons\n    # total disk usage: 'df -h' returns 'please stop'\n  image-optimizer:\n    image: sharp:latest\n    # this container optimizes images\n    # it has never run successfully\n    # the entry in docker-compose is aspirational",
  "league-of-data-base.highlights.0": "ENV RIOT_API_KEY=RGAPI-expired-3-days-ago\n# the key rotation is 'someone remembers during standup'",
  "league-of-data-base.highlights.1": "docker system df\n# hard links save 60% disk space. the other 40% is Docker layers.",
  "league-of-data-base.highlights.2": "docker run --rm -it league-data sh -c 'ls /app/public/*.jpg | wc -l'\n# 14,847 images. responsive: no. present: very yes.",
  "league-of-data-base.lessonsLearned.0": "# Learned: API key rotation should be automated, not 'standup-driven'",
  "league-of-data-base.lessonsLearned.1": "# Learned: hard links in Docker layers is actually clever. we're proud of this one.",
  "league-of-data-base.lessonsLearned.2": "# Learned: 'aspirational containers' in docker-compose is a new pattern we invented",
  "blender-collection.shortDescription": "docker run -d --name blender-hub -v addons:/app/addons blender-collection:latest",
  "blender-collection.longDescription": "services:\n  blender-hub:\n    image: blender-collection:latest\n    volumes:\n      - addons:/app/addons    # 200GB of Blender addons\n      - uploads:/app/uploads   # user uploads. unvalidated. unchecked. unhinged.\n    deploy:\n      resources:\n        limits:\n          memory: 32G  # the zip operation needs... a lot.\n  worker:\n    image: blender-worker:latest\n    entrypoint: ['sh', '-c', 'while true; do process_queue; sleep 5; done']\n    # the immortal worker. dockerized version.\n    # restart: always + while true = double immortality\n    restart: always\n  admin-dashboard:\n    image: admin:analytics\n    environment:\n      - QUERY_TIMEOUT=never  # the analytics query takes 30s. the timeout is... never.\n    depends_on:\n      - postgres\n    # the dashboard container uses more CPU than the actual app\n    # because COUNT(*) on 2M rows every page load",
  "blender-collection.highlights.0": "docker exec blender-hub sh -c 'ls /app/uploads'\n# user roles: checked via volume mount permissions. root = admin.",
  "blender-collection.highlights.1": "docker stats admin-dashboard --no-stream\n# CPU: 98%. Memory: 89%. Purpose: displaying a number.",
  "blender-collection.highlights.2": "docker inspect worker --format '{{.State.StartedAt}}'\n# started: 8 months ago. PID 1. unkillable. we've accepted it.",
  "blender-collection.lessonsLearned.0": "# Learned: user uploads + no validation = docker exec surprise",
  "blender-collection.lessonsLearned.1": "# Learned: COUNT(*) should be cached. not containerized.",
  "blender-collection.lessonsLearned.2": "# Learned: restart:always + while true + docker = immortal process that survives everything including your will to live",
  "symfony-session.shortDescription": "docker run -d --name symfony-session -v session-data:/var/lib/php/sessions symfony:session-manager",
  "symfony-session.longDescription": "services:\n  symfony-app:\n    image: symfony:session-manager\n    volumes:\n      - session-data:/var/lib/php/sessions  # 4MB per session. 10,000 users. do the math.\n      - /tmp/sessions:/tmp/sessions          # overflow sessions go to /tmp. /tmp is 512MB. it fills up.\n    environment:\n      - SESSION_LIFETIME=forever  # not a valid PHP value but the container doesn't complain\n    deploy:\n      resources:\n        limits:\n          memory: 64G  # 60GB of that is session data\n  session-cleaner:\n    image: alpine:latest\n    entrypoint: ['sh', '-c', 'while true; do rm -rf /sessions/sess_*; sleep 604800; done']\n    volumes:\n      - session-data:/sessions\n    # the session cleaner: runs weekly\n    # deletes all sessions every Sunday night\n    # Monday morning: 'why am I logged out?'\n    # Jira ticket: 'WONTFIX - it\\'s a feature'",
  "symfony-session.highlights.0": "docker run --rm captcha-generator:comic-sans\n# captcha generator. uses Comic Sans. bots solve it faster than humans.",
  "symfony-session.highlights.1": "docker exec symfony-app php bin/console make:crud\n# CRUD via artisan... wait, wrong framework. bin/console. same energy.",
  "symfony-session.highlights.2": "docker run --rm pdf-generator:fpdf -v calendar:/app/output\n# PDF generation container. 800MB image. generates one PDF. exits.",
  "symfony-session.lessonsLearned.0": "# Learned: SESSION_LIFETIME=forever is not valid but the container respected it anyway. terrifying.",
  "symfony-session.lessonsLearned.1": "# Learned: weekly session deletion is a 'feature' if you label the Jira ticket correctly",
  "symfony-session.lessonsLearned.2": "# Learned: 64GB memory limit for sessions means you've made several life choices",
  "timeline.featured": "LABEL featured=true priority=critical\n# featured projects get their own node in the swarm",
  "timeline.detail": "docker inspect project --format '{{json .Config}}'",
  "timeline.aria": "# timeline: accessible via docker logs --timestamps",
  "modal.close": "docker stop modal && docker rm modal # close with extreme prejudice",
  "modal.image_fullscreen": "docker run --rm --privileged -e DISPLAY=:0 image-viewer:fullscreen",
  "modal.previous": "docker rollback project-viewer # previous = rollback. same thing.",
  "modal.next": "docker service update --image next:tag project-viewer",
  "modal.video_title": "docker run --rm --device /dev/video0 video-player:latest",
  "modal.description": "docker logs project --tail 50 # description = last 50 log lines",
  "modal.lessons": "docker history project:latest # lessons = layer history",
  "modal.highlights": "docker inspect project --format '{{.Config.Labels}}' # highlights = labels",
  "modal.links": "docker network inspect project-links",
  "modal.demo": "docker run --rm -p 8080:80 demo:latest # demo: ephemeral container. gone when you close it.",
  "modal.site": "kubectl port-forward svc/project-site 8080:80 # we switched to k8s mid-sentence",
  "today": "docker exec timekeeper date +%Y-%m-%d # dedicated container for current date"
}
