[{"data":1,"prerenderedAt":1215},["ShallowReactive",2],{"navigation":3,"home:/es:es":59,"index-blogs":190,"mdc-z19idx-key":1040,"mdc-o0crq2-key":1074,"mdc-cb5dab-key":1121,"mdc-9g9zyc-key":1130,"mdc-5ze1aa-key":1146,"mdc--7u7ply-key":1183},[4],{"title":5,"path":6,"stem":7,"children":8,"page":34},"Blog","/blog","blog",[9,13,35,39,43,47,51,55],{"title":10,"path":11,"stem":12},"Using the official exchange rate in Venezuela's e-commerce","/blog/bcv-scraper","blog/bcv-scraper",{"title":14,"path":15,"stem":16,"children":17,"page":34},"Case Studies","/blog/case-studies","blog/case-studies",[18,22,26,30],{"title":19,"path":20,"stem":21},"Rebuilding an e-commerce platform on NestJS + Next.js + Medusa in three months","/blog/case-studies/retail-ecommerce-rebuild","blog/case-studies/retail-ecommerce-rebuild",{"title":23,"path":24,"stem":25},"Stabilizing an inherited e-commerce platform on NestJS + Strapi","/blog/case-studies/retail-ecommerce-recovery","blog/case-studies/retail-ecommerce-recovery",{"title":27,"path":28,"stem":29},"A nationwide mobile sales-quotation app, integrated with SAP","/blog/case-studies/retail-sap-quotation-app","blog/case-studies/retail-sap-quotation-app",{"title":31,"path":32,"stem":33},"Shipping a high-scale PWA from an inherited e-commerce project","/blog/case-studies/supermarket-pwa-recovery","blog/case-studies/supermarket-pwa-recovery",false,{"title":36,"path":37,"stem":38},"ChronoCal: Time tracking inside Google Calendar","/blog/chronocal","blog/chronocal",{"title":40,"path":41,"stem":42},"Fixing GNOME Google Drive Mounts on NixOS 25.11","/blog/fixing-gnome-google-drive-mounts-on-nixos-25-11","blog/fixing-gnome-google-drive-mounts-on-nixos-25-11",{"title":44,"path":45,"stem":46},"From Nuxt to Hugo and Back Again","/blog/from-nuxt-to-hugo","blog/from-nuxt-to-hugo",{"title":48,"path":49,"stem":50},"The Intern is downloading movies again","/blog/intern-movie-torrent","blog/intern-movie-torrent",{"title":52,"path":53,"stem":54},"Japanese input in openSUSE Tumbleweed's KDE","/blog/japanese-input","blog/japanese-input",{"title":56,"path":57,"stem":58},"Jhey Pi: an official landing site for an aspiring musician","/blog/jp-landing","blog/jp-landing",{"id":60,"title":61,"about":62,"blog":65,"body":68,"description":73,"experience":155,"extension":165,"faq":166,"hero":180,"locale":69,"meta":183,"navigation":184,"path":185,"seo":186,"stem":187,"testimonials":188,"__hash__":189},"home/es/home.yml","Iván Álvarez",{"title":63,"description":64},"Sobre mí","En los últimos años he trabajado en plataformas que otros abandonaron — tiendas e-commerce bajo carga, integraciones SAP con documentación perdida, despliegues que nadie quería tocar. Mi trabajo es entrar, leer el código, arreglarlo y dejar al equipo mejor de como lo encontré.\n",{"title":66,"description":67},"Últimos artículos","Notas sobre software, datos y aprendizaje continuo.",{"locale":69,"seo":70,"title":61,"description":73,"hero":74,"about":81,"experience":82,"testimonials":115,"blog":116,"faq":117},"es",{"title":71,"description":72},"Iván Álvarez · Ingeniero Full-Stack","Ingeniero full-stack especializado en recuperación de plataformas y rescate de e-commerce. Docker, Kubernetes, Laravel, NestJS. En camino hacia GCP DataOps.","Ingeniero full-stack en Valencia, Venezuela. Construyo y recupero plataformas que crecieron más allá de su diseño original — e-commerce, integraciones SAP, pipelines de despliegue. Ahora lo hago sobre Google Cloud.\n",{"links":75},[76],{"label":77,"to":78,"color":79,"trailingIcon":80},"Leer el blog","/es/blog","neutral","i-lucide-arrow-right",{"title":63,"description":64},{"title":83,"items":84},"Experiencia",[85,93,101,107],{"position":86,"date":87,"company":88},"Full-Stack Developer","Dec 2025 – Actualidad",{"name":89,"logo":90,"url":91,"color":92},"Crazy Imagine Software","i-lucide-briefcase","https://crazyimagine.com/","#6b7280",{"position":94,"date":95,"company":96},"Software Development Consultant","Oct 2024 – Dec 2025",{"name":97,"logo":98,"url":99,"color":100},"Tiendas Daka C.A.","i-lucide-building-2","https://www.tiendasdaka.com/","#facc15",{"position":102,"date":103,"company":104},"Freelance Full-Stack Developer","Dec 2022 – Sep 2024",{"name":105,"logo":90,"url":106,"color":92},"Self Employed","https://alvarezivan.net",{"position":108,"date":109,"company":110},"Programmer","Oct 2021 – Nov 2022",{"name":111,"logo":112,"url":113,"color":114},"Biomercados (Alimentos FM)","i-lucide-store","#","#0f766e",[],{"title":66,"description":67},{"title":118,"description":119,"categories":120},"Cómo trabajo","Un resumen de las tecnologías que uso y cómo suelo colaborar con equipos. La página [/es/uses](/es/uses) tiene más detalle.",[121,140],{"title":122,"questions":123},"Habilidades",[124,128,132,136],{"label":125,"icon":126,"content":127},"Cloud y DevOps","i-lucide-cloud","- Docker\n- Kubernetes (aprendiendo en profundidad)\n- CI/CD (GitLab, GitHub Actions)\n- Proxmox\n- Ubuntu Server\n- Windows Server\n",{"label":129,"icon":130,"content":131},"Backend","i-lucide-server","- Laravel\n- NestJS\n- Node.js\n- Express.js\n- FastAPI\n",{"label":133,"icon":134,"content":135},"Frontend","i-lucide-layout","- Vue.js\n- Nuxt.js\n- Angular\n- PrimeVue\n- Inertia.js\n- Filament\n- Livewire\n- Flutter\n",{"label":137,"icon":138,"content":139},"Bases de datos","i-lucide-database","- MySQL\n- PostgreSQL\n- MongoDB\n- Redis\n- BigQuery\n",{"title":141,"questions":142},"Precios y tiempos",[143,146,149,152],{"label":144,"content":145},"¿Cuánto cuesta un proyecto?","Depende del alcance, complejidad técnica e integraciones. Los proyectos suelen iniciar en auditorías especializadas y escalar a implementaciones completas.\n",{"label":147,"content":148},"¿Cuáles son tus términos de pago?","Suelo trabajar con un anticipo para reservar la agenda y el pago restante al entregar.\n",{"label":150,"content":151},"¿Cuánto tarda un proyecto?","Los tiempos dependen del alcance. Proyectos pequeños pueden tomar semanas y plataformas más grandes requieren múltiples fases.\n",{"label":153,"content":154},"¿Ofreces soporte continuo?","Sí. También trabajo por retainer mensual para equipos que necesitan desarrollo y mantenimiento continuo.\n",{"title":83,"items":156},[157,159,161,163],{"position":86,"date":87,"company":158},{"name":89,"logo":90,"url":91,"color":92},{"position":94,"date":95,"company":160},{"name":97,"logo":98,"url":99,"color":100},{"position":102,"date":103,"company":162},{"name":105,"logo":90,"url":106,"color":92},{"position":108,"date":109,"company":164},{"name":111,"logo":112,"url":113,"color":114},"yml",{"title":118,"description":119,"categories":167},[168,174],{"title":122,"questions":169},[170,171,172,173],{"label":125,"icon":126,"content":127},{"label":129,"icon":130,"content":131},{"label":133,"icon":134,"content":135},{"label":137,"icon":138,"content":139},{"title":141,"questions":175},[176,177,178,179],{"label":144,"content":145},{"label":147,"content":148},{"label":150,"content":151},{"label":153,"content":154},{"links":181},[182],{"label":77,"to":78,"color":79,"trailingIcon":80},{},true,"/es/home",{"title":71,"description":72},"es/home",[],"U3uq6wjLRb_W3Bdu_t86n4EAKx_a4PviCr7_-RDVIs4",[191,598,879],{"id":192,"title":36,"author":193,"body":198,"category":577,"client":578,"date":579,"description":580,"extension":581,"featured":34,"hidden":34,"image":582,"meta":583,"minRead":584,"navigation":184,"ogImage":578,"outcome_headline":585,"path":37,"repoUrl":539,"role":586,"seo":587,"stack":588,"stem":38,"tags":593,"team_size":447,"type":577,"year":596,"__hash__":597},"blog/blog/chronocal.md",{"name":61,"username":194,"to":195,"avatar":196},"ivanovertime","https://github.com/ivanovertime",{"src":197,"alt":61},"/avatar.jpg",{"type":199,"value":200,"toc":565},"minimark",[201,206,210,213,216,232,236,239,253,257,260,265,271,282,287,298,303,314,319,327,331,334,345,351,357,363,367,370,373,390,394,397,414,418,425,436,523,526,530,533,543,546,561],[202,203,205],"h2",{"id":204},"situation","Situation",[207,208,209],"p",{},"As part of an ongoing effort to simplify my development setup and leverage Google solutions as much as possible—including migrating from Outlook to Google Calendar as I transition to NixOS—I needed a time-tracking solution that lived entirely within Google Workspace.",[207,211,212],{},"If you live in Google Workspace (Calendar, Drive, Meet), context-switching between apps to track time on calendar events creates friction. You're already in Calendar to manage your day—why leave it to start a timer?",[207,214,215],{},"Most time-tracking solutions require either:",[217,218,219,223,226,229],"ul",{},[220,221,222],"li",{},"A separate app you need to remember to open",[220,224,225],{},"A backend service to persist state",[220,227,228],{},"A third-party integration that adds OAuth complexity",[220,230,231],{},"A browser timer that depends on the tab staying active",[202,233,235],{"id":234},"problem","Problem",[207,237,238],{},"Calendar events are already a reliable source of truth for your time commitment. What if you could:",[217,240,241,244,247,250],{},[220,242,243],{},"Start/pause/stop tracking directly from the Calendar event card",[220,245,246],{},"Keep state in Apps Script UserProperties (no backend needed)",[220,248,249],{},"Optionally write the tracked duration back to the event description",[220,251,252],{},"Export sessions to Google Sheets for reporting or analysis",[202,254,256],{"id":255},"solution-chronocal","Solution: ChronoCal",[207,258,259],{},"ChronoCal is a Google Calendar add-on built entirely with Google Apps Script. It provides:",[261,262,264],"h3",{"id":263},"core-features","Core Features",[207,266,267],{},[268,269,270],"strong",{},"In-Event Tracking",[217,272,273,276,279],{},[220,274,275],{},"Open any calendar event on desktop Calendar and a contextual card appears",[220,277,278],{},"Buttons to start, pause, resume, and stop tracking from the card itself",[220,280,281],{},"Session state persists using Apps Script UserProperties—survives browser refresh and tab close",[207,283,284],{},[268,285,286],{},"Flexible Session States",[217,288,289,292,295],{},[220,290,291],{},"Running: timer is actively tracking",[220,293,294],{},"Paused: stopped but not finalized",[220,296,297],{},"Stopped: finalized session ready for export",[207,299,300],{},[268,301,302],{},"Event Description Integration",[217,304,305,308,311],{},[220,306,307],{},"Toggle option to write the final tracked duration directly to event descriptions",[220,309,310],{},"Handles recurring event instances correctly",[220,312,313],{},"Resolves the event across all calendars in your Google Account",[207,315,316],{},[268,317,318],{},"Google Sheets Export",[217,320,321,324],{},[220,322,323],{},"Export completed sessions to a Google Sheet for reporting, billing, or analysis",[220,325,326],{},"Minimal setup: the add-on can create the Sheet or append to an existing one",[261,328,330],{"id":329},"technical-approach","Technical Approach",[207,332,333],{},"The implementation is split across six core modules:",[335,336,341],"pre",{"className":337,"code":339,"language":340},[338],"language-text","src/\n├── main.gs           # Entry point and onOpen handler\n├── cards.gs          # Card UI construction and button handlers\n├── session.gs        # Session state management and timing logic\n├── calendar.gs       # Calendar API interactions and event resolution\n├── sheets.gs         # Google Sheets export logic\n└── config.gs         # Configuration constants and toggles\n","text",[342,343,339],"code",{"__ignoreMap":344},"",[207,346,347,350],{},[268,348,349],{},"No External Backend","\nEverything runs inside Google Apps Script. UserProperties store the session state, so it survives across browser sessions and tabs.",[207,352,353,356],{},[268,354,355],{},"OAuth Scope Minimization","\nThe manifest declares Calendar (read/write), Sheets, and Calendar add-on execution scopes only. No external API calls or third-party storage.",[207,358,359,362],{},[268,360,361],{},"Event Resolution","\nCorrectly handles recurring events and calendar resolution, so you can track time on the same event across multiple calendars without duplication.",[202,364,366],{"id":365},"why-this-approach","Why This Approach",[207,368,369],{},"The friction in traditional time tracking comes from context-switching. If you're already in Google Calendar (which most knowledge workers are), triggering a timer from the event card removes a step.",[207,371,372],{},"Apps Script is ideal for this because:",[217,374,375,378,381,384,387],{},[220,376,377],{},"Native access to Calendar and Sheets without additional APIs",[220,379,380],{},"UserProperties provide persistent client-side state",[220,382,383],{},"Contextual cards integrate directly into Calendar's UI",[220,385,386],{},"Zero infrastructure: no servers to maintain, no databases to back up",[220,388,389],{},"Tight integration with Google Workspace",[202,391,393],{"id":392},"current-state","Current State",[207,395,396],{},"ChronoCal is a functional MVP with pause/resume and Sheets export flows. Planned evolution includes:",[217,398,399,402,405,408,411],{},[220,400,401],{},"Keyboard shortcuts for faster access",[220,403,404],{},"Bulk session export options",[220,406,407],{},"Integration with other Workspace apps (Gmail, Tasks)",[220,409,410],{},"Analytics dashboard within Sheets",[220,412,413],{},"More granular state management (e.g., session notes, billing codes)",[202,415,417],{"id":416},"development-deployment","Development & Deployment",[207,419,420,421,424],{},"The repo includes a complete deployment workflow using ",[342,422,423],{},"clasp"," (Google Apps Script CLI). Prerequisites are minimal:",[217,426,427,430,433],{},[220,428,429],{},"Node.js and npm",[220,431,432],{},"Google account with Apps Script access",[220,434,435],{},"Optional: Nix + direnv (dev environment files included)",[335,437,441],{"className":438,"code":439,"language":440,"meta":344,"style":344},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","direnv allow\nnpx @google/clasp login\nnpx @google/clasp create --type standalone --title \"ChronoCal\" --rootDir .\nnpx @google/clasp push\nnpx @google/clasp open\n","bash",[342,442,443,456,468,503,513],{"__ignoreMap":344},[444,445,448,452],"span",{"class":446,"line":447},"line",1,[444,449,451],{"class":450},"sBMFI","direnv",[444,453,455],{"class":454},"sfazB"," allow\n",[444,457,459,462,465],{"class":446,"line":458},2,[444,460,461],{"class":450},"npx",[444,463,464],{"class":454}," @google/clasp",[444,466,467],{"class":454}," login\n",[444,469,471,473,475,478,481,484,487,491,494,497,500],{"class":446,"line":470},3,[444,472,461],{"class":450},[444,474,464],{"class":454},[444,476,477],{"class":454}," create",[444,479,480],{"class":454}," --type",[444,482,483],{"class":454}," standalone",[444,485,486],{"class":454}," --title",[444,488,490],{"class":489},"sMK4o"," \"",[444,492,493],{"class":454},"ChronoCal",[444,495,496],{"class":489},"\"",[444,498,499],{"class":454}," --rootDir",[444,501,502],{"class":454}," .\n",[444,504,506,508,510],{"class":446,"line":505},4,[444,507,461],{"class":450},[444,509,464],{"class":454},[444,511,512],{"class":454}," push\n",[444,514,516,518,520],{"class":446,"line":515},5,[444,517,461],{"class":450},[444,519,464],{"class":454},[444,521,522],{"class":454}," open\n",[207,524,525],{},"The project also includes contribution guidelines, PR templates, and issue templates for collaborators.",[202,527,529],{"id":528},"key-takeaway","Key Takeaway",[207,531,532],{},"ChronoCal proves that powerful productivity tools don't need complex infrastructure. By leveraging Google Apps Script and UserProperties, you can build a tracking system that lives inside the tool you already use every day—Calendar—without adding operational burden or privacy concerns.",[207,534,535,536],{},"If you use Google Workspace and find yourself constantly switching between Calendar and time trackers, give ChronoCal a try: ",[537,538,542],"a",{"href":539,"rel":540},"https://github.com/ivanovertime/ChronoCal",[541],"nofollow","github.com/ivanovertime/ChronoCal",[544,545],"hr",{},[207,547,548,551,552,556,557],{},[268,549,550],{},"Image attribution:"," Featured image is from Outlook. Photo by ",[537,553,555],{"href":554},"https://unsplash.com/@edhardie?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText","Ed Hardie"," on ",[537,558,560],{"href":559},"https://unsplash.com/photos/a-computer-screen-with-a-calendar-on-it-4BnNnEtAGP0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText","Unsplash",[562,563,564],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":344,"searchDepth":458,"depth":458,"links":566},[567,568,569,573,574,575,576],{"id":204,"depth":458,"text":205},{"id":234,"depth":458,"text":235},{"id":255,"depth":458,"text":256,"children":570},[571,572],{"id":263,"depth":470,"text":264},{"id":329,"depth":470,"text":330},{"id":365,"depth":458,"text":366},{"id":392,"depth":458,"text":393},{"id":416,"depth":458,"text":417},{"id":528,"depth":458,"text":529},"side-project",null,"2026-06-15T00:00:00.000Z","A Google Calendar add-on built with Apps Script that tracks real time spent on events directly in Google Workspace—no external backend, no third-party storage, and no browser-side timer dependency.","md","/blog/chronocal/featuredImage.jpg",{},6,"Published a functional MVP that brings real-time tracking directly into Google Calendar without external infrastructure","Engineer",{"title":36,"description":580},[589,590,591,592],"Google Apps Script","JavaScript","Google Calendar API","Google Sheets API",[594,595],"google-workspace","time-tracking",2026,"CD4RidCTooznvYxDm6V7M4RxoU4fY99kOBc-FTV_eks",{"id":599,"title":40,"author":600,"body":602,"category":868,"client":578,"date":869,"description":870,"extension":581,"featured":34,"hidden":34,"image":619,"meta":871,"minRead":505,"navigation":184,"ogImage":578,"outcome_headline":578,"path":41,"repoUrl":578,"role":578,"seo":872,"stack":578,"stem":42,"tags":873,"team_size":578,"type":578,"year":578,"__hash__":878},"blog/blog/fixing-gnome-google-drive-mounts-on-nixos-25-11.md",{"name":61,"username":194,"to":195,"avatar":601},{"src":197,"alt":61},{"type":199,"value":603,"toc":857},[604,607,613,620,623,627,630,642,645,649,652,656,659,686,689,693,696,726,729,733,736,756,759,762,769,772,776,779,795,798,809,812,816,823,826,830,833,851,854],[207,605,606],{},"I ran into an issue on NixOS 25.11 that took me a while to understand: Google Drive would appear in GNOME Files after signing in with GNOME Online Accounts, but trying to open it failed with:",[608,609,610],"blockquote",{},[207,611,612],{},"\"Location is not mountable\"",[207,614,615],{},[616,617],"img",{"alt":618,"src":619},"GNOME Files showing the \"Unable to access — Location is not mountable\" error dialog","/blog/fixing-gnome-google-drive-mounts-on-nixos/featuredImage.png",[207,621,622],{},"This post shares the fix that worked for me and got Drive mounts working reliably on my system.",[202,624,626],{"id":625},"the-problem","The problem",[207,628,629],{},"At first glance, everything looked correct:",[631,632,633,636,639],"ol",{},[220,634,635],{},"GNOME Online Accounts was enabled.",[220,637,638],{},"I could sign in to Google.",[220,640,641],{},"Drive showed up in the sidebar.",[207,643,644],{},"But the mount itself still failed. The missing piece was that the default GVFS build in this setup did not include Google backend support.",[202,646,648],{"id":647},"what-i-changed","What I changed",[207,650,651],{},"I made three focused changes.",[261,653,655],{"id":654},"_1-force-a-gvfs-build-with-google-support","1) Force a GVFS build with Google support",[207,657,658],{},"I overrode the GVFS package to enable Google support explicitly:",[335,660,664],{"className":661,"code":662,"language":663,"meta":344,"style":344},"language-nix shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","services.gvfs.package = pkgs.gnome.gvfs.override {\n  gnomeSupport = true;\n  googleSupport = true;\n};\n","nix",[342,665,666,671,676,681],{"__ignoreMap":344},[444,667,668],{"class":446,"line":447},[444,669,670],{},"services.gvfs.package = pkgs.gnome.gvfs.override {\n",[444,672,673],{"class":446,"line":458},[444,674,675],{},"  gnomeSupport = true;\n",[444,677,678],{"class":446,"line":470},[444,679,680],{},"  googleSupport = true;\n",[444,682,683],{"class":446,"line":505},[444,684,685],{},"};\n",[207,687,688],{},"Why: without this, GOA can authenticate, but GVFS cannot complete the Drive mount backend path.",[261,690,692],{"id":691},"_2-keep-the-gnome-accountmount-stack-enabled","2) Keep the GNOME account/mount stack enabled",[207,694,695],{},"I ensured these services stayed enabled:",[335,697,699],{"className":661,"code":698,"language":663,"meta":344,"style":344},"services.gnome.gnome-online-accounts.enable = true;\nservices.gvfs.enable = true;\nservices.gnome.gnome-keyring.enable = true;\nservices.accounts-daemon.enable = true;\nprograms.dconf.enable = true;\n",[342,700,701,706,711,716,721],{"__ignoreMap":344},[444,702,703],{"class":446,"line":447},[444,704,705],{},"services.gnome.gnome-online-accounts.enable = true;\n",[444,707,708],{"class":446,"line":458},[444,709,710],{},"services.gvfs.enable = true;\n",[444,712,713],{"class":446,"line":470},[444,714,715],{},"services.gnome.gnome-keyring.enable = true;\n",[444,717,718],{"class":446,"line":505},[444,719,720],{},"services.accounts-daemon.enable = true;\n",[444,722,723],{"class":446,"line":515},[444,724,725],{},"programs.dconf.enable = true;\n",[207,727,728],{},"Why: these are all part of the GNOME identity, credential, and desktop integration chain used for online volumes.",[261,730,732],{"id":731},"_3-allow-the-insecure-libsoup2-package-required-by-this-backend-path","3) Allow the insecure libsoup2 package required by this backend path",[207,734,735],{},"I added:",[335,737,739],{"className":661,"code":738,"language":663,"meta":344,"style":344},"nixpkgs.config.permittedInsecurePackages = [\n  \"libsoup-2.74.3\"\n];\n",[342,740,741,746,751],{"__ignoreMap":344},[444,742,743],{"class":446,"line":447},[444,744,745],{},"nixpkgs.config.permittedInsecurePackages = [\n",[444,747,748],{"class":446,"line":458},[444,749,750],{},"  \"libsoup-2.74.3\"\n",[444,752,753],{"class":446,"line":470},[444,754,755],{},"];\n",[207,757,758],{},"Why: in this channel/version combination, enabling the GVFS Google backend pulls libsoup2, which is flagged insecure. This exception allows rebuild and evaluation to proceed.",[207,760,761],{},"Important note: this is a pragmatic compatibility workaround, not a long-term ideal from a security perspective.",[207,763,764,765,768],{},"From what nixpkgs currently documents, this is not about a single isolated CVE. The ",[342,766,767],{},"libsoup 2"," line is end-of-life and marked with many known unfixed CVEs, so Nix surfaces it as insecure by default.",[207,770,771],{},"I am genuinely grateful this warning exists. It is easy to feel blocked when a rebuild stops, but this policy is doing an important job: reminding us where a compatibility fix trades off against long-term security hygiene.",[202,773,775],{"id":774},"apply-and-verify","Apply and verify",[207,777,778],{},"After updating your configuration:",[335,780,782],{"className":438,"code":781,"language":440,"meta":344,"style":344},"sudo nixos-rebuild switch\n",[342,783,784],{"__ignoreMap":344},[444,785,786,789,792],{"class":446,"line":447},[444,787,788],{"class":450},"sudo",[444,790,791],{"class":454}," nixos-rebuild",[444,793,794],{"class":454}," switch\n",[207,796,797],{},"Then:",[631,799,800,803,806],{},[220,801,802],{},"Open GNOME Online Accounts and confirm your Google account is connected.",[220,804,805],{},"Open Files.",[220,807,808],{},"Select Google Drive from the sidebar.",[207,810,811],{},"If everything is wired correctly, Drive should mount instead of failing with \"Location is not mountable.\"",[202,813,815],{"id":814},"tradeoffs-and-future-direction","Tradeoffs and future direction",[207,817,818,819,822],{},"This fix restores native GNOME Drive mounting, but it currently depends on a package exception (",[342,820,821],{},"libsoup-2.74.3","). A cleaner long-term path is to move to a backend stack that avoids insecure dependencies, or use an alternative mounting approach such as rclone.",[207,824,825],{},"In other words, this got me unblocked quickly, and I appreciate that. At the same time, I treat it as temporary until the Drive backend path can run without the older EOL library line.",[202,827,829],{"id":828},"tldr","TL;DR",[207,831,832],{},"If Google Drive appears in GNOME Files but will not mount on NixOS 25.11, this is the short version of what helped me:",[631,834,835,842,845],{},[220,836,837,838,841],{},"Override GVFS with ",[342,839,840],{},"googleSupport = true",".",[220,843,844],{},"Keep GOA, GVFS, keyring, accounts-daemon, and dconf services enabled.",[220,846,847,848,850],{},"Permit ",[342,849,821],{}," so the build works in current channel constraints.",[207,852,853],{},"I am thankful to the Nix maintainers and GNOME ecosystem here: even when this path is inconvenient, the defaults push us toward safer choices.",[562,855,856],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":344,"searchDepth":458,"depth":458,"links":858},[859,860,865,866,867],{"id":625,"depth":458,"text":626},{"id":647,"depth":458,"text":648,"children":861},[862,863,864],{"id":654,"depth":470,"text":655},{"id":691,"depth":470,"text":692},{"id":731,"depth":470,"text":732},{"id":774,"depth":458,"text":775},{"id":814,"depth":458,"text":815},{"id":828,"depth":458,"text":829},"article","2026-05-31T00:00:00.000Z","How I fixed the 'Location is not mountable' error by enabling GVFS Google support and related GNOME services on NixOS 25.11.",{},{"title":40,"description":870},[874,875,876,877],"nixos","gnome","google-drive","gvfs","Nlie2Digskov25v2_yEhQ_F5egLrDABB1tgjsFRwnuM",{"id":880,"title":56,"author":881,"body":883,"category":577,"client":578,"date":1026,"description":1027,"extension":581,"featured":34,"hidden":34,"image":1028,"meta":1029,"minRead":470,"navigation":184,"ogImage":578,"outcome_headline":1030,"path":57,"repoUrl":1031,"role":586,"seo":1032,"stack":1033,"stem":58,"tags":1037,"team_size":447,"type":577,"year":596,"__hash__":1039},"blog/blog/jp-landing.md",{"name":61,"username":194,"to":195,"avatar":882},{"src":197,"alt":61},{"type":199,"value":884,"toc":1018},[885,887,890,894,897,901,912,916,984,988,991,994,1011,1015],[202,886,829],{"id":828},[207,888,889],{},"Jhey Pi is the official landing site for an aspiring musician. I built it to keep the artist story, Spotify releases, and YouTube videos in one place, with a simple feed that stays easy to scan.",[202,891,893],{"id":892},"context","Context",[207,895,896],{},"The goal was to make a landing site that felt like a proper home for the artist, not just another promo page. The site pulls the latest Spotify releases and YouTube videos into one feed, so the content stays current without turning the page into a manual update burden.",[202,898,900],{"id":899},"constraints","Constraints",[217,902,903,906,909],{},[220,904,905],{},"Keep the experience lightweight and easy to update.",[220,907,908],{},"Merge Spotify releases, artist profile data, and YouTube videos into one feed.",[220,910,911],{},"Support bilingual copy without making the page feel crowded.",[202,913,915],{"id":914},"decisions","Decisions",[917,918,919,935],"table",{},[920,921,922],"thead",{},[923,924,925,929,932],"tr",{},[926,927,928],"th",{},"Decision",[926,930,931],{},"Trade-off",[926,933,934],{},"Chose",[936,937,938,949,962,972],"tbody",{},[923,939,940,944,947],{},[941,942,943],"td",{},"Build a traditional promo site with separate pages for music and video",[941,945,946],{},"More room for content, but harder to keep current",[941,948],{},[923,950,951,954,957],{},[941,952,953],{},"Build a single landing site with a merged, auto-sorted feed",[941,955,956],{},"Easier to scan and maintain; the artist updates in one place",[941,958,959],{},[960,961],"decision-check",{},[923,963,964,967,970],{},[941,965,966],{},"Keep the copy monolingual",[941,968,969],{},"Simpler, but weaker reach for bilingual audiences",[941,971],{},[923,973,974,977,980],{},[941,975,976],{},"Support ES/EN copy",[941,978,979],{},"Better fit for the audience, with a little more content work",[941,981,982],{},[960,983],{},[202,985,987],{"id":986},"outcome","Outcome",[207,989,990],{},"The project shipped as a clean landing site for Jhey Pi with a single content feed, artist profile data, and room for bilingual copy. It also made the update flow simpler: new releases and videos can surface without needing the whole page to be rewritten.",[207,992,993],{},"Feature-wise, the useful pieces were:",[217,995,996,999,1002,1005,1008],{},[220,997,998],{},"Spotify releases and artist profile via Spotify API.",[220,1000,1001],{},"YouTube latest videos via the channel feed.",[220,1003,1004],{},"Auto-sorted, paginated feed with embeds.",[220,1006,1007],{},"Bilingual copy in ES/EN.",[220,1009,1010],{},"ISR caching for the feed endpoint.",[202,1012,1014],{"id":1013},"what-id-do-on-gcp-today","What I'd do on GCP today",[207,1016,1017],{},"If this needed to scale further, I would keep the feed endpoint cached with ISR-style behavior, move the integrations behind a small server layer, and host the front end on Cloud Run or Firebase Hosting with preview environments per pull request.",{"title":344,"searchDepth":458,"depth":458,"links":1019},[1020,1021,1022,1023,1024,1025],{"id":828,"depth":458,"text":829},{"id":892,"depth":458,"text":893},{"id":899,"depth":458,"text":900},{"id":914,"depth":458,"text":915},{"id":986,"depth":458,"text":987},{"id":1013,"depth":458,"text":1014},"2026-05-18T00:00:00.000Z","Official landing site for Jhey Pi, built with Nuxt 4 and Nuxt UI to combine Spotify releases and YouTube videos into a single feed.","/projects/jp-landing.png",{},"Shipped a focused artist landing site with a single feed for music updates and video content","https://github.com/ivanovertime/jp-landing",{"title":56,"description":1027},[1034,1035,1036],"Nuxt","Vue","Tailwind CSS",[1038,577],"nuxt","BWRpSOIbkkTSlj-wkJl21Uq4LTkKkKxd40uHB-ddkC4",{"data":1041,"body":1042},{},{"type":1043,"children":1044},"root",[1045],{"type":1046,"tag":217,"props":1047,"children":1048},"element",{},[1049,1054,1059,1064,1069],{"type":1046,"tag":220,"props":1050,"children":1051},{},[1052],{"type":340,"value":1053},"Laravel",{"type":1046,"tag":220,"props":1055,"children":1056},{},[1057],{"type":340,"value":1058},"NestJS",{"type":1046,"tag":220,"props":1060,"children":1061},{},[1062],{"type":340,"value":1063},"Node.js",{"type":1046,"tag":220,"props":1065,"children":1066},{},[1067],{"type":340,"value":1068},"Express.js",{"type":1046,"tag":220,"props":1070,"children":1071},{},[1072],{"type":340,"value":1073},"FastAPI",{"data":1075,"body":1076},{},{"type":1043,"children":1077},[1078],{"type":1046,"tag":217,"props":1079,"children":1080},{},[1081,1086,1091,1096,1101,1106,1111,1116],{"type":1046,"tag":220,"props":1082,"children":1083},{},[1084],{"type":340,"value":1085},"Vue.js",{"type":1046,"tag":220,"props":1087,"children":1088},{},[1089],{"type":340,"value":1090},"Nuxt.js",{"type":1046,"tag":220,"props":1092,"children":1093},{},[1094],{"type":340,"value":1095},"Angular",{"type":1046,"tag":220,"props":1097,"children":1098},{},[1099],{"type":340,"value":1100},"PrimeVue",{"type":1046,"tag":220,"props":1102,"children":1103},{},[1104],{"type":340,"value":1105},"Inertia.js",{"type":1046,"tag":220,"props":1107,"children":1108},{},[1109],{"type":340,"value":1110},"Filament",{"type":1046,"tag":220,"props":1112,"children":1113},{},[1114],{"type":340,"value":1115},"Livewire",{"type":1046,"tag":220,"props":1117,"children":1118},{},[1119],{"type":340,"value":1120},"Flutter",{"data":1122,"body":1123},{},{"type":1043,"children":1124},[1125],{"type":1046,"tag":207,"props":1126,"children":1127},{},[1128],{"type":340,"value":1129},"Ingeniero full-stack en Valencia, Venezuela. Construyo y recupero plataformas que crecieron más allá de su diseño original — e-commerce, integraciones SAP, pipelines de despliegue. Ahora lo hago sobre Google Cloud.",{"data":1131,"body":1132},{},{"type":1043,"children":1133},[1134],{"type":1046,"tag":207,"props":1135,"children":1136},{},[1137,1139,1144],{"type":340,"value":1138},"Un resumen de las tecnologías que uso y cómo suelo colaborar con equipos. La página ",{"type":1046,"tag":537,"props":1140,"children":1142},{"href":1141},"/es/uses",[1143],{"type":340,"value":1141},{"type":340,"value":1145}," tiene más detalle.",{"data":1147,"body":1148},{},{"type":1043,"children":1149},[1150],{"type":1046,"tag":217,"props":1151,"children":1152},{},[1153,1158,1163,1168,1173,1178],{"type":1046,"tag":220,"props":1154,"children":1155},{},[1156],{"type":340,"value":1157},"Docker",{"type":1046,"tag":220,"props":1159,"children":1160},{},[1161],{"type":340,"value":1162},"Kubernetes (aprendiendo en profundidad)",{"type":1046,"tag":220,"props":1164,"children":1165},{},[1166],{"type":340,"value":1167},"CI/CD (GitLab, GitHub Actions)",{"type":1046,"tag":220,"props":1169,"children":1170},{},[1171],{"type":340,"value":1172},"Proxmox",{"type":1046,"tag":220,"props":1174,"children":1175},{},[1176],{"type":340,"value":1177},"Ubuntu Server",{"type":1046,"tag":220,"props":1179,"children":1180},{},[1181],{"type":340,"value":1182},"Windows Server",{"data":1184,"body":1185},{},{"type":1043,"children":1186},[1187],{"type":1046,"tag":217,"props":1188,"children":1189},{},[1190,1195,1200,1205,1210],{"type":1046,"tag":220,"props":1191,"children":1192},{},[1193],{"type":340,"value":1194},"MySQL",{"type":1046,"tag":220,"props":1196,"children":1197},{},[1198],{"type":340,"value":1199},"PostgreSQL",{"type":1046,"tag":220,"props":1201,"children":1202},{},[1203],{"type":340,"value":1204},"MongoDB",{"type":1046,"tag":220,"props":1206,"children":1207},{},[1208],{"type":340,"value":1209},"Redis",{"type":1046,"tag":220,"props":1211,"children":1212},{},[1213],{"type":340,"value":1214},"BigQuery",1783694870364]