Patina について
Patinaは、アカウント活動の日付から、オンラインアカウントの利用期間を示す持ち運び可能な証拠を作成します。アカウントの利用歴を評価するサービス向けで、スコアが示すのは利用期間であり、個人の一意性ではありません。
主な機能
- 018つの情報源からアカウント履歴の日付を集約
- 02経過年数、継続性、裏付けなどをスコア化
- 03アカウント内容を破棄し、月単位の日付を保存
- 04オフライン検証可能な署名付き証明を作成
- 05MCP サーバー経由でスコアを提供
活用シーン
- 01登録や利用資格の確認でアカウント歴を評価
- 02他のSybil耐性チェックと履歴証拠を併用
- 03Patinaへの問い合わせなしで署名付きスコアを検証
- 04ハンドルを使い、AIエージェントから利用期間を照会
MCP 対応
MCPサーバーは、会話中にAIエージェントがアカウント利用期間のスコアを取得できるようにします。ハンドル解決機能は、本人の身元を明かさずに利用期間を返します。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://patinadata.xyz/api/mcpclaude mcp add --transport http ramakrishnanhulk20-patina https://patinadata.xyz/api/mcp{"mcpServers":{"ramakrishnanhulk20-patina":{"type":"http","url":"https://patinadata.xyz/api/mcp"}}}Cursor で開く{"servers":{"ramakrishnanhulk20-patina":{"type":"http","url":"https://patinadata.xyz/api/mcp"}}}VS Code で開く{"mcpServers":{"ramakrishnanhulk20-patina":{"type":"http","url":"https://patinadata.xyz/api/mcp"}}}{"mcpServers":{"ramakrishnanhulk20-patina":{"serverUrl":"https://patinadata.xyz/api/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
4| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| username | string | はい | patinadata.xyz/u/<username> に表示されている、その人の Patina ユーザー名。 |
詳細
Patina
すでに所有しているアカウントの利用期間を証明
新しいアカウントは誰でも作れます。古いアカウントは誰にも作れません。
ライブアプリ • ドキュメント • MCP サーバー • スコアを検証
概要 • スコア • プライバシー • アーキテクチャ • クイックスタート • インテグレーション • テスト結果
ライブデプロイ
| 項目 | URL |
|---|---|
| アプリ | patinadata.xyz |
| ドキュメント | patinadata.xyz/docs |
| MCP | patinadata.xyz/api/mcp |
| 検証ツール | patinadata.xyz/verify |
| レジストリ | io.github.ramakrishnanhulk20/patina |
| 署名アドレス | 0x3989bdFaf3BA242d27B4D0cEed98F446d0c52DAD |
概要
Patina は、ユーザーがすでに所有しているアカウントの履歴を読み取り、実在する人間が何年も利用してきたことを示す、持ち運び可能で検証可能な証拠に変換します。Vana のデータポータビリティプロトコルを基盤としており、データはユーザー自身のパーソナルサーバーに保管されます。ユーザーが取り消せる許可のもとで一度だけ読み取られ、そのデータを保持する OAuth の受け渡しを通過することはありません。
シビル攻撃への耐性を実現する方法は、まだ確立されておらず、コストもかかります。エアドロップ、二次資金調達ラウンド、DAO 投票、無料トライアルでは、相手が千人なのか、千個のウォレットを持つ一人なのかを知る必要があります。一般的な対策には問題があります。生体認証は侵襲的で、KYC はプライバシーを損ない、行動に関するシグナルの大半は簡単に買えてしまいます。
アカウントファームを運営する人は、フォロワーを買い、投稿を一括アップロードし、もっともらしいプロフィールを作成できます。しかし、10年分の履歴を買うことはできません。
Patina を選ぶ理由
| KYC | World ID/実在人物の証明 | Patina | |
|---|---|---|---|
| 長年にわたる履歴を証明 | いいえ | いいえ | はい |
| 唯一無二の人物であることを証明 | 一部 | はい | いいえ |
| ウォレットなしで利用可能 | はい | いいえ | はい |
| 生体認証なしで利用可能 | はい | いいえ | はい |
| ユーザーが後からアクセスを取り消せる | いいえ | いいえ | はい |
| 別のアプリで証明を再利用可能 | いいえ | はい | はい |
Patina が証明するのは一意性ではなく、利用期間です。唯一無二の人間であることが確認された人を今日生み出すことはできますが、16年にわたる裏付けのある活動を生み出すことはできません。この2つは互いを補完するものであり、ツールの説明でも、そうでないかのように示すのではなく、その点を明記しています。
特徴
所有者を推測ではなく証明
コレクションはVana Desktop経由で実行される想定です。Vana Desktopはユーザー自身のマシンでブラウザーを開き、サインインを求めます。認証情報がデバイスの外に出ることはなく、Patinaがパスワードを見ることもありません。
ただし、Vanaには2つのコレクション経路があり、どちらを使ったかは報告されません。もう一方は、プロフィール URL から公開ページを読み取ります。これでアカウントの存在は証明できますが、誰がそのアカウントを持っているかは証明できません。つまり、「デスクトップ限定」と求めるだけでは不十分で、強制する必要があります。
そのため、各ソースでは公開ページでは取得できないスコープを1つ必須とし、その情報が返ってこない場合は読み取りを拒否します。LinkedInではつながった日付、Spotifyでは保存したトラック、YouTubeでは「後で見る」リストが必要です。非公開情報を含むリクエストには公開プロフィールから回答できないため、ユーザーはDesktopに移り、ログインする必要があります。ソースを追加した際にこれが満たされていなければ、sources.test.tsが失敗します。
Steamも同じ理由で取り下げました。そのコネクターは誰にもサインインを求めません。Steam Web APIキーとSteam IDを受け取りますが、Steam IDは公開情報です。誰でも他人のアカウントの利用年数やフレンドになった日付を、自分のものとしてPatinaに渡せました。YouTubeとは違い、代わりに必須にできる非公開スコープもありませんでした。
Instagramは関連する理由で一時停止中です。証明スコープのinstagram.postsは、Vanaのサーバーが公開ページから収集するものです。そのため、読み取りで証明できるのはアカウントの存在だけで、誰がそのアカウントを持っているかは証明できません。また、Patinaが代わりに必須にできる非公開スコープもInstagramにはありません。Instagramは提供されず、Vanaのどちらの経路でも費用が発生する前に拒否され、ハンドルリゾルバーも検索せず、すでに保存済みのInstagramデータもすべてのスコアから除外されます。スマートフォンアプリによるサインイン済みの証明が実現したら、再開します。
8つのソース、17のスコープ
GitHub、LinkedIn、Spotify、YouTube、Amazon、Uber、DoorDash、Shopです。各ソースのスコープはすべて1回の承認にまとめられるため、マニフェスト全体で承認は17回ではなく8回です。
タイムスタンプのみを取得し、コンテンツは破棄
各スコープでは日付を取得します。キャプション、住所、メールアドレス、トラック名、ゲームタイトル、他人の名前などは、保存する前に破棄されます。これらがストアに届いた場合はテストが失敗します。
署名付き、ポータブル、期限付き
暫定スコア以外のすべてのスコアには、誰でもオフラインでPatinaの公開アドレスを照合して検証できるEIP-191アテステーションが付与されます。キー不要、OAuth不要、CORSはオープンです。
アテステーションには30日で期限が切れます。期限は署名メッセージの外側ではなく内側に含まれるため、有効期間の確認もオフラインで行えます。失効リストを使えば同じ問題を解決できましたが、そうするとすべての検証者がPatinaに問い合わせる必要があり、これはユーザーが連携を避けるためにまさに解消しようとしている依存関係です。スコアの署名にはエスクロー残高を保持するキーとは別のキーを使うため、一方が漏えいしても、もう一方まで漏えいすることはありません。
自身について回答可能
/api/healthはデータベースへの実際の書き込みと読み取り、およびエスクロー残高のライブ読み取りを実行します。デプロイ環境が次のユーザーに対応できない場合は503を返すため、通常の稼働監視で、何を確認すべきか事前に教えなくても障害を検出できます。パスワードで保護された/adminでは、ファネル、ユーザー数、残高であと何件の連携に資金を提供できるかを確認できます。ファネルの集計はサーバー側で行います。サードパーティのスクリプトも、Cookieバナーも、プライバシーページの内容と矛盾するものもありません。
エージェントから読み取り可能
MCP サーバーが会話の途中でAIエージェントにスコアを公開します。また、本人を特定せずに利用期間を返すハンドルリゾルバーを備えています。
スコア
6つの要素で構成され、それぞれの重みは金額ではなく、かかる時間に基づいています。
| 要素 | 最大点 | 測定する内容 |
|---|---|---|
| 経過年数 | 30 | 接続されたすべてのソースを通じて証明できる最も古い日付です。12年で満点になります。 |
| 継続性 | 25 | 実際に本人が利用していた月の数に、カバー率を掛けた値です。 |
| 裏付け | 15 | 日付が独立したソース間で一致する度合いです。各ソースの経過年数に応じて重み付けします。 |
| 推薦 | 12 | 他の人がつながりを選んだ「時期」であり、人数ではありません。どれだけ前のことかに応じて重み付けします。 |
| 深さ | 10 | 実際に作成したものを測定します。短期間にまとめて作成された場合は割り引かれます。 |
| 幅広さ | 8 | 互いに裏付け合う独立したアカウントの数です。 |
経過年数と2つの時間要素は、そのまま得点になります。深さ、推薦、幅広さには条件があります。攻撃者は午後のうちに活動量や友人、幅広さを捏造できるため、実際に経過した期間による裏付けがある範囲でのみ加点されます。最低点を15%にすることで、本当に若い人が19歳であるというだけでゼロにされるのを防ぎます。
score.test.ts で定義された参照プロフィールに対するスコアです。
| プロフィール | スコア |
|---|---|
| 4つのアカウントで11年間活動 | 95 |
| 一般的な例:11年間、2つのアカウント、実際の空白期間あり | 71 |
| 本当に若い人:3年間、2つのアカウント、実際の活動 | 26 |
| アカウントファーム:購入したフォロワー3,900人、1週間で投稿120件 | 2 |
一般的な人とアカウントファームの差は69点です。この差こそが、この製品の価値です。
誠実に伝えるべき限界
サイト自体に明記しており、ここに埋もれさせてはいません。
- 人間であることは証明できません。 確認できる実際の履歴の量を示します。それは証拠であり、証明書ではありません。
- スコアが低くても、非難しているわけではありません。 新しいアカウント、非公開アカウント、あまり使われていないアカウントはスコアが低くなりますが、何も問題はありません。
- 古いアカウントは購入できます。 これにより無料でできる攻撃が高価なものになり、その価格は経過年数とともに上がります。不可能になるわけではありません。
- デスクトップで確認できるのはセッションの操作権であり、所有権ではありません。 購入したアカウントにはパスワードが付属します。基準は「ユーザー名を知っている」から「認証情報を保持している」へと移ります。これは大きな違いですが、同じ意味ではありません。
- 証明の範囲が保証するのは最低限のことです。 非公開情報を必須にすることで実際のサインインを強制できますが、実際のサインインもあくまでサインインにすぎません。これにより防げるのは、他人の公開プロフィールを使ってスコアを計算することです。それができてしまえば、スコア全体が無意味になります。
取得するデータと保持するデータ
以下のすべてのスコープは、タイムスタンプを取得するためにリクエストされます。この表は信頼性を支える説明であり、プライバシーページにもほぼ同じ形式で掲載しています。
| スコープ | 保持するデータ | 取得時に破棄するデータ |
|---|---|---|
linkedin.connections | dateConnected[]、件数 | 名前、見出し、プロフィール URL |
instagram.posts | Instagram の利用を一時停止している間は新しいデータなし。すでに保持しているデータはスコアに使用しない | キャプション、画像、いいね、who_liked[] 配列全体 |
spotify.savedTracks | added_at[]、合計数 | トラック名、アーティスト、アルバム |
github.history | createdAt[]、ハッシュ化したリポジトリ、合計エンゲージメント | PR と Issue のタイトルおよび本文 |
linkedin.experience / education | 解析した日付範囲 | 会社、役職、学校、成績 |
uber.trips | requestTime[]、件数 | 乗車地と降車地の住所、運賃、都市 |
amazon / doordash / shop の注文 | 日付、件数 | 商品、販売業者、合計金額、配送先住所 |
youtube.profile | joinedDate、件数 | メールアドレス |
youtube.watchLater | 一切なし | すべて。このデータはサインイン済みセッションの証明としてのみリクエストされます |
保存前に、タイムスタンプは月単位の区分にまとめられます。スコア計算では月単位の情報しか参照しないため、それより細かい情報を保持する理由はありません。
アーキテクチャ
接続の流れ
graph LR
U[User] -->|1 Connect| P[Patina]
P -->|2 createAccessRequest<br/>all scopes for one source| V[Vana Account API]
V -->|3 approval URL| U
U -->|4 Open in Vana Desktop| D[Vana Desktop]
D -->|5 Playwright signs in<br/>on the user's machine| S[Source]
S -->|6 import| PS[Personal Server]
P -->|7 read every scope<br/>one grant| PS
P -->|8 settle 402| E[Escrow Gateway]
P -->|9 normalise, discard, score| R[(Redis)]
読み取りと確定
sequenceDiagram
participant App as Patina
participant PS as Personal Server
participant GW as Escrow Gateway
App->>PS: GET /v1/data/{scope}
PS-->>App: 402 with accessRecord
App->>GW: authorizeGrantPayment(accessRecord)
GW-->>App: receipt
App->>PS: GET /v1/data/{scope}
PS-->>App: 200 payload
Note over App: repeat per scope, same grant
App->>PS: acknowledgeRead (once, at the end)
acknowledgeRead はリクエストを completed に移行します。これは終端状態であり、読み取り可能な状態ではありません。最初のスコープの後にこれを実行すると、ユーザーが支払いを済ませた後、残りのスコープが提供されなくなった Personal Server に取り残されます。
スコアリングパイプライン
graph LR
RAW[Raw payload] -->|readScope| F[Fragment<br/>months + counts]
F -->|stored by scope| DB[(Redis)]
DB -->|evidenceFrom| EV[Evidence]
EV -->|scorePatina| SC[Score]
EV -->|buildStory| ST[Story]
SC -->|buildAttestation| AT[Signed attestation]
フラグメントはスコープごとにキー付けされ、スコアリング時に統合されます。読み取りを再試行すると、フラグメントは追加ではなく上書きされます。そのため、接続が不安定でもスコアが水増しされることはありません。
クイックスタート
前提条件
- Node 20+
- account.vana.org/developers で取得した Vana アプリの ID
- Upstash Redis の認証情報(ローカルでは任意。設定しない場合はインメモリストアにフォールバックします)
インストール
git clone https://github.com/ramakrishnanhulk20/Patina.git
cd Patina
npm install
cp .env.example .env.local
VANA_APP_PRIVATE_KEY と VANA_APP_URL を設定し、アプリを登録してエスクローに資金を追加します。
npm run register:moksha
開発
npm run dev # development server
npm test # 118 tests
npm run typecheck # types, ignoring generated route types
npm run verify # typecheck + tests + production build
統合
REST
キーも OAuth も不要で、公開スコアを取得できます。
curl https://patinadata.xyz/api/verify/ramkumar
{
"username": "ramkumar",
"score": 71,
"verdict": "Well established",
"oldestYear": 2012,
"yearsOfHistory": 13.6,
"sourcesConnected": ["github", "linkedin", "spotify"],
"provisional": false,
"issuedAt": "2026-08-26T12:00:00.000Z",
"expiresAt": "2026-09-25T12:00:00.000Z",
"attestation": {
"app": "0x...",
"message": "Patina score attestation
username: ramkumar
score: 71/100
...
expiresAt: 2026-09-25T12:00:00.000Z
app: 0x...",
"signature": "0x...",
"expiresAt": "2026-09-25T12:00:00.000Z"
}
}
アテステーションをオフラインで検証する
import { verifyMessage } from "viem";
const ok = await verifyMessage({
address: "0x3989bdFaf3BA242d27B4D0cEed98F446d0c52DAD",
message: attestation.message,
signature: attestation.signature,
});
MCP
{
"mcpServers": {
"patina": {
"type": "http",
"url": "https://patinadata.xyz/api/mcp"
}
}
}
| ツール | 説明 |
|---|---|
get_patina_score | Patina ユーザー名の詳細な内訳を取得します |
check_threshold | 年数、スコア、またはソース数を基準に信頼性を可否判定します |
resolve_identity | GitHub または LinkedIn のハンドルからスコアを取得します。本人の特定につながる情報は開示しません。Instagram の検索は一時停止中です |
verify_attestation | 署名が本当に Patina のものかを検証します |
テスト結果
174 passing
| スイート | テスト数 | 対象 |
|---|---|---|
normalize.test.ts | 30 | エンベロープの形式、破棄の保証、冪等性 |
score.test.ts | 27 | 各コンポーネント、ゲーティング、参照プロフィール |
store.test.ts | 26 | デバイス間の ID、ユーザー名、ソースの個別削除、削除処理 |
mcp-lookup.test.ts | 20 | リゾルバーのプライバシールール、署名、判定の不一致 |
story.test.ts | 19 | タイムライン、アクティビティ、展示項目、履歴が少ない場合の説明文 |
routes.test.ts | 15 | 実際の HTTP リクエスト:セッション、署名の最低要件、管理者ゲート |
device.test.ts | 12 | 接続を完了できるデバイス |
attest.test.ts | 10 | 署名、有効期限、鍵の分離、公開アドレスの不一致 |
sources.test.ts | 9 | 所有権の不変条件:公開ページから回答できるソースがあってはならない |
ratelimit.test.ts | 4 | エスクローの不正利用制限 |
altcha.test.ts | 2 | プルーフ・オブ・ワークによるボットチェック |
知っておく価値のあるテストがひとつあります。nothing sensitive survives normalisation は、実在しそうなメールアドレス、自宅住所 2 件、キャプション、同僚の氏名と LinkedIn URL、ゲームタイトル、勤務先、曲名、商品名、レストラン名を詰め込んだペイロードを入力し、その 13 項目のいずれも保存されるデータのどこにも含まれないことを検証します。
プロジェクト構成
src/
├── app/
│ ├── api/
│ │ ├── vana/ request + data: the connect spine
│ │ ├── patina/ me, username, data, restore, delete
│ │ ├── admin/ session, index rebuild
│ │ ├── verify/ public score lookup
│ │ ├── badge/ embeddable SVG badge
│ │ └── mcp/ MCP server
│ ├── connect/ the evidence board, and the phone handoff
│ ├── my-data/ see it, download it, remove any of it
│ ├── admin/ the funnel, the numbers, the money left
│ ├── u/[username]/ public page and story
│ ├── verify/ verifier, including an offline checker
│ └── docs/ integration reference
└── lib/
├── score.ts the whole argument, weight by weight
├── normalize.ts 18 scopes in, timestamps out
├── sources.ts the manifest, and the proof scope each source must return
├── device.ts whether this device can finish a connection at all
├── vana.ts app identity, one controller per source
├── vana-settle-read.ts paid reads, escrow settlement
├── escrow-balance.ts how many more connections the balance will fund
├── metrics.ts the funnel, counted server-side and nowhere else
├── store.ts profiles keyed on Personal Server identity
├── admin.ts one operator, one password, fails closed
├── story.ts evidence as a narrative, and exhibits
├── attest.ts signed attestations, expiring, on their own key
└── mcp-lookup.ts what an agent is allowed to learn
src/lib/score.ts が設計思想のすべてです。各重みには、その値を採用した理由を説明するコメントが付いています。
技術スタック
| レイヤー | 採用技術 |
|---|---|
| フレームワーク | Next.js 16、React 19、App Router |
| 言語 | TypeScript |
| スタイリング | Tailwind CSS 4、カスタムトークンレイヤー |
| データ | Vana Data Portability Protocol(@opendatalabs/vana-sdk) |
| ストレージ | Upstash Redis、インメモリへのフォールバック |
| 署名 | viem、EIP-191 |
| エージェント | Model Context Protocol(mcp-handler) |
| 不正利用対策 | ALTCHA プルーフ・オブ・ワーク、セッションごとのレート制限 |
| テスト | node:test、フレームワーク不使用 |
ライセンス
MIT。詳細は LICENSE をご覧ください。
謝辞
Vana と、その Data Portability Protocol を基盤に構築されています。コネクターは PDP-Connect/data-connectors のものを使用しています。ブランドグリフは Simple Icons と Font Awesome の無料ブランドセットから取得し、アプリが外部にリクエストを送信しないよう、どちらもインライン化しています。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。