feat(app): startup reaper for MCP system tokens (spec §6.4)

- Runs after ACP startup reaper marks stuck sessions as crashed
- mcpRepo.ReapStaleSystemTokens revokes tokens bound to crashed/exited sessions
- Single audit entry per batch: mcp.token.expire_on_restart with count + token_ids
- Errors logged but don't block boot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-08 13:08:21 +08:00
parent 6917acb778
commit 0644fc9b3b
+24
View File
@@ -329,6 +329,30 @@ func New(ctx context.Context, cfg *config.Config, dist embed.FS) (*App, error) {
}
}
// Phase 1.5 (post-ACP-reaper): revoke MCP system tokens still bound to crashed/exited
// sessions. spec §6.4 — must run AFTER ACP reaper so sessions are already marked crashed.
revokedTokenIDs, rerr := mcpRepo.ReapStaleSystemTokens(ctx)
if rerr != nil {
log.Warn("mcp.startup_reaper.failed", "err", rerr.Error())
} else if len(revokedTokenIDs) > 0 {
log.Info("mcp.startup_reaper.revoked", "count", len(revokedTokenIDs))
_ = auditRec.Record(ctx, audit.Entry{
Action: "mcp.token.expire_on_restart",
TargetType: "mcp_token",
TargetID: "(batch)",
Metadata: map[string]any{
"count": len(revokedTokenIDs),
"token_ids": func() []string {
out := make([]string, 0, len(revokedTokenIDs))
for _, id := range revokedTokenIDs {
out = append(out, id.String())
}
return out
}(),
},
})
}
// Phase 2: build supervisor + session service.
acpProjAccess := acpProjectAccessAdapter{wsRepo: wsRepo, projectRepo: projectRepo}
acpSup := acp.NewSupervisor(