安全审计日志¶
Specivo 会记录一份安全审计日志,涵盖登录、访问变更、管理操作、单点登录(SSO)与账户预配,以及 AI 智能体(AI agent)通过 MCP 执行的所有操作。每个实例都会记录,无需额外开启。管理员可在 管理 → 审计日志 中查看,也可以通过 API 读取。
记录的内容¶
每一行记录包含时间、事件类型(如 login_failure 或 scim_user_created 这样的代码)、执行操作的账户,以及请求来源:客户端地址、请求 ID 和浏览器或客户端名称(user agent)。涉及项目或某个对象的记录还会注明该项目或对象。详情 中保存 ID、键、计数和简短名称,详见详情中保存的内容。
查看器把事件类型分为以下几类:
| 类别 | 包含的事件 |
|---|---|
| 登录 | 密码登录及登录失败、被拒绝的凭据(auth_failure)、密码重置与修改、电子邮件地址变更。 |
| 访问 | 被拒绝的权限检查(access_denied)、通过 API 查看资源、搜索、项目成员与用户组变更、匿名访问设置。 |
| 智能体 | 仅由智能体通过 MCP 执行的操作:读取和列出工作项(Issue)、维基页面、项目、成员和版本;创建或修改工作项、评论、维基页面、版本、关联、重复模板、工时记录和 AI 订阅。 |
| 工作项和维基 | 附件、维基页面的删除与恢复、冲刺(Sprint)的开始与完成、标签、丢弃的计时。由智能体写入的记录会标记为 MCP。 |
| 管理 | 项目标识重命名、仓库集成切换到其他提供商、元数据架构变更、停用账户、货币的启用或停用、报告货币变更、汇率的添加、编辑、删除或导入、汇率设置及汇率抓取的开启或关闭、AI 模型价格的添加、修改或删除、内置价格的加载、管理员对他人 AI 订阅的添加、修改或删除,以及 AI 订阅自助服务的开启或关闭。管理员对用户的操作(创建、锁定、重置密码、API 密钥)归入“访问”类别,事件类型为 member_change。 |
| 单点登录 | 单点登录的登录及失败、账户的创建、关联或解除关联、角色同步、提供商配置。 |
| 预配 | SCIM 用户与用户组变更、被拒绝的 SCIM 请求、SCIM 令牌变更。 |
| 会话 | 被撤销的会话、单点登录登出(包括 back-channel 登出)、身份提供商(IdP)拒绝续期的会话。 |
| AI 使用 | 以他人名义记录的 AI 使用、由非所有者修改的条目、从报告中排除或重新纳入的条目、删除的条目、价格变更后重新计算的费用,以及智能体通过 MCP 记录或修改的 AI 使用。 |
每一次 MCP 工具调用都会记录,详情中带有 source: mcp,并包含该次调用的客户端地址、请求 ID 和 user agent。
不记录的内容¶
- 人工对工作项和维基页面的编辑。 在 Web 界面或通过 REST API 编辑工作项、发表评论或修改维基页面不属于审计事件。这些变更保存在工作项的历史和维基页面的版本中,包含修改人和时间。智能体通过 MCP 做的同类编辑则两处都有记录。
- 通过的权限检查。 打开一个你有权查看的页面不会写入任何记录;被拒绝的检查才会(
access_denied)。 - 没有凭据的请求。 未登录的浏览器标签页、爬虫或端口扫描器调用 API 不会写入记录,刚好过期的浏览器会话也不会。
- 重复的拒绝。 被提交并被拒绝的凭据(错误的令牌,或无效、停用、过期的 API 密钥),按客户端地址和原因每分钟只记录一次
auth_failure,而不是每次请求都记。API 与 MCP 的拒绝分开计数,来自 MCP 端点的记录标记为 MCP。 - 对公开项目的 匿名读取。
查看方式¶
管理 → 审计日志(/admin/audit-log/)按时间倒序列出事件。可按类别、事件类型、用户登录名、项目标识、日期范围和客户端地址筛选;日期按你所在时区的自然日计算,页面默认显示最近 30 天。时间按你的时区显示。点击某一行的 显示 可查看该行的详情、请求 ID 和 user agent。此后已被删除的用户或项目显示为 # 加其 ID。
同样的数据也可通过 GET /api/v1/admin/audit-logs/ 获取,支持筛选参数 event_type、event_types(可重复传入多个)、user_id、login、project_id、ip_address、after、before、offset 和 limit。每条记录带有 user_login 和 project_key。传入未知的事件类型或登录名会返回 422。
只有实例管理员可以读取日志。管理员的 API 密钥可用于该 API;限定在部分项目的密钥则不行。
保留期限¶
默认保留 12 个月,按完整的自然月计算:在 2026 年 11 月 4 日,保留的最早一天是 2025 年 11 月 1 日。每天运行一次的任务(worker 启动时也会运行)会删除更早的月份。通过环境变量修改,然后重启整个服务栈:
| 变量 | 含义 |
|---|---|
AUDIT_LOG_RETENTION_MONTHS |
当前月份之前保留的完整月数。默认 12。0 表示全部保留。 |
这个设置刻意没有放进管理页面:安全证据的保留时长不应该能从管理员的会话或 API 密钥中修改。
日志按月分区存储在 security_audit_logs 表中,因此删除一个月的数据很快。如果任务在五秒内拿不到该表的锁,就会放弃并在次日重试。日志是数据库的一部分,所以备份中会包含它。
详情中保存的内容¶
对象的 ID、键、slug、名称和标题(文件名、页面标题、冲刺名称)、代码、计数、账户登录名、用户组名称,以及截断到 500 个字符的搜索文本。绝不保存令牌、密钥或任何以密码形式输入的内容,也绝不保存工作项、评论、描述或维基页面的正文。
登录失败时,只有登录名对应现有账户才会记录该登录名;否则记录中写 login_known: false,这样误输入登录名栏的密码不会被保存。密码重置请求对邮箱地址的处理相同:只有匹配到账户时才保存,否则记录中写 email_known: false。
电子邮件地址变更¶
| 事件 | 账户 | 详情 |
|---|---|---|
email_change_requested |
本人 | new_email、address_in_use(该地址已被另一账户使用,未发送链接)、resend |
email_change_failed |
本人 | reason:密码错误、未在身份提供商处重新登录,或确认链接已使用、已被替换、已取消、已过期、其地址在此期间被另一账户占用 |
email_changed |
本人或管理员 | by(user 或 admin)、target_user_id、target_login、old_email、new_email;本人修改时还有 revoked_sessions |
与任何请求都不匹配的确认链接不会写入记录。参见个人资料。
仓库提供商变更¶
| 事件 | 账户 | 详情 |
|---|---|---|
repository_provider_changed |
保存变更的人 | old_provider、new_provider、old_repository、new_repository、secret_rotated;记录中注明项目和集成(repository_integration 及其 ID) |
更换集成的提供商会生成新的 webhook URL 和新的密钥;密钥本身绝不会记入日志。仓库集成的其他编辑不属于审计事件。参见仓库集成。
货币¶
| 事件 | 账户 | 详情 |
|---|---|---|
currency_toggled |
管理员 | code、is_active |
reporting_currency_changed |
管理员或项目经理 | scope(instance 或 project)、项目级别时有 project_key、old、new;项目的 null 表示跟随实例设置 |
保存与当前相同的值不会写入记录。参见货币与报告货币。
开发链接¶
| 事件 | 账户 | 详情 |
|---|---|---|
development_link_detached |
解除链接的人 | issue、item_type(commit 或 pull_request)、item_id、item(commit SHA,或 #N / !N)、repository;记录中注明项目和工作项 |
development_link_moved |
移动链接的人 | 同上,另加 target_issue 和 target_project_id |
指通过 API 将 commit 或 pull request 从工作项上解除,或移动到另一个工作项。commit 消息和 pull request 标题绝不会记入日志。参见仓库集成。
汇率¶
| 事件 | 账户 | 详情 |
|---|---|---|
exchange_rate_created |
管理员 | rate_id、rate_date、base、quote、rate、source(manual) |
exchange_rate_updated |
管理员 | 同上,另加 changes:每个变更的字段各带 old 和 new |
exchange_rate_deleted |
管理员 | 被删除的汇率:rate_id、rate_date、base、quote、rate、source |
exchange_rates_imported |
管理员 | rows、created、updated、unchanged、date_from、date_to |
exchange_rates_fetch_toggled |
管理员 | enabled、url |
exchange_rate_settings_changed |
管理员 | changes:pivot、max_age_days 或 fetch_url,各带 old 和 new |
CSV 预览、被拒绝的导入以及保存未变更的值都不会写入记录。每日抓取本身不是审计事件,其最近一次结果显示在管理页面上。参见汇率。
AI 模型价格¶
| 事件 | 账户 | 详情 |
|---|---|---|
ai_model_price_created |
管理员 | price_id、provider、model、currency、effective_from、effective_to、source(admin)、is_active、prices(每百万 token 的各项设定价格);若它结束了该模型上一个价格的有效期,则还有 closed_price_id |
ai_model_price_updated |
管理员 | 同上,另加 changes:每个变更的字段(effective_to、is_active、aliases、note)各带 old 和 new |
ai_model_price_deleted |
管理员 | 被删除的价格,字段同 ai_model_price_created |
ai_model_prices_seeded |
管理员;若由 worker 在启动时加载则无账户 | as_of(内置价格表的日期)、inserted、closed、history_inserted、aliases_updated、unchanged、skipped(均为计数) |
保存未变更的值,以及加载一份不带来任何变化的内置价格表,都不会写入记录。安装后的首次加载在数据库迁移中完成,不记入审计。参见AI 模型价格。
AI 订阅¶
| 事件 | 账户 | 详情 |
|---|---|---|
ai_subscription_created |
管理员 | subscription_id、target_user_id、target_login(订阅持有人)、provider、plan_key、plan_name、tool_slugs、amount、currency、billing_period、starts_on、ends_on |
ai_subscription_updated |
管理员 | 同上,另加 changes:每个变更的字段各带 old 和 new;备注的变更只显示为 notes: {"changed": true} |
ai_subscription_deleted |
管理员 | 被删除的订阅,字段同 ai_subscription_created |
ai_subscriptions_self_service_toggled |
管理员 | enabled |
ai_subscriptions_changed |
智能体所用密钥对应的账户,通过 MCP(specivo_ai_subscriptions 添加、更新、结束或删除) |
tool: "ai_subscriptions"、source: "mcp"、op、subscription_ids、target_login(订阅属于谁),修改时还有变更的 fields |
只有管理员对 他人 订阅的修改才记录为 ai_subscription_*。用户修改自己的订阅,以及管理员修改自己的订阅,都不会记录;但与所有智能体工具调用一样,通过 MCP 的每一次修改都会记录为 ai_subscriptions_changed。备注绝不会复制到日志中。参见AI 订阅。
AI 使用¶
| 事件 | 操作者 | 详情 |
|---|---|---|
ai_usage_logged_for_other |
记录该使用量的账户 | entry_id、target_user_id、target_login(使用量属于谁)、source、tool、provider、model、usage_date、project_key、issue、reporter_api_key_id;当上报方为已替该用户记录过的条目发送新的总量时,还有 status: "updated" 和 revision(重复发送且无新内容时不写入记录) |
ai_usage_updated_by_other |
修改他人条目的人 | 同上,另加 changes:每个变更的字段各带 old 和 new;评论的变更只显示为 comments: {"changed": true} |
ai_usage_excluded、ai_usage_included |
将条目从报告中排除或重新纳入的人 | 该条目,字段同上 |
ai_usage_deleted |
删除条目的人 | 该条目,另加其 tokens 和 cost_type |
ai_usage_logged、ai_usage_changed |
智能体账户,通过 MCP(specivo_log_ai_usage;specivo_ai_usage 更新、排除或重新纳入条目) |
tool: "log_ai_usage" 或 "ai_usage"、source: "mcp" 和 entry_id;记录新条目时还有 target_login、tool、model、usage_date、project_key 和 issue,修改时则有 op 和变更的 fields |
ai_usage_costs_recomputed |
发起重算的管理员 | provider、models、from、to、entries(读取数)、changed,以及边际成本和 API 等价价值按货币汇总的 before / after 总额 |
用户通过 Web 或 REST API 为自己记录的使用量不在此记录:他们随时可以查看自己的条目。通过 MCP 记录的则会记录(ai_usage_logged),与所有智能体工具调用一样。评论是自由文本,绝不会复制到日志中;AI 使用记录中也绝不会保存任何提示词、代码或文件名。
个人数据¶
审计日志包含个人数据,所有实例管理员都可读取:
- 所记录的人员和智能体的 客户端地址(IP) 和 user agent。
- 执行操作或被操作的账户的 登录名。
- 邮箱变更事件中的 电子邮件地址:旧地址、新地址,以及申请变更时填写的地址。
- 人员和智能体输入的 搜索文本(最多 500 个字符)。
保留期限不要超过实际需要,在隐私声明中说明这份日志,并在回应个人数据请求时把它包括在内。如果部署在反向代理之后,请设置 TRUSTED_PROXIES,让日志记录客户端地址而不是代理地址。