Action safety: confirmation, fresh sign-in and approval¶
Some actions are hard to undo: deleting an app, decommissioning a cluster,
deprovisioning a managed service, opening a console in a workload, and reading
or changing a secret. Astrolift gates these actions on the server. Every gate is
on by default: the action asks for a confirmation and a fresh sign-in before it
runs, whatever the install-wide step-up switch (REQUIRE_STEP_UP_AUTH) says.
An organization can also require approval from other admins before a
destructive delete runs.
Manage the gates in Admin › Permissions › Action safety
(/administration/permissions/safety).
Gated actions¶
| Action key | Shown as | Mutations and paths |
|---|---|---|
app.deregister |
Deregister an app | deregisterAstroliftApp, softDeleteApp, tearDownApp |
cluster.decommission |
Decommission a cluster | decommissionCluster, unregisterTenantCluster |
managed_service.deprovision |
Deprovision a managed service | deprovisionManagedService, deprovisionProjectManagedService, deprovisionClusterModel |
exec.attach |
Open a console in a workload | The exec/console WebSocket relay |
secret.reveal |
Reveal a secret | App, agent, agent-bundle and project-bundle secret reveal |
secret.change |
Change a secret | Set, delete, rotate and bulk-import app secrets; set and delete agent, agent-bundle, project-bundle and pipeline secret values |
The gate runs after the target and permission checks and before any side effect. It does not replace RBAC: the caller still needs the permission the action itself requires.
Fresh sign-in¶
When a browser session has not stepped up recently, a gated mutation returns
the STEP_UP_REQUIRED envelope. The dashboard opens its sign-in prompt and
retries the action once you have signed in again.
A console has no mutation envelope. The relay closes the WebSocket with code
4428 before accepting it; the terminal shows "Sign in again to open a console
here.", opens the same prompt and attaches again after it succeeds.
API-token calls, including astro exec, pass the fresh sign-in requirement:
issuing the token was the authentication. They do not skip approval.
Rules and scopes¶
A rule applies to one action at one scope: the organization (ORG), a team
(TEAM), an app (APP) or a person (USER). The strictest requirement from
any applicable scope wins.
- Organization: can turn a gate off or back on, turn off admin self-approval, and require approval.
- Team, app and person: can only add. They can turn a gate on, require
approval or forbid self-approval, but never turn a gate off. The API refuses
enabled: falsebelow the organization with aVALIDATIONerror. - Self-approval is allowed by default and is off if any applicable scope turns it off.
- Approval is off by default. Any applicable scope can require it on a destructive delete; the largest quorum asked for wins, up to five approvers.
The Action safety page lists every gated action with two organization switches, Confirm and sign in again and Admins may approve their own, plus an Approval column on approvable rows. A row that a team, app or person has also set says Also set for a team, app or person. Under Add a rule, pick a team or app from the scope tree or search for a person, then choose Ask before (confirm and sign in again), Require approval with the number of approvers needed, or No approving your own. Removing a narrower rule makes that scope inherit again. Show what applies on picks an app and shows, read-only, the gates in force there for you after every rule.
Changing a rule asks for a fresh sign-in and requires org.update. A rule
write changes only the switches it sends; a switch left out keeps its value.
Approval for destructive deletes¶
Approval applies to app.deregister, cluster.decommission and
managed_service.deprovision. The API refuses to add it to other actions.
Secret changes keep the app's own secret-approval setting, which already has
a quorum, so a rule cannot add a second approval to secret.change.
When approval is required:
- The gated mutation records a request bound to the organization, action,
target and requester, and fails with
APPROVAL_REQUIRED. The error carriesapprovalRequestId. Running the action again while the request is open returns the same request. The dashboard shows a notice with View requests. - Approvers open Approval requests on the Action safety page and choose Approve or Reject. One rejection closes the request. Each approver decides once. The requester can Withdraw a pending or approved request.
- Once enough approvals arrive, the request shows "Approved: run it again to go ahead". The requester runs the same action again. The server rechecks current RBAC and the target, then spends the approved request exactly once.
A request expires 24 hours after it opens. An expired request is never spent; running the action again opens a new one.
Who can approve¶
An approver needs the authority the action itself needs:
| Action | Approver permission |
|---|---|
app.deregister |
app.delete on the app |
cluster.decommission |
cluster.unregister on the organization |
managed_service.deprovision |
app.update on the owning app, or org.update for a service with no app |
Approving or rejecting may also ask the approver for a fresh sign-in.
An admin may approve their own request only when every applicable rule allowed
self-approval when the request was made and still does, and when they hold
org.update. Otherwise the approval is refused and another approver is
needed.
Notifications¶
When a request first opens, every active organization member who holds the approver permission on that target, other than the requester, gets an inbox notification. Up to 500 members are checked per request. When the request is decided, the requester is told it was approved (run it again) or rejected. A self-approval does not notify the person who made it. Notifications link to the Action safety page; opening one never approves or runs anything.
Audit¶
Request, consume, expire and cancel are audited, and a self-approval is
recorded as action_safety.approval.self_approve. A mutation refused with
APPROVAL_REQUIRED is audited with a DENY decision.
Use the API¶
Read what each gated action asks of you, optionally on one app:
query ActionSafety($appSlug: String) {
astroliftActionSafety(appSlug: $appSlug) {
action label enabled confirmation verification
allowSelfApproval approvable approval minimumApprovals sources
}
}
sources names the rules that shaped each answer (ORG, TEAM:<id>,
APP:<id>, USER:<id>). astroliftActionSafetyRules lists every rule set in
the organization. astroliftActionApprovalRequests(status) lists requests,
newest first; status is one of pending, approved, rejected,
cancelled, expired or consumed. These reads require org.read.
Write rules with setActionSafetyRule and remove them with
clearActionSafetyRule. For a team, app or person, pass scopeId; a person
rule uses the user id.
mutation RequireApproval {
setActionSafetyRule(input: {
action: "cluster.decommission"
scopeKind: "ORG"
requireApproval: true
minimumApprovals: 2
}) {
ok
errors { code message field }
data { action scopeKind requireApproval minimumApprovals }
}
}
minimumApprovals must be between 1 and 5. Decide a request with
approveActionRequest, rejectActionRequest or cancelActionRequest, each
taking { id, reason }. As with other mutations, check ok and errors
rather than the HTTP status. An automation that receives APPROVAL_REQUIRED
should record approvalRequestId, wait for the decision, then run the same
action again.
See Running an org for organization administration and Using the dashboard for how the dashboard decides which actions to offer.