Capabilities

Deployment approval governance — gate every CI/CD release

AccessFlow puts a CI/CD release request through the same machinery as a database query — AI analysis, routing policies, named approvers, an audit trail — and gives your CI job a gate to block on that answers not releasable whenever it is unsure. Shipping to production is an access decision, and most pipelines record it as a protected-branch rule or a chat message, which is neither reviewable nor auditable after the fact.

Pipelines, environments and who may trigger them

A deployment pipeline is the governed unit — a name, a provider (GitHub Actions, GitLab CI, Azure Pipelines, Jenkins, CircleCI, Bitbucket Pipelines, or generic), an optional repository reference, a review plan, and its own AI switch. Under it sit ordered environments: dev, staging, production. Each environment can require review, override the required approval count and the review plan, and opt in to break-glass, which is off by default.

Who may trigger a release is a per-pipeline grant rather than a global role: a non-admin needs one, and an org admin short-circuits that check — but never the break-glass one. Grants exist per user and per group, each carrying trigger and break-glass flags and an optional expiry, and the effective answer is the most-permissive union of a user's direct grant and their unexpired group grants. The trigger endpoint, the gate and the outcome endpoint all resolve permission through that one path, so they cannot disagree. Administration needs a deployment-pipeline permission, reviewing needs a deployment-review permission, and every read is organization-scoped: an id from another organization returns 404, never 403, so no endpoint can be used to probe for what exists.

Deleting a pipeline removes its configuration but not its history — past requests, decisions and rollback reviews survive as evidence.

The gate is fail-closed by construction

A CI job opens a request by calling AccessFlow with an ordinary API key — no deployment-specific token. The key should belong to a service account, an identity of its own that can never sign in; whoever owns the key is the submitter, which is what makes the self-approval ban meaningful for a machine-triggered release. When the pipeline names the engineer who started it, that engineer cannot approve the release either. The trigger is idempotent on the CI run id at the database level, so a retried step never opens a second review.

From there the request behaves like any other: the AI analyzer scores the release metadata, routing policies keyed on environment, provider, version glob, risk or time window can auto-approve, auto-reject or raise the approval bar, and reviewers decide in the UI — from the shared review queue, or from the deployment's own page once they have read its version, commit and justification. Review is single-stage, a rejection is immediately terminal, and self-approval is refused first and always — including for the submitting API key's owner, and including for an admin. Where the resolved review plan names approvers, only those users may decide.

The job then polls one endpoint and branches on one field. A release is releasable only when all three hold: the request is exactly approved, no freeze window is active, and any scheduled time has already passed. Everything else answers not releasable — a pending request, a rejected one, an unknown pipeline or version, a caller who may not see the request, and any internal error at all. A failure to evaluate degrades to a refusal, never to an accidental yes.

A deployment request in AccessFlow: pipeline, target environment, version and commit alongside a pending-review status, Approve and Reject buttons for the reviewer, the submitter's justification, an approvals table reading 0 of 1, and an approval timeline running submitted, AI analysis skipped on this request, human review awaiting a reviewer, then release.
/deployments/<id> — this one is still pending review, so the gate answers not releasable; approval is what flips it. An eligible reviewer decides right here; everyone else sees no buttons at all.

Freeze windows, and the emergency exit

A freeze window suspends releases across an organization, one pipeline, or a single environment. It is either one-off — a start and an end — or recurring on chosen weekdays in a named timezone, evaluated as local wall-clock so a window that crosses midnight behaves the way an operator means it to. Among simultaneously active windows the most specific scope wins.

Windows come in two behaviours. A rejecting window turns a release away at submission. A holding window lets the request be analyzed, reviewed and approved as normal and only withholds releasability at the gate — so the release proceeds the moment the window closes, with no re-approval. A window that cannot be evaluated at all fails closed to a hold, never to a rejection: a broken definition can stall deployments but can never destroy requests.

Break-glass deploys are gated twice with no admin bypass — the submitter needs a break-glass grant on the pipeline and the environment must allow it. In exchange they fan out instantly to every org admin, record an audit entry naming any freeze window they bypassed, and open a mandatory retro-review that an admin, never the submitter, must acknowledge. Deployment pipeline docs →

What happened after the deploy

Approval without an outcome is half a record, so the pipeline reports back: succeeded, failed, or rolled back. Repeating the same report is idempotent; reporting a different outcome is a conflict rather than a silent overwrite. A failure report flips the request from executed to failed — the one post-terminal transition the state machine allows.

A rollback on an environment that requires review opens a follow-up review in the same transaction, so a governed rollback can never exist without one. As everywhere else, the submitter cannot close their own: acknowledgement is somebody else's, and it is a latch rather than a contested decision.

Which version is where

A matrix lists every environment you deploy to, the version it is running now, the one it was running before, when it got there, and how that deploy ended. It answers the question the gate cannot: not whether a release may go out, but what is already out there.

Each row also says how far behind it has fallen, counted two ways — how many newer releases have gone out since it last moved, and how many days passed between its deploy and the newest one. "Behind" is a plain comparison against the newest release that succeeded on that pipeline, not an attempt to parse your version numbers, so it works whether you ship 2.4.1, a date, or a commit hash. Nothing is scheduled and nothing is alerted on: the comparison is made when you look.

Environments carry short tags of your choosing — up to ten each, a customer, a region, a tier — and the matrix filters on them. That is what turns one application deployed for many customers into a single readable view: one environment per customer, all tagged alike, all lined up beside their versions. Each environment also keeps its own history, newest first, and a rolled-back environment says what it reverted to rather than quietly showing a version it is no longer running.

Schema changes, environment by environment

The same environments can carry database changes, not just releases. Give an environment the database it runs on, then write a change set — the statements that create a table, add a column or drop an index — once. AccessFlow checks every statement with that database's own parser before saving it, and turns away anything that reads or changes data rather than structure. From the first promotion on, the list is locked, so production receives exactly the text that ran in staging.

A change set climbs the environments in order. Each step is reviewed like any other request, and a step stays blocked until every lower environment with a database has applied it successfully; the page says which one is holding it up. Promoting needs the right to change that database's structure — for admins too — and a freeze window stops it just as it stops a release. There is no undo: statements run one after another, so a failure part-way leaves the earlier ones in place and the step marked as partly applied, and the fix is a new change set.

Once changes are out, a scheduled scan you switch on per pipeline compares each environment's tables, columns and foreign keys against the one below it, a reference environment, or what it looked like right after its last promotion — the last catches a change someone made by hand. Differences land in a list to acknowledge. The scan never corrects anything, and it cannot see indexes, constraints, defaults or views. Schema change set docs →

One step in your pipeline

The wiring is deliberately small. AccessFlow ships composite GitHub Actions, hidden GitLab CI jobs, and an Azure Pipelines step template that each do the same four things: submit the request idempotently by run id, poll the gate, confirm execution, and report the outcome. There is a documented plain-curl walkthrough for everything else.

All of them are fail-closed in the direction that matters: an unknown release or any non-retryable error fails the job rather than letting it through. Pipelines are addressed by id rather than by name, because a trigger-only key is not permitted to resolve names — which is the same fail-closed instinct applied to identifiers. CI gate setup → Service-account API keys →

The teams that reach for this are usually the same ones removing shared production credentials elsewhere — see the release-engineering use case and the platform-engineering one. Pipelines and environments are configured in the admin UI; the Terraform provider covers the rest of the estate — datasources, review plans, routing, masking and row-security policies, AI configs and notification channels. Terraform provider docs →