关于 Address Validator
Address Validator 是面向需要规范化和地理编码配送地址的服务的按次付费 HTTP API。它返回可配送性与置信度评分、缓存键,并在供应商不可用时提供降级结果。
主要功能
- 01规范化可识别的配送地址
- 02返回纬度和经度
- 03提供可配送性与置信度评分
- 04缓存重复地址的结果
- 05供应商故障时返回降级响应
适用场景
- 01配送前规范化原始地址
- 02检查地址坐标和配送可能性
- 03供应商不可用时处理地址验证
MCP 相关
该 API 提供 `/manifest`,用于向 x402 或 MCP 目录描述服务及其模式。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 仍会返回
200,并设置degraded: true— 绝不会返回可能让人误以为地址确定无效的错误。
规范中的成功标准,以及自动验证方式:
| # | 标准 | 验证方式 |
|---|---|---|
| 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 在边缘层执行(nginx-hardening.conf:每 IP 每秒 10
个请求,突发上限为 20,超出时返回 429)。旧版支付方式(共享密钥 HMAC 证明,D15)不能再仅凭签名通过验证:自 W1 加固(12/10/2026)以来,链上确认(D27)默认已启用,并要求在 Base 上向收款钱包转入已确认的 USDC。合成 HMAC 证明只有在明确声明的配方(TEST_MODE=true)中才会获得 200,相应写入行会标记为 test_mode,因此不计入实际收入(D40)。所有内容均有文档记录(README.md
§7 和 §9、SECURITY.md §3 和 §6、AUDIT_TRAFIC_VPS.md §5),而非刻意隐瞒。
2. 快速开始
前置条件:Python 3.12(仓库虚拟环境运行于 3.12.10,与 Dockerfile 中的
python:3.12-slim 镜像一致)。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 起默认启用)——仅当 TEST_MODE=true 时才允许设为 false;在验收测试之外,届时将拒绝所有共享密钥证明——参见 §7 |
base_rpc_url | BASE_RPC_URL | https://mainnet.base.org | D27 启用时使用的 Base JSON-RPC 节点(链 ID 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 和页面的 Site 与 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 分钟、仅限使用一次,数据库中只存储指纹),以设置密码(bcrypt 指纹);对未知邮箱也返回相同响应;每个账户每小时最多发送 3 个链接;之后通过邮箱登录 | app/pilotage_comptes.py、app/routes/dashboard.py |
GET POST | /dashboard/connexion、/dashboard/deconnexion | 仪表板登录页面(D69),取代 Basic Auth 挑战窗口:签名 Cookie __Host-av_pilotage(HMAC,有效期 12 小时,HttpOnly、Secure、SameSite=Strict);相同的 5 次尝试限制,之后暂停 15 分钟;拒绝来自其他网站的表单;无会话的浏览器会被重定向到此页面,脚本仍使用 Basic Auth 挑战 | 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 每小时最多注册 5 次 | app/routes/signup.py、app/accounts.py |
GET POST | /validate-form | 公开免费试用(D46)——公开、可被索引;每个 IP 每 24 小时最多试用 3 次(D94)(超出时返回 429 和准确的 Retry-After);不写入数据、不加载任何内容,并明确标示降级结果;也接受单行地址(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,以及图标;公共页面带有 `` 区块(规范链接、Open Graph、JSON-LD);www. 会通过 301 重定向至 PUBLIC_BASE_URL | 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 Agent 的地址验证(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 小时);每个 IP 和账户输入 5 次错误密码后封锁 15 分钟;拒绝来自其他网站的表单 | app/routes/connexion.py |
GET POST | /compte/mot-de-passe-oublie、/compte/nouveau-mot-de-passe | 忘记密码(D63)——通过邮件发送使用 PUBLIC_BASE_URL 生成的链接,数据库中只存储指纹,有效期 30 分钟且仅限使用一次;无论账户是否存在,均返回相同响应;每个账户每小时最多发送 3 封邮件,每个 IP 每小时最多请求 5 次;设置新密码后会关闭所有会话;通过 SMTP_HOST 发送(空值 = 不发送邮件) | app/routes/mot_de_passe.py |
POST | /validate | 付费——完整验证;两种付费方式:订阅(X-API-Key,D45)或按次付款(x402 / HMAC 证明) | app/routes/validate.py |
GET | /docs | 交互式文档(FastAPI) | 自动生成 |
GET | /llms.txt、/AGENTS.md、/.well-known/security.txt | 面向 Agent 的发现文件,由 Nginx 静态提供(参见 DISCOVERY.md) | nginx-discovery.conf |
POST | /mcp | MCP 服务(JSON-RPC 2.0):免费发现,tools/call 付费(x402 v2 传输:付款信息位于 params._meta["x402/payment"],方案和收据位于工具结果中;接受 v1 X-PAYMENT 请求头) | 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 拒绝的请求都会以元数据形式记录日志 | |||
(时间戳、endpoint、User-Agent、伪名化的 IP):绝不会记录已验证的地址。 | |||
设置与保留期限:PRIVACY.md §3、DISCOVERY.md §4。 |
4.1 实际收入与登记收入(D40)
/metrics/revenue 会公布同一登记簿的三种统计口径,绝不混用:
| 字段 | 内容 |
|---|---|
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:
服务记录交易时设置了 TEST_MODE=true(正在进行验收测试),或
使用 scripts/migrate_test_mode.py 标记已写入的行——阈值为
TEST_MODE_CUTOFF_UTC(默认值 2026-10-11T12:00:00+00:00,即当天的两笔验收测试交易)。不会根据金额或地址推断:未标记的行仍计入实际收入。/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(供应商)→ 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 同一次验证的两种付费方式(D45)
POST /validate 服务于两类客户端:提供 API 密钥的订阅商店,以及按次付费的自主 Agent(§5)。密钥优先:如果两者都提供,则以订阅方式为准,调用不会通过 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):没有这个请求头,客户端就无法区分调用是由订阅覆盖,还是已扣款。密钥模式下,另外两个请求头也会说明账户状态——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 计费:密钥模式没有影响自主 Agent。/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 小时计算;窗口为固定窗口,从首次试用开始,拒绝请求不会延长窗口 |
| 拒绝 | 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,因为其中包含一个地址 |
| 降级结果 | 按实际情况显示:“服务提供方未响应,此响应无法说明该地址的情况”——绝不将其显示为无效地址 |
| 输入 | 对输入进行转义并重新显示,以便修改;国家代码不足两个字母时会被拒绝(400),且不会调用任何服务提供方 |
/metrics 中可查看上限计数器:只有记录上限拒绝,才能区分“无人尝试”和“所有人都被拦截”。
4.4 公开集成指南(D48)
GET /guide 回答刚收到密钥的客户端会提出的问题:身份验证(X-API-Key 或 x402)、准确的 JSON 契约、八种返回代码及相应操作、缓存和限额规则,以及四个实用案例(PowerShell 直接调用、Python 导入 CSV、Shopify、WooCommerce)。
| 项目 | 行为 |
|---|---|
| 发布的信息 | 公共基础 URL、价格、免费试用限额、X-Payment-Mode / X-Plan / X-Quota-Remaining 请求头和套餐:从代码和配置中读取,绝不手动复制(如果模板尚未替换,门禁会拒绝) |
| 机密信息 | 无:该页面可被索引,因此本身就是公开的。示例密钥是模板(sk_live_VOTRE_CLE) |
| 独立性 | 无脚本、无外部资源,共用样式表,Cache-Control: public, max-age=300,form-action 'none' |
| 导航 | 只有一处定义(app/web.py: site_head()):可从所有公开页面访问 /guide,而且 /pricing 不再复制自己的 <nav> |
| 明示的限制 | 不提供 Shopify 应用或 WooCommerce 插件(由客户的服务发起调用);degraded 结果表示“需要重新核验”,绝不表示地址无效 |
4.5 商店欢迎邮件(D49)
这不是一个页面,而是一条消息:发送给刚创建账户的商店。内容存放在 EMAIL_BIENVENUE.md 中,有两种形式——供 n8n 加载的模板(包含 {{…}} 变量,并在表格中说明)以及一份可直接发送给首位客户(Tiitoune Art)的示例。
| 项目 | 行为 |
|---|---|
| 数字 | 试用天数、赠送配额和月费从代码(accounts.JOURS_ESSAI、VALIDATIONS_ESSAI)及 /pricing 中读取;如果邮件未包含这些信息,门禁会拒绝 |
| 声明的方式 | 订阅(X-API-Key):不包含 X-PAYMENT、X-Payment-Proof 或 tx_hash——门禁禁止在此消息中使用这些内容(按次付费是 Agent 使用的方式,相关说明见 /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 六种语言(D57)
法语页面仍位于根路径;另外五种语言使用各自的前缀。所有公开页面均提供六种语言版本:展示页、指南(D58)、免费试用(D59)、价格(D60)、注册和客户空间(D61),每个页面都使用对应的法语地址,并在前面加上语言前缀(/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 Agent 与访问:衡量而非拦截(D72、D73)
服务会将每个调用方分类为六类(agent_ia、
annuaire_mcp、moteur、outil、navigateur、inconnu),将 AI Agent
和 MCP 目录记录到 D30 的 JSONL 日志中——绝不记录人工访问——并发布
五项指标(agents、top_ip、top_agents、
top_pages、conversion)。不会拦截任何 Agent:读取
/pricing 的 Agent 是没有浏览器的潜在客户。编辑方发布的 25 个 AI Agent
白名单位于 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 会生成七个部分——关键数据、时间、热门页面、
热门 IP、状态码、客户端、异常(探测、敏感路径、泄露)。其中的地址会
假名化(SHA-256 + 盐值,D30),除非明确指定 --ip-en-clair;泄露指
敏感路径以 2xx 提供服务(301 会重定向到其他位置,不会暴露内容)。两个
分析器读取同一分类表:端点和报告对 Agent 的定义不会相互矛盾。
验证:.\\.venv\\Scripts\\python.exe scripts\\run_tests.py tests\\test_agents_ia.py tests\\test_visites.py。
4.10 法国地址:国家地址数据库(D79)
对于 PAYS_BAN 中的国家/地区(法国本土及海外地区:FR、RE、
GP、MQ、GF、YT、BL、MF、PM),验证会通过国家
地址数据库(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 Ko
缓冲区,且 PAYMENT-REQUIRED 响应头中也有相同的 v2 报价。
- 使用有效证明时,响应为
200。以下是 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)。
同一调用在 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 表示“服务提供商未响应”,而非“地址无效”。保留一个月会让短暂故障长期固定;保留 5 分钟足以避免频繁请求故障中的服务提供商。而已验证的结论仍保留 30 天 | D47 |
| 缓存冷却 | 后端发生故障后,缓存会被视为不可用 30 秒:Redis 故障只产生一次开销,而非每次请求都产生开销。/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 |
2026/10/10 在此工作站上测得的延迟(PostgreSQL 和 Redis 已关闭,
出站网络可用,服务提供商凭据为空,因此响应为 401):
| 测量项 | 数值 | 方法 |
|---|---|---|
GET /health | 2,13 s | 实际 HTTP 请求(D22 的 2,0 秒上限已达到,两个探测均失败) |
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,只产生一次开销) |
| 服务提供商调用(401 重试 3 次) | 6,77 s | 进程内测量 |
| Nominatim 地理编码 | 0,86 s | 进程内测量 |
完整流水线(validate_full) | 7,62 s | 进程内测量 |
这些数字如实呈现,并不光鲜:降级延迟主要由服务提供商造成,其错误 4xx 会被重试(参见
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 目录检查所要求的行为。
按此顺序进行两项检查(app/x402.py):
- 本地离线检查 — 显式重新计算 EIP-712 摘要(
TransferWithAuthorization、EIP-3009)(使用eth_abi、eth_hash、eth_keys),将恢复出的签名与authorization.from比较,并核对目标地址、金额、代币、网络和有效期是否与已发布的报价一致。此检查通过前,不会有任何内容离开服务端; - 结算 — 向 Facilitator 发送
POST /verify,然后发送POST /settle(默认使用 Coinbase 的 Facilitator,通过使用CDP_API_KEY_ID/CDP_API_KEY_SECRET签名的 CDP JWT 进行身份验证)。结算哈希会写入持久化账本(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 结算:它不包含 X-PAYMENT-RESPONSE。
MCP。 同一个工具也通过 POST /mcp 以 MCP 形式提供(JSON-RPC 2.0、无状态、无 SSE 流)。从 D78 开始,支付采用 MCP x402 v2 传输:不含支付信息的 tools/call 会返回一个 isError: true 的工具结果,其 structuredContent 为 v2 PaymentRequired(且 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 支付,并继续使用其结算标头),而 settle_payment 是两种传输唯一的验证/收款路径。GET /.well-known/mcp-server 卡片由应用提供,因此不会描述已关闭的端点。目录发布文件:server.json、.mcp.json、smithery.yaml(见 DISCOVERY.md §1)。
实际测试。 scripts/test_x402_payment.py 有三种模式:计划(验证 /manifest、402 正文和 MCP 传输报价发布的是同一份报价)、--check-domain(将已公布的域与链上合约进行比较)以及 --pay(实际支付;需要在 Base 上持有已注资 USDC 的买家钱包,并会花费 0.02 USDC)。
8. 测试与验证
| 工具 | 验证内容 | 命令 |
|---|---|---|
pytest | 单元测试和集成测试行为 | .\.venv\Scripts\python.exe -m pytest -q |
scripts/run_tests.py | 测试预言机:JUnit XML 报告 + 退出代码,不依赖 pytest 的摘要行(重定向输出时该行会消失) | .\.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 | 真实 HTTP 测试流程,包含覆盖 4 项成功标准的 22 项检查 | .\.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 项测试 — 45 项通过, 1 项跳过(跳过的测试是一项集成测试,需要真实的 SmartyStreets
凭据和网络);scripts/e2e_check.py →
22/22 项检查通过。完整详情、方法和限制见
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(Dockerfile 基于
python:3.12-slim,docker-compose.yml 用于 API 及其依赖项)。
生产 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 限制为 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,则改为编译安装。
实际部署记录(版本、日志、已接受的差异、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 s 内重放 | 同一证明可在有效窗口内多次使用 | §7、SECURITY.md §6 |
address_validations 从未填充 | 数据库中没有地址记录:数据保留依赖 Redis 缓存(30 j) | PRIVACY.md §3 |
record_transaction 遇到无记录时会拒绝服务 | 自 D41 起,写入失败会返回 500(ledger_write_failures_total):付款已接受,但不会提供验证结果,且必须核对收款情况 | SECURITY.md §5、D41 |
| 付费计划不会过期 | 手动授予的计划会一直保持开放,直到手动撤销:只有月度配额会关闭访问权限(D45)。没有自动计费,因此也没有需要强制执行的订阅结束日期 | SECURITY.md §3、D45 |
| 代码中有两个产品级限额,反滥用仍由 edge 处理 | Nginx 按 IP 限制为 10 次请求/s(突发 20 次,超出时返回 429),用于防止滥用;代码还会增加面向客户端的业务限额:每个 IP 每小时最多注册 5 次(/signup,D44);使用 /validate-form 时,每个 IP 每 24 h 最多免费试用 3 次(项目 3,修订后的 D21) | §9、SECURITY.md §6、D21(修订版)、D44 |
| 指标存储在内存中 | 重启后丢失,没有 Prometheus 导出或告警 | SECURITY.md §5 |
| 会重试供应商的 4xx 错误 | 测得的延迟恶化至 11,1 s;退避机制针对的是不可重试错误 | 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 Ko,标头保持在 4 Ko 以下 | §5、§7、D80 |
deploy.sh 不会创建 .env | 如果忘记手动执行该步骤,启动时会使用默认密钥 | §9 |
建议的后续工作,按价值排序:
- 仅重试供应商的网络错误和 5xx:移除对
401的退避,并将恶化后的延迟降至约 3 s 以下。 - 消耗付款证明(防重放),并在
API_SECRET_KEY仍为默认值时拒绝启动。 - 将指标接入实际监控,并在部署时生成
.env。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。