コンテンツにスキップ

認証

このページでは、thinkingface インスタンスに対して自分が誰であるかを証明するあらゆる方法を扱います。 Web UI へのサインイン、アクセストークンの発行と利用、SSH 鍵の登録、そしてそれらすべてに関わるセキュリ ティ上の制御です。既存のインスタンスを利用する人向けの内容であり、サーバー側でこれらの動作を設定する 環境変数については 設定 を参照してください。

Web UI へのサインイン

Web UI のログインページ(/login)には Log in タブと Sign up タブがあります。ログインに成功 すると tf_session Cookie がセットされます。この Cookie は署名済みで HttpOnly、SameSite=Lax で あり、インスタンスが HTTPS で配信されている場合は Secure も付与されます。Cookie 自体には読み取り可能 な情報は含まれておらず、サーバーがリクエストごとに署名を検証し、アカウントの現在のセッション状態と照 合します。そのため、サインアウトやパスワード変更を行うと、Cookie の値自体はまだ期限切れになっていな くても、その場で無効化されます。

デフォルトでは Cookie は発行から 7 日間有効です(インスタンスの運用者が TF_SESSION_TTL で設定変更可 能)。サインアウトすると Cookie はクリアされ、ブラウザセッションは匿名アクセスにフォールバックします。 匿名アクセスでも、公開されているリポジトリに対しては引き続き利用できます。

セルフサービスでのサインアップ

Sign up タブが実際にアカウントを作成するかどうかは、インスタンスの設定によって異なります。運用者 はセルフサービスでのサインアップを無効化でき(TF_ALLOW_SIGNUP=false)、その場合は管理者が直接作成 したアカウントしか存在しなくなります。サインアップが有効な場合、新しいアカウントの作成にはユーザー 名、メールアドレス、そして 8 文字以上のパスワードが必要です(パスワードは bcrypt でハッシュ化される ため、最大 72 バイトまでです)。

サインアップを完全に閉じずに絞り込むための設定が 2 つあります。

  • TF_SIGNUP_EMAIL_DOMAINS は、受け付けるメールドメインを列挙して制限します。判定は大文字小文字を 区別せず、完全一致です。example.com を指定したインスタンスは alice@example.com を受け入れ、 alice@sub.example.com は拒否します。ドメインが自動的にそのサブドメインすべてを許可してしまうのは、 意図しないアカウントができて初めて気づく類の驚きだからです。拒否された場合は、受け付けるドメインが 利用者に伝えられるため、フォームは引き続き記入可能です。
  • TF_SIGNUP_REQUIRE_APPROVAL は、新規サインアップを「承認待ち」にします。アカウントは作成されます が、セッションは発行されず、管理者が Settings → Users で承認するまでどの経路でも認証され ません。パスワードも、アクセストークンも、SSH 鍵もです。サインアップフォームはサインインしたふりを せず、その旨を表示します。承認前にログインを試みると 403 account_pending が返ります(返るのは 正しいパスワードを入力した場合だけなので、アカウントの存在が他人に漏れることはありません)。

すべてのインスタンスは初回起動時に、TF_ADMIN_USERNAME / TF_ADMIN_PASSWORD / TF_ADMIN_EMAIL から管理者アカウントを 1 つ作成します(運用者が変更していなければデフォルトは admin / admin / admin@example.com です。他者にインスタンスを公開する前に、必ずこれらを変更してください)。

パスワードを変更する

自分のパスワードは Settings → Account(/settings/account)で変更します。フォームでは現在の パスワード、新しいパスワード、そして新しいパスワード(確認)を入力します。現在のパスワードは必ず 検証されます。サインイン済みであること自体は、そのセッションの元になった資格情報を置き換える許可 にはならないためです。新しいパスワードは 8 文字以上・72 バイト以内で、サインアップと同じルールが 適用されます。

パスワードを変更すると、他のすべてのブラウザからサインアウトされます。セッション Cookie には 失効カウンタが含まれており、変更時にこれが繰り上がるため、古い Cookie を保持しているものは次のリ クエストで拒否されます。変更を行ったタブには新しい Cookie が再発行され、サインインしたままになり ます。

アクセストークンは引き続き有効です。 トークンは独自の一覧と削除ボタンを持つ別個の資格情報で あり、通常のパスワード変更はトークンについて何も語りません。すべてを失効させると、無人で動作する クライアント(CI ジョブ、git、tf CLI)が何の利益もなく壊れてしまいます。トークン自体が漏洩し た場合は、Settings → Access tokens でそのトークンを削除してください。

管理者がパスワードをリセットする

TF_ADMIN_PASSWORD が使われるのは、users テーブルが空のインスタンスで最初のアカウントを作成する ときだけです。あとから変更しても何の効果もありません。したがって、パスワードを忘れた場合は、環境 変数を書き換えるのではなく別の管理者がリセットします。別の管理者がいない場合は、サーバーのコマンド ラインから復旧します(締め出されたインスタンスを復旧する)。

サイト管理者権限を持つアカウントには Settings → Users(/settings/admin/users)が表示され、 インスタンス上のすべてのアカウントが一覧されます。ユーザー名とメールアドレスに対する検索ボックス もあります。各行では次の操作ができます。

  • Reset password — そのアカウントに新しいパスワードを設定します。対象が開いているすべての ブラウザからサインアウトされますが、アクセストークンは自分でパスワードを変更した場合と同じく、 そのまま有効です。メールによる確認手順はないため、新しいパスワードは別の手段で本人に伝え、 Settings → Account から本人に変更してもらってください。
  • Make admin / Revoke admin — サイト管理者権限を付与または剥奪します。インスタンスに 2 人目の管理者を作る唯一の方法であり、最初の管理者がパスワードを失っても復旧できる状態を保つ ための手段でもあります。
  • Suspend / Restore — アカウントを停止し、また復帰させます。停止されたアカウントは どの経路でも認証されません。Web UI も、HTTP Basic 経由のパスワードも、アクセストークンも、 登録済みの SSH 鍵もです。退職者の締め出しにはこれが必要です。パスワードのリセットだけでは、 そのアカウントのトークンと鍵は git push に対して有効なまま残るからです。停止は何も破壊しないので、 復帰させれば元どおりになります。ただしセッションは失効したままです。
  • Revoke credentials — そのアカウントが持つアクセストークンと SSH 鍵をすべて削除し、 サインアウトさせます。停止と違い取り消せませんが、アカウント自体は動き続けます。これは 「漏洩した疑いのある資格情報」(紛失した端末、ビルドログに出たトークン)に対する操作で、 新しい資格情報を発行すれば業務を継続できるアカウント向けです。
  • Approve / Hold for approval — アカウントをサインアップの承認待ちから解放する、あるいは 承認待ちに戻します。これは TF_SIGNUP_REQUIRE_APPROVAL を有効にしたインスタンスでのみ表示されます。 そこではセルフ登録したアカウントはどの経路でも認証できない状態から始まるため、承認こそがその人を 実際に受け入れる操作です。承認待ちのアカウントにはバッジが付き、一覧の先頭に並び、上部に通知が 表示されるので、誰かが気づかれないまま締め出され続けることはありません。承認待ちに戻すと、その場で すべての端末からサインアウトされます。停止とは独立した設定で、承認しても停止は解除されず、 復帰させても承認済みにはなりません。

一覧には各アカウントの Last login(最終ログイン)も表示されます。これはパスワードでサインイン した最後の時刻です。アクセストークンと SSH 鍵はそれぞれ独自の最終利用日時を持っており、意図的にこの 列を動かしません。この列が答えるのは「このアカウントはまだ誰かに使われているか」という問いであり、 毎晩動く CI のトークンはその答えを誤らせるからです。Never は文字どおり一度もない、という意味で、 この列が存在する前から使われていたアカウントもそう表示されます。

同じ画面の上部にある Add user では、アカウントを直接作成できます。ユーザー名、メールアドレス、 パスワード、そして必要に応じて管理者フラグを指定します。この操作はセルフサービスのサインアップが 有効かどうかに関係なく動作します。それがこの機能の目的です。TF_ALLOW_SIGNUP=false で運用している インスタンスでは、新しい人がアカウントを得る唯一の方法がこれであり、サインアップを閉じることが 一方通行にならないようになっています。ユーザー名はそのアカウントの名前空間(dana/*)になり後から 変更できないため、予約語を含めサインアップと同じルールが適用されます。リセットと同様に招待メールは 送られません。パスワードは別の手段で本人に伝え、Settings → Account から本人に変更してもらって ください。

インスタンスが自分自身を締め出さないように、2 つのルールがあります。自分自身の管理者権限の解除と 自分自身の停止はできません(別の管理者に依頼してください)。そして、稼働中の管理者が最後の 1 人に なる場合、その管理者は降格も停止もできません。停止中の管理者はこの「最後の 1 人」に数えません。 サインインできないアカウントは、存在しないアカウントと同じくインスタンスを復旧できないからです。 これらの操作はいずれも UI で隠しているだけでなく、サーバー側で強制されています。

サイト管理はブラウザ経由のみ

/api/v1/admin のエンドポイントはセッションクッキーしか受け付けません。アクセストークンや HTTP Basic の資格情報は、その持ち主が管理者であっても 403 session_required で拒否されます。 以前は write スコープのトークンが 1 本漏れるだけで、アカウントの作成、任意ユーザーのパスワード 強制変更、そしてトークンを失効させても残る SSH 鍵の登録までできてしまいました。ブラウザの セッションを必須にすることで、自動化をその影響範囲から外しています。操作は Settings 配下の 画面から行ってください。

締め出されたインスタンスを復旧する

ここまでの説明は、誰かがまだサインインできることを前提にしています。誰もサインインできなくなった とき — 唯一の管理者がパスワードを忘れ、/api/v1/admin はブラウザのセッションしか受け付けない — 復旧はサーバー自身の上、データベースのすぐ隣で行います。

# サーバーを動かしているコンテナまたはホストで、サーバーと同じ
# DATABASE_URL を使って実行します:
thinkingface admin passwd alice     # エコーなしで 2 回入力を求めます
thinkingface admin promote alice    # サイト管理者権限を付与します
  • パスワードは決して引数で渡しません。 端末からはエコーなしで 2 回読み取り(両者は一致する必要が あります)、端末がない場合は標準入力から読み取るため、自動化された実行でも無人で行えます。
printf '%s' "$NEW_PASSWORD" | thinkingface admin passwd alice

引数で渡したパスワードは、入力した人のシェル履歴と、そのマシンの他のすべてのユーザーから見える プロセス一覧に残ります。だからこそ、そのためのフラグは用意されていません。

  • admin passwd は Web UI と同じルール(8 文字以上・72 バイト以内)を適用し、他のあらゆる パスワード変更と同様に、そのアカウントをすべての端末からサインアウトさせます。アクセストークンと SSH 鍵はそのままです。
  • admin promote は、管理者が 1 人もいなくなったインスタンスが管理者を取り戻すための手段です。対に なる「demote」はありません。権限を剥奪するのは緊急事態ではなく通常の管理業務であり、すでに画面と 「最後の管理者」ガードが用意されているからです。
  • どちらのコマンドも、誰に対して何をしたかを出力します。さらに、直したアカウントがまだ停止中または 承認待ちの場合は警告します。どちらの状態も正しいパスワードすら拒否するため、警告がなければ作業が 終わったと誤解してしまうからです。

3 つ目のサブコマンド admin token create は、同じ場所からブラウザなしでアクセストークンを発行します。 インスタンスのプロビジョニングや CI ジョブ向けです。 コマンドラインからトークンを発行する を参照してください。

ここでの認可は、デプロイ先へのシェルアクセスそのものです。このコマンドを実行できる人は、そもそも データベースを直接読めます。それが要点で、これは動作するアカウントを必要としない唯一の復旧手段であり、 最後の手段としての「users テーブルを手で書き換える」に取って代わるものです。

失敗したバックグラウンドジョブ

管理者には Settings → Sync jobs(/settings/admin/sync-jobs)も表示されます。push のたびに、 リポジトリのファイル一覧・検索エントリ・オブジェクトストレージへのエクスポートを作り直す バックグラウンド処理がスケジュールされます。この処理が繰り返し失敗すると停止状態になり、 リポジトリは前回の push の内容でインデックスされたままになります。push は成功しているのに、 表示されるファイルが古いという状態です。この画面には停止したジョブと最後の試行のエラーが一覧され、 Retry でキューに戻せます。一覧が空であることが正常な状態です。

アクセストークン

アクセストークン(tf_xxxxxxxxxxxx)は、ブラウザ以外のあらゆるクライアントが使用するものです。 huggingface_hub、git、tf CLI、そして直接の API 呼び出しがこれに該当します。Web UI にサインイン した状態で Settings → Access tokens(/settings/tokens)からトークンを発行します。

アクセストークンページ。トークン作成フォームと既存トークンの一覧が表示されている

トークンに名前、スコープ、有効期限、そして必要に応じて制限対象のリポジトリを設定してから作成します。

Scope Meaning
read アクセス権のあるリポジトリを読み取れます。プッシュ、作成、削除、トークン / SSH 鍵の管理はできません
write read でできることに加えて、コミットのプッシュ、リポジトリの作成・削除、自分自身のトークンと SSH 鍵の管理ができます

有効期限は 無期限、7 日、30 日、60 日、90 日、365 日のいずれかから選べます(365 日が上限で す)。有効期限が過ぎたトークンは、削除された場合と同様にリクエストを認証できなくなります。ただし トークン一覧には引き続き表示され、期限切れを示すバッジが付くため、なぜ動作しなくなったのかが分か り、削除することもできます。

トークンの値は作成直後の一度だけ表示されます。表示された画面から離れる前に必ずコピーしてください。 サーバーはトークンのハッシュしか保存しないため、再度値を表示することはできません。紛失した場合は、そ のトークンを削除して新しく作成してください。

トークン一覧には各トークンの名前、スコープ、制限対象のリポジトリ(制限のないトークンは「すべて」)、 作成日、最終使用日、有効期限(無期限の場合は「無期限」)が表示され、いつでも(確認ステップを経て) トークンを削除できます。削除は即座に反映されます。これは有効期限が切れるのを待つのと同じ効果です。

トークンの発行には write スコープが必要です

トークンや SSH 鍵の作成・削除には常に write スコープが必要です。一覧の閲覧にはサインインしてい るだけで済みますが、それとは異なります。read スコープのトークンで、自分自身を使ってより強い権限 のトークンを発行することはできません。リポジトリ制限付きのトークン(下記)でも同様です。

トークンをリポジトリに制限する

write トークンは、リポジトリのリストに制限できます。トークンのフォームで Restrict to repositories に 1 行に 1 つずつ、datasets/NAMESPACE/NAME または models/NAMESPACE/NAME の形式(Web UI の URL と同じ複数形の種別)で入力します。空欄のままにすれば、通常の制限なしのトークンになります。この欄は write トークンでのみ表示されます。read トークンはもともと何も変更できないため、リストを指定しても何も 制限しないからです。

制限付きトークンは次のように動作します。

  • 読み取りは、同じユーザーの制限なしのトークンとまったく同じです。この制限は何を変更できるかに 関するもので、何を見られるかに関するものではありません。
  • 書き込み(git push、huggingface_hub のアップロードとコミット、LFS のアップロード、ファイル 編集、実験の ingest、run のアノテーション、プロジェクトのノート、メトリクスの目標)は、リストに あるリポジトリに対してのみ、しかも自分自身がまだそのリポジトリへの書き込み権限を持っている間に 限って行えます。
  • リポジトリの管理操作(削除、アーカイブ、名前変更、設定変更、移管)は、リストにあるリポジトリを 含め、どこに対しても行えません。
  • アカウントレベルの書き込み(リポジトリの作成、トークンの発行や失効、SSH 鍵の登録、プロフィールや パスワードの変更、Organization や webhook の管理)は行えません。

これら以外の操作は、エラー種別 token_restricted 付きの 403 で拒否されます。huggingface_hub の 便利機能が 1 つだけ引き続き動作します。push_to_hub やほとんどのアップロードスクリプトが最初に呼ぶ create_repo(..., exist_ok=True) は、リストにあるリポジトリがすでに存在する場合は成功します。

リストに含める各リポジトリは、トークン作成時点で存在し、かつ自分が書き込み権限を持っている必要が あります。制限付きトークンではリポジトリを作成できないため、先にリポジトリを作成してください。リスト は名前ではなくリポジトリ単位で保存されます。そのため、付与された権限は名前変更や移管があってもその リポジトリについていき、後からリストにある名前を名乗った別のリポジトリに移ることはありません。 リポジトリがすべて削除されたトークンは何も変更できなくなります。制限なしのトークンになることは ありません。

API 経由では、トークン作成時にリストを repos として渡します(最大 32 件)。

curl -X POST http://localhost:8080/api/v1/tokens \
  -H "Authorization: Bearer tf_xxxxxxxxxxxx" -H "Content-Type: application/json" \
  -d '{"name": "ocr-agent", "scope": "write", "expires_in_days": 7,
       "repos": ["datasets/alice/trackio-metrics"]}'

このリクエストに使う資格情報自体は、制限なしの write 資格情報である必要があります。

AI エージェントや CI には、制限付きで期限のあるトークンを渡す

AI エージェントや CI ジョブ、レンタルした GPU マシンに渡すトークンは、そのジョブに必要なことだけ ができ、それ以外は何もできないようにすべきです。ジョブがログを書き込む実験リポジトリ 1 つに 制限し、7 日または 30 日で期限切れになる write トークンであれば、そこでメトリクスの ingest、run の アノテーション、ノートの編集ができます。そして漏洩しても、リポジトリを削除することも、他の場所に push することも、自分の後継となるトークンを発行することもできません。このトークンによるすべての リクエストは、サーバーのアクセスログ(token_id、token_name)で追跡できます。 AI エージェントから thinkingface を使う を参照してください。

コマンドラインからトークンを発行する

thinkingface admin token create は、ブラウザなしでアクセストークンを発行します。新しいインスタンスの プロビジョニングや、自動化にトークンを渡す場合に使います。admin passwd と同様に、サーバーと同じ DATABASE_URL を使ってデータベースのすぐ隣で実行し、Web UI とまったく同じルールを適用します。

thinkingface admin token create <username> [--name NAME] [--scope read|write]
    [--expires-in-days N] [--repo KIND/NS/NAME ...] [--output FILE]
フラグ デフォルト 意味
--name admin-cli トークン一覧に表示されるラベル
--scope write read または write
--expires-in-days 0(無期限) 1〜365 日
--repo (制限なし) トークンをこのリポジトリに制限します。形式は datasets/NS/NAME または models/NS/NAME。複数回指定できます
--output (標準出力) 代わりにこのファイルへトークンを書き出します。ファイルはモード 0600 で作成され、すでに存在する場合は拒否されます

標準出力(または --output)に出力されるのはトークンだけです。何を、誰に対して、いつまで有効なものと して作成したかは標準エラー出力に出るため、TOKEN=$(thinkingface admin token create ...) でちょうど トークンだけを取得できます。Docker Compose では、端末が割り当てられないように -T を付けて api コンテナ内で実行してください(端末があると標準エラー出力が標準出力に混ざってしまいます)。

docker compose exec -T api thinkingface admin token create admin \
    --name ci --expires-in-days 30 --repo datasets/admin/trackio-metrics > ci-token.txt

発行は actor: admin-cli 付きの access token created としてログに記録されます。他の admin コマンド と同様に、これを実行できる人はすでにデータベースを手にしています。

トークンを使う

huggingface_hub / datasets の場合: HF_ENDPOINT を自分のインスタンスに向け、HF_TOKEN に トークンの値を設定します(HF_HUB_DISABLE_XET=1 も設定してください。理由は 互換性 を参照してください)。

export HF_ENDPOINT=http://localhost:8080
export HF_TOKEN=tf_xxxxxxxxxxxx
export HF_HUB_DISABLE_XET=1

HTTP 経由の git の場合: トークンを Basic 認証のパスワードとして使用します。ユーザー名は無視され るため、空でなければ何でも構いません。

git clone http://admin:tf_xxxxxxxxxxxx@localhost:8080/datasets/admin/imdb-reviews.git

tf CLI の場合: tf login を対話的に実行するか、--token を渡すか、環境変数 THINKINGFACE_API_KEY を設定します。資格情報の解決順序の全体については tf CLI を参照してください。

生の API を使う場合: bearer トークンとして送信します。

curl -H "Authorization: Bearer tf_xxxxxxxxxxxx" http://localhost:8080/api/whoami-v2

HTTP 経由の git もこれと同じヘッダー形式を受け付けます。上記の Basic 認証形式が必要になるのは、ツール がユーザー名 / パスワードの入力を要求してくる場合だけです。

SSH 鍵

インスタンスの運用者が git-over-SSH を有効化している場合、Settings → SSH keys (/settings/ssh-keys)で公開鍵を登録すれば、URL にトークンを含めずに HTTP の代わりに SSH でクロー ンやプッシュができます。

git clone ssh://git@localhost:2222/admin/imdb-reviews.git

.pub ファイルの全行をそのまま貼り付けてください(例: ssh-ed25519 AAAA... you@example.com)。受け 付けられる鍵の種類は Ed25519、ECDSA、RSA(2048 ビット以上)です。DSA 鍵は理由の説明付きで拒否されま す。鍵の登録には、トークンの発行と同様に write スコープの資格情報が必要です。各フィンガープリントは インスタンス全体で 1 つのアカウントにしか登録できません。

セキュリティに関する注意事項

  • デフォルトでは、認証が制御するのは書き込みであり、読み取りではありません。 thinkingface には リポジトリ単位の可視性設定がないため、インスタンスに到達できる誰もが、未認証であっても、すべての リポジトリを読み取ることができます。トークンはその保持者が書き込める範囲を制御するものであり、 リポジトリを未読の状態に保つためのものではありません。 互換性 を参照してください。運用者は TF_REQUIRE_AUTH_FOR_READ でインスタンス全体の匿名読み取りを閉じることができます (デプロイ)。その場合でも、サインイン 済みのすべてのアカウントと有効なすべてのトークンは、引き続きすべてのリポジトリを読み取れます。
  • レート制限: パスワード認証の失敗(Web UI のログインフォームと、すべてのルートが受け付ける HTTP Basic 認証の両方)は、クライアントのアドレス(IPv4 アドレス、または IPv6 の /64)ごとに制限され、 さらに同じアドレスからの同じユーザー名にはその半分、全アドレス合計での同じユーザー名にはその 5 倍 の上限がかかります。これにより、単一のホストやネットワークからの失敗が、別の場所からログインする人 をロックアウトすることはなく、多数のアドレスに分散した推測にも上限がかかります。その裏返しとして、 十分に多くのネットワークに分散した攻撃者は、この上限を使い切り、攻撃が続く間そのアカウントを制限 状態に保つことができます。カウントされるのは失敗のみで、ログイン成功はこの制限枠を消費しません。制限にかかったリクエストは Retry-After ヘッダー付きの 429 を受け取ります。基本レートはインスタンスの運用者が TF_AUTH_RATE_LIMIT_PER_MIN で制御します(デ フォルトは 1 分あたり 10 回。0 に設定すると制限を完全に無効化します)。
  • git のスマートプロトコルのみが受け付けられます。 ダム HTTP プロトコルにフォールバックする git クライアントには、わかりにくい失敗ではなく、最新の git クライアントを使うよう促す明確なエラーが返 されます。
  • CORS: セッション Cookie で認証された状態変更リクエストは、許可リストに登録されたオリジン(Web UI 自身のオリジンに加え、開発環境ではプレーンな HTTP での localhost:3000)からのみ受け付けられま す。トークンで認証されたリクエストはこの制限の対象外です。Cookie のようにブラウザから受動的に発行 されることがないためです。Web UI が自身の同一オリジンプロキシを通じて API に到達する場合 (デフォルト)は、プロキシが転送前に自身のオリジンに対して同等のチェックを行います。

関連ページ

  • tf CLI — 資格情報の解決順序と tf login / tf status コマンド
  • 互換性 — HF_TOKEN、git、SSH がそれぞれ何をカバーするか
  • 設定 — 上記で触れた環境変数について、運用者側の視点 から