hrva.cc について
hrva.cc は、リンクの安全性検査とプライバシーを重視したクリック統計を備える URL 短縮バックエンドです。MCP コネクターを通じて、AI アプリから短縮リンクの作成、一覧表示、統計確認ができます。
主な機能
- 01ランダムまたは任意の別名でリンク作成
- 02有効期限、訪問数、パスワードの設定
- 03フィッシングとマルウェアの検査
- 04日別クリック統計の確認
- 05QR コードとキャンペーンタグの作成
活用シーン
- 01キャンペーン用の短縮リンク作成
- 02リンクのクリック統計の確認
- 03報告された不審なリンクの確認
MCP 対応
https://hrva.cc/mcp を通じて、AI アプリに hrva.cc のリンク管理機能を提供します。認証には OAuth 2.1 を使用します。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://hrva.cc/mcpclaude mcp add --transport http cc-hrva-url-shortener https://hrva.cc/mcp{"mcpServers":{"cc-hrva-url-shortener":{"type":"http","url":"https://hrva.cc/mcp"}}}Cursor で開く{"servers":{"cc-hrva-url-shortener":{"type":"http","url":"https://hrva.cc/mcp"}}}VS Code で開く{"mcpServers":{"cc-hrva-url-shortener":{"type":"http","url":"https://hrva.cc/mcp"}}}{"mcpServers":{"cc-hrva-url-shortener":{"serverUrl":"https://hrva.cc/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
詳細
URL-Shortener
概要
hrva.cc は、リンクの安全性チェック機能を備えた、プライバシーを最優先する URL 短縮サービスです。このリポジトリはバックエンドであり、短縮リンクの作成とリダイレクト、フィッシングやマルウェアのチェック、ユーザーを追跡しないクリック数の集計を行う Spring Boot API を提供します。フロントエンドは HrvaLabs/URL-Shortener-FE にあります。
変更履歴は app.hrva.cc/changelog で確認でき、CHANGELOG.md にも記載されています。AGENTS.md にはアーキテクチャ、コマンド、エンドポイント、設定が記載されています。
機能
リンク
- ランダムな短縮リンク、またはサインイン時に指定できるカスタムエイリアス。アカウントを作成せずにリンクを作ることもできますが、レート制限がより厳しくなります。
- 有効期限、アクセス回数の上限、パスワード保護、タグ。
- 一括作成、および API キー(
X-Api-Key)を使う API。各キーのデフォルトの上限は 1 時間あたり 600 リクエスト(サイト設定)です。すべてのレスポンスにはRateLimit-Limit、RateLimit-Remaining、RateLimit-Resetが含まれ、上限を超えたキーにはRetry-Afterを伴う 429 が返されます。キーは選択した日数(1〜3,650 日)後、デフォルトでは 12 か月後、または無期限で失効します。キーは SHA-256 ハッシュとして保存され、作成時にのみ完全なキーが表示されます。キーの一覧には先頭の数文字が表示されます。 - キャンペーンタグ(UTM)ビルダー。
- スキャン数を集計する QR コード。
- メール、Discord、Slack、Telegram、ntfy、または Webhook によるオプトイン通知:リンク切れ、期限切れのリマインダー、日次または週次の概要。
カスタムドメイン
go.example.com/menuのように、独自のサブドメインで短縮リンクを作成できます。Settings から申請し、管理者の承認後に CNAME レコードを 1 つ追加すると、Cloudflare for SaaS を通じて証明書が発行されます。- 独自ドメインのエイリアスは hrva.cc のエイリアスとは別に管理されます。独自ドメインのリンクでも、同じ統計情報、制限、パスワード、有効期限を利用できます。
プロフィールページ
hrva.cc/@yournameにページを 1 つ作成し、短縮リンクごとにボタンを設置できます。- 閲覧数は日ごとに集計され、ハンドルを変更しても以前のハンドルは 30 日間保持されます。
安全性
- 新しいリンク、すべてのクリック、夜間の再スキャンは、以下を通じてチェックされます。
- Google Safe Browsing
- 管理者が管理するブロックリスト(HaGeZi、OpenPhish、PhishTank、Phishing.Database、URLhaus)
- Spamhaus DBL
- ドメインの登録年数(RDAP)
- リダイレクトチェーンと類似ドメインのチェック
- 訪問者はリンクやプロフィールページを報告できます。
- 疑わしいリンクは、無条件にブロックされるのではなく、レビューキューに送られます。
- 新しいリンクの多くが同じ新しいドメインを指している場合、フィッシング攻撃の急増を知らせるアラートが表示されます。
プライバシーを最優先する分析
- クリック数はリンクごとの日次集計としてのみ保存されます。集計対象は訪問者、参照元、デバイス、ブラウザー、国、および他のクリック数に対する QR コードのスキャン数です。
- ユニーク訪問者数は、毎日変更されるキーを使った一方向ハッシュから算出され、Redis にのみ保存されます。
- ボットは別途集計されます。
- 公開ページ hrva.cc の統計情報 では、サイト全体の集計を確認できます。
アカウント
- メールアドレスとパスワード、Google、パスキー、またはセキュリティキーでサインインできます。
- すべてのサインイン方法で、認証アプリまたはパスキーとリカバリーコードを使った 2 段階認証を利用できます。
- データをエクスポートし、アカウントを削除できます。
AI コネクター
- Claude などの AI アプリは、OAuth 2.1 でサインインして
https://hrva.cc/mcpの MCP 経由で hrva.cc を利用できます。
管理者
- 統計情報、レビューキュー、モデレーション、ブロックリスト、ドメインルール、予約済みエイリアス、プロフィールページ、カスタムドメイン、監査ログの各ページ。
- Discord アラート、週次概要、日次の判断内容をまとめたメール。
使用技術
- バックエンド: Java 25、Spring Boot 4.1、Spring Security、Spring Authorization Server、Spring AI(MCP)
- データベース: Liquibase マイグレーションを使用する PostgreSQL、および Redis
- フロントエンド: Cloudflare Workers 上の Next.js 16(別リポジトリ)
- デプロイ: GitHub Actions が Docker イメージをビルドし、Portainer スタックの Webhook を呼び出します。サイトは Cloudflare の背後で稼働します。
実行方法
ビルド、テスト、実行コマンド、ローカル開発ユーザー、エンドツーエンドチェック(scripts/mcp-e2e.sh)については、AGENTS.mdを参照してください。概要としては、docker compose -f docker/docker-compose.yml up -dでPostgresとRedisを起動してから、mvn spring-boot:run -Dspring-boot.run.profiles=localを実行します。テストを実行するには、mvn -B testを実行してください。
本番環境の要件
- Docker イメージ:
eclipse-temurin:25-jreを使った 2 段階構成で、非 root ユーザーapp(uid 10001)として実行し、ポート 8080 で待ち受けます。30 秒ごとに/actuator/health/livenessを確認するHEALTHCHECKを設定します(起動期間は 120 秒とし、初回起動時や Liquibase の実行中に異常と判定されないようにします)。チェックではプライマリホスト(HEALTHCHECK_HOST、設定されていない場合はAPP_DOMAINS_PRIMARY_HOSTSの最初のエントリー、それもなければhrva.cc)を指定します。ほかのホストはすべて 404 を返すためです。CI は:latestと不変の:<commit sha>をプッシュします。Java が PID 1 なので、docker stopは SIGTERM でアプリを正常に終了し、30 秒のstop_grace_period内に収まります。- コミットで固定(任意): Portainer スタックで
image: ghcr.io/hrvalabs/url-shortener:latestをimage: ghcr.io/hrvalabs/url-shortener:<full commit sha>に変更し、スタックを更新します。Webhook は同じ sha を再デプロイするため、スタックの変更が反映されるのは編集したときだけです。以前の sha に戻すだけでロールバックできます。プッシュのたびにデプロイする場合は:latestのままにします。 - ポート: Nginx Proxy Manager は Docker ネットワーク上の
url-shortener-backend:8080に接続するため、アプリでポートを公開する必要はありません。Portainer スタックではポートを公開していないため、Cloudflare を迂回してオリジンにアクセスすることはできません。その状態を維持してください(ホストのポートが必要になった場合は127.0.0.1にバインドします)。
- コミットで固定(任意): Portainer スタックで
- Actuator: HTTP 経由で
healthを公開し、livenessとreadinessの各プローブも公開します。また(v2.30)、APP_METRICS_TOKENが設定されている場合に限りprometheusを公開します。Authorization: Bearer <APP_METRICS_TOKEN>を付けてアクセスすると、トークンが間違っているか未設定の場合は 401、変数が設定されていない場合は 404 を返します。env、beans、metrics、caches、infoは Web から公開しません。https://hrva.cc/actuator/healthはデータベースと Redis(どちらかがダウンしている場合は 503)、ディスク容量、ping、SSL をチェックしますが、メールサーバーはチェックしません(management.health.mail.enabled: false)。このチェックは呼び出しごとに SMTP 接続を確立し、各サービスへのネットワーク遅延が 30 ms のローカル実行では、185 ms のうち約 110 ms を占めました。コンテナーのチェックには liveness を使います。 - サインインの有効期間(v2.31):サインインは、Web アプリが最後に更新してから
app.jwt-token-validity-seconds(10 時間)が経過すると終了します。Web アプリは使用中に更新しますが、サインインからAPP_JWT_SESSION_MAX_SECONDS(デフォルトは 2592000、30 日)が経過すると、それ以上は有効になりません。「ログイン状態を保持する」の有効期間は従来どおり 30 日です。 - Daily Browser Run の利用上限(v2.30):
APP_INSPECTION_BROWSER_SECONDS_DAILY_LIMIT(デフォルトは 600。Cloudflare の無料枠)は、管理者の Health ページで本日のスクリーンショットの秒数と比較される値です。 - Redis がダウンした場合: レート制限はリクエストを通しますが、Redis が復旧するまで拒否する操作が 3 つあります。アカウントなしでのリンク作成、リンクパスワードによるロック解除、パスワードでのサインインです(いずれも
Retry-After: 60を付けて 503 を返します)。サインイン済みのユーザー、API キー、MCP による作成は続行できます。 - 管理者アカウントでは 2 段階サインインが必要です(v2.23):デプロイ前、またはデプロイ直後に、すべての管理者アカウントで有効にしてください。有効にするまでは、管理者ページと MCP のモデレーションツールは「管理者ページを利用するには、2 段階サインインを有効にしてください。」と応答します。
- アウトバウンド通信(v2.23):アプリが行うすべてのフェッチは検証済みのパブリックアドレスに固定されますが、ホスト側でもコンテナーから
169.254.169.254およびスタックの Docker ネットワーク以外のプライベート範囲へのアウトバウンド通信をブロックしてください(DOCKER-USERiptables ルール)。 - コードスキャン(v2.23):
codeql.ymlにはリポジトリでのコードスキャンが必要で、dependencies.ymlの PR レビューには依存関係グラフが必要です。プライベートリポジトリでは、どちらにも GitHub Advanced Security が必要です。 - 非アクティブなアカウントは、5 年間利用がないと無効化される 30 日前にメールを受け取ります(サインイン、API キー、接続済み MCP アプリは利用としてカウントされます)。設定は不要です。デプロイ後の初回実行時に、すでに期限を過ぎているすべてのアカウントに警告を送り、30 日後に無効化します。
- PostgreSQL 15 以降(分析カウンターで
MERGEを使用)と Redis 6.2 以降(GETDEL、SMISMEMBER)。 - 環境変数: 一覧は AGENTS.md(CI / Deploy、および MCP connector → Configuration)にあります。
APP_MCP_SIGNING_KEYは必須で、設定されていないとアプリは起動しません。秘密の RSA JWK を生成し、git とログに含めないでください。node -e "const {generateKeyPairSync, randomUUID} = require('node:crypto'); const {privateKey} = generateKeyPairSync('rsa', {modulusLength: 2048}); console.log(JSON.stringify({...privateKey.export({format: 'jwk'}), kid: randomUUID(), use: 'sig', alg: 'RS256'}))" APP_NOTIFICATIONS_KEYも必須で、設定されていないとアプリは起動しません。通知チャネルの URL とトークンを暗号化し、配信停止リンクに署名するため、MCP 署名キーと同じ場所に保管してください。これを失うと、全員がチャネルを再登録する必要があります。ランダムな 32 バイトを生成します。openssl rand -base64 32- アカウントなしで作成されたリンクの人間確認には、
APP_TURNSTILE_SITE_KEYとAPP_TURNSTILE_SECRET_KEY(Turnstile ウィジェットのキー)が必要です。これらがなくてもアプリは起動しますが、確認は実行されません。シークレットキーを git とログに含めないでください。 - カスタムドメインには
CLOUDFLARE_API_TOKENとCLOUDFLARE_ZONE_IDが必要です。設定されていなくてもアプリは起動しますが、承認を拒否します。その他の変数(APP_DOMAINS_ENABLED、APP_DOMAINS_MAX_ACTIVE、APP_DOMAINS_FREE_ALLOWANCE、APP_DOMAINS_PER_USER、APP_DOMAINS_RESERVED、APP_DOMAINS_PRIMARY_HOSTS、APP_DOMAINS_IGNORED_HOSTNAMES、APP_DOMAINS_CNAME_TARGET、APP_CLOUDFLARE_API_BASE)には既定値があり、AGENTS.md(Config Profiles)に記載されています。カスタムドメインを使う初回デプロイの前に、以下を確認してください。- O1:
CLOUDFLARE_ZONE_IDは hrva.cc ゾーンの ID であり、トークンには hrva.cc ゾーンだけで Zone、SSL and Certificates、Edit の権限が付与されています。このトークンをほかの場所では使わないでください。 - O2: リバースプロキシの全ホストを受け付ける
default_serverは、プロキシホストと同様に Cloudflare 以外のアドレスからのアクセスを拒否します(444)。カスタムホスト名はこの経路でオリジンに到達します。 - O3: バックエンドへの接続を維持する必要があるホスト名はすべて
APP_DOMAINS_PRIMARY_HOSTSに含めてください(デフォルトはhrva.cc,domains.hrva.cc。app.server-urlのホストは常に含まれます)。デプロイ後、ほかのHostはすべて 404 を返します。コンテナーへの IP アドレスまたはlocalhostによるリクエストも同様です。そのため、ヘルスチェックはhttps://hrva.cc/actuator/health経由で実行してください。 - O4:
APP_DOMAINS_IGNORED_HOSTNAMESには、カスタムドメインではない Cloudflare ホスト名を設定します(デフォルトはtest.karlo.cc)。これにより、夜間の調整処理でそれらのホスト名が問題として検出されなくなります。それらのホスト名は 404 を返します。 - O5: 管理者の System タブで PostgreSQL 15 以降であることを確認してください。それ以外の場合、マイグレーション 032 により起動が停止します。
- O6: カスタムホスト名は、hrva.cc ゾーンの Cloudflare ルールを通過します。必要なキャッシュ、設定、リダイレクト、WAF スキップの各ルールが、
http.host eq "hrva.cc"のみに一致するようになっていないことを確認してください。また、リダイレクトがキャッシュされないようにしてください。
- O1:
- Cloudflare のみからアクセスできるオリジン。 ネットワークごとのレート制限は
CF-Connecting-IPを信頼し、国別カウントはCF-IPCountryを信頼します。そのため、オリジンへのトラフィックは Cloudflare Tunnel 経由、またはCloudflare の IP 範囲に限定したファイアウォール経由で、Cloudflare からのみ受け付ける必要があります。そうしないと、誰でもこれらのヘッダーを偽装できます。 - MCP のパスとワンクリック配信停止に対する Cloudflare ルール。 MCP クライアントはブラウザーではなく、メールプロバイダーも同様です。メールプロバイダーはメールの
List-Unsubscribeヘッダーを使って、ワンクリック配信停止(RFC 8058)の POST リクエストを送信します。チャレンジを設定すると、どちらも機能しなくなります。/mcp、/oauth2/*、/.well-known/*、/api/v1/notifications/unsubscribeを WAF のマネージドチャレンジと Super Bot Fight Mode の対象外にしてください。Free プランで Bot Fight Mode がこれらにチャレンジを課す場合は、無効にしてください。- エッジで
GET /oauth2/authorizeや/.well-known/*をキャッシュしないでください。
- デプロイ後のスモークテスト。 発行者は
https://hrva.cc、リソースはhttps://hrva.cc/mcpで、/mcpはトークンなしの場合に 401 を返すことを確認してください。curl -s https://hrva.cc/actuator/health curl -s https://hrva.cc/.well-known/oauth-authorization-server | jq '{issuer, registration_endpoint}' curl -s https://hrva.cc/.well-known/oauth-protected-resource/mcp | jq '{resource, authorization_servers}' curl -s -o /dev/null -w '%{http_code}\n' -X POST https://hrva.cc/mcp
コントリビューション
コミュニティからのコントリビューションを歓迎します!
ライセンス
このプロジェクトは GNU General Public License v3.0 の下でライセンスされています。詳細については、LICENSE ファイルをご覧ください。
お問い合わせ
質問、不具合、機能のリクエストがある場合は、GitHub で issue を作成するか、メールで直接ご連絡ください。
URL-Shortener をご利用いただき、またはこのページをご覧いただき、ありがとうございます!
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。