Quireblog - Tự "cất nhà" sau 20 năm ở trọ
- Ngày 13 tháng 7 năm 2009, Yahoo 360 đóng cửa. Tôi nhớ cái cảm giác mở blog lên, thấy nguyên một dãy entry mình viết suốt mấy năm trời, và biết rằng trong vài tuần nữa tất cả sẽ biến mất. Yahoo có cho cái nút "chuyển nhà" sang 360 Plus, nhưng ai từng dùng cũng biết nó là một cái nhà xập xệ hơn, thiếu cả trang Home, dột chỗ này lủng chỗ kia. Mà rồi 360 Plus cũng chết. Đổi thành Yahoo Blog. Rồi Yahoo Blog cũng đóng cửa nốt vào năm 2013. Đám entry của tôi, của hàng triệu người Việt khác, bốc hơi sạch.
Đó không phải lần đầu, và cũng không phải lần cuối. Nhưng nó là lần buồn nhất, vì lúc đó tôi còn ngây thơ đủ để tin rằng cái gì mình viết ra trên mạng thì nó là của mình, thời đó làm gì có khái niệm backup hay một công ty sẽ dẹp đi một sản phẩm nào đó của họ.
Mười sáu năm sau, tôi ngồi gõ lệnh npm run dev trên con Mac Studio, mở localhost:3000, và nhìn cái blog do chính tay tôi dựng lên hiện ra. Không phải WordPress. Không phải Ghost. Không phải Substack hay Medium hay bất cứ cái nền tảng nào mà một ngày đẹp trời người ta có thể bán đi, đóng lại, hoặc nhồi quảng cáo vào giữa bài viết của tôi hay phải trải qua các bản cập nhật với hàng tá tính năng tôi không cần thiết. Một cái blog mà toàn bộ mã nguồn nằm trong tay tôi, MIT license, ai muốn fork về xài cũng được. Tôi đặt tên nó là Quire.
Bài này tôi viết để kể chuyện đó. Chuyện một người không rành code tự làm ra cái platform blog cho riêng mình, và tại sao tôi nghĩ thời điểm để làm chuyện này chưa bao giờ chín như bây giờ.
Một đời đi ở trọ
Tôi viết blog từ thời Xanga. Hồi đó mạng còn dial-up, nghe tiếng modem rít lên là biết sắp được kết nối với thế giới. Xanga rồi tới Yahoo 360, cái thời hoàng kim mà ai có Internet ở Việt Nam gần như đều có một cái blog 360, viết entry thật dài, thật "deep", để câu view và câu comment. Cộng đồng blogger Việt thời đó sống động kinh khủng. Người ta viết nhật ký, viết truyện, cãi nhau, yêu nhau, qua những cái blog đó.
Rồi tới khúc đứt cáp quang. Ai sống ở Việt Nam giai đoạn đó đều nhớ. Cái tuyến cáp AAG cứ vài tháng lại đứt một lần, mỗi lần đứt là Internet quốc tế chậm như rùa bò, vô được Yahoo 360 thôi cũng đã là một sự kiên nhẫn. Chính trong cái khoảng trống đó mà các nền tảng nội địa mọc lên. Tôi nhảy qua một cái blog Việt tên Yobanbe, về sau nó bán cho Zing, thành Zing Me. Lại một lần dọn nhà. Lại một lần đám chữ của mình bị bứng đi nơi khác mà không hỏi han gì. Dù rằng tôi thú thật chả có gì giá trị, tôi thì viết dở tệ, nó cũng chỉ là một thú vui, nhưng tôi luôn xem chúng như tài sản vô giá của bản thân.

Nhìn lại hai mươi năm đó, tôi thấy một sợi chỉ rất rõ. Mọi cái nhà tôi từng ở, tôi đều không sở hữu. Yahoo 360 chết vì Yahoo quyết định nó không sinh lời. Yobanbe bị bán. Tumblr bị thâu tóm rồi bỏ bê. Mỗi lần như vậy, tôi mất một phần ký ức viết lách của mình, hoặc ít nhất là mất cái cảm giác yên tâm rằng nó sẽ còn đó ngày mai. Tôi cứ đi ở trọ hết nhà này tới nhà khác, và lần nào chủ nhà cũng có quyền đuổi tôi đi.
WordPress khá hơn ở chỗ tôi tự host, dữ liệu là của tôi. Nhưng WordPress có cái gánh nặng riêng của nó. Một con database MySQL phải chăm. Đống plugin lúc nào cũng có lỗ hổng bảo mật. Cache tầng này tầng kia. PHP. Mỗi lần update là một lần hồi hộp coi có có vấn đề gì không. Tôi điều hành hơn chục cái site WordPress cho công việc kinh doanh rồi, tôi quá hiểu cái mệt đó. Riêng cái blog cá nhân, chỗ tôi viết về mấy chuyện linh tinh trong đầu, tôi không muốn nó là thêm một cái server phải lo.
Tại sao là bây giờ
Cách đây ba năm, ý tưởng "tự code một cái platform blog" với một người như tôi là chuyện viễn tưởng. Tôi không có nền tảng lập trình. Tôi không phải là dân kỹ thuật. Cái gần nhất với code mà tôi đụng tới là sửa vài dòng CSS trong WordPress, mà mỗi lần sửa là một lần cầu trời.
Cái thay đổi mọi thứ là Claude Code, và rộng hơn là cái mà người ta hay gọi là vibe coding. Tôi không ngồi viết từng dòng TypeScript. Tôi mô tả thứ tôi muốn, tôi tranh luận về kiến trúc, tôi đọc cái nó viết ra và hỏi tại sao chỗ này làm vậy, tôi yêu cầu sửa khi thấy không ổn. Vai trò của tôi không phải thợ gõ phím. Vai trò của tôi là người quyết định. Người biết mình muốn gì, và đủ gu để biết khi nào cái máy làm sai ý, tôi biết thế nào là hiệu quả, thế nào là đủ, thế nào là vừa ý mình.
Đây là chỗ tôi muốn nói thẳng một điều mà nhiều người hiểu lầm về vibe coding. Người ta tưởng AI viết code thay mình thì sản phẩm sẽ là một mớ hổ lốn, chắp vá, không có linh hồn. Sự thật ngược lại, nếu anh biết mình muốn gì. Cái AI giỏi là biến ý định thành code chạy được. Cái nó không làm thay anh được là cái gu, là quyết định thẩm mỹ, là biết khi nào nói "không, cái này xấu, làm lại". Quire có 112 commit và đi qua mười mấy bản, không phải vì tôi may mắn gõ một câu lệnh rồi ra ngay. Mà vì tôi tranh luận với nó như tranh luận với một người cộng sự kỹ thuật giỏi nhưng không có gu, còn gu thì tôi giữ. Và một khi cái rào cản "phải biết code" bị gỡ bỏ, câu hỏi không còn là "tôi có làm được không" mà là "tôi muốn cái blog của tôi trông như thế nào, vận hành như thế nào, nếu không có bất kỳ giới hạn nào". Đó là một câu hỏi sướng. Lần đầu tiên sau hai mươi năm tôi được hỏi câu đó mà không phải gói gọn trong cái khuôn của một nền tảng do người khác dựng sẵn.
Bộ ruột
Để tôi kể cái nó được làm bằng gì, cho những ai quan tâm phần kỹ thuật.
Nền là Next.js 16 với App Router, React 19, TypeScript ở chế độ strict. Tôi chọn strict ngay từ đầu vì tôi không phải dân code, nên tôi cần cái máy bắt lỗi giùm tôi càng nhiều càng tốt. TypeScript strict là một người gác cổng khó tính, và với người như tôi thì càng khó tính càng tốt.
Trình soạn thảo dùng TipTap 3, viết bằng Markdown. Khi tôi upload một tấm hình, thư viện sharp tự động đẻ ra các bản responsive, encode sẵn AVIF và WebP ngay lúc lưu, nhưng vẫn giữ nguyên bản gốc không đụng tới. Trang công khai dùng thẻ <picture> để trả về định dạng nhẹ nhất mà trình duyệt hỗ trợ, chỉ khi các biến thể đã sẵn sàng. Tôi từng bị ám ảnh chuyện tối ưu hình ảnh trên WordPress, phải cài plugin này plugin nọ. Ở đây nó là một phần của hệ thống, không phải miếng vá dán thêm.
Mỗi bài viết có một cái máy thời gian lưu ba phiên bản gần nhất. Lỡ tay sửa hỏng thì lùi lại được. Đăng nhập dùng NextAuth v5, qua OAuth của Google hoặc GitHub, và chỉ đúng một email được phép vào trang quản trị, là email của tôi. Đây là blog một người, không cần hệ thống phân quyền phức tạp làm gì.
Về giao diện, tôi làm sáu bảng màu, mỗi bảng có cả chế độ sáng và tối, và tùy chỉnh được tới từng màu. Khách đọc blog có thể tự đổi bảng màu, tự chọn sáng tối hoặc để theo giờ trong ngày. Cái blog cũng cài được lên màn hình chính của iPhone hay Android như một app (PWA), mở ra chạy toàn màn hình. Nhưng tôi cố tình không làm service worker, không làm offline. Vì một cái blog không cần đọc offline, và mỗi tính năng thừa là một chỗ để hỏng.
Phần mà tôi đổ nhiều mồ hôi nhất, ngược đời thay, lại là cache. Trang công khai được cache theo kiểu ISR, khách vào là có HTML dựng sẵn, nhanh. Nhưng mỗi lần tôi lưu ở trang quản trị, hệ thống chủ động xóa cache của đúng những trang bị ảnh hưởng, qua một chỗ duy nhất trong code (src/lib/revalidate.ts). Đăng bài mới thì làm mới trang danh sách, sửa bài thì làm mới luôn trang của bài đó, đổi cài đặt thì xóa cache cả site. Mỗi lần xóa là một tập hợp rộng hơn cái thực sự thay đổi, để chắc chắn không bao giờ bị sót. Nên cuối cùng phải bẩm bụng chuyển từ no-database sang PostgreSQL, đỡ đau đầu hẳn.
Một chi tiết nhỏ mà tôi thích: cả cái blob store lẫn các hàm xử lý đều đặt ở Singapore (sin1), vì đó là chỗ gần Việt Nam nhất, gần độc giả của tôi nhất. Khi tôi mở mã nguồn cho người khác, tôi ghi rõ trong tài liệu rằng hãy đổi cái này sang vùng gần anh, đừng để mặc định theo tôi. Vì cái blog này làm cho tôi, nhưng tôi muốn nó cũng làm được cho bất kỳ ai, ai cũng xài được.
Phần thú vị nhất: cái blog mà AI vận hành được
Tới đây mới là chỗ tôi tâm đắc nhất, và là lý do thật sự khiến tôi nghĩ cái blog này khác hẳn mọi thứ tôi từng dùng.
Vì kiến trúc đơn giản tới mức chỉ là đọc ghi file JSON vào một cái kho, nên nó là kiến trúc mà một con AI có thể vận hành trọn vẹn. Tôi dựng một MCP server ngay trên blog (manhhung.me/api/mcp). MCP là cái giao thức để AI nói chuyện với công cụ bên ngoài. Nghĩa là tôi có thể ngồi nói chuyện với Claude, bảo nó "đăng bài này lên blog giùm tôi", và nó đăng thật. Không cần tôi mở trang quản trị, copy paste, bấm nút. Bài luận về phim Mai tôi viết hồi trước, tôi đẩy thẳng lên manhhung.me qua cái MCP server đó, rồi tôi kêu nó unpublish mà không cần tôi nhúng tay vào.
Đây không phải tính năng làm cho vui. Đây là cái triết lý nền tảng của toàn bộ dự án. Tôi muốn một cái blog mà công việc tay chân (định dạng, tối ưu hình, dọn dẹp, xuất bản) thì máy lo, còn tôi chỉ lo cái duy nhất mà máy không lo được: viết cái gì, và viết hay tới đâu. Cái blog tự nó cũng có thể được dựng lên bởi AI. Trong tài liệu hướng dẫn, tôi viết hẳn một mục: nếu anh lười click qua Vercel, cứ đưa cho một con AI agent cái repo này, đưa nó token, bảo nó fork, tạo project, dựng blob store, đặt biến môi trường, deploy, rồi trả về cái link. Phần duy nhất AI làm chưa được là tạo cái OAuth app, vì cái đó cần đăng nhập tài khoản Google của anh.
Tôi nghĩ đây là hình hài của xuất bản trong vài năm tới. Không phải AI viết thay người, cái đó vừa chán vừa vô hồn. Mà là AI làm sạch toàn bộ cái khâu vận hành rườm rà giữa "tôi có một ý tưởng" và "ý tưởng đó nằm trên mạng cho người ta đọc". Khoảng giữa đó, suốt hai mươi năm, lúc nào cũng có ma sát. Phải đăng nhập. Phải chờ build. Phải sửa ảnh. Phải lo cái theme. Quire là cố gắng của tôi để xóa cái ma sát đó về gần số không, để thứ duy nhất còn lại là chữ.
Tôi đã thử và bỏ những gì
Để công bằng, tôi không phải nhảy thẳng vào tự code. Tôi có xem xét mấy lựa chọn có sẵn trước.
Ghost là cái tôi cân nhắc lâu nhất. Nó đẹp, nó tập trung vào viết, nó có hệ sinh thái tốt. Nhưng Ghost vẫn là một cái server phải nuôi, vẫn có database, và quan trọng hơn, nó là nhà của người khác. Tôi tùy biến được tới một mức rồi đụng tường. Bởi vì Ghost tập trung vào tệp khách hàng riêng biệt, không phải tôi.
Astro và mấy bộ tạo trang tĩnh thì ngược lại, nhẹ và nhanh, nhưng có một thứ tôi không chịu nổi: mỗi lần đăng bài là phải build lại cả site. Với người viết ngẫu hứng như tôi, sửa một câu, thêm một tấm hình, mà phải chờ build rồi deploy, là một cái ma sát giết chết hứng viết. Tôi muốn bấm đăng là nó lên, ngay lập tức, như hồi Yahoo 360. Vercel Blob cho tôi đúng cái đó: xuất bản tức thì, không build lại.
Cuối cùng tôi nhận ra mọi lựa chọn có sẵn đều bắt tôi đánh đổi một thứ tôi không muốn đổi. Hoặc là sở hữu, hoặc là sự đơn giản, hoặc là tốc độ xuất bản, hoặc là khả năng cho AI vận hành. Không cái nào cho tôi đủ cả bốn. Nên tôi tự làm, và lần này tôi không phải đánh đổi gì.
Vết thương cũ, lời giải mới
Quay lại cái ngày Yahoo 360 đóng cửa. Cái nút "chuyển nhà sang 360 Plus" đó, tôi nhớ mãi, vì nó là biểu tượng cho toàn bộ sự bất lực. Người ta cho anh viết, cho anh xây dựng cả một cộng đồng, rồi một ngày người ta đổi ý, và lựa chọn duy nhất của anh là dọn sang cái nhà mà người ta chỉ định, hoặc mất trắng.
Quire là câu trả lời cho cái nút đó, mười sáu năm sau.
Mã nguồn là MIT, mở hoàn toàn, ai cũng fork về tự chạy được, không cần ghi công tôi. Nội dung tôi viết thì bản quyền của tôi, không nằm trong cái repo đó, không ai bê đi được. Hai lớp tách bạch: phần mềm mở cho tất cả mọi người, còn chữ của tôi là của tôi. Nếu một ngày Vercel chết, hay tăng giá, hay làm điều gì tôi không thích, tôi bê cái blob đi chỗ khác, dựng lại trong một buổi chiều. Không có ai cầm chìa khóa nhà tôi ngoài tôi.
Tôi không nghĩ mình làm ra cái gì cách mạng. Có cả ngàn cách dựng blog, và cái của tôi chỉ hợp với một người: tôi. Nhưng đó mới là điểm. Lần đầu tiên trong hai mươi năm, tôi có một cái blog hợp với đúng mình, không phải cái khuôn của ai khác, không phải nhà trọ của ai khác. Một thằng không biết code làm được chuyện đó, vào năm 2026, với một con AI ngồi cạnh làm thợ. Cái rào cản kỹ thuật từng cao ngất, giờ thấp tới mức bước qua được.
Cái blob ở Singapore vẫn đang chạy. Mỗi lần tôi đăng một bài, nó ghi thêm một file JSON. Lần này tôi biết, cái file đó sẽ còn đó ngày mai, vì cái khóa nằm trong túi tôi.
Cái tên Quire, nếu anh tò mò, là một từ trong nghề đóng sách. Đó là tập giấy gấp đôi lồng vào nhau, đơn vị nhỏ nhất mà người ta gom lại để khâu thành một cuốn sách. Tôi thích nó vì đó đúng là thứ tôi muốn làm. Không phải một nền tảng to tát. Chỉ là cái đơn vị nhỏ để gom chữ lại thành thứ giữ được.
Hôm nay tôi đăng bài này, kể về chính cái thứ vừa đăng nó lên. Dĩ nhiên bạn có thể xem qua và tải về dùng nếu thấy thích.
.
