Một bộ công cụ bình tĩnh để tự bảo vệ khi nghi có deepfake.

Không cần đăng ký. Không gửi dữ liệu lên server. Bạn làm 3 việc: làm theo checklist, ghi lại bằng chứng (chỉ metadata), rồi xuất một gói báo cáo dạng JSON để chia sẻ với người tin cậy hoặc cơ quan phù hợp.

Mở bộ công cụ
Mặc định lưu trên thiết bị của bạn · Không hứa “phát hiện” tự động · Có thể chạy offline sau khi tải trang

Bắt đầu nhanh (30 giây)

Nếu bạn đang lo lắng ngay lúc này, hãy làm theo thứ tự tối thiểu dưới đây.

1) Dừng lan truyền
Không chia sẻ lại. Chụp lại bằng chứng một cách có kiểm soát.
2) Ghi lại “điểm neo”
Link nguồn, thời gian, tài khoản, mô tả ngắn. Không tải file lên trang này.
3) Xuất báo cáo
Xuất JSON để gửi cho người tin cậy hoặc lưu trữ an toàn.
Chỉ xóa dữ liệu trong trình duyệt này (localStorage). Không thể thu hồi nội dung đã lan truyền ở nơi khác.

Vấn đề ngoài đời

Một video/ảnh/giọng nói bị giả mạo có thể xuất hiện bất ngờ, gây hoảng loạn, ảnh hưởng công việc, gia đình, danh dự, hoặc an toàn cá nhân. Trong khoảnh khắc đó, điều khó nhất là biết “làm gì trước”.

Bạn có thể bị thúc ép phản ứng ngay
Nhắn tin dồn dập, đe dọa, tống tiền, hoặc gây áp lực công khai.
Bằng chứng dễ bị mất
Bài đăng bị xóa, tài khoản đổi tên, link thay đổi, file bị chuyển tiếp.
Sai một bước, mọi thứ phức tạp hơn
Chia sẻ lại vô tình khuếch đại. Tố cáo thiếu thông tin khiến xử lý chậm.

Cách hoạt động (3 bước)

Một quy trình gọn, không hứa hẹn vượt khả năng. Mục tiêu là bình tĩnh, rõ ràng, và có thể bàn giao.

1 Checklist
Đi theo từng bước: an toàn trước, sau đó mới đến bằng chứng và báo cáo.
2 Evidence (metadata)
Ghi lại “dấu vết” hợp lệ: link, thời gian, nền tảng, tài khoản, mô tả, hash tùy chọn.
3 Export JSON
Xuất gói báo cáo để lưu trữ hoặc chia sẻ cho người có trách nhiệm.

Lưu ý: Bộ công cụ này không “tự động phát hiện deepfake”. Nó giúp bạn xử lý đúng trình tự và giữ bằng chứng rõ ràng.

Bộ công cụ

Chọn một tab, hoàn thành từng phần. Bạn có thể lưu cục bộ và quay lại sau.

Đánh dấu các bước bạn đã làm. Mục tiêu: an toàn trước, lan truyền sau.

0/0 đã hoàn thành

Ghi lại dấu vết. Không tải file lên trang này. Nếu bạn có file, hãy lưu ở nơi an toàn và chỉ ghi metadata.

Hash tùy chọn (nếu bạn có file)
Nếu bạn có file gốc hoặc bản tải về, bạn có thể tự tạo SHA-256 hash offline bằng công cụ hệ điều hành, rồi dán vào đây. Hash giúp xác định “đúng file đó”.

macOS/Linux: shasum -a 256 FILE
Windows: certutil -hashfile FILE SHA256

Lưu evidence
Lưu vào thiết bị của bạn để không mất khi tải lại trang.

Dữ liệu lưu bằng localStorage. Không gửi đi đâu.

Xem trước báo cáo JSON. Bạn có thể tải xuống, hoặc copy vào clipboard. Hãy cân nhắc ẩn/loại bỏ dữ liệu nhạy cảm trước khi chia sẻ.


          

Gói JSON có timestamp, phiên bản schema, checklist status, evidence, và các trường mô tả. Không chứa file media.

FAQ

thì fill vào đây const ldFaq = document.getElementById("ldFaq"); if(!ldFaq) return; const faqLd = { "@context":"https://schema.org", "@type":"FAQPage", "mainEntity": FAQ.map(f => ({ "@type":"Question", "name": (f.q[lang] || f.q.vi || f.q.en), "acceptedAnswer": { "@type":"Answer", "text": (f.a[lang] || f.a.vi || f.a.en) } })) }; ldFaq.textContent = JSON.stringify(faqLd); }; // ========= RENDER ========= const renderChecklist = (lang) => { const wrap = document.getElementById("checklist"); if(!wrap) return; const st = getStore(); wrap.innerHTML = ""; CHECKS.forEach((c) => { const label = (c[lang] || c.vi || c.en); const row = document.createElement("label"); row.className = "check"; row.innerHTML = ` ${label.t} ${label.d} `; wrap.appendChild(row); }); wrap.onchange = (e) => { const el = e.target; if(!(el instanceof HTMLInputElement)) return; const id = el.getAttribute("data-check"); if(!id) return; const s2 = getStore(); s2.checklist = s2.checklist || {}; s2.checklist[id] = !!el.checked; setStore(s2); updateProgress(); refreshPreview(); }; updateProgress(); }; const updateProgress = () => { const st = getStore(); const total = CHECKS.length; const done = CHECKS.reduce((acc,c)=> acc + (st.checklist?.[c.id] ? 1 : 0), 0); const pt = document.getElementById("progressText"); const bar = document.getElementById("progressBar"); if(pt) pt.textContent = `${done}/${total}`; if(bar) bar.style.width = `${total ? Math.round((done/total)*100) : 0}%`; }; const renderFAQ = (lang) => { const wrap = document.getElementById("faqList"); if(!wrap) return; wrap.innerHTML = ""; FAQ.forEach((f) => { const q = f.q[lang] || f.q.vi || f.q.en; const a = f.a[lang] || f.a.vi || f.a.en; const d = document.createElement("details"); d.innerHTML = `${q}

${a}

`; wrap.appendChild(d); }); }; // ========= APPLY I18N ========= const applyI18n = (lang) => { const t = I18N[lang] || I18N[DEFAULT_LANG]; // top / nav / hero applyText("brandTitle", t.brandTitle); applyText("brandSub", t.brandSub); applyText("navToolkit", t.navToolkit); applyText("navPrivacy", t.navPrivacy); applyText("navFaq", t.navFaq); applyText("heroTitle", t.heroTitle); applyText("heroDesc", t.heroDesc); applyText("heroCta1", t.heroCta1); applyText("exportBtnTop", t.exportBtnTop); applyText("saveBtnTop", t.saveBtnTop); applyText("panicBtn", t.panicBtn); applyText("heroKpi1", t.heroKpi1); applyText("heroKpi2", t.heroKpi2); applyText("heroKpi3", t.heroKpi3); // quick / reset applyText("quickTitle", t.quickTitle); applyText("quickDesc", t.quickDesc); applyText("quick1T", t.quick1T); applyText("quick1D", t.quick1D); applyText("quick2T", t.quick2T); applyText("quick2D", t.quick2D); applyText("quick3T", t.quick3T); applyText("quick3D", t.quick3D); applyText("resetBtn", t.resetBtn); applyText("resetHint", t.resetHint); // problem / how applyText("problemTitle", t.problemTitle); applyText("problemDesc", t.problemDesc); applyText("p1T", t.p1T); applyText("p1D", t.p1D); applyText("p2T", t.p2T); applyText("p2D", t.p2D); applyText("p3T", t.p3T); applyText("p3D", t.p3D); applyText("howTitle", t.howTitle); applyText("howDesc", t.howDesc); applyText("h1T", t.h1T); applyText("h1D", t.h1D); applyText("h2T", t.h2T); applyText("h2D", t.h2D); applyText("h3T", t.h3T); applyText("h3D", t.h3D); applyText("howHint", t.howHint); // toolkit applyText("toolkitTitle", t.toolkitTitle); applyText("toolkitDesc", t.toolkitDesc); applyText("tabChecklist", t.tabChecklist); applyText("tabEvidence", t.tabEvidence); applyText("tabReport", t.tabReport); applyText("checkIntro", t.checkIntro); applyText("progressLabel", t.progressDone); applyText("markAllBtn", t.markAll); applyText("unmarkAllBtn", t.unmarkAll); // evidence labels applyText("evIntro", t.evIntro); applyText("lblTitle", t.lblTitle); applyText("lblType", t.lblType); applyText("lblPlatform", t.lblPlatform); applyText("lblWhen", t.lblWhen); applyText("lblUrl", t.lblUrl); applyText("lblAccount", t.lblAccount); applyText("lblClaim", t.lblClaim); applyText("lblNotes", t.lblNotes); applyText("hashTitle", t.hashTitle); applyText("hashDesc", t.hashDesc); applyText("lblHash", t.lblHash); applyText("lblFileMeta", t.lblFileMeta); applyText("evidenceSaveTitle", t.evidenceSaveTitle); applyText("evidenceSaveDesc", t.evidenceSaveDesc); applyText("evidenceSaveHint", t.evidenceSaveHint); // report applyText("repIntro", t.repIntro); applyText("exportBtn", t.exportBtnTop || "Export"); applyText("copyBtn", t.copyJson || "Copy"); applyText("regenBtn", t.regen || "Regenerate"); applyText("repHint", t.repHint); // privacy applyText("privacyTitle", t.privacyTitle); applyText("privacyDesc", t.privacyDesc); applyText("pr1T", t.pr1T); applyText("pr1D", t.pr1D); applyText("pr2T", t.pr2T); applyText("pr2D", t.pr2D); applyText("pr3T", t.pr3T); applyText("pr3D", t.pr3D); applyText("ds1", t.ds1); applyText("dp1", t.dp1); applyText("ds2", t.ds2); applyText("dp2", t.dp2); applyText("ds3", t.ds3); applyText("dp3", t.dp3); // footer applyText("faqTitle", t.faqTitle); applyText("footNote", t.footNote); applyText("footLegal", t.footLegal); applyText("footTop", t.footTop); applyText("footHome", t.footHome); applyText("footDownload", t.footDownload); // panic applyText("panicTitle", t.panicTitle); applyText("panicSub", t.panicSub); applyText("panicDesc", t.panicDesc); applyText("panicCloseBtn", t.close); applyText("lockTitle", t.lockTitle); applyText("lockDesc", t.lockDesc); applyText("quickExitBtn", t.quickExit); applyText("backToToolkit", t.backToToolkit); applyText("contactTitle", t.contactTitle); applyText("contactHint", t.contactHint); applyText("c1NameL", t.cName); applyText("c1PhoneL", t.cPhone); applyText("c2NameL", t.cName); applyText("c2PhoneL", t.cPhone); applyText("c1Label", t.c1Label); applyText("c2Label", t.c2Label); applyText("panicHint2", t.panicHint2); document.documentElement.lang = lang; buildLD(lang); renderFAQ(lang); renderChecklist(lang); refreshPreview(); }; // ========= TABS ========= const setActiveTab = (tabId) => { const map = { tabChecklist:"panelChecklist", tabEvidence:"panelEvidence", tabReport:"panelReport" }; $$(".tab").forEach(b => b.setAttribute("aria-selected", b.id===tabId ? "true":"false")); $$(".panel").forEach(p => p.classList.remove("active")); const pid = map[tabId]; if(pid) document.getElementById(pid).classList.add("active"); }; // ========= EVIDENCE SAVE/LOAD ========= const safeUrl = (s) => { const v = String(s ?? "").trim(); if(!v) return ""; try{ return new URL(v).href; }catch(e){ return ""; } }; const safeText = (s, max=2000) => String(s ?? "").slice(0,max); const bindEvidence = (lang) => { const t = I18N[lang] || I18N[DEFAULT_LANG]; const st = getStore(); const ev = st.evidence || {}; const setV = (id, v) => { const el = document.getElementById(id); if(el) el.value = v || ""; }; setV("evTitle", ev.title); const evType = document.getElementById("evType"); if(evType) evType.value = ev.type || "video"; setV("evPlatform", ev.platform); setV("evWhen", ev.when_local); setV("evUrl", ev.url); setV("evAccount", ev.account); setV("evClaim", ev.claim); setV("evNotes", ev.notes); setV("evHash", ev.sha256); setV("evFileMeta", ev.file_meta); const collect = () => { const urlRaw = document.getElementById("evUrl")?.value || ""; const url = safeUrl(urlRaw); if(urlRaw.trim() && !url) toast(t.toastInvalidUrl); return { title: safeText(document.getElementById("evTitle")?.value, 120), type: safeText(document.getElementById("evType")?.value, 20), platform: safeText(document.getElementById("evPlatform")?.value, 60), when_local: safeText(document.getElementById("evWhen")?.value, 40), url, account: safeText(document.getElementById("evAccount")?.value, 80), claim: safeText(document.getElementById("evClaim")?.value, 120), notes: safeText(document.getElementById("evNotes")?.value, 2000), sha256: safeText(document.getElementById("evHash")?.value, 128) .toLowerCase().replace(/[^0-9a-f]/g,"").slice(0,64), file_meta: safeText(document.getElementById("evFileMeta")?.value, 200) }; }; const save = () => { const s2 = getStore(); s2.evidence = collect(); setStore(s2); toast(t.toastSaved); refreshPreview(); }; document.getElementById("saveBtnTop")?.addEventListener("click", save); document.getElementById("saveBtnMid")?.addEventListener("click", save); document.getElementById("clearEvidenceBtn")?.addEventListener("click", () => { ["evTitle","evPlatform","evWhen","evUrl","evAccount","evClaim","evNotes","evHash","evFileMeta"].forEach(id=>{ const el = document.getElementById(id); if(el) el.value = ""; }); const ty = document.getElementById("evType"); if(ty) ty.value = "video"; const s2 = getStore(); s2.evidence = {}; setStore(s2); toast(t.toastSaved); refreshPreview(); }); }; // ========= REPORT ========= const buildReport = (lang) => { const st = getStore(); const checklist = {}; CHECKS.forEach(c => checklist[c.id] = !!st.checklist?.[c.id]); return { schema:"deepfake-self-defense-kit", schema_version: SCHEMA_VERSION, created_at: st.created_at || nowISO(), updated_at: nowISO(), lang, checklist, evidence: st.evidence || {}, notes:{ local_first:true, no_uploads:true, no_tracking:true, no_auto_detection:true } }; }; const refreshPreview = () => { const lang = currentLang(); const pre = document.getElementById("jsonPreview"); if(!pre) return; pre.textContent = JSON.stringify(buildReport(lang), null, 2); }; const downloadJSON = () => { const lang = currentLang(); const blob = new Blob([JSON.stringify(buildReport(lang), null, 2)], { type:"application/json" }); const a = document.createElement("a"); const ts = new Date().toISOString().replace(/[:.]/g,"-"); a.href = URL.createObjectURL(blob); a.download = `deepfake-incident-${ts}.json`; document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(a.href), 1200); }; const copyJSON = async () => { const lang = currentLang(); const t = I18N[lang] || I18N[DEFAULT_LANG]; const text = JSON.stringify(buildReport(lang), null, 2); try{ if(navigator.clipboard?.writeText){ await navigator.clipboard.writeText(text); toast(t.toastCopied); }else{ toast(t.toastNoClipboard); } }catch(e){ toast(t.toastNoClipboard); } }; // ========= CONTACTS (PANIC) ========= const normalizePhone = (p) => safeText(p,30).replace(/[^\d+]/g,""); const makeTel = (p) => p ? `tel:${p}` : "#"; const makeSms = (p) => p ? `sms:${p}` : "#"; const bindContacts = (lang) => { const t = I18N[lang] || I18N[DEFAULT_LANG]; const saved = loadJSON(STORE_CONTACTS, CONTACT_DEFAULTS); const setV = (id, v) => { const el = document.getElementById(id); if(el) el.value = v || ""; }; setV("c1Name", saved.c1?.name); setV("c1Phone", saved.c1?.phone); setV("c2Name", saved.c2?.name); setV("c2Phone", saved.c2?.phone); const renderOne = (idx, c) => { const name = safeText(c?.name,40).trim(); const phone = normalizePhone(c?.phone || ""); const v = document.getElementById(`c${idx}Value`); const callA = document.getElementById(`c${idx}Call`); const smsA = document.getElementById(`c${idx}Sms`); if(v) v.textContent = (name || phone) ? `${name ? name+" · " : ""}${phone}` : t.notSet; if(callA) callA.href = phone ? makeTel(phone) : "#"; if(smsA) smsA.href = phone ? makeSms(phone) : "#"; }; renderOne(1, saved.c1); renderOne(2, saved.c2); document.getElementById("saveContactsBtn")?.addEventListener("click", () => { const c = { c1:{ name:safeText(document.getElementById("c1Name")?.value,40), phone:normalizePhone(document.getElementById("c1Phone")?.value) }, c2:{ name:safeText(document.getElementById("c2Name")?.value,40), phone:normalizePhone(document.getElementById("c2Phone")?.value) } }; saveJSON(STORE_CONTACTS, c); renderOne(1, c.c1); renderOne(2, c.c2); toast(t.toastContactsSaved); }); document.getElementById("clearContactsBtn")?.addEventListener("click", () => { localStorage.removeItem(STORE_CONTACTS); setV("c1Name",""); setV("c1Phone",""); setV("c2Name",""); setV("c2Phone",""); renderOne(1, {name:"",phone:""}); renderOne(2, {name:"",phone:""}); toast(t.toastContactsCleared); }); }; // ========= OFFLINE DOWNLOAD ========= const downloadOfflineHtml = () => { const lang = currentLang(); const t = I18N[lang] || I18N[DEFAULT_LANG]; refreshPreview(); const html = "\n" + document.documentElement.outerHTML; const blob = new Blob([html], { type:"text/html;charset=utf-8" }); const a = document.createElement("a"); const ts = new Date().toISOString().slice(0,10); a.href = URL.createObjectURL(blob); a.download = `deepfake-self-defense-kit-offline-${ts}.html`; document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(a.href), 1200); toast(t.toastOfflineReady); }; // ========= PANIC OVERLAY ========= const openPanic = () => { const o = document.getElementById("panicOverlay"); if(!o) return; o.classList.add("show"); o.setAttribute("aria-hidden","false"); document.getElementById("panicCloseBtn")?.focus({ preventScroll:true }); }; const closePanic = () => { const o = document.getElementById("panicOverlay"); if(!o) return; o.classList.remove("show"); o.setAttribute("aria-hidden","true"); document.getElementById("panicBtn")?.focus({ preventScroll:true }); }; const quickExit = () => { try{ window.location.replace("about:blank"); } catch(e){ window.open("about:blank","_self"); } }; // ========= RESET ========= const resetAll = () => { const lang = currentLang(); const t = I18N[lang] || I18N[DEFAULT_LANG]; localStorage.removeItem(STORE_KEY); toast(t.toastCleared); applyI18n(lang); bindEvidence(lang); bindContacts(lang); refreshPreview(); updateProgress(); }; // ========= LANG SWITCH ========= const setLang = (lang) => { const st = getStore(); st.lang = SUPPORTED.includes(lang) ? lang : DEFAULT_LANG; setStore(st); $$(".lang button").forEach(b => b.setAttribute("aria-pressed", b.dataset.lang===st.lang ? "true":"false")); setLangInUrl(st.lang); applyI18n(st.lang); bindEvidence(st.lang); bindContacts(st.lang); }; // ========= INIT ========= const init = () => { const urlLang = getLangFromUrl(); const st = getStore(); const startLang = (urlLang && SUPPORTED.includes(urlLang)) ? urlLang : (st.lang || DEFAULT_LANG); st.lang = startLang; setStore(st); // lang buttons $$(".lang button").forEach((b) => { b.addEventListener("click", () => setLang(b.dataset.lang)); }); // tabs document.getElementById("tabChecklist")?.addEventListener("click", () => setActiveTab("tabChecklist")); document.getElementById("tabEvidence")?.addEventListener("click", () => setActiveTab("tabEvidence")); document.getElementById("tabReport")?.addEventListener("click", () => setActiveTab("tabReport")); // mark/unmark document.getElementById("markAllBtn")?.addEventListener("click", () => { const lang = currentLang(); const t = I18N[lang] || I18N[DEFAULT_LANG]; const s2 = getStore(); s2.checklist = s2.checklist || {}; CHECKS.forEach(c => s2.checklist[c.id] = true); setStore(s2); renderChecklist(lang); toast(t.toastSaved); refreshPreview(); }); document.getElementById("unmarkAllBtn")?.addEventListener("click", () => { const lang = currentLang(); const t = I18N[lang] || I18N[DEFAULT_LANG]; const s2 = getStore(); s2.checklist = s2.checklist || {}; CHECKS.forEach(c => s2.checklist[c.id] = false); setStore(s2); renderChecklist(lang); toast(t.toastSaved); refreshPreview(); }); // report buttons document.getElementById("exportBtn")?.addEventListener("click", downloadJSON); document.getElementById("exportBtnTop")?.addEventListener("click", downloadJSON); document.getElementById("copyBtn")?.addEventListener("click", copyJSON); document.getElementById("regenBtn")?.addEventListener("click", () => { const lang = currentLang(); const t = I18N[lang] || I18N[DEFAULT_LANG]; refreshPreview(); toast(t.toastRegenerated); }); // panic document.getElementById("panicBtn")?.addEventListener("click", openPanic); document.getElementById("panicCloseBtn")?.addEventListener("click", closePanic); document.getElementById("quickExitBtn")?.addEventListener("click", quickExit); // reset document.getElementById("resetBtn")?.addEventListener("click", resetAll); // offline download document.getElementById("footDownload")?.addEventListener("click", (e) => { e.preventDefault(); downloadOfflineHtml(); }); // apply all applyI18n(startLang); bindEvidence(startLang); bindContacts(startLang); refreshPreview(); // a11y: Esc closes panic window.addEventListener("keydown", (e) => { if(e.key === "Escape"){ const o = document.getElementById("panicOverlay"); if(o?.classList.contains("show")) closePanic(); } }); }; init();