Address Validator について
Address Validatorは、配送先住所の標準化とジオコーディングを必要とするサービス向けの従量課金HTTP APIです。配達可能性と信頼度のスコア、キャッシュキーを返し、プロバイダーが停止している場合は縮退結果を返します。
主な機能
- 01認識された配送先住所を標準化
- 02緯度・経度を返却
- 03配達可能性と信頼度のスコアを提供
- 04重複する住所の結果をキャッシュ
- 05プロバイダー障害時に縮退レスポンスを返却
活用シーン
- 01出荷処理前に住所を正規化
- 02住所の座標と配達可能性を確認
- 03プロバイダー停止時の住所検証を継続
MCP 対応
x402またはMCPディレクトリ向けに、サービスとスキーマを説明する`/manifest`を提供します。READMEにはMCPツールやMCPサーバーインターフェースの説明はありません。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://kder-ai.com/mcpclaude mcp add --transport http kder974-address-validator https://kder-ai.com/mcp{"mcpServers":{"kder974-address-validator":{"type":"http","url":"https://kder-ai.com/mcp"}}}Cursor で開く{"servers":{"kder974-address-validator":{"type":"http","url":"https://kder-ai.com/mcp"}}}VS Code で開く{"mcpServers":{"kder974-address-validator":{"type":"http","url":"https://kder-ai.com/mcp"}}}{"mcpServers":{"kder974-address-validator":{"serverUrl":"https://kder-ai.com/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
1| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| address | any | はい |
詳細
住所検証 API
リクエストごとに課金される HTTP API(x402 プロトコル、デフォルト価格
0.02 USDC)です。未加工の配送先住所を受け取り、標準化した住所、
その座標、配達可能性スコア、キャッシュキーを返します。
1. 概要
- 唯一の有料エンドポイント:
POST /validate— 有効な支払い証明がない場合は402 Payment Requiredを返します。 - 出力:標準化された住所(
line1、line2、city、state、zip、country)。住所が認識された場合はプロバイダーによって修正されます(D84: 綴り、市区町村、郵便番号の追加)。latitude/longitude(Nominatim によるジオコーディング)、deliverability、confidence、degraded、cache_key。 - サービスの検出:
GET /manifestは、x402/MCP ディレクトリ向けにサービスと そのスキーマを記述します。 - 耐障害性:プロバイダーに接続できない場合でも、API は
degraded: trueを付けて200を返します — 住所が確実に無効だと思わせる エラーは返しません。
仕様の成功基準と、自動検証箇所:
| # | 基準 | 検証 |
|---|---|---|
| 1 | 支払い証明なしの呼び出しは 402 を受け取る(価格とプロトコルが通知される) | tests/test_payment.py(8 件のテスト)、tests/test_routes_extra.py::test_metrics_count_unpaid_requests、scripts/e2e_check.py |
| 2 | 有効な支払い証明を伴う呼び出しは 200 と結果を受け取る | tests/test_cache.py(実際の有料呼び出し)、tests/test_validation.py::test_valid_address_with_mocked_provider、scripts/e2e_check.py |
| 3 | キャッシュにより、同じ住所でプロバイダーを再度呼び出さずに済む | tests/test_cache.py::test_second_identical_request_hits_cache と ::test_distinct_requests_call_provider_twice、scripts/e2e_check.py |
| 4 | プロバイダーの障害時に、明示的な縮退応答を迅速に返す | tests/test_validation.py::test_degraded_flag_set_when_provider_unreachable、tests/test_circuit_breaker.py、tests/test_latency_budget.py |
このサービスが行わないこと:アプリケーションレベルのレート制限はありません(D21)—
制限は Nginx の edge レベルで適用されます(nginx-hardening.conf:IP ごとに 10
リクエスト/秒、バースト 20、それを超えると 429)。従来の支払い方式(共有シークレットによる HMAC 証明、D15)は、
署名だけでは受け付けなくなりました:W1 のセキュリティ強化(12/10/2026)以降、
オンチェーン確認(D27)がデフォルトで有効になっており、Base 上で
入金用ウォレットへの確認済み USDC 送金を要求します。合成 HMAC 証明で 200 を取得できるのは、
宣言されたレシピ(TEST_MODE=true)でのみです。この場合、書き込まれる行には
test_mode のマークが付き、実際の収益から除外されます(D40)。すべての内容は
隠すのではなく、文書化されています(README.md
§7 および §9、SECURITY.md §3 および §6、AUDIT_TRAFIC_VPS.md §5)。
2. クイックスタート
前提条件:Python 3.12(リポジトリの venv は、Dockerfile の
python:3.12-slim イメージと同じく 3.12.10 で動作します)。PostgreSQL と Redis は
任意です:これらがなくても API は起動し、適切に縮退します(/health で確認できます)。
# 1. Environnement virtuel
py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt -r requirements-dev.txt
# 2. Configuration (jamais de secret dans le dépôt)
Copy-Item .env.example .env # renseigner SMARTY_AUTH_ID / API_SECRET_KEY / clés CDP
# 3. Lancer l'API
.\.venv\Scripts\python.exe -m uvicorn app.main:app --reload --port 8000
# Vérifications
.\.venv\Scripts\python.exe -m pytest -q # suite complète
.\.venv\Scripts\python.exe scripts\run_tests.py # oracle de tests (rapport JUnit)
.\.venv\Scripts\python.exe scripts\doc_check.py README.md 10 # sections de ce fichier
.\.venv\Scripts\python.exe scripts\e2e_check.py http://127.0.0.1:8000 # recette bout-en-bout
コンテナでの起動:docker compose up -d(docker-compose.yml と
Dockerfile を参照)。
本番環境へのデプロイ(Debian 12 VPS、Nginx + HTTPS、x402 支払い):
DEPLOIEMENT_VPS.md — 実運用の記録、計画との差異として許容した点、
運用上の注意事項。
3. 設定
すべての値は環境変数または .env から取得します(pydantic-settings、
app/config.py)。不明な変数は無視されます(決定 D8)。
| 項目 | 変数 | デフォルト | 役割 |
|---|---|---|---|
api_host / api_port | API_HOST / API_PORT | 127.0.0.1 / 8000 | uvicorn のリッスン先(127.0.0.1:サービスは Nginx 経由で公開され、直接公開されません) |
api_secret_key | API_SECRET_KEY | change_me | 支払い証明の HMAC キー — 必ず変更してください |
database_url | DATABASE_URL | postgresql://user:password@localhost:5432/address_validator | PostgreSQL(支払いログ) |
redis_url | REDIS_URL | redis://localhost:6379/0 | 結果のキャッシュ |
smarty_auth_id / smarty_auth_token | SMARTY_AUTH_ID / SMARTY_AUTH_TOKEN | 空 | 検証プロバイダーの認証情報 |
cdp_api_key_id / cdp_api_key_secret | CDP_API_KEY_ID / CDP_API_KEY_SECRET | 空 | Coinbase CDP SDK(任意) |
cdp_wallet_address | CDP_WALLET_ADDRESS | 空 | 402 で通知されるウォレット |
x402_price | X402_PRICE | 0.02 | 検証ごとの価格(USDC) |
x402_onchain_verify | X402_ONCHAIN_VERIFY | true | USDC 転送のオンチェーン確認(D27、W1 からデフォルトで有効)— false が許可されるのは TEST_MODE=true の場合のみです。受け入れテスト以外では、共有シークレットによる証明はすべて拒否されます — §7 を参照 |
base_rpc_url | BASE_RPC_URL | https://mainnet.base.org | D27 が有効な場合に使用する Base JSON-RPC ノード(チェーン 8453) |
x402_usdc_contract | X402_USDC_CONTRACT | 0x833589…4bda02913 | Base のネイティブ USDC コントラクト |
x402_min_confirmations | X402_MIN_CONFIRMATIONS | 1 | 支払いを受け入れる前に必要な最小確認数 |
x402_network / x402_chain_id | X402_NETWORK / X402_CHAIN_ID | base / 8453 | accepts[] で通知されるネットワークとチェーン |
x402_domain_name / x402_domain_version | X402_DOMAIN_NAME / X402_DOMAIN_VERSION | USD Coin / 2 | トークンの EIP-712 ドメイン — scripts/test_x402_payment.py --check-domain によってオンチェーンで検証されます |
x402_facilitator_url | X402_FACILITATOR_URL | https://api.cdp.coinbase.com/platform/v2/x402 | ファシリテーター(/verify の後に /settle) |
x402_facilitator_verify | X402_FACILITATOR_VERIFY | true | 決済前に /verify を呼び出します |
x402_require_settlement | X402_REQUIRE_SETTLEMENT | true | 入金後にのみサービスを提供します(false は本番環境では禁止) |
public_base_url / mcp_public_url | PUBLIC_BASE_URL / MCP_PUBLIC_URL | https://kder-ai.com / .../mcp | accepts[].resource、MCP カード、ディレクトリで公開される URL |
discovery_log_* | DISCOVERY_LOG_ENABLED, DISCOVERY_LOG_PATH, DISCOVERY_LOG_RAW_IP, DISCOVERY_LOG_SALT, DISCOVERY_LOG_RETENTION_DAYS | ログ有効、パスは空(journald)、IP は仮名化、30 日 | 402 の拒否ログと適用される保持期間(PRIVACY.md 3) |
dashboard_user / dashboard_password | DASHBOARD_USER / DASHBOARD_PASSWORD | kder / 空 | /dashboard — 空の場合はエンドポイントが閉鎖されます(503)。IP ごとに LOGIN_MAX_FAILURES 回パスワードを間違えると、LOGIN_LOCK_MINUTES 分間ブロックされます(D67) |
geoip_db_path / audience_retention_days | GEOIP_DB_PATH / AUDIENCE_RETENTION_DAYS | /var/lib/address-validator/dbip-country.mmdb / 400 | オーディエンス追跡(D67):scripts/maj_geoip.sh で配置される DB-IP Lite の国データベース(存在しない場合は国不明);page_visits および api_calls 行の保持期間 |
pilotage_comptes | PILOTAGE_COMPTES | 空 | ダッシュボードのアカウント(D74)。「名前=email」の形式でカンマ区切り。公開リポジトリのコードには絶対に含めないでください |
cost_server_eur / cost_smarty_eur / cost_smarty_per_call_usd / cost_mail_eur | COST_SERVER_EUR / COST_SMARTY_EUR / COST_SMARTY_PER_CALL_USD / COST_MAIL_EUR | 0 | ダッシュボードが読み取るコスト(D68):すべて 0 の場合、当月の結果は計算されません |
test_mode / test_mode_cutoff_utc | TEST_MODE / TEST_MODE_CUTOFF_UTC | false / 2026-10-11T12:00:00+00:00 | test_mode とマークされたトランザクションは受け入れテストです。real_* から除外され、test_* と total_* に計上されます(D40) |
environment / log_level | ENVIRONMENT / LOG_LEVEL | development / INFO | 環境とログの詳細度 |
4. エンドポイントとデータ契約
| メソッド | パス | 役割 | ソース |
|---|---|---|---|
GET | / | サービスの識別情報とリンク | app/main.py:57-65 |
GET | /manifest | x402 の説明(価格、通貨、スキーマ) | app/routes/manifest.py |
GET | /health | PostgreSQL と Redis の実際の状態。タイムアウトは 2 秒 | app/routes/health.py |
GET | /metrics | カウンター、サーキットブレーカーの状態、キャッシュの状態 | app/routes/metrics.py |
GET | /metrics/revenue | 24 時間/7 日/30 日の集計収益 — real_* はテスト環境を除外、total_* はレジストリ全体(D40)— およびレジストリの可用性 | app/routes/revenue.py |
GET | /dashboard | 運用ダッシュボード — 非公開(Basic Auth。DASHBOARD_PASSWORD が未設定の場合は 503。IP ごとにパスワードを 5 回間違えると 15 分間ブロック、D67)。D67 以降、公開ページへの訪問と API 呼び出しはそれぞれ追跡行を記録します(国、参照元、所要時間、ステータス。IP と Cookie は記録しません)。D69 以降、v2 デザイン(サイドバー、結果の大型カード、ドットマップ、ソース・robots・ページを含むサイトと SEO、訪問から有料利用までの経路)。D68 以降、収支画面(実際の入金額、請求予定のサブスクリプション、コスト、結果、予測、国別・チャネル別収益)、API画面(ステータス、応答時間、キャッシュ、チャネル、国)、顧客画面(期限切れになるトライアル、クォータ、未使用アカウント)、アラート、期間 `?p=jour | 7j |
GET | /dashboard/argent, /dashboard/api, /dashboard/clients, /dashboard/site, /dashboard/registre | 運用管理の各セクションのページ(D71)。/dashboard と同じアクセス制御。期間は ?p= で保持 | app/routes/dashboard.py, app/pilotage_page.py |
GET POST | /dashboard/mot-de-passe, /dashboard/nouveau-mot-de-passe | ダッシュボードアカウント(D74):PILOTAGE_COMPTES に登録されたメールアドレスに、パスワードを設定するリンクを送信します(有効期限 30 分、1 回限り、データベースにはハッシュのみを保存、bcrypt ハッシュ)。アドレスが未登録の場合も同じ応答を返します。アカウントごとに 1 時間あたり 3 件のリンクを発行。以後はメールアドレスでログイン | app/pilotage_comptes.py, app/routes/dashboard.py |
GET POST | /dashboard/connexion, /dashboard/deconnexion | ダッシュボードのログインページ(D69)。Basic 認証のダイアログに代わり、署名付き Cookie __Host-av_pilotage(HMAC、12 時間、HttpOnly、Secure、SameSite=Strict)を使用します。同じく 5 回失敗すると 15 分間ロックされ、別サイトからのフォーム送信は拒否されます。セッションのないブラウザーはログインページにリダイレクトされ、スクリプトには Basic 認証のチャレンジが表示されます | app/routes/dashboard.py, app/pilotage_page.py |
GET | /prospection | 営業開拓 CRM(一覧、フィルター、マーク、エクスポート)— 非公開。/dashboard と同じアクセス制御(パスワードが未設定、または PROSPECTION_DB が存在しない場合は 503) | app/routes/prospection.py, prospection/README.md |
GET | /pricing | 商用プランの公開ページ(D43)— 公開、インデックス登録可能、5 分間キャッシュ。外部リソースは使用せず、/dashboard とスタイルシートを共有(app/web.py) | app/routes/pricing.py |
GET POST | /signup | セルフサービス登録(D44)— 公開。パスワードは bcrypt でハッシュ化し、API キー sk_live_ は一度だけ表示。IP ごとに 1 時間あたり 5 件の登録 | app/routes/signup.py, app/accounts.py |
GET POST | /validate-form | 公開無料トライアル(D46)— 公開、インデックス登録可能。IP ごとに 24 時間あたり 3 回まで(D94。超過時は 429 と正確な Retry-After を返します)。書き込みは行わず、何も読み込みません。劣化した結果はその旨を明示します。1 行の住所(フィールド address、D86)も受け付けます | app/routes/validate_form.py, app/ratelimit.py |
POST | /stripe/webhook | Stripe 決済(D91):署名付きの checkout.session.completed(Stripe-Signature、HMAC-SHA256、5 分)が届くと、client_reference_id のアカウントを有料プランに切り替えます(2 900 → Starter、7 900 → Pro)。customer.subscription.deleted が届くと、期限切れのトライアルに戻します。STRIPE_WEBHOOK_SECRET が未設定の場合は 503 | app/routes/stripe_webhook.py, app/accounts.py |
GET | /robots.txt, /sitemap.xml, /favicon.ico | 検索エンジン最適化(D65)— 検索エンジンと AI に公開されたサイト。公開ページを 6 言語と各 hreflang で掲載するサイトマップ、アイコン。公開ページには `` ブロック(canonical、Open Graph、JSON-LD)が含まれます。www. は PUBLIC_BASE_URL に 301 リダイレクトします | app/seo.py |
GET | /819a36fb2a9140f4f142b6e80ae19cc3.txt | IndexNow キー(D81)。設計上公開されています。scripts/indexnow.py --envoyer はサイトマップのページを Bing、Yandex、Seznam、Naver に通知します | app/seo.py |
GET | /guide | 公開連携ガイド(D48)— 公開、インデックス登録可能、5 分間キャッシュ可能。シークレットは含みません。JSON 契約、戻りコード、キャッシュ、クォータ、直接呼び出し/CSV/Shopify/WooCommerce の例を記載 | app/routes/guide.py |
GET | /agents, /compare | コンテンツページ(D66)— 公開、インデックス登録可能、6 言語対応。AI エージェント向け住所検証(MCP、x402、アカウント不要)と、Google、Smarty、PostGrid、Geoapify の公表価格の比較(2026/10/14 時点の調査、出典を明記、「どのサービスを選ぶか」)。当社の数値はコードと設定に基づいています | app/routes/contenu.py |
GET POST | /compte, /compte/cle | 顧客ページ:月次ダッシュボード(D64:月次の数値、アカウントの状態から推定した次のステップ、日ごとのグラフ、アドバイス)、API キー、プランと請求 — 非公開(/compte/connexion でログインするか、スクリプトから送信された Basic Auth を使用。セッションがない場合はログインページに 303 リダイレクト。キーの再生成にはパスワードの再入力が必要。データベースが応答しない場合は 503) | app/routes/signup.py, app/accounts.py |
GET POST | /compte/connexion, /compte/deconnexion | 顧客ページへのログイン(D62)— 非公開ページ(no-store、noindex)。Cookie __Host-av_session(ランダムトークン。データベースにはハッシュのみを保存。有効期限は最大 12 時間)。パスワードを 5 回間違えると、IP とアカウントごとに 15 分間ブロックされます。別サイトからのフォーム送信は拒否されます | app/routes/connexion.py |
GET POST | /compte/mot-de-passe-oublie, /compte/nouveau-mot-de-passe | パスワードを忘れた場合(D63)— PUBLIC_BASE_URL を使用して生成するメール内リンク。データベースにはハッシュのみを保存。有効期限 30 分、1 回限り。アカウントの有無にかかわらず同じ応答を返します。アカウントごとに 1 時間あたり 3 通、IP ごとに 1 時間あたり 5 件のリクエストまで。新しいパスワードを設定すると、すべてのセッションを終了します。SMTP_HOST 経由で送信(空の場合はメールを送信しません) | app/routes/mot_de_passe.py |
POST | /validate | 有料 — 完全な検証。2 つの支払い方法:サブスクリプション(X-API-Key、D45)または呼び出しごとの決済(x402/HMAC 証明) | app/routes/validate.py |
GET | /docs | インタラクティブドキュメント(FastAPI) | 自動 |
GET | /llms.txt, /AGENTS.md, /.well-known/security.txt | エージェント向けの検出ファイル。Nginx が静的ファイルとして配信します(DISCOVERY.md を参照) | nginx-discovery.conf |
POST | /mcp | MCP サーバー(JSON-RPC 2.0):検出は無料、tools/call は有料(x402 v2 トランスポート:支払いは params._meta["x402/payment"] に指定し、オファーとレシートはツールの結果に含めます。ヘッダー X-PAYMENT による v1 も受け付けます) | app/routes/mcp.py |
GET | /.well-known/mcp-server | MCP 検出カード(アプリケーションが配信) | app/routes/mcp.py |
GET | /.well-known/x402 | x402 検出マニフェスト(IETF ドラフト draft-hawkins-x402-dns-discovery-03)。アプリケーションが配信します。/openapi.json では API のみを公開します(D95) | app/routes/manifest.py |
拒否された 402 リクエストは、メタデータ(タイムスタンプ、エンドポイント、User-Agent、仮名化された IP)の形式で記録されます。検証済みの住所が記録されることはありません。設定と保持期間については、PRIVACY.md §3、DISCOVERY.md §4 を参照してください。 |
4.1 実収益とレジストリ収益(D40)
/metrics/revenue は同じレジストリについて、混同することなく3種類の値を公開します。
| フィールド | 内容 |
|---|---|
real_validations_paid, real_revenue_usdc | クライアントが支払った金額 — 公開可能な数値 |
total_validations_paid, total_revenue_usdc | リハーサルを含むレジストリ全体(監査用) |
test_validations_paid, test_revenue_usdc | 実収益から除外される金額。real_* + test_* = total_* |
エイリアス validations_paid と revenue_usdc(D31)は実収益を指します。
トランザクションが test_mode としてマークされる方法は、次の2つだけです。
サービスが記録する時点での TEST_MODE=true(リハーサル中)、または
すでに記録済みの行に対する scripts/migrate_test_mode.py の実行です。しきい値は
TEST_MODE_CUTOFF_UTC(デフォルトは 2026-10-11T12:00:00+00:00。その日の2件の
リハーサル)です。金額や住所から推測されることはありません。マークされていない行は実収益のままです。/dashboard では実収益を先頭に表示し、リハーサルは専用のカードに表示します。
すでに記録済みのレジストリは、再デプロイせずに遡及処理できます。
venv/bin/python scripts/migrate_test_mode.py(行ごとのシミュレーション)を実行してから、
--apply を指定します。再実行しても新たな書き込みは発生しません(冪等性)。結果は
curl -s http://127.0.0.1:8000/metrics/revenue で確認できます。
リクエスト(app/schemas.py:11-16):{"address": {"street", "city", "state", "zip", "country"}} — country のデフォルト値は US です。それ以外のフィールドは必須です(フィールドが欠けている場合は 422)。
レスポンス(app/schemas.py:19-37):{"result": {"validated", "standardized", "latitude", "longitude", "deliverability", "confidence", "degraded", "cache_key"}, "price_paid", "transaction_id"}。
検証パイプライン(app/validation/__init__.py:26-41):
parse → standardize → verify (fournisseur) → geocode → score。D84 以降、認識された住所は、ジオコーディングの前に、プロバイダーが修正した形式(corrige)で parse → standardize を再実行します。
| 出力 | 意味 |
|---|---|
degraded: true | プロバイダーに問い合わせできませんでした(障害、タイムアウト、サーキットブレーカーが開いている状態)。その場合、deliverability は invalid になりますが、住所が存在しないことの証拠にはなりません |
deliverability / confidence | DPV コードに基づきます:Y → residential 0,99;S → residential 0,95;D → commercial 0,99;X → unknown 0,50;N → invalid 0,00;座標を取得できた場合は +0,05(app/validation/scorer.py:17-36) |
cache_key | 正規化された生の住所の SHA-256 ハッシュです。キャッシュ項目を識別し、削除に使用できます(PRIVACY.md §5 を参照) |
4.2 同じ検証に対する2つの支払い方法(D45)
POST /validate は2種類のクライアントに対応します。APIキーを提示するサブスクリプション契約店舗と、呼び出しごとに支払う(§5)自律型エージェントです。APIキーが優先されます。両方が送信された場合はサブスクリプションが適用され、呼び出しに対するUSDCの請求は行われません。
| モード | 入力ヘッダー | 引き落とされるもの | price_paid |
|---|---|---|---|
| サブスクリプション | X-API-Key: sk_live_… | アカウントのカウンター(customer_validations) | 0.0 |
| x402 / HMAC | X-PAYMENT または X-Payment-Proof | 支払い台帳(D29) | 0.02(USDC) |
レスポンスには X-Payment-Mode(api_key、x402、hmac)が含まれます。これがなければ、クライアントはサブスクリプションでカバーされた呼び出しか、請求された呼び出しかを区別できません。APIキーモードでは、アカウントの状態を示す2つのヘッダー、X-Plan と X-Quota-Remaining も返されます(無制限プランでは省略され、省略されている場合は「無制限」を意味します)。
| 状況 | コード | このコードになる理由 |
|---|---|---|
| 有効なキーで、当月のクォータに空きがある | 200 | リクエストを処理し、アカウントから引き落とします。USDC台帳はそのままです |
| 不明なキー(または空のキー) | 401 | 修正が必要なのはクライアントの設定です |
無料トライアル終了(trial プラン) | 403 | アカウントは存在しますが、サービスを利用できなくなっています |
| 月間クォータを使い切った | 402 | 利用上限であり、認証の失敗ではありません |
| アカウントデータベースに接続できない | 503 | 検証されていないキーは、拒否されたキーではありません。402 は決して返しません |
クォータは /pricing に掲載されているとおり月単位です。期間は暦月のUTC(accounts.month_start())で、累計カウンターは /compte に引き続き表示されますが、利用を制限することはありません。拒否時の本文は判別可能で({"mode": "api_key", "reason": "cle_inconnue" | "compte_expire" | "quota_atteint"})、キーのプレフィックス(sk_live_ab12)のみを含み、次の手段(upgrade → /pricing)を示します。
$cle = "sk_live_..." # affichée une seule fois à l'inscription, régénérable sur /compte
$corps = '{"address":{"street":"1 Rue de Paris","city":"Saint-Denis","state":"RE","zip":"97400","country":"RE"}}'
$r = Invoke-WebRequest -Uri http://127.0.0.1:8000/validate -Method POST `
-Headers @{ "X-API-Key" = $cle } -ContentType "application/json" -Body $corps
$r.Headers["X-Payment-Mode"], $r.Headers["X-Plan"], $r.Headers["X-Quota-Remaining"]
$r.Content | ConvertFrom-Json | Select-Object price_paid, transaction_id
# -> api_key trial 99 ; price_paid = 0.0, transaction_id = $null
X-API-Key ヘッダーなしの同じ呼び出しは、引き続きx402で課金されます。APIキーモードによって、自律型エージェントの機能が損なわれることはありません。/metrics で公開されるカウンター:api_key_requests、api_key_validations、api_key_denied、api_key_unavailable。
4.3 公開無料トライアル(D46)
GET /validate-form は公開ページを提供します。検索エンジンに登録可能で、5分間キャッシュでき、単独で動作します。スクリプトも外部リソースも使わず、共有スタイルシート(app/web.py)を使用します。訪問者が最初に目にするページであり、CSP(form-action 'self')により、コードの約束をブラウザーで検証できます。
| 項目 | 動作 |
|---|---|
| 上限 | VALIDATE_FORM_RATE_LIMIT_PER_DAY(デフォルトは10)により、IPアドレスごとに24時間あたり10回まで試行できます。期間は固定で、最初の試行時に開始し、拒否のたびに延長されることはありません |
| 拒否 | 429 + 正確な Retry-After(期間の残り秒数)。上限を再掲し、/signup へのリンクを表示します |
| 書き込み | なし:customers、customer_validations、payment_transactions への書き込みはありません。validate_form_requests / validate_form_validations / validate_form_denied のカウンターのみが増加します |
| パイプライン | /validate と同じ(perform_validation):判定もキャッシュも同じです |
| キャッシュとインデックス登録 | 空のフォームは public, max-age=300 でキャッシュされ、インデックス登録されます。結果、エラー、拒否を含むすべての POST レスポンスは、住所を含むため no-store + noindex です |
| 劣化時の結果 | その旨を表示します:「プロバイダーから応答がありません。この応答から住所について何も判断できません」——住所が無効だとは決して表示しません |
| 入力 | エスケープして再表示し、修正できるようにします。2文字でない国コードは、プロバイダーを呼び出す前に拒否されます(400) |
上限カウンターは /metrics で確認できます。上限による拒否をカウントすることで初めて、「誰も試していない」のか「全員がブロックされている」のかを区別できます。
4.4 公開統合ガイド(D48)
GET /guide は、キーを受け取ったばかりのクライアントが知りたいことに答えます。認証(X-API-Key または x402)、正確な JSON 仕様、実行すべきアクションを含む8 つの戻りコード、キャッシュされる情報と上限が設けられる情報、さらに 4 つの実用例(PowerShell での直接呼び出し、Python での CSV インポート、Shopify、WooCommerce)です。
| 項目 | 動作 |
|---|---|
| 公開される情報 | 公開データベース、価格、無料トライアルの上限、ヘッダー X-Payment-Mode / X-Plan / X-Quota-Remaining、プラン:コードと設定から読み取られ、転記されることはありません(置換されていないひな形はゲートが拒否します) |
| シークレット | ありません。このページはインデックス可能であり、設計上公開されています。サンプルキーはひな形です(sk_live_VOTRE_CLE) |
| 自立性 | スクリプトなし、外部リソースなし、共有スタイルシート、Cache-Control: public, max-age=300、form-action 'none' |
| ナビゲーション | 定義は 1 か所のみ(app/web.py: site_head()):/guide はすべての公開ページからアクセスでき、/pricing は独自の <nav> のコピーを持たなくなりました |
| 明示する制限 | Shopify アプリも WooCommerce 拡張機能も提供されません(呼び出しはクライアント側のサービスから行います)。degraded の結果は「再確認が必要」であり、無効な住所ではありません |
4.5 ショップ向けウェルカムメール(D49)
ページではなく、アカウントを作成したばかりのショップに送信するメッセージです。EMAIL_BIENVENUE.md にあり、2 つの形式で提供されます。n8n に読み込むひな形(変数 {{…}} は表で説明)と、最初のクライアント(Tiitoune Art)向けの送信準備済みのメールです。
| 項目 | 動作 |
|---|---|
| 数値 | トライアル日数、無料クォータ、月額料金はコード(accounts.JOURS_ESSAI、VALIDATIONS_ESSAI)と /pricing から読み取られます。これらが反映されていないメールはゲートが拒否します |
| 案内する方法 | サブスクリプション(X-API-Key):X-PAYMENT、X-Payment-Proof、tx_hash は含みません。ゲートはこのメッセージでこれらを禁止しています(呼び出しごとの支払いはエージェント向けの方法であり、/guide に記載されています) |
| シークレット | キーは一切含みません。本文では /signup と /compte を案内します。ゲートは完全な sk_live_ キーや、リポジトリのデフォルト値を拒否します |
| 明示する制限 | 自動引き落としなし、住所について「何も示さない」degraded、保存されるのは指紋(住所ではありません)、cache_key による消去 |
| 送信 | メール本文に差し込み変数が残っていないこと(確認済み)、ひな形の各変数が説明されていること(確認済み) |
4.6 顧客追跡(D50)
追跡表の価値は、実際に提供されている内容と一致しているかどうかで決まります。
アカウントテーブルにすでに含まれている情報で、収益を左右する唯一の問いに答えられます。どのアカウントが存在し、どのアカウントがひそかに失われつつあり、何を伝えるべきか? ルールは app/suivi.py(状態、しきい値、緊急度の順序)に、実行処理は scripts/suivi_clients.py にあります。
# rapport lisible, sur la base de la configuration (DATABASE_URL)
.\.venv\Scripts\python.exe scripts\suivi_clients.py
# export pour un tableur, seuils resserrés
.\.venv\Scripts\python.exe scripts\suivi_clients.py --format csv --sortie suivi.csv --essai-bientot-jours 5
# expliquer après coup un classement : rejouer le rapport à une date donnée
.\.venv\Scripts\python.exe scripts\suivi_clients.py --maintenant 2026-10-01T09:00:00+00:00
| 項目 | 動作 |
|---|---|
| 公開状態 | 有料ルートの状態:accounts.plan_status / consumption / refusal_reason と、REFUS_HTTP で読み取るHTTPコードです。しきい値を複製しないため、食い違いは生じません |
| 順序 | 緊急度の順です。まず現在拒否されているもの、次に試用期間の終了が近いもの、またはクォータにまもなく達するもの、その次に初期設定が完了していないものです。同じ優先度の場合は、期限が最も近いものを先にします |
| シークレット | キーのプレフィックス(電話でアカウントを識別するためのもの)のみを使用し、キー全体もそのフィンガープリントも使用しません。有効性が確認されたメールアドレスは読み取りません |
| 読み取り専用 | SELECT のみです。レポートから本番データベースを変更することはできません |
| エクスポート | --format csv はUTF-8、区切り文字「;」(フランス語版Excel向け)、改行コード \n で書き込みます。スクリプトは書き込み時にこれを通知します |
| 戻りコード | 0 はレポートを生成(空のデータベースも測定結果です)、2 は何も測定できなかったことを示します。空のレポートは「すべて順調」という意味ではありません |
検証:.\.venv\Scripts\python.exe scripts\decisions_check.py D50 — このゲートでは実際のデータベースを用意し、公開状態とルートが適用する拒否内容を比較します。試用期間が終了した有料顧客のケースも対象です。
4.7 ホームページ(D53)
GET / はブラウザー向けにサイトのトップページを提供し、プログラム向けには従来のJSONを維持します(どちらにも Vary: Accept を設定)。デザインは app/templates/accueil/fr.html にあります(元のモックアップ:docs/maquettes/page-accueil/)。試用期間、クォータ、価格、x402価格、連絡先などの数値は、コードと設定から app/routes/accueil.py によって挿入されます。
curl.exe -s https://kder-ai.com/ # JSON, comme avant
curl.exe -s -H "Accept: text/html" https://kder-ai.com/ # la vitrine
検証:.\.venv\Scripts\python.exe scripts\decisions_check.py D53。
4.8 6言語対応(D57)
フランス語はルートに置き、ほかの5言語にはプレフィックスを付けます。公開ページはすべて6言語で利用できます。サイトのトップページ、ガイド(D58)、無料トライアル(D59)、料金(D60)、登録ページと顧客ページ(D61)が、それぞれフランス語のURLの前に言語プレフィックスを付けたアドレスで提供されます(/en/guide、/ar/signup など)。
| 言語 | ホーム | 表示通貨(請求はユーロ) |
|---|---|---|
| フランス語 | / | 29 € / 79 € |
| 英語 | /en/ | $33 / $90 |
| 中国語 | /zh/ | ¥223 / ¥608 |
| ヒンディー語 | /hi/ | ₹3,160 / ₹8,620 |
| スペイン語 | /es/ | 29 € / 79 € |
| アラビア語(右から左) | /ar/ | $33 / $90 |
app/i18n.py は各言語のアドレス(chemin(lang, page))、数値と価格(prix、mention_euros、2026年9月の為替レート)を生成し、テンプレートをレンダリングします(rendre)。ユーロ建ての金額は accounts.OFFRES(pricing.OFFRES からも参照可能)に基づきます。換算後の表を更新せずに価格を変更すると、tests/test_i18n.py とD57ゲートのチェックが失敗します。写真はアプリケーションから /images/ で配信されます。公開CSPで許可されるのは img-src 'self' のみです。
curl.exe -s https://kder-ai.com/en/ # la vitrine en anglais
curl.exe -sI https://kder-ai.com/images/commercante-colis.jpg # image/jpeg, nosniff
検証:.\.venv\Scripts\python.exe scripts\decisions_check.py D57。
4.9 AI エージェントと訪問:ブロックせずに測定する(D72、D73)
サービスは、呼び出し元を6つの分類(agent_ia、
annuaire_mcp、moteur、outil、navigateur、inconnu)に分類し、AI
エージェントとMCP ディレクトリのアクセスをD30のJSONLログに記録します(人間による訪問は記録しません)。また、5つの指標(agents、top_ip、top_agents、
top_pages、conversion)を公開します。エージェントは一切ブロックされません。/pricingを読むエージェントは、ブラウザーを使わない見込み客です。各社が公開している25のAI エージェントの許可リストは、docs/AGENTS-IA.mdと
/metrics/agentsのレスポンスにあります。
curl.exe -s https://kder-ai.com/metrics/agents # en direct (compteurs du processus)
.\.venv\Scripts\python.exe scripts\analyser_agents.py --since 7d # ce qui est écrit (journal JSONL)
.\.venv\Scripts\python.exe scripts\analyser_visites.py --since 24h # tout le trafic (journal Nginx), 7 sections
scripts/analyser_visites.pyは、7つのセクション(主要指標、時間、人気ページ、人気IP、ステータスコード、クライアント、異常(プローブ、機密性の高いパス、漏えい))を出力します。IPアドレスは、--ip-en-clairを明示的に指定しない限り、仮名化されます(SHA-256+ソルト、D30)。漏えいとは、機密性の高いパスが2xxで配信されることです(301は別の場所に転送するため、情報を公開しません)。2つの解析ツールは同じ分類テーブルを参照するため、エンドポイントとレポートでエージェントの定義が食い違うことはありません。
確認:.\\.venv\\Scripts\\python.exe scripts\\run_tests.py tests\\test_agents_ia.py tests\\test_visites.py。
4.10 フランスの住所:Base Adresse Nationale(D79)
PAYS_BANの国(フランス本土および海外領土:FR、RE、
GP、MQ、GF、YT、BL、MF、PM)では、検証はBase
Adresse Nationale(app/validation/ban.py、https://data.geopf.fr/geocodage)を通じて行い、フランスの住所を判定できないSmartyStreetsは決して使用しません。
- BANスコアが0.8以上であること、返された郵便番号が入力された郵便番号と一致すること、市区町村名が一致すること(アクセント、大小文字、ハイフン、句読点を正規化);
housenumber(0,99)とstreet(0,95)のタイプのみを使用します。その他(municipality、locality)では道路が認識されていません;- BANが停止した場合は
degraded: trueを返し、SmartyStreetsにはフォールバックしません; - 米国では、DPVコード
Y/S/Dのみが住所を有効とします。XとNでは有効にならず、DPVのない候補ではdegraded: trueを返します。
この修正前に計算されたフランス本土/海外領土の結果(キャッシュ期間30日)は、scripts/purger_cache_ban.pyで削除できます(デフォルトはシミュレーション、削除するには--applyを指定)。2026年10月17日の測定では、「8 rue madelaine, Lyon,
75005」はvalidated: true(キャッシュ)からvalidated: falseに変わり、Paris 75002、Lyon 69001、Saint-Denis 97400は引き続き有効です。
5. 有料呼び出しの例
- 証明を作成します(クライアントとAPIで
API_SECRET_KEYを共有します)。
.\.venv\Scripts\python.exe -c "from app.payment import build_payment_proof; print(build_payment_proof('0x-demo-tx'))"
# -> 0x-demo-tx:1770000000.123:<hmac_sha256_hex>
- APIを呼び出します。
curl.exe -X POST http://127.0.0.1:8000/validate `
-H "Content-Type: application/json" `
-H "X-Payment-Proof: 0x-demo-tx:1770000000.123:<hmac>" `
-H "X-Transaction-Id: 0x-demo-tx" `
-d "{\"address\":{\"street\":\"1600 Amphitheatre Parkway\",\"city\":\"Mountain View\",\"state\":\"CA\",\"zip\":\"94043\",\"country\":\"US\"}}"
- 証明がない場合、または期限切れ/不正な形式/偽造された証明の場合、レスポンスは
402です(app/payment.py:77-85)。
{"error": "Payment Required", "price": 0.02, "currency": "USDC",
"wallet": "", "protocol": "x402", "version": "1.0"}
上記で公開している402の本文は抜粋です。x402Version、network、accepts[](x402 v1オファー)も含まれ、さらに現在はaccepts[0].outputSchema(ValidationResponseのPydanticスキーマ、D80)も含まれます。2026年10月17日の測定では、本文は1,788バイトで、ヘッダーはnginxの4 KBバッファー内に収まり、v2の同じオファーはPAYMENT-REQUIREDヘッダーに含まれます。
- 有効な証明がある場合、レスポンスは
200です。2026年10月10日にこのマシンでステージング環境にて測定した例です(プロバイダーに接続できなかったため、degraded)。
{"result": {"validated": false, "standardized": {"line1": "1600 Amphitheatre Parkway", "...": "..."},
"deliverability": "invalid", "confidence": 0.05, "degraded": true,
"cache_key": "8f1c…"},
"price_paid": 0.02, "transaction_id": "0x-demo-tx"}
transaction_idは、受信したX-Transaction-Idヘッダーをそのまま反映します(app/routes/validate.py:67)。
本番環境で同じ呼び出しを行った場合の例(2026年10月11日、https://kder-ai.com、実際のプロバイダーが稼働中、有効なHMAC証明)。
{"result": {"validated": true, "deliverability": "residential", "confidence": 1.0,
"degraded": false, "cache_key": "250a6087…"},
"price_paid": 0.02, "transaction_id": null}
deliverabilityはプロバイダーから返されるDPVコードに基づいています(Y →
residential、scorer.py:17-23)。また、confidenceにはジオコーディングのボーナスが加算されます(0,99 + 0,05、上限1,0)。プロバイダーのmetadata.rdi(この住所ではCommercial)は未加工データに残り、APIからは公開されません。
6. キャッシュ、レジリエンス、レイテンシ予算
| メカニズム | 動作 | 決定 |
|---|---|---|
| Redis キャッシュ | キー addr:<sha256>、TTL 30 日、ネットワーク呼び出しの前に参照 | D5 |
| 劣化結果の TTL | 5 分であり、30 日ではありません。degraded: true は「プロバイダーが応答しなかった」ことを示し、「アドレスが無効」であることを示すものではありません。1 か月保持すると一時的な障害が固定化されてしまいます。5 分保持すれば、障害中のプロバイダーへのリクエストの集中を防ぐには十分です。一方、検証済みの判定は 30 日保持します | D47 |
| キャッシュのクールダウン | バックエンドでエラーが発生した後、キャッシュは 30 秒間利用不可と見なされます。Redis の障害によるコストは 1 回だけ発生し、リクエストごとには発生しません。/health は実際のプローブを維持します | D25 |
| Redis タイムアウト | 接続 1 秒、読み取り 1 秒。エラーカウンターを公開 | D12 |
| プロバイダーのサーキットブレーカー | closed → open → half-open → closed、アプリケーションレベルのしきい値はエラー 3 回、回復時間は 60 秒。開状態になると、ローカルで即座に応答します | D23 |
| プロバイダーのタイムアウト | 個別に設定:接続 2 秒、読み取り 4 秒(全体で 10 秒ではありません) | D23 |
| ジオコーダーのタイムアウト | 接続 2 秒、読み取り 3 秒。失敗してもリクエスト全体を失敗させず、latitude/longitude は null のままになります | D26 |
/health プローブ | イベントループ外で並列実行し、上限を 2 秒に設定 | D22 |
| オブザーバビリティ | サーキットブレーカーの状態とキャッシュの可用性を /metrics で公開 | D24 |
この環境で 10/10/2026 に測定したレイテンシ(PostgreSQL と Redis は停止、
外向きネットワークは有効、プロバイダーの認証情報は空のため応答は 401):
| 測定項目 | 値 | 方法 |
|---|---|---|
GET /health | 2,13 s | 実際の HTTP リクエスト(D22 の上限:2,0 s に到達。2 つのプローブはいずれも失敗) |
POST /validate 有料、1 回目の呼び出し | 11,117 s | scripts/e2e_check.py |
POST /validate 有料、2 回目の呼び出し | 11,036 s | scripts/e2e_check.py |
| Redis 読み取り、バックエンド不在 | 2,03 s | プロセス内で測定(D25 によりコストが発生するのは 1 回のみ) |
| プロバイダー呼び出し(401 を 3 回再試行) | 6,77 s | プロセス内で測定 |
| Nominatim ジオコーディング | 0,86 s | プロセス内で測定 |
パイプライン全体(validate_full) | 7,62 s | プロセス内で測定 |
これらの数値は正直なものであり、見栄えのよいものではありません。劣化時のレイテンシはプロバイダーの影響が大きく、そのエラーはバックオフによって再試行されます(TEST_REPORT.md の「既知の制限」を参照)。
コードに記録されている基準値(D23 より前は 15,2 s、D22 より前は 6,03 s)は、今日再測定されていません。TEST_REPORT.md では過去の値として記載されています。
7. x402 決済:検証されるもの、決済されるもの、従来の仕組みとして残るもの
このサービスはx402 v1(リポジトリ coinbase/x402 の仕様 specs/transports-v1/http.md)を採用し、Base 上で実際に USDC 決済を行います。402 の本文は PaymentRequired ドキュメントです。
{ "x402Version": 1, "error": "...", "accepts": [ { "scheme": "exact",
"network": "base", "maxAmountRequired": "20000", "asset": "0x833589…2913",
"payTo": "0x0fe9…d339", "resource": "https://kder-ai.com/validate",
"mimeType": "application/json", "maxTimeoutSeconds": 300,
"extra": { "name": "USD Coin", "version": "2" } } ] }
クライアントは署名を X-PAYMENT(PaymentPayload の base64 エンコード {signature, authorization:{from,to,value,validAfter,validBefore,nonce}})で返し、X-PAYMENT-RESPONSE({success, transaction, network, payer} の base64 エンコード)を受け取ります。
x402 v2 にも対応(D77)。 同じ 402 が、PAYMENT-REQUIRED ヘッダーに v2 のオファーを含みます(ネットワーク eip155:8453、フィールド amount、Coinbase カタログの拡張 bazaar)。v2 クライアントは PAYMENT-SIGNATURE で支払い、PAYMENT-RESPONSE を受け取ります。支払い手段がない場合、本文が空でも /validate は 402 を返します。これは Coinbase カタログのチェックが期待する動作です。
次の順序で行う2つの検証(app/x402.py):
- ローカルでオフライン検証 — EIP-712 ダイジェスト(
TransferWithAuthorization、EIP-3009)を明示的に再計算し(eth_abi、eth_hash、eth_keys)、復元した署名をauthorization.fromと照合します。また、送信先、金額、トークン、ネットワーク、有効期間を公開済みのオファーと照合します。この検証に通るまで、サーバーから外部へ情報が送信されることはありません; - 決済 — ファシリテーターに
POST /verify、続けてPOST /settleを送信します(デフォルトは Coinbase のファシリテーターで、CDP_API_KEY_ID/CDP_API_KEY_SECRETを使用して署名した JWT CDP で認証します)。決済ハッシュは永続レジストリに記録されます(D29)。明示的な拒否、障害、ハッシュの欠落、決済済み nonce のいずれも拒否として扱います。
通知される EIP-712 ドメイン(extra.name / extra.version)には、コントラクトの値が使用されます。scripts/test_x402_payment.py --check-domain でオンチェーン検証できます。2026年10月11日に Base で測定した値は、name() = "USD Coin"、version() = "2"、decimals() = 6 です。
従来の仕組みとして残るもの(D15)。 X-Payment-Proof ヘッダー(tx_hash:timestamp:hmac_sha256)は、運用者向けツールと統合済みクライアントのために引き続き受け付けます。有効期間は前後300秒で、HMAC は時間差に依存しない方法で比較します。また、Base 上のレシート確認が必須です(D27。デフォルトで有効。TEST_MODE=true が明示されたレシピだけが対象外)。確認には JSON-RPC を使用します(app/onchain.py。web3 パッケージも鍵ストアパッケージも使用しません)。HMAC による支払いを x402 の決済として扱うことはありません。HMAC には X-PAYMENT-RESPONSE が含まれないためです。
MCP。 同じツールを MCP として POST /mcp で公開します(JSON-RPC 2.0、ステートレス、SSE ストリームなし)。D78 以降、決済はMCP x402 v2 トランスポートに従います。支払いなしの tools/call は、structuredContent に v2 の PaymentRequired を含む isError: true のツール結果を返します(content[0].text にも同じドキュメントが含まれ、ネットワークは CAIP-2、リソースは mcp://tool/validate_address です)。クライアントは v2 の PaymentPayload を params._meta["x402/payment"] に入れ、result._meta["x402/payment-response"] で決済結果を受け取ります。このトランスポートでは JSON-RPC の 402 は送信されません。x402 MCP クライアントは HTTP ステータスではなく結果を読み取るためです。D34/D37 との互換性は維持しており、v1 クライアントは X-PAYMENT で支払い、決済ヘッダーを引き続き使用できます。2つのトランスポートの検証と入金処理には、唯一の経路として settle_payment を使用します。GET /.well-known/mcp-server のカードはアプリケーションから提供されるため、停止中のエンドポイントが記載されることはありません。ディレクトリへの公開:server.json、.mcp.json、smithery.yaml(DISCOVERY.md §1 を参照)。
実際の決済テスト。 scripts/test_x402_payment.py には3つのモードがあります。plan(/manifest、402 の本文、MCP トランスポートのオファーで同じオファーが公開されていることを確認)、--check-domain(通知されたドメインをオンチェーンのコントラクトと比較)、--pay(実際の決済。Base 上で USDC を入金済みの購入者ウォレットが必要で、0.02 USDC を使用します)。
8. テストと検証
| ツール | 検証内容 | コマンド |
|---|---|---|
pytest | ユニットテストおよび統合テストの動作 | .\.venv\Scripts\python.exe -m pytest -q |
scripts/run_tests.py | テストオラクル:pytest の要約行(リダイレクトされた出力では表示されなくなる)に依存せず、JUnit XML レポートと終了コードを出力します | .\.venv\Scripts\python.exe scripts\run_tests.py |
scripts/doc_check.py | ドキュメントに、期待される数の ## N. セクションが正確に含まれ、番号に抜けがないこと | .\.venv\Scripts\python.exe scripts\doc_check.py README.md 10 |
scripts/e2e_check.py | 4 つの成功基準を網羅する 22 項目の実際の HTTP テスト | .\.venv\Scripts\python.exe scripts\e2e_check.py http://127.0.0.1:8000 |
GATES.md | ゲート台帳:実行可能なオラクル(CHECK: / EXPECT:)と手動レビュー | 下記「台帳の実行方法」を参照 |
.cline/skills/unlazy/scripts/gate-lint.mjs | オラクルが実際に失敗すること(台帳の品質) | node .cline\skills\unlazy\scripts\gate-lint.mjs GATES.md |
このマシンで 10/10/2026 に測定した結果:46 tests collectés — 45 passés, 1 ignoré(無視されたテストは、実際の SmartyStreets 認証情報とネットワークを必要とする統合テストです);scripts/e2e_check.py →
22/22 vérifications OK。詳細、方法、制限事項については TEST_REPORT.md を参照してください。
プロジェクトのルートから台帳を実行する方法:
node .cline\skills\unlazy\scripts\gate-lint.mjs address-validator\GATES.md
node .cline\skills\unlazy\scripts\gate-check.mjs --status address-validator\GATES.md # lit, n'exécute rien
node .cline\skills\unlazy\scripts\gate-check.mjs --approve address-validator\GATES.md # approuve après lecture
node .cline\skills\unlazy\scripts\gate-check.mjs address-validator\GATES.md # exécute les oracles approuvés
9. デプロイ
deploy.sh は Debian/Ubuntu VPS を対象とし、次の処理を順に実行します:パッケージのインストール
(python3.12、PostgreSQL、Redis、Nginx、Certbot)、/opt へのクローン、
仮想環境の作成、pip install -r requirements.txt、PostgreSQL データベースと
ユーザーの作成、Redis の有効化、nginx.conf のコピー、証明書の取得
(certbot --nginx)、続いて systemd サービスのインストールと起動。
スクリプトは set -euo pipefail(D20)で実行されるため、最初の
エラーで停止します。
注意点:deploy.sh は .env ファイルを作成しません。 手動で設定しないと、
サービスはデフォルト値で起動します。つまり、API_SECRET_KEY=change_me、
PostgreSQL のパスワードは change_me のままで、SmartyStreets の認証情報もありません
(この場合、プロバイダーは 401 を返し、すべての応答が劣化します)。デプロイ直後に次を実行してください:
# sur le serveur, dans /opt/address-validator
cp .env.example .env # puis renseigner API_SECRET_KEY, SMARTY_*, DATABASE_URL, REDIS_URL
systemctl restart address-validator
systemctl status address-validator
curl -s http://127.0.0.1:8000/health
journalctl -u address-validator -n 50
コンテナーを使用する方法:docker compose up -d(python:3.12-slim をベースにした
Dockerfile、API とその依存関係用の docker-compose.yml)。
本番 VPS(kder-ai.com)のデプロイ:scripts/deploy-kder-ai.sh
— サーバーで実際に実行されたスクリプトで、ここでバージョン管理されています
(11/10/2026 より前は、ワークステーションと /opt にしかなく、バージョン管理の対象外でした)。
上記の 11 ステップを実行し、さらに次を追加します:
API_SECRET_KEYがまだchange_meのまま、または 32 文字未満の場合、 あるいは.envが CRLF の場合、デプロイを拒否します;nginx-hardening.confを/etc/nginx/conf.d/にインストールします:HSTS (1 年間、includeSubDomains)、X-Content-Type-Options: nosniff、Referrer-Policy: no-referrer、IP ごとにlimit_reqを 10 req/s(バースト 20)に設定し、 リクエスト本文を 16 ko に制限します。これは意図的に仮想ホストの外に配置しています。 仮想ホストはcertbot --nginxが更新のたびに書き換えるためです;- 強化設定が失われている場合に失敗する(または通知する)チェック:
127.0.0.1:8000のみでリッスンしていること(10.6)、セキュリティヘッダー(10.7)、 60 件の同時呼び出しに対する429(10.8); - Debian リポジトリで Python 3.12 が提供されていない場合の代替手段として、 Python 3.12 をコンパイルします。
実際のデプロイ記録(バージョン、ログ、想定された差異、11/10/2026 の修正)は
DEPLOIEMENT_VPS.md にあります。
本番環境へのデプロイ後は、/metrics と /docs へのアクセスを制限し、Redis のパスワードを有効にして、
Nginx による HTTPS リダイレクトが正しく設定されていることを確認してください
(SECURITY.md §4 および §6)。
10. 既知の制限と今後の対応
| 制限 | 影響 | ドキュメントの記載箇所 |
|---|---|---|
宣言されたテスト設定(TEST_MODE=true)では、オンチェーンを照会せずに共有シークレットによる証明を受け付けます | 運用者によるテスト専用です。書き込まれた行には test_mode のマークが付き、実際の収益から除外されます。テスト設定以外では、USDC の送金が確認されていない証明には 402 が返されます | §7、SECURITY.md §3、D27、D40、D42 |
| 証明は300秒間リプレイできます | 同じ証明をウィンドウ内で複数回使用できます | §7、SECURITY.md §6 |
address_validations にデータが一度も書き込まれません | データベースにアドレスの記録は残りません。保持は Redis キャッシュ(30日)に依存します | PRIVACY.md §3 |
record_transaction は記録がない場合、処理を拒否します | D41以降、書き込みに失敗すると 500(ledger_write_failures_total)が返されます。支払いは受理されますが、検証は提供されず、入金を照合する必要があります | SECURITY.md §5、D41 |
| 有料プランの有効期限がありません | 手動で付与したプランは、手動で取り消すまで有効です。利用を制限するのは月間クォータのみです(D45)。自動課金がないため、適用すべきサブスクリプションの終了日はありません | SECURITY.md §3、D45 |
| コードに製品レベルのレート制限が2つあり、不正利用対策は edge が担います | Nginx は不正利用対策として、IP ごとに毎秒10リクエスト(バースト20、超過時は 429)に制限します。コードでは、クライアントに通知されるビジネス上の制限を追加します。/signup はIPごとに1時間あたり5件の登録(D44)、また /validate-form では、3つ目の項目としてIPごとに24時間あたり3回の無料試行(D21改訂版)です | §9、SECURITY.md §6、D21(改訂版)、D44 |
| メトリクスがメモリ内に保存されます | 再起動時に失われ、Prometheus へのエクスポートもアラートもありません | SECURITY.md §5 |
| プロバイダーの4xxエラーが再試行されます | 測定された遅延は11.1秒に悪化しています。バックオフは再試行できないエラーに対して適用されます | TEST_REPORT.md、「既知の制限」 |
CORS *、/metrics、/docs が公開され、コードではデフォルトのシークレット change_me も引き続き受け付けられます | さらに強化する必要があります。ただし、デプロイ時には change_me と32文字未満のすべてのシークレットが拒否されます(scripts/deploy-kder-ai.sh) | §9、SECURITY.md §2および§6 |
| フランス本土・海外領土のカバレッジ | Base Adresse Nationale(D79)で確認済みです。スコア、郵便番号、都市名を検証します。BAN の障害時は degraded となり、判定は行いません。修正前にキャッシュされた結果は一度だけ削除されます(scripts/purger_cache_ban.py) | §4.10、D79 |
402 の契約 | outputSchema(派生した出力スキーマ)とルートの network が公開されています。本文は約1.8 KBとなり、ヘッダーは4 KB未満に収まります | §5、§7、D80 |
deploy.sh は .env を作成しません | 手動の手順を忘れると、デフォルトのシークレットで起動します | §9 |
推奨される今後の対応(優先度順):
- プロバイダーへの再試行はネットワークエラーと5xxに限定します。これにより、
401に対するバックオフをなくし、悪化時の遅延を約3秒未満に抑えられます。 - 支払い証明を消費して(リプレイを防止)、
API_SECRET_KEYがデフォルト値のままの場合は起動を拒否します。 - メトリクスを実際の監視システムに接続し、デプロイ時に
.envを生成します。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。