為何安全
兩本帳, 而它們必須吻合.
票券是無記名憑證。誰持有誰就能用,所以唯一重要的問題是:眼前這一張是真的、而且還沒被用過嗎。我們的回答方式,是把紀錄放在兩個無法被同一雙手同時改動的地方。
bullmark
保存票券本身 — 誰持有它、它值多少、是否已被使用。
送出雜湊值 絕不送內容
axowl
保存錨點 — 那筆紀錄的指紋,以及它自己的時鐘所記下的到達時間。
axowl 從不看到票券寫了什麼。它拿到的是一個指紋,並以自己的時間戳保存起來。整個安排就是這樣,而這正是讓兩邊彼此獨立的原因:改動一張票券就會改動它的指紋,而那個指紋不歸我們改。
涵蓋了什麼
指紋是對這些東西取的
不是整列 — 而是一份具名的清單,好讓它是可知的,而不是信任問題。改動其中任何一項,指紋就不再吻合。
產品 品牌 誰持有它 它的核銷碼 它的序號 消費者價格 它給出的價格 何時到期 是否已被使用 何時被使用 持有者的電子郵件
最後三項是在 2026 年 8 月加入的 ,而它們正是重要的那些。過去,把資料庫裡一張已使用的票券翻回未使用,它的指紋仍然不變,還能通過每一項檢查 — 核銷紀錄在別的地方,所以那一列本身可以說自己從沒被動過。現在不行了。
有兩樣東西是刻意 排除在外 的,因為把它們放進去會製造假警報。 保留 是一把鎖,不是關於價值的事實 — 一張在櫃台被押住三十秒的票券,並不是一張被改過的票券。 過期 已經可以推導出來,因為到期日本身就被封印了;連狀態也一起封印,會讓一張週一驗證乾淨的票券,在沒有任何人動過它的情況下,週二就驗證失敗。一台老是喊狼來了的檢查器,人們會學會直接略過它。
每種攻擊死在哪裡
從內部作弊的四種方式,以及什麼擋住了它們
在資料庫裡改一張票券
把價格調低、把到期日往後推、把自己的名字填上去。指紋會改變,不再與存放在 axowl 的錨點吻合。重新計算我們自己那份也沒用 — 做出判定的那一份不在我們的資料庫裡。
把同一張票券用兩次
核銷現在在封印裡面,所以把它改回未使用會改變指紋。而且核銷本身還被單獨記成一筆封存的紀錄,並串到前一筆上 — 這個謊需要兩份紀錄同時同意,而其中一份不是我們的。
鑄造一張從未被發行的票券
發行會把整批封印成一個事實:多少張、什麼價格、到哪一天為止。第一百零一張票券會和一筆已經寫下的紀錄相矛盾。而且新的錨點帶的是 axowl 的時鐘,不是我們的 — 它可以在今天被建立,但絕不可能在去年。
悄悄把它移給別人
持有者在封印裡面,所以票券無法靠改一個欄位就易主。轉交會關閉給出方的那一份,並為接收方發行屬於他自己、帶著相同序號的一份,兩半都留在紀錄裡。不存在只有一邊的版本。
被複製的代碼
如果有人複製了我的 QR 呢?
QR 不是票券。它是指向我們這邊某一行編號紀錄的指標,而那一行才是票券。所以一張 QR 的照片,其價值正好等於在某人出示它的那一刻、它背後那一行的價值 — 用這個角度看下面每一種情況最清楚。
捏造的代碼
核銷是去查那個代碼;它不是從圖片上讀出一個數值。從未被發行的代碼什麼都指不到,無論它周圍的 QR 看起來多像真的。沒有辦法把一張票券畫出來讓它存在。
一份真品的複本
複本是同一組代碼,所以是同一張票券 — 不是第二張。第一次核銷會被封進那一行;之後每一次嘗試,無論來自複本還是原件,都會被告知它已被使用。十張截圖依然只是一張票券。
賣掉之前留下的截圖
轉交一張票券 — 送人也好、轉售也好 — 會關閉給出方的那一行,並在同一個序號下為接收方發行一行帶著 新代碼 的新紀錄。舊截圖上的代碼現在指向一行已關閉的紀錄。賣掉票券之後再搶在買家之前去用,行不通。
被別人先看到的代碼
這一項是真的風險,而我們寧可直說:票券是無記名憑證,用代碼核銷的品牌並不會被告知是誰拿著那支手機。掃描 QR 不會洩漏代碼 — 掃描只顯示這張票券是真的且未使用,代碼只給已登入的持有者和發行品牌看 — 但你張貼或唸出來的代碼,就是別人可以拿去用的代碼。請把它當成禮物卡號碼看待。想把這個缺口也堵上的品牌,可以不接受手打的代碼,改成向我們詢問 那位已登入的顧客 實際持有哪些票券,讓他從清單中挑選 — 這樣被複製的代碼就沒有地方可以輸入了。
而且不管往哪個方向走,都會留在紀錄上。 發行、轉交、保留與核銷各自都是一筆封存的紀錄,串在前一筆之後。品牌可以拉出自家任何一張票券的完整鏈 — 連內容一起 — 並自行重新推導每一個指紋,所以「這是誰花的、什麼時候花的」有一個不依賴我們說法的答案。
品牌點數
我們能不能偷偷印它?
品牌點數長得像錢:品牌為它加值、拿它發行票券、把賺到的提領成現金。所以真正該問的不是外人能不能闖進來 — 而是 我們 能不能在自己的帳上加一行,然後指望沒人發現。
帳本只會增長
紀錄只會被追加,永遠不會被編輯或刪除。更正是另一筆說明它更正了什麼的紀錄,所以錯誤和修正都會留著被看見。不存在把那行難看的紀錄拿掉之後的餘額版本。
每一筆紀錄都被封存並串接
每一筆都帶著前一筆的指紋。改動一筆舊紀錄會弄壞它之後的每一筆,所以一次安靜的編輯,代價是重寫整條鏈 — 而且是在兩個地方,其中一個不是我們的。
內部人員也不例外
在這裡,資料庫存取權不等於帳本存取權。對我們正式資料庫擁有完整權限的人,仍然可以改動一列 — 他只是無法讓 axowl 上的錨點事後與之吻合。把第二份副本放在「我們的管理員不是管理員」的地方,重點就在這裡。
你可以不問我們就自行稽核
品牌可以把自己的紀錄 id 交給會計師,由會計師從公開的鏈重新推導指紋,並核對總數是否對得上。這個檢查不會經過 bullmark,所以我們沒有可以「幫忙」的餘地。
我們不宣稱的事
我們驗證的是電子郵件。界線就在那裡.
我們不檢查護照、不經營身分認證機構,也不假裝知道一個地址背後是誰。封印證明的是一筆紀錄未被更動 — 它不會讓一個未經驗證的主張變成真的,而把這兩件事混在一起的公司,是在賣給你一種感覺。
所以確切的承諾是這樣: 這張票券,以這個價格,在這個時間,被發給這個電子郵件地址,而且自那之後未被動過。 那個電子郵件是帳號用來驗證的那一個,絕不是某人在表單裡打進去的欄位。超出那個地址的東西,就在我們能證明的範圍之外,而我們寧可現在說清楚,也不要讓你之後才發現。
自己去驗證
你不必相信我們的說法
每一筆封存的紀錄都可以讀取,而產生它的數學也是公開的。把那條鏈交給我們的 JavaScript SDK — 或任何能做 sha-256 的東西 — 自己重新推導那些指紋。這個檢查完全在你這邊跑;它不會問 bullmark 任何事。
GET /integrity/events/VoucherInstance/{id}
它回傳的是雜湊值與判定結果,絕不是內容或核銷碼 — 所以它可以保持公開,而不會變成讀取別人票券的手段。
啟動這個循環
不用先付現金.
刊登你的新創 運作方式