Warn when a seeded model key does not become active
Why: Settings plaintext loses to secret_keys, so replacing the env key did not change the key a conversation actually sent. Evidence: python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_seed_replaces_old_system_secret tests.test_svc.SvcDbRecoveryTests.test_seed_warns_when_old_secret_stays_active tests.test_svc.SvcDbRecoveryTests.test_seed_puts_without_credentials_when_auth_off (SELF). Impact: Embedded databases now compare the active system secret fingerprint after seeding, and the backend gitlink carries the adoption fix. Refs: fix/model-key-secret-overlay Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -581,6 +581,9 @@ dotenv 格式:KEY=VALUE,支持 # 注释、成对引号、export 前缀;只
|
||||
SVC_TAVILY_KEYS Tavily 密钥池
|
||||
SVC_SEED_OWNER 播种目标账号(缺省取库内首个管理员)
|
||||
播种幂等:设置持久化在 agent 数据目录,播过一次后续启动无需再传。
|
||||
密钥表 secret_keys 的生效行优先于设置明文。换 key 后以 /api/secrets 为准;
|
||||
播种写入系统设置时,当前后端会把非掩码密钥同步成系统行的生效密钥。
|
||||
嵌入式库里生效密钥与环境变量指纹不一致时,启动会告警且不打印明文。
|
||||
认证开启(PG 档默认)时播种自动铸临时密钥(name=svc-seed,用后即回收),
|
||||
不依赖手动到设置页配置。
|
||||
|
||||
@@ -3564,11 +3567,53 @@ for u in users:
|
||||
[ -n "${SVC_MINIMAX_BASE:-}${SVC_MINIMAX_KEY:-}${SVC_MINIMAX_CHAT:-}${SVC_MINIMAX_FAST:-}" ] && did="${did:+$did、}MiniMax 备用"
|
||||
[ -n "${CLI_TAVILY:-${SVC_TAVILY_KEYS:-}}" ] && did="${did:+$did、}Tavily"
|
||||
c_ok "[$ENV_NAME] 已播种设置:$did(持久化于 $ENV_DATA,后续启动无需再传)"
|
||||
verify_seeded_secrets
|
||||
else
|
||||
c_warn "[$ENV_NAME] 设置播种未成功:${resp:-<无响应>}(认证开启时需库内已有账号以铸临时密钥,或用 --seed-owner 指定;也可在设置页手动配置)"
|
||||
fi
|
||||
}
|
||||
|
||||
# verify_seeded_secrets:嵌入式库里,系统行生效密钥要和环境变量是同一把。
|
||||
# 只比较 SHA-256 前 8 位,不一致就告警。没有库、没有密钥表或还没有生效行时跳过。
|
||||
verify_seeded_secrets() {
|
||||
local db="${ENV_DATA:-}/agent-system.db"
|
||||
[ -f "$db" ] || return 0
|
||||
local chat="${CLI_MKEY:-${SVC_MODEL_KEY:-}}" light="${CLI_LKEY:-${SVC_LIGHT_KEY:-}}"
|
||||
[ -n "$chat$light" ] || return 0
|
||||
command -v python3 >/dev/null 2>&1 || return 0
|
||||
local report
|
||||
report="$(python3 - "$db" "$chat" "$light" <<'PY'
|
||||
import hashlib, sqlite3, sys
|
||||
db, chat, light = sys.argv[1:4]
|
||||
con = sqlite3.connect("file:%s?mode=ro" % db, uri=True)
|
||||
tables = {row[0] for row in con.execute("SELECT name FROM sqlite_master WHERE type='table'")}
|
||||
if "secret_keys" not in tables:
|
||||
raise SystemExit(0)
|
||||
def active(service):
|
||||
row = con.execute(
|
||||
"SELECT value FROM secret_keys WHERE owner=? AND service=? AND active=1 ORDER BY created_at DESC LIMIT 1",
|
||||
("", service),
|
||||
).fetchone()
|
||||
return row[0] if row else None
|
||||
def fp(value):
|
||||
return hashlib.sha256(value.encode()).hexdigest()[:8]
|
||||
parts = []
|
||||
for service, expect in (("models.chat", chat), ("models.lightweight", light)):
|
||||
if not expect:
|
||||
continue
|
||||
got = active(service)
|
||||
if got is None or fp(got) == fp(expect):
|
||||
continue
|
||||
parts.append("%s fp=%s expected=%s" % (service, fp(got), fp(expect)))
|
||||
if parts:
|
||||
print("; ".join(parts))
|
||||
PY
|
||||
)" || return 0
|
||||
if [ -n "$report" ]; then
|
||||
c_warn "[$ENV_NAME] 播种后的生效密钥与环境变量不一致:$report。密钥表优先于设置明文,换 key 后以 /api/secrets 为准"
|
||||
fi
|
||||
}
|
||||
|
||||
# seed_settings_db:rdm 产物不能铸 API key 时,把同一份配置合并进嵌入式系统设置。
|
||||
# 库还不存在时不创建,避免在空数据目录里写出半套表。
|
||||
seed_settings_db() {
|
||||
|
||||
Reference in New Issue
Block a user