Background
Today every purchase goes through approval-by-email — see internal/config/defaults.go:59, internal/config/types.go:15 (ApprovalRequired bool, default true). There is no way for a sufficiently privileged user to skip the email round-trip and execute a purchase directly from the dashboard, even though they're logged in, RBAC-authorized, and looking at the rec they want to buy.
For trusted operators (admin role, finance lead, automation accounts) the email round-trip is friction without security benefit — they already have the authority that the email step is verifying.
What needs to happen
Permission verb
Add execute-any:purchases and execute-own:purchases to internal/auth/types.go:284-300, mirroring the existing cancel-any/cancel-own and retry-any/retry-own shape:
RoleAdmin → execute-any:purchases (admin-implies-everything precedent)
RoleUser → no default grant. Must be explicitly granted per-user/per-role; this is a finance-impacting permission.
execute-own:purchases exists for parity with the other *-own verbs (lets a delegated buyer execute purchases they themselves drafted) but is similarly opt-in.
Backend: bypass approval when permission holds
The current executePurchase flow (find via frontend/src/app.ts:351, 433 → internal/api/handler_purchases.go) routes every request through SendPurchaseApprovalRequest (:1167). Branch the path:
if h.executor.SessionHasExecutePermission(session, plan) {
// Direct execution — bypass approval, charge immediately.
result, err := h.executor.ExecuteImmediately(ctx, plan)
// Audit trail: persist executed_by_user_id, executed_at, and a
// non-null pre_approval_skip_reason = "direct-execute permission".
...
} else {
// Existing path: send approval email, return pending_approval.
...
}
Audit fields are non-optional — finance auditors need to be able to ask "who direct-executed this $X purchase?".
Frontend: warning + confirmation gate
openPurchaseModal (frontend/src/recommendations.ts:2084) renders the modal. When the session has execute-any:purchases:
- Toggle / radio: "How would you like to handle this purchase?" with two options:
Send for Approval (default, current behavior) and Execute Now.
- When
Execute Now is selected, render a styled red callout:
⚠️ This will charge $X,XXX upfront immediately. This bypasses the approval step. AWS allows cancellation within 24 hours via the Account & Billing console (see # for the revocation procedure).
- The Execute button label becomes
"Execute Purchase Now".
- A second-confirmation step before firing the API call — either a typed-confirmation ("type EXECUTE to confirm") or a styled red
confirmDialog with the rec summary echoed back. The Cancel button's confirmDialog shape is sufficient precedent.
Email notification (sibling issue)
When direct-execute fires, an email STILL goes out — but it's a notification ("we executed this purchase on your behalf, here's a cancel link") rather than an approval request. See sibling: "Post-execution cancel-link email".
Acceptance
- A user with
execute-any:purchases sees the Send-for-Approval / Execute-Now toggle in the purchase modal.
- Selecting Execute-Now shows the cost callout + the cancellation-window note + the second-confirmation gate.
- The API call results in an immediate charge (the actual cloud-side commit fires inline rather than enqueueing the approval).
- A user without
execute-any:purchases only sees the Send-for-Approval path; the toggle is absent.
- Audit fields (
executed_by_user_id, executed_at, pre_approval_skip_reason) are populated for direct executes.
- Tests:
- Unit: backend branches correctly on permission.
- Frontend: toggle renders only when the permission is present.
- End-to-end: a direct-execute by an admin produces an executed row and triggers the cancel-link email (sibling issue).
Severity
p2 / severity/medium / impact/few — affects only the small set of users who'd hold the new permission. Not a bug; the current flow is safe-by-default. This is a deliberate convenience escape hatch with audit + warning + cancellation-window guardrails.
References
internal/auth/types.go:284-300 (default permission grants — the cancel-own / retry-own precedents)
internal/api/handler_purchases.go (the full executePurchase + SendPurchaseApprovalRequest flow)
internal/config/defaults.go:59 + internal/config/types.go:15 (the global approval-required default — stays as the fallback)
frontend/src/recommendations.ts:2084 (the purchase modal — needs the toggle + warning + confirm-gate)
frontend/src/app.ts:351, 433 (the execute-button text reset — needs the new label branch)
- Sibling: "Purchase modal copy clarification" — must land before or with this so the default-path button reads correctly even before this lands.
- Sibling: "Post-execution cancel-link email" — the after-the-fact notification path for direct-executes.
- Sibling: "AWS RI/SP revocation via support case" — the actual AWS-side cancellation mechanism the warning references.
Background
Today every purchase goes through approval-by-email — see
internal/config/defaults.go:59,internal/config/types.go:15(ApprovalRequired bool, defaulttrue). There is no way for a sufficiently privileged user to skip the email round-trip and execute a purchase directly from the dashboard, even though they're logged in, RBAC-authorized, and looking at the rec they want to buy.For trusted operators (admin role, finance lead, automation accounts) the email round-trip is friction without security benefit — they already have the authority that the email step is verifying.
What needs to happen
Permission verb
Add
execute-any:purchasesandexecute-own:purchasestointernal/auth/types.go:284-300, mirroring the existingcancel-any/cancel-ownandretry-any/retry-ownshape:RoleAdmin→execute-any:purchases(admin-implies-everything precedent)RoleUser→ no default grant. Must be explicitly granted per-user/per-role; this is a finance-impacting permission.execute-own:purchasesexists for parity with the other*-ownverbs (lets a delegated buyer execute purchases they themselves drafted) but is similarly opt-in.Backend: bypass approval when permission holds
The current
executePurchaseflow (find viafrontend/src/app.ts:351, 433→internal/api/handler_purchases.go) routes every request throughSendPurchaseApprovalRequest(:1167). Branch the path:Audit fields are non-optional — finance auditors need to be able to ask "who direct-executed this $X purchase?".
Frontend: warning + confirmation gate
openPurchaseModal(frontend/src/recommendations.ts:2084) renders the modal. When the session hasexecute-any:purchases:Send for Approval(default, current behavior) andExecute Now.Execute Nowis selected, render a styled red callout:"Execute Purchase Now".confirmDialogwith the rec summary echoed back. The Cancel button'sconfirmDialogshape is sufficient precedent.Email notification (sibling issue)
When direct-execute fires, an email STILL goes out — but it's a notification ("we executed this purchase on your behalf, here's a cancel link") rather than an approval request. See sibling: "Post-execution cancel-link email".
Acceptance
execute-any:purchasessees the Send-for-Approval / Execute-Now toggle in the purchase modal.execute-any:purchasesonly sees the Send-for-Approval path; the toggle is absent.executed_by_user_id,executed_at,pre_approval_skip_reason) are populated for direct executes.Severity
p2 / severity/medium / impact/few — affects only the small set of users who'd hold the new permission. Not a bug; the current flow is safe-by-default. This is a deliberate convenience escape hatch with audit + warning + cancellation-window guardrails.
References
internal/auth/types.go:284-300(default permission grants — the cancel-own / retry-own precedents)internal/api/handler_purchases.go(the full executePurchase + SendPurchaseApprovalRequest flow)internal/config/defaults.go:59+internal/config/types.go:15(the global approval-required default — stays as the fallback)frontend/src/recommendations.ts:2084(the purchase modal — needs the toggle + warning + confirm-gate)frontend/src/app.ts:351, 433(the execute-button text reset — needs the new label branch)