<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>jbngan</title><description>Blog</description><link>https://jbngan.com/</link><language>en</language><item><title>Xin chào</title><link>https://jbngan.com/posts/hello-world/</link><guid isPermaLink="true">https://jbngan.com/posts/hello-world/</guid><description>Bài viết đầu tiên trên jbngan.com.</description><pubDate>Wed, 04 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Xin chào! Đây là bài viết đầu tiên trên blog &lt;strong&gt;jbngan.com&lt;/strong&gt;, nơi mình ghi lại những điều học được và những suy nghĩ linh tinh.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const greet = (name: string): string =&amp;gt; `Xin chào, ${name}!`;
console.log(greet(&quot;thế giới&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Blog chạy bằng Astro và theme Fuwari.&lt;/p&gt;
</content:encoded></item><item><title>Dùng Claude Code để gắn tag và dọn dẹp 1Password</title><link>https://jbngan.com/posts/sap-xep-1password-bang-claude-code/</link><guid isPermaLink="true">https://jbngan.com/posts/sap-xep-1password-bang-claude-code/</guid><description>Kinh nghiệm dùng Claude Code và 1Password CLI để phân loại hơn 700 mật khẩu, gắn tag tự động và gom các tài khoản cần xóa một cách an toàn.</description><pubDate>Wed, 04 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sau nhiều năm dùng LastPass rồi chuyển sang 1Password, kho mật khẩu của mình có hơn 700 item. Tag mang theo từ LastPass thì lộn xộn: ngân hàng nằm trong &quot;My Sites&quot;, &lt;code&gt;usps.com&lt;/code&gt; lại thuộc &quot;Business&quot;, và item nào cũng dính thêm một tag kiểu &lt;code&gt;Imported November 1 2026 09:08:54&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Gắn tag lại bằng tay cho 700 item thì mình chịu. Vì vậy mình nhờ Claude Code làm, và bài này ghi lại cách làm cùng những điều mình rút ra, nhất là về &lt;strong&gt;an toàn&lt;/strong&gt;, vì đây là kho mật khẩu chứ không phải một thư mục code bình thường.&lt;/p&gt;
&lt;h2&gt;Kết quả&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hơn 700 item được phân loại vào khoảng 55 tag tiếng Anh, có phân cấp như &lt;code&gt;Finance/Banking&lt;/code&gt;, &lt;code&gt;Tech/Hosting&lt;/code&gt;, &lt;code&gt;Shopping/Fashion&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tag riêng cho từng công ty mình làm hoặc từng làm, để tách tài khoản công việc khỏi tài khoản cá nhân.&lt;/li&gt;
&lt;li&gt;Item không đủ thông tin để phân loại được gắn &lt;code&gt;Needs Review&lt;/code&gt; để mình tự xem.&lt;/li&gt;
&lt;li&gt;Khoảng 80 tài khoản của một công ty cũ được gắn &lt;code&gt;zDeleting&lt;/code&gt; để mình rà và xóa dần.&lt;/li&gt;
&lt;li&gt;Mật khẩu, username, ghi chú và custom field &lt;strong&gt;không bị thay đổi&lt;/strong&gt;. Claude kiểm tra điều này trên từng item.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Chuẩn bị&lt;/h2&gt;
&lt;h3&gt;1. Cài 1Password CLI&lt;/h3&gt;
&lt;p&gt;Trên Windows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;winget install AgileBits.1Password.CLI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sau đó vào app 1Password, chọn &lt;strong&gt;Settings → Developer&lt;/strong&gt; và bật &lt;strong&gt;Integrate with 1Password CLI&lt;/strong&gt;. Khi đã bật, lệnh &lt;code&gt;op&lt;/code&gt; xác thực qua app 1Password, có thể bằng Windows Hello hay vân tay. Bạn không cần dán session token vào terminal, nên Claude Code cũng không bao giờ thấy mật khẩu chính của bạn.&lt;/p&gt;
&lt;h3&gt;2. Xuất danh sách item, chỉ lấy metadata&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;op item list --format json &amp;gt; items.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Điểm quan trọng: &lt;code&gt;op item list&lt;/code&gt; &lt;strong&gt;chỉ trả về metadata&lt;/strong&gt; như tiêu đề, URL, tag, danh mục và vault. Mật khẩu không nằm trong đó. Trường &lt;code&gt;additional_information&lt;/code&gt; thường chứa username, nên vẫn nên coi file này là dữ liệu nhạy cảm.&lt;/p&gt;
&lt;p&gt;Đừng dùng tính năng export đầy đủ của app (&lt;code&gt;.1pux&lt;/code&gt; hay CSV) cho việc này, vì các file đó có chứa mật khẩu.&lt;/p&gt;
&lt;h2&gt;Quy trình làm việc với Claude Code&lt;/h2&gt;
&lt;p&gt;Câu lệnh đầu tiên của mình rất đơn giản:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;tôi export items.json từ 1Password, hãy đọc và đề xuất Tags, gắn tag và giúp mình update vào 1password. Tag phải là tiếng anh.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Phần đáng nói là cách Claude Code xử lý yêu cầu này.&lt;/p&gt;
&lt;h3&gt;Bước 1: Xem cấu trúc trước khi đọc dữ liệu&lt;/h3&gt;
&lt;p&gt;Claude không mở file ra đọc ngay. Nó viết một script nhỏ để in &lt;strong&gt;cấu trúc&lt;/strong&gt; của JSON (tên khóa, kiểu dữ liệu, số lượng) mà không in giá trị nào. Nhờ đó nó biết đây là output của &lt;code&gt;op item list&lt;/code&gt;, có 740 item, phần lớn là &lt;code&gt;LOGIN&lt;/code&gt;, và tag cũ được phân bố ra sao.&lt;/p&gt;
&lt;p&gt;Sau đó nó tạo một file chỉ mục gọn với tiêu đề, danh mục, domain và tag cũ, đồng thời &lt;strong&gt;cố ý bỏ qua &lt;code&gt;additional_information&lt;/code&gt;&lt;/strong&gt; vì trường đó chứa username. Nó chỉ dùng phần thông tin này để phân loại.&lt;/p&gt;
&lt;h3&gt;Bước 2: Đề xuất hệ tag&lt;/h3&gt;
&lt;p&gt;Claude đề xuất một hệ tag phân cấp, vì 1Password hiển thị tag có dấu &lt;code&gt;/&lt;/code&gt; thành dạng cây ở sidebar:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nhóm&lt;/th&gt;
&lt;th&gt;Ví dụ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Finance&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Finance/Banking&lt;/code&gt;, &lt;code&gt;Finance/Credit&lt;/code&gt;, &lt;code&gt;Finance/Payments&lt;/code&gt;, &lt;code&gt;Finance/Mortgage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Business/Ecommerce&lt;/code&gt;, &lt;code&gt;Business/Shipping&lt;/code&gt;, &lt;code&gt;Business/Print on Demand&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tech&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Tech/Hosting&lt;/code&gt;, &lt;code&gt;Tech/Domains &amp;amp; SSL&lt;/code&gt;, &lt;code&gt;Tech/Cloud&lt;/code&gt;, &lt;code&gt;Tech/Dev&lt;/code&gt;, &lt;code&gt;Tech/Monitoring&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopping&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Shopping&lt;/code&gt;, &lt;code&gt;Shopping/Fashion&lt;/code&gt;, &lt;code&gt;Shopping/Electronics&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cá nhân&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Government&lt;/code&gt;, &lt;code&gt;Health&lt;/code&gt;, &lt;code&gt;Insurance&lt;/code&gt;, &lt;code&gt;Travel&lt;/code&gt;, &lt;code&gt;Education&lt;/code&gt;, &lt;code&gt;Entertainment&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Đặc biệt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Notes&lt;/code&gt;, &lt;code&gt;Needs Review&lt;/code&gt;, và tag riêng cho từng công ty&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Việc phân loại dựa vào domain: một bảng quy tắc ánh xạ domain sang tag, trong đó &lt;strong&gt;domain cụ thể nhất được ưu tiên&lt;/strong&gt;. Ví dụ &lt;code&gt;ship.amazon.com&lt;/code&gt; được gắn &lt;code&gt;Business/Shipping&lt;/code&gt;, &lt;code&gt;signin.aws.amazon.com&lt;/code&gt; được gắn &lt;code&gt;Tech/Cloud&lt;/code&gt;, còn &lt;code&gt;amazon.com&lt;/code&gt; là &lt;code&gt;Shopping&lt;/code&gt;. Những item mà URL bị thiếu hoặc không khớp với tiêu đề thì được chỉ định thủ công, hoặc gắn &lt;code&gt;Needs Review&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Bước 3: Xem trước, chưa ghi gì&lt;/h3&gt;
&lt;p&gt;Trước khi động vào 1Password, Claude tạo một file &lt;code&gt;preview.csv&lt;/code&gt; gồm tiêu đề, domain, tag mới và tag cũ để mình mở bằng Excel, kèm bảng thống kê số item theo từng tag.&lt;/p&gt;
&lt;p&gt;Nó cũng hỏi mình một quyết định mà nó không nên tự quyết: &lt;strong&gt;xử lý tag cũ thế nào&lt;/strong&gt;. Lý do là &lt;code&gt;op item edit --tags&lt;/code&gt; &lt;strong&gt;thay toàn bộ&lt;/strong&gt; tag chứ không cộng thêm. Mình chọn thay hết, nhưng giữ các tag tên công ty vì chúng giúp phân biệt tài khoản của từng công ty. Mình còn bổ sung một quy tắc: tài khoản Etsy của công ty thì gắn tag công ty kèm &lt;code&gt;Business/Ecommerce&lt;/code&gt;, không để chung với &lt;code&gt;Shopping&lt;/code&gt; cá nhân. Claude cập nhật lại và cho mình xem preview lần nữa.&lt;/p&gt;
&lt;h3&gt;Bước 4: Kiểm tra lại dữ liệu thật trước khi ghi&lt;/h3&gt;
&lt;p&gt;Khi mình đồng ý, Claude chưa ghi ngay mà lấy lại danh sách hiện tại từ vault và so với file export. Nó phát hiện vault chỉ còn 739 item thay vì 740: một item rác (dòng tiêu đề CSV bị import nhầm thành item &lt;code&gt;name&lt;/code&gt; / &lt;code&gt;url&lt;/code&gt;) đã bị xóa sau khi export. Nó bỏ qua item đó, và chỉ sửa những item có &lt;code&gt;version&lt;/code&gt; vẫn khớp với lúc export. Cách này tránh ghi đè lên một thay đổi mình vừa làm trong app.&lt;/p&gt;
&lt;h3&gt;Bước 5: Chạy thử, và dừng lại khi có điều bất thường&lt;/h3&gt;
&lt;p&gt;Claude chạy thử trên 5 item đại diện, gồm một login thường, một identity, một tag có ký tự &lt;code&gt;&amp;amp;&lt;/code&gt; và hai secure note. Với mỗi item, script lấy hash toàn bộ nội dung trước và sau khi sửa (bỏ qua tag, version và thời gian cập nhật) để chắc chắn &lt;strong&gt;chỉ có tag thay đổi&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ba item đạt, còn hai secure note thì báo hash bị thay đổi. Claude &lt;strong&gt;dừng lại, không chạy tiếp cho toàn bộ&lt;/strong&gt;, và tìm nguyên nhân:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;So hai lần &lt;code&gt;op item get&lt;/code&gt; liên tiếp thì giống hệt nhau, nên hash không tự dao động.&lt;/li&gt;
&lt;li&gt;Dùng &lt;code&gt;op item edit --dry-run&lt;/code&gt; cũng không tái hiện được.&lt;/li&gt;
&lt;li&gt;Sửa thật một note khác, rồi so &lt;strong&gt;từng đường dẫn trường&lt;/strong&gt; (chỉ in tên trường và độ dài, không in giá trị). Kết quả cho thấy chỉ có trường &lt;code&gt;additional_information&lt;/code&gt; thay đổi, từ không có thành có. Đây là dòng tóm tắt mà 1Password tự sinh khi lưu một item trước đó chưa có. Username, password, ghi chú và custom field đều giữ nguyên.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sau đó script được chỉnh để chấp nhận &lt;strong&gt;đúng thay đổi đó&lt;/strong&gt;: &lt;code&gt;additional_information&lt;/code&gt; được phép xuất hiện nếu trước đó chưa có, còn mọi khác biệt khác vẫn bị báo lỗi.&lt;/p&gt;
&lt;h3&gt;Bước 6: Chạy toàn bộ và đối chiếu&lt;/h3&gt;
&lt;p&gt;Script chạy nền với 4 luồng, ghi log để chạy lại tiếp được nếu bị ngắt giữa chừng, và tự thử lại khi gặp giới hạn tần suất. Kết quả là 733/733 item thành công, không có lỗi.&lt;/p&gt;
&lt;p&gt;Cuối cùng, Claude đọc lại toàn bộ vault và so với kế hoạch:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tag mismatches: 0
items without tags: 0
distinct tags live: 56 | outside plan: []
version bumps != 1: Counter({1: 739})
title changed: 0
urls changed: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Phần thêm: gom tài khoản công ty cũ để xóa&lt;/h2&gt;
&lt;p&gt;Sau đó mình nhờ:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;thêm tag zDeleting và gom tất cả items liên quan đến [công ty cũ] để mình check và xóa&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lúc này Claude mới tìm cả trong username, nhưng chỉ in ra tên công ty khớp được chứ không in username. Kết quả là khoảng 80 item, trong khi chỉ khoảng 20 item có sẵn tag công ty. Phần lớn là tài khoản đăng ký bằng email công ty mà tiêu đề không hề nhắc đến công ty.&lt;/p&gt;
&lt;p&gt;Lần này Claude &lt;strong&gt;chỉ thêm&lt;/strong&gt; tag &lt;code&gt;zDeleting&lt;/code&gt; và giữ nguyên các tag cũ, không xóa item nào. Tên bắt đầu bằng &lt;code&gt;z&lt;/code&gt; để tag này nằm cuối sidebar. Claude cũng nhắc mình vài điều mình suýt bỏ qua:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Một số tài khoản trong nhóm đó là ngân hàng hay hồ sơ visa, chỉ tình cờ đăng ký bằng email công ty. Những item này cần xem kỹ trước khi xóa.&lt;/li&gt;
&lt;li&gt;Xóa item trong 1Password &lt;strong&gt;không đóng tài khoản&lt;/strong&gt; ở dịch vụ đó. Nếu mình không còn truy cập email công ty cũ, mất mật khẩu đồng nghĩa với việc không đặt lại được nữa. Với tài khoản còn dùng, nên đổi email trước rồi mới xóa.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Những điều mình rút ra&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Dùng &lt;code&gt;op item list&lt;/code&gt; thay vì export đầy đủ.&lt;/strong&gt; Muốn gắn tag thì chỉ cần metadata. Claude Code không cần thấy mật khẩu để làm việc này.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Luôn xem preview trước khi ghi.&lt;/strong&gt; Một file CSV mở bằng Excel giúp rà nhanh hơn rất nhiều so với đọc log trong terminal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nhớ rằng &lt;code&gt;--tags&lt;/code&gt; thay toàn bộ tag.&lt;/strong&gt; Muốn thêm tag thì phải truyền cả tag cũ lẫn tag mới.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chạy thử vài item trước.&lt;/strong&gt; Lỗi &lt;code&gt;additional_information&lt;/code&gt; chỉ xuất hiện với secure note. Nếu chạy cả 700 item ngay từ đầu, mình đã không biết nguyên nhân nằm ở đâu.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kiểm tra bằng hash, đừng in giá trị.&lt;/strong&gt; Cách này xác nhận dữ liệu không đổi mà mật khẩu không bao giờ xuất hiện trong terminal hay trong log.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Để AI dừng lại khi có điều bất thường.&lt;/strong&gt; Điểm mình đánh giá cao nhất là Claude Code không cố chạy tiếp khi kết quả kiểm tra không khớp, mà dừng lại tìm hiểu rồi mới đi tiếp.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Có thể gặp &lt;code&gt;authorization timeout&lt;/code&gt;.&lt;/strong&gt; Sau một đợt chạy dài, &lt;code&gt;op&lt;/code&gt; thỉnh thoảng báo lỗi này. Mở khóa lại app 1Password và phê duyệt là chạy tiếp được.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dọn file tạm sau khi xong.&lt;/strong&gt; File &lt;code&gt;items.json&lt;/code&gt; chứa tiêu đề, URL và username, nên hãy xóa hoặc cất vào nơi an toàn khi không cần nữa.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Nếu kho mật khẩu của bạn cũng đang lộn xộn sau nhiều năm, bạn có thể thử cách này. Chỉ cần nhớ: &lt;strong&gt;xem trước, chạy thử, kiểm tra rồi mới làm hàng loạt.&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>