diff --git a/.claude/patterns/notification-fanout.md b/.claude/patterns/notification-fanout.md index 1f6c99f87..fc9c6cc80 100644 --- a/.claude/patterns/notification-fanout.md +++ b/.claude/patterns/notification-fanout.md @@ -24,7 +24,7 @@ frontend. **Verified 2026-08-12** (#625) — re-derive rather than trust these l | **Email — subject args** | `notifications/internal/strategy/EmailNotificationStrategy.java` | 235 | ⚠️ **silent** — has a `default` | | **Ticket body** | `notifications/internal/strategy/TicketDescriptionBuilder.java` | 21 | ⚠️ **silent** — has a `default` | | Dispatcher / channel scope | `notifications/internal/NotificationDispatcher.java` | `resolveChannels` | org-wide events must be listed explicitly | -| PagerDuty / ticketing triggers | `internal/codec/{PagerDutyTrigger,TicketingTrigger}.java` | | absent ⇒ never pages / never opens a ticket (usually right) | +| PagerDuty / ticketing triggers | `internal/codec/{PagerDutyTrigger,TicketingTrigger}.java` | | absent ⇒ never pages / never opens a ticket (usually right). A `TicketingTrigger` may map **several** event types — `DEPLOYMENT_SUBMITTED` (#991) opens on `DEPLOYMENT_SUBMITTED` *or* `DEPLOYMENT_APPROVED` — so check every trigger's event set, not just its name. The ticket headline in `strategy/TicketDescriptionBuilder.java` has explicit deployment cases and a `default` that drops the "on " suffix when the context has none (deployment-aware) — a new event that can open a ticket still wants its own case | | Thymeleaf template | `backend/src/main/resources/templates/email/.html` | | silent | | i18n subject/body | `i18n/messages.properties` + 6 locales | | `MessagesParityTest` | | **Frontend — in-app label** | `frontend/src/components/common/NotificationBell.tsx` (`labelFor` switch + `routeForNotification`) | | ⚠️ **silent** — has a `default` returning `notifications.events.fallback` | diff --git a/README.md b/README.md index 838613d0e..6768f39aa 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ A glance at the day-to-day flows engineers and approvers actually use. - **Personalized dashboard** — the default post-login home (`/dashboard`) gives every user a self-scoped view of what's waiting on them: pending approvals as a reviewer, their recent queries with status/risk trend sparklines, an actionable AI optimization-suggestion backlog (dismissable, one-click "open in editor"), and their own behavioural-anomaly alerts. Widgets are customizable — show/hide, collapse, and drag-and-drop reorder, persisted across sessions. The week's summary is exportable on demand as a **digitally signed** PDF/CSV, and an opt-in **weekly email digest** delivers it on a schedule. - **Observability** — OpenTelemetry OTLP trace export of the full proxy pipeline (parse → AI analyze → pool acquire → execute) to Tempo / Jaeger / Honeycomb, Prometheus metrics at `/actuator/prometheus`, and two pre-built Grafana dashboards (shipped via the Helm chart) covering query volume, approval SLAs, AI usage/cost, rejection rates, and connection-pool stats. Structured JSON logging (logstash / ECS / GELF) with a `traceId` correlated across logs, error responses, and traces. - **Real-time updates** — single WebSocket at `/ws` fans review-queue, status, and AI-analysis events to connected clients. -- **Notifications** — Email (SMTP), Slack, Discord, Telegram, Microsoft Teams, PagerDuty, and HMAC-signed outbound webhooks with retry policy. **ServiceNow & Jira ticketing**: auto-create an incident/issue when a query is rejected, escalated, or times out awaiting review, with linked tickets on the query detail page and signed inbound webhooks syncing ticket status back — a ticket resolution can even approve/reject the pending query (bi-directional sync). +- **Notifications** — Email (SMTP), Slack, Discord, Telegram, Microsoft Teams, PagerDuty, and HMAC-signed outbound webhooks with retry policy. **ServiceNow & Jira ticketing**: auto-create an incident/issue when a query is rejected, escalated, or times out awaiting review, with linked tickets on the query detail page and signed inbound webhooks syncing ticket status back — a ticket resolution can even approve/reject the pending query (bi-directional sync). The same channels open **change tickets for deployments** to environments that require one, and the deployment gate waits until the ticket is approved. - **Slack approve/reject** — a configured Slack app adds **Approve** / **Reject** buttons to review-request messages; the decision runs through the same self-approval and RBAC guards as the REST API (HMAC-verified Interactive Components). - **Mobile approvals (PWA) with one-tap push** — install AccessFlow as a Progressive Web App with an offline-capable review queue, and get **Web Push** notifications when a query needs your approval. Approve or reject in one tap — the decision only commits after a **step-up re-verification** (password, or TOTP when 2FA is on), and the self-approval guard is enforced server-side on every channel. - **Identity & SSO** — JWT access tokens (15 min) + HttpOnly refresh cookies, optional SAML 2.0 SSO, OAuth 2.0 / OIDC sign-in with built-in templates for Google, GitHub, GitHub Enterprise Server, Microsoft, GitLab, and self-managed GitLab plus a generic `OIDC` provider for other IdPs (Keycloak, Auth0, Okta, Authentik, Zitadel), password reset and user-invitation flows, and SCIM 2.0 provisioning so the IdP drives user & group lifecycle (create, update, deactivate, group sync — Okta / Entra ID / Keycloak / OneLogin). @@ -135,7 +135,7 @@ A glance at the day-to-day flows engineers and approvers actually use. - **Neo4j (NoSQL graph)** — a first-class graph connector speaking **Cypher** over Bolt, shipped as the seventh on-demand engine plugin (`engines/neo4j/`, native Neo4j Java driver). Cypher is clause-based, so the query type is the strongest write clause present (DELETE/REMOVE → DELETE, CREATE/MERGE → INSERT, SET → UPDATE, else a `MATCH … RETURN` / `SHOW` read → SELECT), with index/constraint/database/role schema commands as DDL; `LOAD CSV`, procedure calls outside a read-only allow-list, and multi-statement input are rejected up front. Row-level security ANDs property predicates onto each `MATCH`'s `WHERE` (Cypher named parameters, node-label policies), failing closed on anonymous / write-creates-policied-label shapes; field masking is label-aware and recursive. Connection is host/port + database with the SSL mode encoded in the Bolt scheme, **or** a full `bolt://` / `neo4j+s://` URI (Neo4j Aura / clustered routing). - **Cloud data warehouses — Snowflake, Google BigQuery, Databricks SQL** — first-class `WAREHOUSE` connectors (the eighth, ninth, and tenth engine plugins: `engines/snowflake/`, `engines/bigquery/`, `engines/databricks/`) whose native auth models replace host/port/username/password: Snowflake authenticates with a password **or** a key-pair PKCS#8 private key — encrypted (passphrase-protected) or not — against the account host and opens billed warehouse sessions per request only; BigQuery connects with a service-account key JSON + GCP project (optionally `project.dataset`); Databricks talks to the SQL Statement Execution REST API with a personal access token against a workspace host + warehouse HTTP path, and falls back to the `EXTERNAL_LINKS` disposition for result sets above the inline ~25 MiB cap — streaming the presigned chunks up to the proxy's row and byte caps, never persisting the URLs. All three classify warehouse SQL onto the same approval workflow (scripting, data-movement, and maintenance commands like `COPY INTO` / `OPTIMIZE` / `VACUUM` are rejected up front), splice row-level security into the WHERE clause with bound parameters (failing closed on shapes that cannot be filtered), and apply field masking post-fetch — BigQuery recursively across nested RECORD fields. - **API Access Governance (AF-500)** — govern outbound **API** calls (REST / SOAP / GraphQL / gRPC), not just databases. An admin registers an **API connector** (URL + auth + protocol + admin-defined default headers; secrets AES-256-GCM encrypted) — including **OAuth2 with automatic token fetch, caching & refresh** (client-credentials / refresh-token / resource-owner password), so no bearer token is pasted by hand — uploads its schema (OpenAPI / WSDL / GraphQL SDL / gRPC proto, by paste, file upload, or URL fetch) which is parsed into a normalized operation catalog with read/write classification, and shares governed connectivity with the team via per-user permissions. Users **compose calls like Postman** — query params, custom headers, raw / form-data / x-www-form-urlencoded / binary file bodies. Every call flows through the same machinery as a database query: AI risk scoring, attribute-based routing, multi-stage human approval (no self-approval), **connector-level response masking** (policies targeting a schema field, JSON path, XML/XPath, or regex — with a masking strategy and role / group / user reveal scoping) and **data-classification tags** (PII/PCI/PHI/GDPR/FINANCIAL/SENSITIVE that auto-derive a masking policy and raise the AI risk — AF-518), immutable response snapshots (downloadable in full), W3C trace-context propagation (filterable by trace/span id), **dynamic variables** that compute a value per request and substitute it into headers, path, query or body via `{{name}}` placeholders — HMAC request signing, nonces, timestamps, idempotency keys — resolved *after* authentication so a signature can cover the freshly minted token, with opt-in per-request overrides for requesters who hold the grant (AF-613), break-glass, scheduled execution, and natural-language **text-to-API**. See [`docs/17-api-governance.md`](https://github.com/bablsoft/accessflow/blob/main/docs/17-api-governance.md). -- **Deployment approval governance (epic AF-682)** — gate CI/CD **deployments** behind the same submit → AI → review → approve machinery. Admins define **deployment pipelines** (GitHub Actions, GitLab CI, Azure Pipelines, Jenkins, CircleCI, Bitbucket Pipelines, or generic) with ordered per-environment policies (required approvals, review-plan overrides, break-glass opt-in) and per-user/per-group `can_trigger` / `can_break_glass` grants. A CI job triggers a deployment request over the REST API (API-key auth, idempotent on the CI run id), AI analyzes the release metadata, routing policies (environment / provider / version glob / risk / time window) can auto-approve, auto-reject or escalate, and the pipeline polls a **fail-closed deployment gate** until every approval is granted — with **freeze windows** (one-off or weekly recurring, `HOLD` or auto-`REJECT`), scheduled deploys, audited break-glass, post-deploy **outcome reporting** (success / failure / rollback) and a mandatory rollback retro-review. Native GitHub Action / GitLab CI / Azure Pipelines wrappers reduce the CI glue to one step, and the web UI ships the full surface: a deployment list + detail view (risk, approvals, releasability banner, outcome timeline), a reviewer queue with a rollback-review worklist, and pipeline administration incl. a copy-paste CI setup panel. Past the gate, AccessFlow tracks **which version is running where**: each environment carries free-form tags (customer / region / tier) and a current+previous version updated inside the execution and outcome transactions, surfaced as an org-wide **version matrix** with per-row drift ("N versions / D days behind" against the pipeline's newest successful release — read-time string comparison, no semver, no alerting), a per-environment deployment-history timeline, and drift badges on the deployment list and detail pages. See [docs/18-deployment-governance.md](https://github.com/bablsoft/accessflow/blob/main/docs/18-deployment-governance.md). +- **Deployment approval governance (epic AF-682)** — gate CI/CD **deployments** behind the same submit → AI → review → approve machinery. Admins define **deployment pipelines** (GitHub Actions, GitLab CI, Azure Pipelines, Jenkins, CircleCI, Bitbucket Pipelines, or generic) with ordered per-environment policies (required approvals, review-plan overrides, break-glass opt-in, a required ServiceNow / Jira change ticket) and per-user/per-group `can_trigger` / `can_break_glass` grants. A CI job triggers a deployment request over the REST API (API-key auth, idempotent on the CI run id), AI analyzes the release metadata, routing policies (environment / provider / version glob / risk / time window) can auto-approve, auto-reject or escalate, and the pipeline polls a **fail-closed deployment gate** until every approval is granted — with **freeze windows** (one-off or weekly recurring, `HOLD` or auto-`REJECT`), scheduled deploys, audited break-glass, post-deploy **outcome reporting** (success / failure / rollback) and a mandatory rollback retro-review. Native GitHub Action / GitLab CI / Azure Pipelines wrappers reduce the CI glue to one step, and the web UI ships the full surface: a deployment list + detail view (risk, approvals, releasability banner, outcome timeline), a reviewer queue with a rollback-review worklist, and pipeline administration incl. a copy-paste CI setup panel. Past the gate, AccessFlow tracks **which version is running where**: each environment carries free-form tags (customer / region / tier) and a current+previous version updated inside the execution and outcome transactions, surfaced as an org-wide **version matrix** with per-row drift ("N versions / D days behind" against the pipeline's newest successful release — read-time string comparison, no semver, no alerting), a per-environment deployment-history timeline, and drift badges on the deployment list and detail pages. See [docs/18-deployment-governance.md](https://github.com/bablsoft/accessflow/blob/main/docs/18-deployment-governance.md). - **Schema change governance (epic #870)** — author a set of schema statements **once**, under a deployment pipeline, and promote it environment by environment (`dev → staging → prod`): an environment only receives a change set that already applied successfully on every lower environment bound to a database. Each statement is parsed with the target engine's own parser before it can be saved — data statements (`SELECT` / `INSERT` / `UPDATE` / `DELETE`) are refused, and a blocking SQL review rule refuses the save — and the list freezes, with a SHA-256 checksum, the moment it is first promoted. Promotion requires the DDL grant on the target datasource for everyone, **admins included**, is refused inside a freeze window, and runs as an ordered, reviewed request group. There is **no rollback**: statements run one by one, so a failure part-way leaves the environment `PARTIALLY_APPLIED`. An opt-in scheduled **schema drift** scan compares each environment against the previous one, a reference environment, or the snapshot taken after its last promotion — tables, columns and foreign keys, not indexes, constraints, defaults, views or type precision — and records findings to acknowledge; it never writes. See [`docs/20-schema-change-governance.md`](https://github.com/bablsoft/accessflow/blob/main/docs/20-schema-change-governance.md). - **Request chaining & grouping (AF-501)** — bundle several steps into one **grouped request** reviewed and approved as a single element, then executed as an **ordered sequence**. Members can mix **queries** across different datasources and **API calls** against governed connectors; a builder lets you add steps, drag-reorder them, and author each one in a **full-parity editing drawer** — query steps get the complete Query Editor surface (schema autocomplete, AI analyze, dry-run, text-to-SQL, templates), API steps the complete API Editor surface (operation picker, request composer, AI analyze, text-to-API) — with an aggregate risk badge, and saved **drafts re-open for editing** without losing the composed request. Bundling never weakens a member's policy: each member is validated against your permission for its target (break-glass groups require `can_break_glass` on **every** target), the required approvers are the **union** across all member plans, and the group is approved only when **every** member plan is satisfied — the submitter can never approve their own group. On execute, members run in order; on the first failure the run stops and the rest are skipped (`continue-on-error` runs them all instead). There is **no distributed rollback** — an approved group is not atomic, already-applied members stay, and this is surfaced clearly. Each member records its own snapshot + audit row alongside group-level audit and live WebSocket progress. - **Data Lifecycle Manager (AF-499, AF-519)** — govern not just *who reads* data, but *when it is retired*. Admins define **retention/erasure rules** per datasource — target a table / column set / classification tag with a retention window plus **arbitrary conditions** (a structured, parameter-bound predicate builder and a JSqlParser-validated raw-`WHERE` escape hatch) and an action (**hard-delete**, **soft-delete**, or **pseudonymize**) — with an optional **cron schedule**, a clustered scan job, a **dry-run preview**, and **automatic execution** through the proxy. Any user can file a GDPR/CCPA **right-to-erasure** request with the *same* rich configuration; it flows through AI-assisted scope detection and **review-plan-based peer review** (REVIEWER-eligible, multi-stage, no self-approval, auto-reject on timeout) before executing. Enforcement is transparent: soft-deleted rows vanish from reads, `DELETE`s become marker updates, and aged PII resolves to an irreversible **salted hash** at read time — so aggregates survive while the PII does not — all with tamper-evident **proof-of-deletion** audit records and a retention-adherence compliance report. @@ -324,7 +324,7 @@ accessflow/ | [`docs/15-engine-sdk.md`](https://github.com/bablsoft/accessflow/blob/main/docs/15-engine-sdk.md) | Engine-plugin SDK — authoring guide for native (non-JDBC) engines | | [`docs/16-iac.md`](https://github.com/bablsoft/accessflow/blob/main/docs/16-iac.md) | Infrastructure as Code — Terraform/OpenTofu provider, CI Actions, authenticating pipelines with a service account, registry publishing | | [`docs/17-api-governance.md`](https://github.com/bablsoft/accessflow/blob/main/docs/17-api-governance.md) | API Access Governance — govern outbound REST/SOAP/GraphQL/gRPC calls (connectors, schema ingestion, permissions; review/AI pipeline planned) | -| [`docs/18-deployment-governance.md`](https://github.com/bablsoft/accessflow/blob/main/docs/18-deployment-governance.md) | Deployment Approval Governance — gate CI/CD deployments: pipelines, environments, trigger grants, the fail-closed gate, freeze windows, break-glass, outcome reporting | +| [`docs/18-deployment-governance.md`](https://github.com/bablsoft/accessflow/blob/main/docs/18-deployment-governance.md) | Deployment Approval Governance — gate CI/CD deployments: pipelines, environments, trigger grants, the fail-closed gate, freeze windows, change tickets, break-glass, outcome reporting | | [`docs/19-sql-review.md`](https://github.com/bablsoft/accessflow/blob/main/docs/19-sql-review.md) | Deterministic SQL Review Rules — the named rule catalog, per-environment `OFF` / `WARN` / `BLOCK` severity, ruleset resolution, the submission chokepoint (`BLOCK` escalates, never rejects), live editor lint, documented exemptions | | [`docs/20-schema-change-governance.md`](https://github.com/bablsoft/accessflow/blob/main/docs/20-schema-change-governance.md) | Schema Change Governance — governed DDL change sets authored once under a deployment pipeline: the "not DML" validation gate and what it admits, deterministic-SQL-review `BLOCK` refusing the save, freeze-on-promotion, the statements checksum, the statement cap, promotion along the environment ladder, and the scheduled schema drift job with its fidelity limits | | [`docs/21-aggregate-disclosure.md`](https://github.com/bablsoft/accessflow/blob/main/docs/21-aggregate-disclosure.md) | Aggregate Disclosure Guard (design) — what a minimum-group-size rule would and would not protect against, where it would be enforced, and why v1 detects aggregates over classified data rather than suppressing results | diff --git a/docs/03-data-model.md b/docs/03-data-model.md index f521da3f7..619b178bc 100644 --- a/docs/03-data-model.md +++ b/docs/03-data-model.md @@ -2047,6 +2047,8 @@ Sensitive `config` fields encrypted with AES-256-GCM and masked on read: ## query_tickets +> See also [`deployment_tickets`](#deployment_tickets) — the deployment change-ticket sibling (#990), owned by `deploygov` rather than core, with one ticket per deployment per channel and a persisted `decision`. + Tickets auto-created in an external ticketing system (ServiceNow incident / Jira issue, AF-453) for workflow events on a query, plus the state last synced back by the signed inbound webhook. Owned by the core module so both notifications (writer) and workflow (query-detail reader) can reach it without a module cycle. | Column | Type / Notes | @@ -3144,7 +3146,7 @@ plus the state last synced back by the signed inbound webhook — the deployment read and written only through `deploygov.api.DeploymentTicketService`. Rows are written by the ticketing channels when they open a change ticket (#991, see [docs/08-notifications.md → Deployment change tickets](08-notifications.md#deployment-change-tickets-991)); -the inbound status sync (#992) and the gate that reads `decision` (#993) land in later sub-issues. +the inbound status sync stamps `decision` (#992) and the deployment gate reads it (#993) — see below. | Column | Type / Notes | |--------|-------------| diff --git a/docs/04-api-spec.md b/docs/04-api-spec.md index b5df7a7da..ef28a16ef 100644 --- a/docs/04-api-spec.md +++ b/docs/04-api-spec.md @@ -8988,8 +8988,8 @@ land on, validated through the org-scoped datasource lookup so a missing or cros DATASOURCE_NOT_FOUND`; `null` = deploy-only — #877), and — #741 — `tags` (string array, ≤10 tags of ≤32 chars each; free-form labels with no fixed semantics — customer, region, tier. The service trims, drops blanks and de-duplicates), and — #990 — `changeTicketRequired` (default `false`; -marks an environment whose deployments must carry an approved external change ticket — stored -only for now, the gate does not read it yet). Environment responses include `tags`, +marks an environment whose deployments must carry an approved external change ticket — since +#993 the gate refuses to release until one is linked; see "Deployment gate" below). Environment responses include `tags`, `datasourceId` and `changeTicketRequired`; on the update body `changeTicketRequired = null` (or omitted) leaves it unchanged. On the **update** body `tags = null` (or omitted) leaves the tag list unchanged; an explicit `[]` diff --git a/docs/05-backend.md b/docs/05-backend.md index c6eeb8288..64c9005b4 100644 --- a/docs/05-backend.md +++ b/docs/05-backend.md @@ -1830,7 +1830,7 @@ imports `apigov.events` and `deploygov.events` for the two break-glass retro-rev importing `workflow.api` back would close a Modulith cycle — and widening one shared enum with values that are `SKIP` for two kinds out of three would report "this did not happen" where the truth is "this does not exist here". See [docs/17 §7](17-api-governance.md#7-decision-trace-af-967) and -[docs/18 §10](18-deployment-governance.md#10-decision-trace-af-967). +[docs/18 §11](18-deployment-governance.md#11-decision-trace-af-967). `AiOutcome` (`COMPLETED` / `SKIPPED` / `FAILED`) is what turns the three listener branches into one parameter. `FAILED` short-circuits before any lookup, exactly as the live listener does — routing is diff --git a/docs/07-security.md b/docs/07-security.md index 4d8e072a7..a0e7aad61 100644 --- a/docs/07-security.md +++ b/docs/07-security.md @@ -23,6 +23,7 @@ The `SecurityConfiguration` `permitAll()` list is short and intentional. Every o - `/api-docs/**`, `/swagger-ui/**`, `/actuator/health`, `/actuator/info` — docs + probes. - `/ws` — WebSocket upgrade (auth happens in `JwtHandshakeInterceptor`, not here). - `/api/v1/integrations/slack/actions`, `/api/v1/integrations/slack/commands` — inbound Slack callbacks. JWT-exempt because Slack cannot attach an `Authorization` header to these server-to-server posts; they are authenticated instead by the `X-Slack-Signature` HMAC inside the controller (see [Slack request verification](#slack-request-verification-af-362)). The self-service linking endpoints (`/api/v1/integrations/slack/link-codes`, `/api/v1/integrations/slack/link`) stay JWT-authenticated. +- `/api/v1/integrations/servicenow/webhook/{channelId}`, `/api/v1/integrations/jira/webhook/{channelId}` — inbound ticket status callbacks (AF-453). JWT-exempt for the same reason; authenticated inside the controller by `X-AccessFlow-Signature: sha256=HMAC-SHA256(webhook_secret, "v1:{timestamp}:{rawBody}")` over the channel's encrypted secret, compared in constant time, with `X-AccessFlow-Timestamp` held to `accessflow.notifications.ticketing.signature-tolerance` (default `PT5M`) and a Redis replay guard (`TicketingReplayGuard`) refusing a second sighting of a signature. See [08-notifications.md → Ticketing inbound webhooks](08-notifications.md#ticketing-inbound-webhooks--bi-directional-sync-af-453). ### Two-factor authentication (TOTP) @@ -1746,6 +1747,19 @@ the freeze at submission. The rollback follow-up worklist deployment's submitter can never acknowledge their own rollback — the same "never the submitter" rule as break-glass, enforced in the service. +**Change tickets (epic #989).** A deployment's ServiceNow / Jira change ticket syncs back through +the **same** inbound webhook as a query ticket — same endpoint, HMAC signature, timestamp +tolerance, replay guard and JWT exemption; the `external_id` is matched against query tickets +first, then deployment tickets. The gate's change-ticket conjunct reads only the persisted +`deployment_tickets.decision`, never live channel configuration, and fails closed (no ticket, +an unresolved ticket or a lookup error is *not releasable*). One control boundary is documented +rather than enforced: the callback's `actor` is free text that AccessFlow cannot map to a user, so +**the never-approve-your-own-request rule does not cover a ticket approval** — a submitter who +approves their own change ticket is not stopped. The actor is recorded in the audit row; the +compensating control is `bidirectional_sync = false` on the channel, under which a ticket can +satisfy the gate but only an AccessFlow reviewer decides the deployment. See +[18-deployment-governance.md → 7. Change tickets](18-deployment-governance.md#7-change-tickets-epic-989). + --- ## Schema change promotion security (#880) diff --git a/docs/08-notifications.md b/docs/08-notifications.md index 89bdc49fe..2d9513c8a 100644 --- a/docs/08-notifications.md +++ b/docs/08-notifications.md @@ -423,7 +423,9 @@ A ServiceNow or Jira channel with the `DEPLOYMENT_SUBMITTED` trigger opens a **c - **One ticket per deployment per channel.** The dedupe key is `(channel, deployment request)` with no event dimension, so the second opening event is a no-op. The link is persisted in `deployment_tickets` through `deploygov.api.DeploymentTicketService`; its `trigger_event` records which event opened it (`DEPLOYMENT_SUBMITTED` or `DEPLOYMENT_APPROVED`). The dedupe check runs before the first HTTP attempt, so if the submit-time create is still in its retry schedule when the approval arrives, both can reach the ticketing system: the link table keeps the first ticket that lands and the other stays unlinked in ServiceNow / Jira. - **Content.** Headline `[AccessFlow] Deployment to awaiting approval ()` (or `… approved (…)`); the description lists the pipeline, environment, version, commit, artifact, CI run URL, submitter, AI risk and score, the freeze status (the active freeze window's reason, its behavior — `HOLD` / `REJECT` — when the window has no reason, or `none`), the justification, and the `/deployments/{id}` link. ServiceNow sets `correlation_id=accessflow-deployment-`; Jira adds the `deployment` label. - **Audit.** A successful create writes `TICKET_CREATED` against resource type `deployment_ticket`, with `deployment_request_id` in the metadata. -- **Outbound only.** Nothing reads the ticket back yet: inbound status sync onto a deployment (#992) and the gate that waits on an approved ticket (#993) are separate changes. +- **Create-only.** AccessFlow never updates or closes the ticket: a deployment that is cancelled, rejected or timed out leaves it open. Status flows the other way only — the signed inbound callback stamps the ticket's verdict and can decide the deployment (#992, [below](#ticketing-inbound-webhooks--bi-directional-sync-af-453)), and the deployment gate waits for an approved ticket (#993). + +The end-to-end picture — the environment flag, the gate conjunct, break-glass and the limits — is [docs/18-deployment-governance.md → 7. Change tickets](18-deployment-governance.md#7-change-tickets-epic-989). --- @@ -453,7 +455,7 @@ POST /api/v1/integrations/jira/webhook/{channelId} **Sync semantics.** A verified update always refreshes the linked `query_tickets` row (status + resolution) and writes a `TICKET_STATUS_SYNCED` audit row. When the channel has `bidirectional_sync=true` and the inbound `resolution`/`status` matches (case-insensitively) the channel's `reject_statuses` (checked first) or `approve_statuses`, and the query is still `PENDING_REVIEW`, the workflow's `ExternalDecisionService` force-applies the decision: `PENDING_REVIEW → APPROVED` or `→ REJECTED`, attributed to the external system (no `review_decisions` row — mirroring the review-timeout path) with the ticket key and actor recorded in the audit reason. Defaults when the lists are omitted: approve = `resolved, closed, done, approved, complete`; reject = `rejected, declined, cancelled, canceled, won't do`. Queries already in a terminal state only get the ticket-metadata update — a late ticket change never reopens or flips a decided query. -**Deployment change tickets (#992).** The `external_id` is resolved against `query_tickets` first; only when no query ticket matches is it looked up in `deployment_tickets` (the change tickets opened at the first of `DEPLOYMENT_SUBMITTED` / `DEPLOYMENT_APPROVED`, #991). Neither matches → `ignored`. For a deployment ticket: +**Deployment change tickets (#992; overview in [docs/18 §7](18-deployment-governance.md#7-change-tickets-epic-989)).** The `external_id` is resolved against `query_tickets` first; only when no query ticket matches is it looked up in `deployment_tickets` (the change tickets opened at the first of `DEPLOYMENT_SUBMITTED` / `DEPLOYMENT_APPROVED`, #991). Neither matches → `ignored`. For a deployment ticket: - The mapped decision (`APPROVED` / `REJECTED` from the same `reject_statuses` / `approve_statuses` lists, or `null` when the status matches neither) is **stamped on the `deployment_tickets` row on every verified callback**, whether or not `bidirectional_sync` is on. An `APPROVED` stamp is **sticky**: a later callback whose status matches neither list leaves it in place, so a change moving on after approval (*Implement*, *Closed*) keeps the gate open, and only a `reject_statuses` match overrides it; a `REJECTED` stamp is reset to `null` (and `decided_at` cleared) by such a callback. The stamp is the input for the change-ticket gate conjunct (#993), which reads the row, never the channel config. The `TICKET_STATUS_SYNCED` audit row is written against resource type `deployment_ticket` with `deployment_request_id` and `decision` in its metadata. - Only when the channel has `bidirectional_sync=true` and the mapping is non-null is the still-`PENDING_REVIEW` deployment decided (`→ APPROVED` / `→ REJECTED`) through `ExternalDecisionService.applyDeploymentTicketDecision`, which delegates to `deploygov.api.DeploymentExternalDecisionService`. The decision is system-attributed: no `deployment_review_decisions` row, a `DEPLOYMENT_APPROVED` / `DEPLOYMENT_REJECTED` audit row with a null actor and `trigger=ticket` (plus `ticket_system`, `external_key`, `channel_id`, `actor`), and the submitter is notified as for any other system verdict. A deployment that a manual decision already moved on answers `synced`, never an error. diff --git a/docs/12-roadmap.md b/docs/12-roadmap.md index ecd1eeb96..04c0c197f 100644 --- a/docs/12-roadmap.md +++ b/docs/12-roadmap.md @@ -272,6 +272,8 @@ **Theme:** Deployments accountable end to end — change tickets, DORA/SLA metrics, API replay & diff, verified no-AI operation, custom SQL review rules. +- **Deployment change tickets** — an environment can require an approved change ticket before anything is released to it. A ServiceNow or Jira channel carrying the new *Deployment submitted* trigger opens one ticket per deployment when it reaches review, or when it is auto-approved; the ticket's signed status callback records the verdict on the link and — when the channel has bi-directional sync on — approves or rejects the deployment itself, attributed to the ticketing system in the audit log. The fail-closed deployment gate gains a fourth condition: on a flagged environment it answers releasable only once a linked ticket is approved and none is rejected, with break-glass bypassing it under an audit record. The deployment page shows a Change ticket card and the list a Ticket column. AccessFlow never writes back to the ticket, and because a ticket's approver cannot be matched to a user, keeping sync off is how to stop a submitter approving their own change (epic #989 — #990–#995) — see [docs/18-deployment-governance.md](18-deployment-governance.md#7-change-tickets-epic-989) + - **Custom SQL review rules** — admins write rules of their own next to the eighteen built-ins, without code: a rule is a condition over the parsed statement (statement type, referenced tables and columns by glob, functions called, the presence of `WHERE` / a row limit / `ORDER BY`, an always-true `WHERE`, a join without a condition, a leading-wildcard `LIKE`, whether the statement is inside a transaction) or a bounded regular expression over its normalised text, with a message that names the tables and functions it matched. Drafts are tested against a piece of SQL before they are switched on; once stored a custom rule is configured per environment in every ruleset exactly like a built-in, so a `BLOCK` sends the query to a reviewer and never rejects it. At most 50 per organisation, audited on every change, managed on the **Custom rules** tab of `/admin/sql-review` and by the `accessflow_sql_review_rule` Terraform resource (epic #1008 — #1009–#1012) — see [docs/19-sql-review.md](19-sql-review.md#custom-rules-1009-epic-1008) --- diff --git a/docs/17-api-governance.md b/docs/17-api-governance.md index af971ecc0..d90594849 100644 --- a/docs/17-api-governance.md +++ b/docs/17-api-governance.md @@ -391,7 +391,7 @@ connector, carrying the simulated user, the AI outcome, the risk level and the s deliberately no request path, headers or body. Full request/response contract: [docs/04-api-spec.md](04-api-spec.md#decision-traces-for-api-calls-and-deployments-af-967). -The deployment sibling is [§10 of docs/18](18-deployment-governance.md#10-decision-trace-af-967); +The deployment sibling is [§11 of docs/18](18-deployment-governance.md#11-decision-trace-af-967); the query one is the [AF-859 access explainer](05-backend.md#access-explainer-af-859). --- diff --git a/docs/18-deployment-governance.md b/docs/18-deployment-governance.md index 95b1e95c8..a0c3ba12d 100644 --- a/docs/18-deployment-governance.md +++ b/docs/18-deployment-governance.md @@ -21,10 +21,12 @@ concepts are the **pipeline / environment** hierarchy, **freeze windows**, and t > Azure / generic-curl CI wrappers (#694), the notification and audit fan-out (#695), and the web > UI (#696). The multi-environment version-tracking foundation — environment tags and the > per-environment deployed-version projection (#741) — landed after v2.4, followed by the -> read-only version inventory & drift API (#742, section 9 below) and the version-matrix UI +> read-only version inventory & drift API (#742, section 10 below) and the version-matrix UI > (#743): the org-wide matrix at `/deployment-versions`, the per-pipeline matrix and > environment-history drawer, drift badges on the deployment list and detail pages, and the tags -> field on the environment editor. +> field on the environment editor. v2.8 adds change tickets (epic #989, #990–#994, section 7 +> below): a flagged environment opens a ServiceNow / Jira change ticket per deployment, syncs its +> verdict back, and the gate waits for it. --- @@ -33,7 +35,10 @@ concepts are the **pipeline / environment** hierarchy, **freeze windows**, and t ``` com.bablsoft.accessflow.deploygov/ ├── api/ # PipelineProvider, FreezeBehavior, DeploymentOutcome, DeploymentRoutingAction, -│ # DeploymentRollbackReviewStatus, thirteen service interfaces, view + command records, +│ # DeploymentRollbackReviewStatus, seventeen service interfaces (incl. +│ # DeploymentTicketService + DeploymentExternalDecisionService, #990/#992), +│ # view + command records (DeploymentTicketView, RecordDeploymentTicketCommand, +│ # DeploymentTicketProvenance), │ # the exception hierarchy (JDK + project types only) ├── events/ # DeploymentSubmitted/AnalysisCompleted/AnalysisSkipped/AnalysisFailed/ │ # StatusChanged/Decided/BreakGlassExecuted/OutcomeReported/ReleasableEvent @@ -47,6 +52,7 @@ com.bablsoft.accessflow.deploygov/ │ # DeploymentReviewDecisionEntity, DeploymentRollbackReviewEntity, │ # DeploymentRoutingPolicyEntity, │ # DeploymentEnvironmentVersionEntity (#741), + │ # DeploymentTicketEntity (#990), │ # the two grant entities + repos ├── routing/ # DeploymentRoutingPolicyEngine ├── scheduled/ # DeploymentTimeoutJob, ScheduledDeploymentReleaseJob @@ -80,8 +86,10 @@ UUID with no foreign key, so past requests, decisions and rollback reviews survi **Environments** are the ordered promotion targets under a pipeline — `dev`, `staging`, `production` — each with a `sort_order`, a `require_review` flag, optional `required_approvals` -and `review_plan_id` **overrides** that win over the pipeline's, and an `allow_break_glass` -opt-in that defaults to `false`. Approval count resolves in a fixed precedence: +and `review_plan_id` **overrides** that win over the pipeline's, an `allow_break_glass` +opt-in that defaults to `false`, and a `change_ticket_required` flag (default `false`, #990) that +makes releases wait for an approved ServiceNow / Jira change ticket — see +[section 7](#7-change-tickets-epic-989). Approval count resolves in a fixed precedence: `environment.required_approvals` → the resolved plan's `minApprovalsRequired()` → 1. Plan resolution follows the same shape: the environment override wins over the pipeline's. @@ -111,7 +119,7 @@ reverts to previous (single-level undo — a second consecutive rollback leaves version honestly unknown); an outcome for a non-current request changes nothing. The projection is a **read model only** — it never feeds gate, approval, or routing decisions, and history stays derived from `deployment_requests`. The read API over it — the version matrices, history, -and the drift indicator — is section 9 below (#742), surfaced in the web UI by #743. +and the drift indicator — is section 10 below (#742), surfaced in the web UI by #743. **Who may trigger** is a per-pipeline grant, not a functional permission. Both a per-user table and a per-group table carry `can_trigger`, `can_break_glass` and an optional `expires_at`, and @@ -299,27 +307,18 @@ adds `change_ticket_required` and `change_ticket` (`system`, `external_key`, `ur none is linked), and the CI wrappers add `change_ticket=` plus the ticket's decision once it has one (the GitHub action also prints the ticket status) or `change_ticket=none linked yet` to their waiting line. AccessFlow only learns a ticket's verdict from the ticketing system's signed callback (#992), so without that callback a -linked ticket never reads as approved, and a `REJECTED` ticket blocks the request for good (the -response then shows the rejected ticket). An approval is **sticky**: a later callback whose status +linked ticket never reads as approved, and a `REJECTED` ticket blocks the request while it stays +rejected (the response then shows the rejected ticket). Unlike an approval, a rejection is not +sticky: a later callback matching neither list clears it, and one matching the approve list turns +it `APPROVED` — though with `bidirectional_sync` on, the rejection has usually already rejected the +deployment itself, which is terminal. An approval is **sticky**: a later callback whose status matches neither the channel's approve nor reject list (a change moving on to *Scheduled* / *Implement* / *Closed*) keeps the ticket `APPROVED`, so a release that opened stays open; only a status in the reject list overrides it, and that closes the gate again. The environment row is read before the break-glass shortcut, so a request whose environment was deleted is not -releasable — break-glass included. A [decision trace](#10-decision-trace-af-967) for a flagged environment always reports *not releasable*: -a hypothetical request has no ticket yet. - -**In the web UI (#994).** The `change_ticket_required` switch sits on the pipeline settings -**Environments** tab, after *Break-glass*. Its help text warns that a ServiceNow or Jira channel must -enable the *Deployment submitted* trigger (`DEPLOYMENT_SUBMITTED`, a checkbox on both channel forms), -or the gate never opens. The deployment detail page shows a **Change ticket** card: the key, linked to -the ticketing system, plus the system, the status (with its resolution) and an *approved* / *rejected* -tag. On a flagged environment with no ticket yet, the card reads "no change ticket linked yet". When an -approved, unfrozen, not-releasable request on a flagged environment has no approved ticket, the gate -banner reads "waiting for the change ticket" (or "the change ticket was rejected"); that message wins -over the scheduled-time one. `/deployments` adds a **Ticket** column. The card and the column read the -`change_ticket` summary on the deployment request body (see `docs/04-api-spec.md` → "Deployment -requests"), not the gate, so the ticket shows while the request is still in review; the banner reads -the gate. +releasable — break-glass included. A [decision trace](#11-decision-trace-af-967) for a flagged environment always reports *not releasable*: +a hypothetical request has no ticket yet. How tickets open, sync and decide — and what they +cannot do — is [section 7](#7-change-tickets-epic-989). **`frozen` means any active window, `HOLD` or `REJECT`.** A `REJECT` window normally auto-rejects at submission, but a request approved *before* the window opened must not sail through mid-freeze. @@ -383,7 +382,96 @@ the **gate**, so the release proceeds the instant the window closes with no re-a --- -## 7. Outcome reporting & rollback follow-up reviews +## 7. Change tickets (epic #989) + +Regulated change processes — ITIL change management, SOX change control, most release policies — +want a release recorded in ServiceNow or Jira, approved *there*, and only then allowed out. An +environment can demand exactly that. Change tickets reuse the existing ServiceNow and Jira +notification channels ([08-notifications.md → Deployment change tickets](08-notifications.md#deployment-change-tickets-991)); +there is no new channel type, notification event type or module. + +**The environment flag (#990).** `deployment_environments.change_ticket_required` (`BOOLEAN NOT +NULL DEFAULT FALSE`) marks an environment whose releases must carry an approved change ticket. It +is set through the same environment create / update endpoints as `require_review` and +`allow_break_glass`, and it takes effect **immediately**: from that moment the gate refuses every +release to the environment until an approved ticket is linked — so configure the ticketing channel +(and its inbound callback) *before* turning the flag on, or releases there stop. + +**When a ticket opens (#991).** Both conditions are required: the target environment has +`change_ticket_required = true`, **and** a ServiceNow or Jira channel enables the +`DEPLOYMENT_SUBMITTED` trigger. The ticket opens at the **first** of `DEPLOYMENT_SUBMITTED` (the +`PENDING_REVIEW` transition) or `DEPLOYMENT_APPROVED` — the second because a routing or +environment-policy auto-approval never passes through review, and a flagged environment whose +deployments auto-approve would otherwise wait for a ticket that never opens. Break-glass deploys +open **no** ticket: they publish no submission event, and `DEPLOYMENT_BREAK_GLASS_EXECUTED` is not a +ticketing trigger. Creation is asynchronous, on the channel's usual `+30 s / +2 min / +10 min` +retries, and is audited as `TICKET_CREATED` on the `deployment_ticket` resource. + +**One ticket per deployment per channel.** The link lives in `deployment_tickets`, owned by this +module ([03-data-model.md](03-data-model.md) → `deployment_tickets`), never in core's +`query_tickets`. Its unique key is `(channel_id, deployment_request_id)` with no event dimension, +so whichever of the two opening events arrives second is a no-op. Two channels carrying the trigger +open two tickets. + +**The persisted decision versus the deployment decision (#992).** A ServiceNow Business Rule or +Jira Automation rule posts signed status callbacks to the channel's inbound webhook. The +`external_id` is resolved against query tickets first, then deployment tickets. For a deployment +ticket, every verified callback **stamps** `deployment_tickets.decision` — `REJECTED` when the +status matches the channel's `reject_statuses` (checked first), `APPROVED` when it matches +`approve_statuses` — whether or not the channel has `bidirectional_sync`. An `APPROVED` stamp is +**sticky**: a later status matching neither list (the change moving on to *Scheduled*, +*Implement*, *Closed*) keeps it; only a reject status overrides it. A `REJECTED` stamp is not +sticky — a later status matching neither list clears it back to undecided. Separately, only when +`bidirectional_sync = true` does the mapped verdict also **decide** a deployment still in +`PENDING_REVIEW`, through `workflow.api.ExternalDecisionService.applyDeploymentTicketDecision` +→ `deploygov.api.DeploymentExternalDecisionService`. That decision is system-attributed like +routing and timeout: no `deployment_review_decisions` row, a null-actor `DEPLOYMENT_APPROVED` / +`DEPLOYMENT_REJECTED` audit row with `trigger = ticket` plus `ticket_system`, `external_key`, +`channel_id` and `actor`, and a `DeploymentDecidedEvent` reason of `ticket::`, +so the submitter is notified as for any other verdict. A reviewer who decided first wins; the late +callback still stamps the ticket but decides nothing. Leaving sync **off** is how an organisation +requires *both* an AccessFlow approval and a ticket approval: the ticket's state still syncs and +still satisfies the gate, but only an AccessFlow reviewer decides. + +**The gate (#993).** On a flagged environment the gate's fourth conjunct holds only when at least +one linked ticket is `APPROVED` and none is `REJECTED`, read from `deployment_tickets` rows only — +see [section 5](#5-the-deployment-gate) for the full function. No ticket row yet, unresolved +tickets, or a failed lookup all answer *not releasable*, so a flagged environment that no channel +opens tickets for **never releases**, and a request approved before its ticket is approved simply +waits — `ScheduledDeploymentReleaseJob` announces it once, on its next tick after the ticket flips. +Break-glass skips the conjunct as it skips the freeze check, and `confirm-execution` records the +bypass on its `DEPLOYMENT_EXECUTED` row as `change_ticket_bypassed = break_glass`; a normal +confirmation on a flagged environment records `change_ticket_key` instead. + +**In the web UI (#994).** The *Change ticket* switch sits on the pipeline settings +**Environments** tab, after *Break-glass*. Its help text warns that a ServiceNow or Jira channel must +enable the *Deployment submitted* trigger (a checkbox on both channel forms), or the gate never +opens. The deployment detail page shows a **Change ticket** card: the key, linked to the ticketing +system, plus the system, the status (with its resolution) and an *approved* / *rejected* tag. On a +flagged environment with no ticket yet, the card reads "no change ticket linked yet". When an +approved, unfrozen, not-releasable request on a flagged environment has no approved ticket, the gate +banner reads "waiting for the change ticket" (or "the change ticket was rejected"); that message wins +over the scheduled-time one. `/deployments` adds a **Ticket** column. The card and the column read the +`change_ticket` summary on the deployment request body (see `docs/04-api-spec.md` → "Deployment +requests"), not the gate, so the ticket shows while the request is still in review; the banner reads +the gate. + +**Limits.** + +- **AccessFlow never writes back to the ticket.** Outbound is create-only: a deployment that is + cancelled, rejected or timed out leaves its ServiceNow incident or Jira issue open, and an + executed deployment does not close it. Close tickets in the ticketing system. +- **The self-approval ban does not reach the ticket.** The callback's `actor` is a free-text display + name assembled by the Business Rule or Automation rule; AccessFlow cannot map it to a user, so a + submitter who approves their own change ticket is not stopped. The actor is recorded in the audit + row and the decision reason. Where that matters, keep `bidirectional_sync = false` on the channel + so a ticket can satisfy the gate but never decide the deployment. +- **AccessFlow only learns a verdict from the signed callback.** Without the inbound rule configured, + a linked ticket never reads as approved and the flagged environment never releases. + +--- + +## 8. Outcome reporting & rollback follow-up reviews After the deploy runs, the pipeline reports what happened: `POST /api/v1/deployment-requests/{id}/outcome` with `SUCCEEDED`, `FAILED` or `ROLLED_BACK`. @@ -414,7 +502,7 @@ break-glass deployment that later rolls back must still get its own follow-up. --- -## 8. CI wrappers & service-account setup +## 9. CI wrappers & service-account setup Every provider integration follows the same four beats — **submit idempotently by run id → poll the gate → confirm execution → report the outcome** — and every one of them **fails the job** on a @@ -463,7 +551,7 @@ longer than your longest queued run. Full reference: [22-service-accounts.md](22 --- -## 9. Version inventory & drift (#742) +## 10. Version inventory & drift (#742) Once deployments flow through the gate, the tracking projection (section 1) can answer the operational question the epic promised: **which version runs where right now** — including the @@ -526,7 +614,7 @@ who can never decide their own deployment, is told so in place of the buttons. --- -## 10. Decision trace (AF-967) +## 11. Decision trace (AF-967) The sharpest of the three explainers, because a deployment is blocked by the **gate** rather than by the decision, and the freeze-window / plan-approver / break-glass interaction is not visible on any @@ -632,7 +720,7 @@ deploygov read: | **Pending deployment approvals** tile → `/reviews?tab=deployments` | `pending_deployment_approvals_count` | `DEPLOYMENT_REVIEW` | | **My recent deployments** widget | `recent_deployments` (`DeploymentRequestService#list`) | `QUERY_SUBMIT_SELECT` | | **Pending deployment approvals** widget | `recent_pending_deployment_approvals` (`DeploymentReviewService#listPending`) | `DEPLOYMENT_REVIEW` | -| **Environment versions** widget → `/deployment-versions` | the drifted rows of the [§ 9 version inventory](#9-version-inventory--drift-742) | `DEPLOYMENT_PIPELINE_MANAGE` / `DEPLOYMENT_REVIEW` / `QUERY_ADMIN` | +| **Environment versions** widget → `/deployment-versions` | the drifted rows of the [§ 10 version inventory](#10-version-inventory--drift-742) | `DEPLOYMENT_PIPELINE_MANAGE` / `DEPLOYMENT_REVIEW` / `QUERY_ADMIN` | Everything a user sees here is **their own**: the recent feed and the open count filter on `submittedByUserId = me`, so a `DEPLOYMENT_REVIEW` holder who could list the whole organization diff --git a/e2e/screenshots/capture.ts b/e2e/screenshots/capture.ts index 829605d99..cbeb37197 100644 --- a/e2e/screenshots/capture.ts +++ b/e2e/screenshots/capture.ts @@ -408,6 +408,8 @@ async function seedData() { tags: [...tags], requiredApprovals: 1, requireReview: true, + // #990: show the change-ticket column with a value, and the detail page's ticket card. + changeTicketRequired: name === 'production', }); } @@ -458,9 +460,11 @@ async function seedData() { // A rolled-back deployment so the review hub's Rollbacks tab (/reviews?tab=rollbacks) is populated: // reviewer triggers -> admin approves -> confirm execution -> report ROLLED_BACK. + // Staging, not production: production requires a change ticket and no ticketing channel is + // seeded, so its gate stays closed and confirm-execution would 409. const rolled = await triggerDeploymentViaApi(api, reviewerToken, { pipelineId: pipeline.id, - environment: 'production', + environment: 'staging', version: 'v4.11.2', externalRunId: `ci-run-${RUN_SUFFIX}-rollback`, justification: 'Ship the search relevance rebuild.', diff --git a/help-corpus/corpus.jsonl b/help-corpus/corpus.jsonl index a58453a4f..141621fa5 100644 --- a/help-corpus/corpus.jsonl +++ b/help-corpus/corpus.jsonl @@ -220,8 +220,8 @@ {"id":"0969659acece6b75","path":"website/docs/configuration/datasources/index.html","url":"https://accessflow.io/docs/configuration/datasources/#cfg-datasource-health","anchor":"cfg-datasource-health","title":"Datasource health","section":"Reference","order":0,"tokens":282,"text":"AccessFlow Docs > Reference > Datasources > Datasource health\n\nWhat it is. An operational dashboard for spotting a datasource in trouble\n— pool exhaustion, a database that has turned slow, or a sudden volume spike. Use it as the\nfirst place to look when queries start failing or hanging.\n\nConfigure it. Nothing to set up. /admin/datasource-health\n(admin-only, read-only) auto-refreshes every 30 seconds; each card shows live\nconnection-pool utilisation (active / idle / free against the configured maximum) plus a\ntrailing 24-hour summary of query volume, p50 / p95 execution latency, and error count.\nPool gauges read \"pool not initialised\" until the datasource's first query runs (pools are\ncreated on demand).\n\nTune it. ACCESSFLOW_PROXY_HEALTH_CACHE_TTL caches each\nsnapshot so the auto-refresh stays cheap (default PT30S).\n\n/admin/datasource-health — one card per datasource, auto-refreshing every 30 s."} {"id":"67af1cbac99c74cf","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/#cfg-notification-channels","anchor":"cfg-notification-channels","title":"Notification channels","section":"Reference","order":0,"tokens":163,"text":"AccessFlow Docs > Reference > Notifications > Notification channels\n\nThere is a guide for this. Set up email and chat notifications\nstarts with system SMTP — the piece that makes email invitations work at all. This chapter is the reference behind it.\n\nWhat it is. Where AccessFlow sends review notifications — when a query is\nsubmitted, approved, rejected, times out, or scores CRITICAL on AI risk. Wire\nup the channels your team already lives in so reviewers act fast. Configure as many as you\nlike per organization (e.g. one Slack channel per team)."} {"id":"87cf866ab168e52d","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/","anchor":"","title":"Where can AccessFlow send review notifications?","section":"Reference","order":0,"tokens":722,"text":"AccessFlow Docs > Reference > Notifications > Where can AccessFlow send review notifications? (part 1 of 3)\n\nEmail, Slack, Discord, Telegram, Microsoft Teams, PagerDuty, ServiceNow, Jira, and signed HTTP webhooks. Configure as many channels as you need per organization. Organization-wide events — high AI risk, anomalies, break-glass, the weekly digest, deployments, schema changes and schema drift — reach every active channel; the ordinary query lifecycle follows the channels bound to the datasource's review plan. PagerDuty, ServiceNow and Jira additionally carry their own trigger list. There is no general per-channel event picker.\n\nConfigure it. Manage channels at /admin/notifications:\n\n/admin/notifications → Add channel. The form swaps its lower half between Email, Slack, Webhook, Discord, Telegram, Microsoft Teams, PagerDuty, ServiceNow, and Jira fields when you change Type.\n\n- Open /admin/notifications and click Add channel.\n\n- Pick a type:\n\n- Email — supply SMTP host, port, password, TLS, the From address and an optional user / display name. The user is optional; the password is not. An email channel is a full, independent SMTP configuration — it does not inherit system SMTP, and leaving its fields blank does not fall back to it. The system-SMTP fallback is decided per event, over the channels resolved for that event — not over the organization's channel list — so adding an email channel here does not on its own switch it off.\n\n- Slack — paste an incoming-webhook URL for one-way Block Kit messages (header, SQL preview, and a View in AccessFlow link). For interactive Approve / Reject buttons, configure a Slack app instead (see below).\n\n- Webhook — supply a target URL and a signing secret. Every POST carries X-AccessFlow-Signature: sha256=... computed from the body and the secret.\n\n- Discord — paste a channel webhook URL (Server Settings → Integrations → Webhooks). Optional username and avatar URL override the bot identity per-message. Messages render as a rich embed with the SQL preview in a fenced code block.\n\n- Telegram — create a bot via @BotFather, add it to the target group/channel, and configure the bot token plus numeric chat ID. Messages use MarkdownV2 formatting.\n\n- Microsoft Teams — paste the channel's Incoming Webhook (or Power Automate webhook) URL. Messages render as an Adaptive Card with a View in AccessFlow action button."} -{"id":"c6673b948b5d00e7","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/","anchor":"","title":"Where can AccessFlow send review notifications?","section":"Reference","order":1,"tokens":756,"text":"AccessFlow Docs > Reference > Notifications > Where can AccessFlow send review notifications? (part 2 of 3)\n\n- PagerDuty — paste an Events API v2 integration routing key, pick a default severity (critical / error / warning / info), and choose which triggers page the channel: AI critical-risk query, review timeout, routing-policy escalation, and/or review stalled (nobody decided within the plan’s escalation window). Other events never fire it — a nudge reminder in particular never pages. AccessFlow sends a trigger event with a query-stable dedup key so repeat events fold into one incident.\n\n- ServiceNow — supply the instance URL plus a username / password allowed to create incidents via the Table API, optionally an assignment group and urgency (1–3), and choose which triggers open an incident: query rejected, review timeout, routing-policy escalation, and/or deployment submitted. Incidents for queries link back onto the query detail page. The deployment submitted trigger opens an incident for each deployment to an environment that requires a change ticket. That incident is the deployment’s change ticket, and it shows on the deployment’s page.\n\n- Jira — supply the site URL, an account email + API token, the target project key, and optionally an issue type (default Task); pick the same trigger set. Issues are created via REST v2 with an accessflow label and link back onto the query detail page.\n\n- Bi-directional status sync (ServiceNow / Jira). Toggle Bi-directional status sync on the channel and set a webhook secret; then point a ServiceNow Business Rule / Jira Automation rule at POST /api/v1/integrations/servicenow/webhook/{channelId} (or …/jira/webhook/{channelId}) sending {\"external_id\", \"status\", \"resolution\", \"actor\"} signed with X-AccessFlow-Timestamp + X-AccessFlow-Signature: sha256=HMAC-SHA256(secret, \"v1:{timestamp}:{body}\"). Ticket status updates flow onto the query detail page, and a resolution matching the channel's approve / reject status lists decides a query still awaiting review — attributed to the ticketing system in the audit log. Deployment change tickets are matched too: AccessFlow always keeps the approve / reject verdict on its own link to the ticket, and with sync on it also decides the deployment awaiting review. The ticket's actor cannot be matched to an AccessFlow user, so self-approval of a change ticket is not blocked — keep sync off if you need that guarantee."} -{"id":"999c2b15b8129e9e","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/","anchor":"","title":"Where can AccessFlow send review notifications?","section":"Reference","order":2,"tokens":461,"text":"AccessFlow Docs > Reference > Notifications > Where can AccessFlow send review notifications? (part 3 of 3)\n\n- Send a test event. After saving, each channel card has a Test button — the dispatcher fires a synthetic event and reports delivery success.\n\n- Edit or remove. Each card also has Edit and Delete buttons. Deleting a channel is immediate (hard delete) — there is no soft-delete, but webhook retries already scheduled before the delete still complete.\n\nSensitive config fields (SMTP password, webhook secret,\nTelegram bot token, PagerDuty routing key, ServiceNow password,\nJira API token, ticketing webhook secret) are AES-256-GCM encrypted at\nrest and are never shown in plaintext after the channel is saved.\n\nWebhook retry policy. One immediate attempt plus three scheduled\nretries at +30s, +2 min, +10 min. Tune the\ndelays via ACCESSFLOW_NOTIFICATIONS_RETRY_FIRST /\n_SECOND / _THIRD. Failed deliveries log ERROR\nbut never affect query workflow state.\n\nTune it. ACCESSFLOW_PUBLIC_BASE_URL sets the link embedded in\nmessages (default http://localhost:5173); retry delays are\nACCESSFLOW_NOTIFICATIONS_RETRY_FIRST / _SECOND / _THIRD\n(PT30S / PT2M / PT10M). For air-gapped installs that\nroute through an internal proxy, override\nACCESSFLOW_NOTIFICATIONS_TELEGRAM_API_BASE_URL\n(https://api.telegram.org/) and\nACCESSFLOW_NOTIFICATIONS_PAGERDUTY_API_BASE_URL\n(https://events.pagerduty.com/). The inbound ticketing-webhook signature\nwindow is ACCESSFLOW_NOTIFICATIONS_TICKETING_SIGNATURE_TOLERANCE\n(PT5M)."} +{"id":"c6673b948b5d00e7","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/","anchor":"","title":"Where can AccessFlow send review notifications?","section":"Reference","order":1,"tokens":473,"text":"AccessFlow Docs > Reference > Notifications > Where can AccessFlow send review notifications? (part 2 of 3)\n\n- PagerDuty — paste an Events API v2 integration routing key, pick a default severity (critical / error / warning / info), and choose which triggers page the channel: AI critical-risk query, review timeout, routing-policy escalation, and/or review stalled (nobody decided within the plan’s escalation window). Other events never fire it — a nudge reminder in particular never pages. AccessFlow sends a trigger event with a query-stable dedup key so repeat events fold into one incident.\n\n- ServiceNow — supply the instance URL plus a username / password allowed to create incidents via the Table API, optionally an assignment group and urgency (1–3), and choose which triggers open an incident: query rejected, review timeout, routing-policy escalation, and/or deployment submitted. Incidents for queries link back onto the query detail page. The deployment submitted trigger opens an incident for each deployment to an environment that requires a change ticket. That incident is the deployment’s change ticket, and it shows on the deployment’s page.\n\n- Jira — supply the site URL, an account email + API token, the target project key, and optionally an issue type (default Task); pick the same trigger set. Issues are created via REST v2 with an accessflow label and link back onto the query detail page. With the deployment submitted trigger, the issue is the deployment’s change ticket: it also gets a deployment label and shows on the deployment’s page."} +{"id":"999c2b15b8129e9e","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/","anchor":"","title":"Where can AccessFlow send review notifications?","section":"Reference","order":2,"tokens":791,"text":"AccessFlow Docs > Reference > Notifications > Where can AccessFlow send review notifications? (part 3 of 3)\n\n- Bi-directional status sync (ServiceNow / Jira). Toggle Bi-directional status sync on the channel and set a webhook secret; then point a ServiceNow Business Rule / Jira Automation rule at POST /api/v1/integrations/servicenow/webhook/{channelId} (or …/jira/webhook/{channelId}) sending {\"external_id\", \"status\", \"resolution\", \"actor\"} signed with X-AccessFlow-Timestamp + X-AccessFlow-Signature: sha256=HMAC-SHA256(secret, \"v1:{timestamp}:{body}\"). Ticket status updates flow onto the query detail page, and a resolution matching the channel's approve / reject status lists decides a query still awaiting review — attributed to the ticketing system in the audit log. Deployment change tickets are matched too: AccessFlow always keeps the approve / reject verdict on its own link to the ticket, and with sync on it also decides the deployment awaiting review. The ticket's actor cannot be matched to an AccessFlow user, so self-approval of a change ticket is not blocked — keep sync off if you need that guarantee.\n\n- Send a test event. After saving, each channel card has a Test button — the dispatcher fires a synthetic event and reports delivery success.\n\n- Edit or remove. Each card also has Edit and Delete buttons. Deleting a channel is immediate (hard delete) — there is no soft-delete, but webhook retries already scheduled before the delete still complete.\n\nSensitive config fields (SMTP password, webhook secret,\nTelegram bot token, PagerDuty routing key, ServiceNow password,\nJira API token, ticketing webhook secret) are AES-256-GCM encrypted at\nrest and are never shown in plaintext after the channel is saved.\n\nWebhook retry policy. One immediate attempt plus three scheduled\nretries at +30s, +2 min, +10 min. Tune the\ndelays via ACCESSFLOW_NOTIFICATIONS_RETRY_FIRST /\n_SECOND / _THIRD. Failed deliveries log ERROR\nbut never affect query workflow state.\n\nTune it. ACCESSFLOW_PUBLIC_BASE_URL sets the link embedded in\nmessages (default http://localhost:5173); retry delays are\nACCESSFLOW_NOTIFICATIONS_RETRY_FIRST / _SECOND / _THIRD\n(PT30S / PT2M / PT10M). For air-gapped installs that\nroute through an internal proxy, override\nACCESSFLOW_NOTIFICATIONS_TELEGRAM_API_BASE_URL\n(https://api.telegram.org/) and\nACCESSFLOW_NOTIFICATIONS_PAGERDUTY_API_BASE_URL\n(https://events.pagerduty.com/). The inbound ticketing-webhook signature\nwindow is ACCESSFLOW_NOTIFICATIONS_TICKETING_SIGNATURE_TOLERANCE\n(PT5M)."} {"id":"ab62165923a4c6c5","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/#cfg-slack","anchor":"cfg-slack","title":"Slack app — Approve / Reject from Slack","section":"Reference","order":0,"tokens":444,"text":"AccessFlow Docs > Reference > Notifications > Slack app — Approve / Reject from Slack\n\nBeyond the one-way Slack webhook channel, an admin can connect a Slack app\nat /admin/slack so reviewers act on requests without leaving Slack. Create the app\nin Slack's console, then paste its App ID, Bot token, Signing secret,\nand a default channel ID. When an app is active, review-request messages are posted via\nthe bot token and carry Approve and Reject buttons.\n\n- Each reviewer links their Slack identity once. On /profile →\nSlack account, click Generate link code, then run\n/accessflow link in Slack. The code is single-use and short-lived\n(ACCESSFLOW_NOTIFICATIONS_SLACK_LINK_CODE_TTL, default 10 min).\n\n- Clicking Approve / Reject runs through the same review service as the UI —\na reviewer can never approve their own query, and role / stage checks apply identically.\nThe original Slack message updates in place with the decision.\n\nInbound clicks are verified by the Slack signing secret (HMAC over the raw body, with a\ntimestamp/replay window set by ACCESSFLOW_NOTIFICATIONS_SLACK_SIGNATURE_TOLERANCE,\ndefault 5 min). The bot token and signing secret are AES-256-GCM encrypted at rest and never\nreturned by the API. Point your Slack app's Interactivity request URL at\n/api/v1/integrations/slack/actions and its slash command at\n/api/v1/integrations/slack/commands.\n\n/admin/slack — connect the interactive Slack app (bot token + signing secret)."} {"id":"32f3a034a58a3dba","path":"website/docs/configuration/notifications/index.html","url":"https://accessflow.io/docs/configuration/notifications/#cfg-smtp","anchor":"cfg-smtp","title":"System SMTP","section":"Reference","order":0,"tokens":336,"text":"AccessFlow Docs > Reference > Notifications > System SMTP\n\nWhat it is. The fallback mail server for transactional email — password\nresets, user invitations, and any email notification channel that doesn't carry its own\nSMTP settings. Set it once per org so those emails can go out.\n\nConfigure it. On /admin/notifications, the first card is\nSystem SMTP:\n\n/admin/notifications → System SMTP card → Configure.\n\n- Open /admin/notifications. The first card is System SMTP — click Configure (or Edit if it's already set).\n\n- Fill the form. Host, port (587 for STARTTLS, 465 for implicit TLS), optional username and password, STARTTLS toggle, and the From address — required and validated as an email. The password field shows \"Leave blank to keep the existing password\" when editing, so secrets aren't echoed back.\n\n- Send a test email. After saving, the card exposes a sink-address input + Send test button so you can verify deliverability without firing a real workflow event.\n\nThe password is AES-256-GCM encrypted at rest. Delete the row from the same card if\nyou'd rather rely solely on per-channel email overrides."} {"id":"ab3787a9c974540a","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-review-plans","anchor":"cfg-review-plans","title":"Review plans","section":"Reference","order":0,"tokens":737,"text":"AccessFlow Docs > Reference > Review workflows > Review plans\n\nThere is a guide for this. Run your first governed query\nwalks through building a review plan and attaching it, and\nGate a CI/CD pipeline applies the same machinery to\ndeployments — including the approver scoping that is easy to miss. This chapter is the reference behind both.\n\nWhat it is. The approval policy attached to a datasource — it decides how\na submitted query gets from \"submitted\" to \"executed\". Use it to require AI scoring, one or\nmore human sign-offs, or both, and to auto-approve low-risk reads. Each plan is a sequence\nof stages; each stage names approvers (by role or specific user) and a minimum number of\napprovals before the query advances.\n\nConfigure it. Build plans at /admin/review-plans — start\nfrom a built-in template or from scratch. A rule that names a role covers every holder of\nthat role, service accounts included. When a rule's role is held by active service\naccounts, the editor warns under that rule and shows how many could approve. The warning\nis advisory: it does not stop you saving the plan.\n\n/admin/review-plans → caret next to Add review plan exposes four built-in templates that prefill the create modal.\n\n/admin/review-plans → Add review plan. Stack approver rows to build multi-stage chains.\n\n- Open /admin/review-plans and click Add review plan, or pick a built-in template from the dropdown caret next to it (Strict — writes need 2 approvals, Lenient — reads auto-approved, AI-only — no human approval, Standard — AI + 1 reviewer) to prefill the modal with sensible defaults.\n\n- Name and describe the policy — e.g. \"Production writes — two reviewers\".\n\n- Pick the gates. Toggle Require AI review to score every query before it queues for humans; toggle Require human approval to demand at least one reviewer sign-off. Auto-approve LOW-risk reads lets SELECTs skip humans entirely when AI risk is below the threshold.\n\n- Set thresholds. Minimum approvals is the number of distinct reviewers needed before the query advances; Approval timeout (hours) is when AccessFlow auto-rejects an idle PENDING_REVIEW query (it scans on a cadence set by ACCESSFLOW_WORKFLOW_TIMEOUT_POLL_INTERVAL, default PT5M).\n\n- Build the approver chain. Click Add approver for each stage; each row names a role (or a specific user) and a stage number. Stages advance sequentially, and a single REJECTED decision at any stage terminates the query."} @@ -245,9 +245,9 @@ {"id":"ff5dc315e1fa1032","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-access-explainer","anchor":"cfg-access-explainer","title":"Effective-access explainer","section":"Reference","order":0,"tokens":576,"text":"AccessFlow Docs > Reference > Review workflows > Effective-access explainer (part 1 of 2)\n\nWhat it is. The answer to why — why would this query be\nauto-approved, why did that one go to a reviewer, who could approve it — without\nsubmitting a probe query against production to find out. An admin describes one\nhypothetical request (a user, a datasource, the SQL) and AccessFlow replays it through\nthe very evaluators that decide real ones: the datasource and quota gates, the merged\neffective permission with each contributing grant named, every routing policy in\npriority order including the ones that nearly matched, the grant-covered fast path, the\nreview plan, who could review it, and the row-security and masking policies that would\napply. The result is an ordered, per-stage trace ending in the status the request would\nreach.\n\nWhat it is not. A simulation writes nothing, executes nothing, publishes\nno event and never calls an AI provider — the AI verdict is the one stage a hypothetical\nrequest cannot have, so the trace says so rather than guessing. Every other\napproximation a hypothetical request cannot avoid is named in the response instead of\nsmoothed over. It is a read-only trace of the decision, not a dry run of the query — the\neditor's Dry run action is for the latter.\n\nWho can write to this table? The reverse question, which no permission\nscreen could answer: for one datasource and one table, every user who could submit a\nread, a write or a DDL statement against it, one row per person, naming each source that\ngrants it — the direct permission row, each inherited group permission, an active\njust-in-time grant (and whether it also skips review), break-glass, and the\nQUERY_ADMIN holders who bypass the per-datasource gate entirely and would\notherwise appear nowhere. Table matching delegates to the enforcement gate's own rules,\nso the report cannot drift from what actually happens at submission."} {"id":"949d06f4ae33b3c4","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-access-explainer","anchor":"cfg-access-explainer","title":"Effective-access explainer","section":"Reference","order":1,"tokens":302,"text":"AccessFlow Docs > Reference > Review workflows > Effective-access explainer (part 2 of 2)\n\nWhere. In the app, under Access control → Access simulation\n(/admin/access-simulations): a Query trace tab that lays the trace\nout stage by stage — skipped stages included, and every routing policy in a table with\nthe one that decided it highlighted — and a Who has access tab for the reverse\nlookup, with break-glass shown in a column of its own. Over the API they are\nPOST /api/v1/admin/access-simulations and\nGET /api/v1/admin/effective-access. Both are gated by the permission that\nalready governs datasource grants; the who-can-write lookup is also open to the\nAUDITOR role, which reads exactly this from the attestation and usage\nreports already, so an auditor sees that tab alone. The same trace exists for\ngoverned API calls and for\ndeployments,\nwhere it can also answer \"would a release at this moment be held by a freeze window?\".\nEvery run is recorded in the audit log."} {"id":"5c304ab87f161594","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-deployment-pipelines","anchor":"cfg-deployment-pipelines","title":"Deployment pipelines","section":"Reference","order":0,"tokens":383,"text":"AccessFlow Docs > Reference > Review workflows > Deployment pipelines (part 1 of 4)\n\nWhat it is. The same review machinery, applied to CI/CD\ndeployments. A deployment pipeline names a provider (GitHub\nActions, GitLab CI, Azure Pipelines, Jenkins, CircleCI, Bitbucket Pipelines, or generic)\nand holds ordered environments — dev,\nstaging, production — each of which can require review, set its\nown approval count, override the review plan, opt in\nto break-glass, and optionally name the database that backs it. A CI job asks AccessFlow for permission to release, the request flows\nthrough AI release-risk analysis, routing policies and\nhuman approval, and the job blocks on a fail-closed gate until every\nrequired approval is granted. The submitter — for a pipeline, the user who owns the API\nkey — can never approve their own deployment.\n\n/deployments — every deployment you submitted, with its status, target environment and release version.\n\n/deployments/ — release metadata, the approval timeline, and the approve and reject actions for whoever may decide this one. Once it is approved, the same page carries the fail-closed releasability banner.\n\nThe Deployments tab of the review queue (/reviews?tab=deployments). A deployment never appears here for its own submitter."} -{"id":"85617ff219a2006e","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-deployment-pipelines","anchor":"cfg-deployment-pipelines","title":"Deployment pipelines","section":"Reference","order":1,"tokens":771,"text":"AccessFlow Docs > Reference > Review workflows > Deployment pipelines (part 2 of 4)\n\nConfigure it. Create pipelines at\n/admin/deployment-pipelines (requires the\nDEPLOYMENT_PIPELINE_MANAGE permission, held by ADMIN). Each\npipeline's settings page carries seven tabs. Alongside General and the\nCI setup snippet sit Environments (promotion\norder, tags, required approvals, review-plan override, break-glass opt-in),\nVersions (which version each environment is running — see below),\nPermissions (per-user and per-group can_trigger /\ncan_break_glass grants with an optional expiry — a user's effective grant is\nthe most-permissive union of their direct grant and every unexpired group grant),\nFreeze windows, and Routing policies. The CI setup\ntab carries copy-paste snippets for GitHub Actions, GitLab CI, Azure Pipelines and\ncurl, chosen from a toggle rather than preselected from the pipeline's provider; a\npipeline set to Jenkins, CircleCI, Bitbucket Pipelines or generic uses the curl one. Reviewers work\ndeployments on the Deployments tab of the review queue\n(/reviews?tab=deployments, requires DEPLOYMENT_REVIEW,\nheld by ADMIN and REVIEWER) — the same page that holds\nquery and API-request reviews, with one tab per kind of request you may review and a\npending count on each. Opening a deployment from that queue does not cost you the\ndecision: approve and reject sit on the deployment's own page too, for anyone eligible\nto decide it. Everyone sees their own at /deployments.\n\nWhere the pipeline id is. A CI job names its pipeline by\nid, never by name, because a trigger-only API key is not allowed to\nlist pipelines and so has nothing to resolve a name against. You can copy that id from\neither place you meet a pipeline: the list's ID column, which shows a\nshortened form, and the pipeline's settings page, which shows it in full beneath the\nname. Both copy the whole UUID, not the shortened form.\n\n/admin/deployment-pipelines — one row per pipeline, with its copyable id and CI provider.\n\nPipeline settings → Environments — promotion order, tags, required approvals, review-plan override and break-glass opt-in, per environment.\n\nEnvironment tags. Each environment can carry short free-form\ntags — at most ten, each up to 32 characters — with no fixed meaning:\na customer, a region, a tier, whatever you group by. They exist so one application\ndeployed for many customers can be modelled as one environment per customer, all\ntagged alike, and then read back as a single view. Add them in the Tags\nfield on the Environments tab and filter on them in the version matrix\nbelow."} -{"id":"4e228753a2f1741d","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-deployment-pipelines","anchor":"cfg-deployment-pipelines","title":"Deployment pipelines","section":"Reference","order":2,"tokens":681,"text":"AccessFlow Docs > Reference > Review workflows > Deployment pipelines (part 3 of 4)\n\nWhich version is where. Once releases have flowed through the gate,\n/deployment-versions shows one row per environment that has been deployed\nto: its tags, the version it is running now, the version it was running before, when\nit got there, and how the last deploy ended. Filter by pipeline, tag, environment\nname, or narrow to just the environments that have fallen behind. The page is open to\npipeline admins, deployment reviewers and admins; someone who can only trigger a\npipeline reaches that pipeline's own matrix by picking it in the pipeline filter on\n/deployments and choosing Version matrix. The pipeline's\nown Versions tab shows the same rows, and additionally lists\nenvironments nothing has been deployed to yet.\n\nEach row says how far behind it is: how many newer releases have gone out since it\nlast moved, and how many days passed between its deploy and the newest one. \"Behind\"\nis a plain comparison against the newest release that succeeded on the pipeline —\nversion strings are yours, so nothing is parsed or ordered numerically. An environment\nwhose last deploy failed or was rolled back shows the version it reverted to, or says\nthe version is unknown when two rollbacks in a row left no earlier record to fall back\non. Every environment also keeps a history, newest first, of every deployment aimed at\nit.\n\nFreeze windows. A freeze window suspends releases for the whole\norganization, one pipeline, or a single environment. It is either one-off\n(a start and end instant) or recurring weekly (days of the week plus a\nstart and end time in a named IANA timezone — an end time before the start time spans\nmidnight). HOLD lets a deployment be submitted, analyzed and approved but\nwithholds releasability until the window closes, so the release proceeds by itself\nafterwards with no re-approval. REJECT auto-rejects at submission instead.\nWhere several windows are active at once the most specific scope wins, and\nREJECT beats HOLD. A window whose definition cannot be evaluated\n— a bad timezone, say — counts as an active HOLD, never a\nREJECT: a broken rule can stall a release but can never destroy a request.\n\nPipeline settings → Freeze windows — one-off or recurring weekly, scoped org-wide, per pipeline or per environment, holding or rejecting."} -{"id":"8bd83ee301d137a2","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-deployment-pipelines","anchor":"cfg-deployment-pipelines","title":"Deployment pipelines","section":"Reference","order":3,"tokens":455,"text":"AccessFlow Docs > Reference > Review workflows > Deployment pipelines (part 4 of 4)\n\nBreak-glass and rollbacks. An emergency deploy needs\nboth a can_break_glass grant on the pipeline and\nallow_break_glass on the target environment — for everyone, with no admin\nbypass. It skips AI analysis, routing and review, and pays for that with an instant\nnotification to every org admin, a prominent audit row naming any freeze window it\nbypassed, and a mandatory retro-review an admin who is never the submitter must\nacknowledge. After a deploy the pipeline reports its outcome; a\nrollback on a review-required environment opens a follow-up review in the\nreviewer queue that the submitter can never close.\n\nThe Rollbacks tab of the review queue (/reviews?tab=rollbacks) — the follow-up worklist a rolled-back deployment opens.\n\nWire up CI. See\nthe deployment gate in CI for the GitHub\nAction, the GitLab CI template, the Azure Pipelines step template, and the plain-curl\npath, plus how to issue the service-account API key a pipeline authenticates with.\n\nPipeline settings → CI setup — snippets for GitHub Actions, GitLab CI, Azure Pipelines and curl, each carrying the pipeline id the job gates on.\n\nTune it. ACCESSFLOW_DEPLOYGOV_TIMEOUT_CHECK (how often\ndeployments awaiting review are checked against their review plan's approval timeout,\ndefault PT5M) and ACCESSFLOW_DEPLOYGOV_RELEASE_CHECK (how often\napproved deployments are scanned to announce that they have become releasable, default\nPT1M). Every transition lands in the\naudit log."} +{"id":"85617ff219a2006e","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-deployment-pipelines","anchor":"cfg-deployment-pipelines","title":"Deployment pipelines","section":"Reference","order":1,"tokens":668,"text":"AccessFlow Docs > Reference > Review workflows > Deployment pipelines (part 2 of 4)\n\nConfigure it. Create pipelines at\n/admin/deployment-pipelines (requires the\nDEPLOYMENT_PIPELINE_MANAGE permission, held by ADMIN). Each\npipeline's settings page carries seven tabs. Alongside General and the\nCI setup snippet sit Environments (promotion\norder, tags, required approvals, review-plan override, break-glass opt-in, and whether\nreleases wait for an approved\nchange ticket),\nVersions (which version each environment is running — see below),\nPermissions (per-user and per-group can_trigger /\ncan_break_glass grants with an optional expiry — a user's effective grant is\nthe most-permissive union of their direct grant and every unexpired group grant),\nFreeze windows, and Routing policies. The CI setup\ntab carries copy-paste snippets for GitHub Actions, GitLab CI, Azure Pipelines and\ncurl, chosen from a toggle rather than preselected from the pipeline's provider; a\npipeline set to Jenkins, CircleCI, Bitbucket Pipelines or generic uses the curl one. Reviewers work\ndeployments on the Deployments tab of the review queue\n(/reviews?tab=deployments, requires DEPLOYMENT_REVIEW,\nheld by ADMIN and REVIEWER) — the same page that holds\nquery and API-request reviews, with one tab per kind of request you may review and a\npending count on each. Opening a deployment from that queue does not cost you the\ndecision: approve and reject sit on the deployment's own page too, for anyone eligible\nto decide it. Everyone sees their own at /deployments.\n\nWhere the pipeline id is. A CI job names its pipeline by\nid, never by name, because a trigger-only API key is not allowed to\nlist pipelines and so has nothing to resolve a name against. You can copy that id from\neither place you meet a pipeline: the list's ID column, which shows a\nshortened form, and the pipeline's settings page, which shows it in full beneath the\nname. Both copy the whole UUID, not the shortened form.\n\n/admin/deployment-pipelines — one row per pipeline, with its copyable id and CI provider.\n\nPipeline settings → Environments — promotion order, tags, required approvals, review-plan override, break-glass opt-in and change-ticket requirement, per environment."} +{"id":"4e228753a2f1741d","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-deployment-pipelines","anchor":"cfg-deployment-pipelines","title":"Deployment pipelines","section":"Reference","order":2,"tokens":766,"text":"AccessFlow Docs > Reference > Review workflows > Deployment pipelines (part 3 of 4)\n\nEnvironment tags. Each environment can carry short free-form\ntags — at most ten, each up to 32 characters — with no fixed meaning:\na customer, a region, a tier, whatever you group by. They exist so one application\ndeployed for many customers can be modelled as one environment per customer, all\ntagged alike, and then read back as a single view. Add them in the Tags\nfield on the Environments tab and filter on them in the version matrix\nbelow.\n\nWhich version is where. Once releases have flowed through the gate,\n/deployment-versions shows one row per environment that has been deployed\nto: its tags, the version it is running now, the version it was running before, when\nit got there, and how the last deploy ended. Filter by pipeline, tag, environment\nname, or narrow to just the environments that have fallen behind. The page is open to\npipeline admins, deployment reviewers and admins; someone who can only trigger a\npipeline reaches that pipeline's own matrix by picking it in the pipeline filter on\n/deployments and choosing Version matrix. The pipeline's\nown Versions tab shows the same rows, and additionally lists\nenvironments nothing has been deployed to yet.\n\nEach row says how far behind it is: how many newer releases have gone out since it\nlast moved, and how many days passed between its deploy and the newest one. \"Behind\"\nis a plain comparison against the newest release that succeeded on the pipeline —\nversion strings are yours, so nothing is parsed or ordered numerically. An environment\nwhose last deploy failed or was rolled back shows the version it reverted to, or says\nthe version is unknown when two rollbacks in a row left no earlier record to fall back\non. Every environment also keeps a history, newest first, of every deployment aimed at\nit.\n\nFreeze windows. A freeze window suspends releases for the whole\norganization, one pipeline, or a single environment. It is either one-off\n(a start and end instant) or recurring weekly (days of the week plus a\nstart and end time in a named IANA timezone — an end time before the start time spans\nmidnight). HOLD lets a deployment be submitted, analyzed and approved but\nwithholds releasability until the window closes, so the release proceeds by itself\nafterwards with no re-approval. REJECT auto-rejects at submission instead.\nWhere several windows are active at once the most specific scope wins, and\nREJECT beats HOLD. A window whose definition cannot be evaluated\n— a bad timezone, say — counts as an active HOLD, never a\nREJECT: a broken rule can stall a release but can never destroy a request."} +{"id":"8bd83ee301d137a2","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-deployment-pipelines","anchor":"cfg-deployment-pipelines","title":"Deployment pipelines","section":"Reference","order":3,"tokens":497,"text":"AccessFlow Docs > Reference > Review workflows > Deployment pipelines (part 4 of 4)\n\nPipeline settings → Freeze windows — one-off or recurring weekly, scoped org-wide, per pipeline or per environment, holding or rejecting.\n\nBreak-glass and rollbacks. An emergency deploy needs\nboth a can_break_glass grant on the pipeline and\nallow_break_glass on the target environment — for everyone, with no admin\nbypass. It skips AI analysis, routing and review, and pays for that with an instant\nnotification to every org admin, a prominent audit row naming any freeze window it\nbypassed, and a mandatory retro-review an admin who is never the submitter must\nacknowledge. After a deploy the pipeline reports its outcome; a\nrollback on a review-required environment opens a follow-up review in the\nreviewer queue that the submitter can never close.\n\nThe Rollbacks tab of the review queue (/reviews?tab=rollbacks) — the follow-up worklist a rolled-back deployment opens.\n\nWire up CI. See\nthe deployment gate in CI for the GitHub\nAction, the GitLab CI template, the Azure Pipelines step template, and the plain-curl\npath, plus how to issue the service-account API key a pipeline authenticates with.\n\nPipeline settings → CI setup — snippets for GitHub Actions, GitLab CI, Azure Pipelines and curl, each carrying the pipeline id the job gates on.\n\nTune it. ACCESSFLOW_DEPLOYGOV_TIMEOUT_CHECK (how often\ndeployments awaiting review are checked against their review plan's approval timeout,\ndefault PT5M) and ACCESSFLOW_DEPLOYGOV_RELEASE_CHECK (how often\napproved deployments are scanned to announce that they have become releasable, default\nPT1M). Every transition lands in the\naudit log."} {"id":"6a9c9a7704a7626c","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-schema-changes","anchor":"cfg-schema-changes","title":"Schema change sets","section":"Reference","order":0,"tokens":639,"text":"AccessFlow Docs > Reference > Review workflows > Schema change sets (part 1 of 2)\n\nWhat it is. A governed way to change a database's structure —\ncreate a table, add a column, drop an index — across your environments in order. A\nschema change set is an ordered list of schema statements written once,\nunder a deployment pipeline, and then\npromoted one environment at a time: dev, then\nstaging, then production. An environment only accepts a change\nset that has already applied successfully on every lower environment that has a\ndatabase behind it, so production can only ever receive a change that worked in staging.\n\nWhat is refused before it is saved. Every statement is checked with the\ntarget database's own parser when you save. Statements that read or change data —\nSELECT, INSERT, UPDATE, DELETE — are\nrefused with a message pinned to the statement, and so are a\nBEGIN … COMMIT wrapper and several statements pasted into one box. If a\nSQL review rule set to block matches, the save is\nrefused too; a rule set to warn shows its warning under the statement and lets\nthe save through. The first promotion freezes the list: from then on\nthe statements cannot be edited, so the text that reaches production is exactly the text\nthat ran in staging.\n\nWhat it will not do. There is no rollback. Statements run one after\nanother, each on its own, so if statement 7 of 12 fails, the first six stay applied and\nthe promotion reads partially applied. Fix what is left with a new change set.\n\nConfigure it. First give each environment a database: on the pipeline's\nEnvironments tab at /admin/deployment-pipelines, pick the\ndatasource that environment's schema changes should land on. Environments without one\nare skipped by the ladder. Then open Schema changes → Change sets\n(/schema-change-sets; requires the SCHEMA_CHANGE_MANAGE\npermission, held by ADMIN), create a change set for the pipeline, and add\nits statements. The detail page shows the promotion ladder: one step\nper environment, each saying whether it is ready to promote, in progress, applied, or\nblocked — and, when blocked, why (a lower environment not yet applied, an active freeze\nwindow, no database bound)."} {"id":"d8a6ea717c07898e","path":"website/docs/configuration/review-workflows/index.html","url":"https://accessflow.io/docs/configuration/review-workflows/#cfg-schema-changes","anchor":"cfg-schema-changes","title":"Schema change sets","section":"Reference","order":1,"tokens":584,"text":"AccessFlow Docs > Reference > Review workflows > Schema change sets (part 2 of 2)\n\nPromote it. Promoting needs the DDL grant on the target datasource for\nthe person clicking Promote, admins included. A promotion is\nrefused inside a freeze window. It then goes\nthrough review as a request group — approvers come from\nthe target datasource's review plan — the environment's own\napproval count and review-plan override do not apply to schema changes — and an\nenvironment that requires review is refused outright if that plan would not ask a\nperson. Once approved, the statements run in order within about a minute, and the\nladder moves on; from that point the promotion can no longer be cancelled.\nReviewers are notified when a promotion needs them; the promoter is notified when it\napplies or fails.\n\nWatch for drift. Schema changes → Schema drift\n(/schema-drift) compares each environment's live structure against the\nenvironment below it and lists the differences — a table missing, a column with a\ndifferent type — for someone to acknowledge. Scan now checks\none environment on demand. Scheduled scans are off until you turn them on per pipeline\nwith PUT /api/v1/schema-drift/configs/{pipelineId}, which also chooses what\nto compare against: the environment below (the default), one reference environment, or\nthe structure recorded right after that environment's last successful promotion — the\none that catches changes made outside AccessFlow. Drift compares tables, columns and\nforeign keys only; indexes, constraints, defaults, views and column lengths are\ninvisible to it, so \"no drift\" means nothing changed in what it can see. It never fixes\nanything itself.\n\nTune it. ACCESSFLOW_SCHEMACHANGE_MAX_STATEMENTS (statements\nper change set, default 50) and the\nACCESSFLOW_SCHEMACHANGE_DRIFT_* settings for the scan cadence, time budget\nand caps — see the\ndeployment reference.\nEvery change set edit, promotion step, scan and acknowledgement lands in the\naudit log."} {"id":"499d311fd927a693","path":"website/docs/configuration/users-roles/index.html","url":"https://accessflow.io/docs/configuration/users-roles/#cfg-organizations","anchor":"cfg-organizations","title":"Organizations & quotas","section":"Reference","order":0,"tokens":747,"text":"AccessFlow Docs > Reference > Users, roles & organizations > Organizations & quotas (part 1 of 2)\n\nThere is a guide for this. Invite your team and assign roles\nis the step-by-step version: create the accounts, pick roles, grant datasource access, and offboard cleanly. This chapter is the reference behind it.\n\nWhat it is. Multi-tenancy. A single AccessFlow deployment hosts\none or more organizations, each a fully-isolated tenant — every user,\ndatasource, query, and audit row belongs to exactly one org, and a tenant can never reach\nanother's data. Reach for it when you host several teams or customers on one deployment and\nwant per-tenant resource caps; most operators run a single org and can ignore the cross-org\ncontrols.\n\nPlatform admin (super-admin). Managing organizations across the cluster\nrequires the platform-admin capability — a flag on a user, separate from the five\nroles below. A platform admin keeps their normal org role and additionally sees a\nPlatform navigation group with the /admin/organizations\nscreen. The first admin you create (via the setup wizard or the\nbootstrap env vars) is provisioned as a platform admin\nautomatically.\n\n/admin/organizations — platform admins manage every tenant and its quotas from one screen.\n\nConfigure it. Platform admins manage tenants and their quotas from\n/admin/organizations:\n\n- Create an organization. From /admin/organizations, click\nNew organization, give it a name (an optional URL-safe slug is derived\nfrom the name when blank), and optionally set quotas. The org starts enabled and empty —\nadd its first admin and datasources afterwards.\n\n- Set per-org quotas. Cap how much a tenant may consume:\nmax datasources, max users, and max queries per day (a rolling\ntrailing-24-hour count). Leave a field blank or set it to 0 for\nunlimited. Quotas are enforced when the resource is created — a breach is\nrejected with HTTP 409 QUOTA_EXCEEDED and a message naming the limit, so the\ndatasource / user / query simply isn't created.\n\n- Edit name & quotas. Open /admin/organizations/\nto change the name or raise / lower a quota, and to see live usage bars (current count vs.\nlimit) for each cap.\n\n- Change what the org governs. The same screen carries the two switches\nfrom the setup wizard — Govern outbound API\ncalls and Gate CI/CD deployments. An org admin does not need a platform\nadmin for this: the same two switches live at /admin/governance-domains,\nunder System in the sidebar."} @@ -300,7 +300,7 @@ {"id":"bf0199709e9562c8","path":"website/docs/guides/deployment-approval/index.html","url":"https://accessflow.io/docs/guides/deployment-approval/#guide-deployment-approvers","anchor":"guide-deployment-approvers","title":"6. Restrict who may approve","section":"Guides","order":0,"tokens":614,"text":"AccessFlow Docs > Guides > Gate a deployment pipeline > 6. Restrict who may approve\n\nDo not skip this step. The permission that lets somebody approve a\ndeployment is held by both ADMIN and REVIEWER. On any instance where\nreviewers exist who should not be shipping releases, a review plan that scopes approval\nis the only thing standing between them and your production deploy. It is mandatory,\nnot optional.\n\n- Create the plan. Sidebar → Security & Access →\nReview plans → Add review plan. Set\nMinimum approvals.\n\n- Add one approver row. Set Role to\nAdmin and Stage to 1.\n\n- Attach it. On the pipeline's General tab, pick it\nunder Review plan.\n\nThree things about approver rows regularly catch people out:\n\n- A plan with no approver rows restricts nothing. Attaching an empty\nplan looks like configuration but leaves the request open to everyone who holds the\ndeployment-review permission.\n\n- Deployment review is single-stage. Only rows at stage\n1 are consulted. A row at stage 2 or higher is ignored silently — no\nwarning, no error.\n\n- Review override bypasses the approver list entirely. It is an\nadmin-only permission and it is meant to work this way, which means the rule that\nultimately protects your pipeline is simply: do not make someone an admin unless they\nshould be allowed to ship.\n\nTo pin approval to one named person rather than to every admin, put their user id in\nthe row's User box. It is a plain text field expecting a UUID, and user\nids are not shown anywhere in the users screen — read one from\nGET /api/v1/admin/users.\n\nWhatever you configure, one rule cannot be configured away: the submitter can never\napprove their own deployment, admins and override holders included. That is why step 1\ninsisted on a separate account. If your pipeline also names the engineer who started it\n(the X-AccessFlow-On-Behalf-Of header, once they have agreed to it — an admin\nrecords that on the service account's On-behalf-of principals tab), that engineer cannot approve\nthe deployment either. The CI steps in this guide do not send that header; add it to your\nown request if you need it."} {"id":"ccb310152697cd07","path":"website/docs/guides/deployment-approval/index.html","url":"https://accessflow.io/docs/guides/deployment-approval/#guide-deployment-ci","anchor":"guide-deployment-ci","title":"7. Add the gate to your pipeline","section":"Guides","order":0,"tokens":652,"text":"AccessFlow Docs > Guides > Gate a deployment pipeline > 7. Add the gate to your pipeline\n\nThe pipeline's CI setup tab carries ready-made snippets with the\npipeline id already filled in. Copy from there rather than retyping the UUID. It offers\nfour, chosen from a toggle that always opens on GitHub Actions — it does not preselect\nthe provider you set in step 3: GitHub Actions, GitLab\nCI, Azure Pipelines and curl. Pipelines set\nto Jenkins, CircleCI, Bitbucket Pipelines or generic use the curl sequence.\n\nEvery provider follows the same four beats:\n\n- Submit a deployment request. It is idempotent on the CI run id, so\na retried job never opens a second review.\n\n- Poll the gate until it answers releasable.\n\n- Confirm execution, immediately before deploying.\n\n- Report the outcome once the deploy finishes.\n\n.github/workflows/deploy.yml\n\n- id: gate\nuses: bablsoft/accessflow/.github/actions/deployment-gate@v1\nwith:\naccessflow-url: https://accessflow.example.com\napi-key: ${{ secrets.ACCESSFLOW_API_KEY }}\npipeline-id: 1f0c9d02-1c2a-4a19-9f0e-6b2f8f1a4c77\nenvironment: production\nversion: ${{ github.ref_name }}\ncommit-sha: ${{ github.sha }}\nwait-timeout: 30m\n\n- run: ./deploy.sh\n\n- uses: bablsoft/accessflow/.github/actions/deployment-outcome@v1\nif: always()\nwith:\naccessflow-url: https://accessflow.example.com\napi-key: ${{ secrets.ACCESSFLOW_API_KEY }}\nrequest-id: ${{ steps.gate.outputs.request-id }}\njob-status: ${{ job.status }}\n\nThe outcome step is separate, and runs under if: always(), because a\ncomposite action has no post-run hook to report from. Pass an explicit\noutcome: ROLLED_BACK when you roll back — on an environment that requires\nreview that opens a follow-up the submitter is not allowed to close.\n\nGitLab CI, Azure Pipelines and the plain-curl sequence for anything else are documented\nalongside the full input reference in\nthe deployment gate in CI.\n\nPipeline settings → CI setup — snippets for GitHub Actions, GitLab CI, Azure Pipelines and curl, each carrying the pipeline id the job gates on."} {"id":"886799162c54ef43","path":"website/docs/guides/deployment-approval/index.html","url":"https://accessflow.io/docs/guides/deployment-approval/#guide-deployment-run","anchor":"guide-deployment-run","title":"8. Your first gated release","section":"Guides","order":0,"tokens":257,"text":"AccessFlow Docs > Guides > Gate a deployment pipeline > 8. Your first gated release\n\nRun the workflow. The gate step opens a request and then waits, printing its poll\nattempts. In AccessFlow the request appears for reviewers on the\nDeployments tab of the review queue (/reviews?tab=deployments,\nthe same page that holds query and API-request reviews), carrying the version, the\ntarget environment, the commit and the AI release-risk verdict if you enabled analysis. It never appears there\nfor the account that submitted it.\n\nAn approver opens it and approves. Within a poll interval the gate answers releasable,\nthe CI job unblocks, your deploy step runs, and the outcome step records how it went.\nThe submitter can watch the whole thing at /deployments.\n\nThe Deployments tab of the review queue — where an approver picks the release up. A deployment never appears here for its own submitter."} -{"id":"5b67475c4c415276","path":"website/docs/guides/deployment-approval/index.html","url":"https://accessflow.io/docs/guides/deployment-approval/","anchor":"","title":"If the job fails instead","section":"Guides","order":0,"tokens":699,"text":"AccessFlow Docs > Guides > Gate a deployment pipeline > If the job fails instead\n\n- The gate returns 404. The key cannot see that request — usually a\nwrong pipeline id, or a key belonging to an account with no can-trigger grant.\nAccessFlow answers 404 rather than 403 on purpose, so a key cannot probe for pipelines\nit has no business knowing about.\n\n- It waits and then times out. Nobody approved inside\nwait-timeout, or a freeze window is holding the release. Check the\nrequest at /deployments — a held release resumes on its own when the\nwindow closes, with no re-approval.\n\n- Approved, but still not releasing. Approval alone is not a green\nlight. The job branches on releasable, which also requires no active freeze\nwindow and any scheduled time to have passed. If the environment requires a change\nticket, the waiting line includes change_ticket=…. A ticket key means the\nrelease waits until AccessFlow hears that the ticket was approved — which only happens\nthrough the ticketing system's callback, so set up\nbi-directional status sync on the\nchannel. A rejected ticket blocks the release for good; submit a new deployment.\nnone linked yet means no ticket was opened — check that a ServiceNow or\nJira channel carries the deployment trigger, or the release will never go out. The\ndeployment's page in AccessFlow shows the same thing: a Change ticket\ncard with the ticket's link, status and decision, and a banner that reads\nwaiting for the change ticket.\n\n- The log says \"rate limited (HTTP 429), retrying\". Not a failure.\nEvery API key has a per-identity request cap (120 per minute by default; a service\naccount can be given its own), and the wrappers simply wait the number of seconds\nthe server asks for before trying again. A pipeline that hits this constantly is\npolling too fast — lengthen the poll interval, or have an admin raise the account's\nlimit on its Limits tab.\n\nA real worked example. The AccessFlow public demo deploys itself\nthrough its own gate. The workflow, the exact one-time setup it needed and the recovery\npath when the instance is down are written up in\ndocs/09-deployment.md\n→ The public demo environment.\n\nOnce a few releases have gone out this way, /deployment-versions becomes\nuseful: it lists every environment you deploy to, the version each is running now, and\nhow far behind the newest release it has fallen — filterable by the tags you set in\nstep 4."} +{"id":"5b67475c4c415276","path":"website/docs/guides/deployment-approval/index.html","url":"https://accessflow.io/docs/guides/deployment-approval/","anchor":"","title":"If the job fails instead","section":"Guides","order":0,"tokens":724,"text":"AccessFlow Docs > Guides > Gate a deployment pipeline > If the job fails instead\n\n- The gate returns 404. The key cannot see that request — usually a\nwrong pipeline id, or a key belonging to an account with no can-trigger grant.\nAccessFlow answers 404 rather than 403 on purpose, so a key cannot probe for pipelines\nit has no business knowing about.\n\n- It waits and then times out. Nobody approved inside\nwait-timeout, or a freeze window is holding the release. Check the\nrequest at /deployments — a held release resumes on its own when the\nwindow closes, with no re-approval.\n\n- Approved, but still not releasing. Approval alone is not a green\nlight. The job branches on releasable, which also requires no active freeze\nwindow and any scheduled time to have passed. If the environment requires a change\nticket, the waiting line includes change_ticket=…. A ticket key means the\nrelease waits until AccessFlow hears that the ticket was approved — which only happens\nthrough the ticketing system's callback, so set up\nbi-directional status sync on the\nchannel. A rejected ticket blocks the release while it stays rejected, and with sync on it also rejects a deployment still awaiting review; then submit a new deployment.\nnone linked yet means no ticket was opened — check that a ServiceNow or\nJira channel carries the deployment trigger, or the release will never go out. The\ndeployment's page in AccessFlow shows the same thing: a Change ticket\ncard with the ticket's link, status and decision, and a banner that reads\nwaiting for the change ticket.\n\n- The log says \"rate limited (HTTP 429), retrying\". Not a failure.\nEvery API key has a per-identity request cap (120 per minute by default; a service\naccount can be given its own), and the wrappers simply wait the number of seconds\nthe server asks for before trying again. A pipeline that hits this constantly is\npolling too fast — lengthen the poll interval, or have an admin raise the account's\nlimit on its Limits tab.\n\nA real worked example. The AccessFlow public demo deploys itself\nthrough its own gate. The workflow, the exact one-time setup it needed and the recovery\npath when the instance is down are written up in\ndocs/09-deployment.md\n→ The public demo environment.\n\nOnce a few releases have gone out this way, /deployment-versions becomes\nuseful: it lists every environment you deploy to, the version each is running now, and\nhow far behind the newest release it has fallen — filterable by the tags you set in\nstep 4."} {"id":"e4ac3107700652aa","path":"website/docs/guides/deployment-approval/index.html","url":"https://accessflow.io/docs/guides/deployment-approval/#guide-deployment-simulation","anchor":"guide-deployment-simulation","title":"Asking \"would this release be held?\" without a release","section":"Guides","order":0,"tokens":330,"text":"AccessFlow Docs > Guides > Gate a deployment pipeline > Asking \"would this release be held?\" without a release\n\nA deployment is blocked by the gate, not only by the decision — so knowing a\nrequest would be approved does not tell you it would ship. The pipeline's\nSimulate tab (or POST /api/v1/admin/deployment-simulations)\nreplays one hypothetical deployment through\nthe very evaluator and the very releasable function the CI job blocks on, and answers\nstage by stage: the trigger grant, the freeze window, routing policies, the environment's\nown policy, who could approve, the deferred-release moment and the gate's verdict. It\naccepts an optional at, so \"would a release this Friday evening be held?\" is\nanswerable now rather than on Friday evening, and the tab puts the gate's releasable\nverdict above the step-by-step trace. Nothing is created, nothing is notified,\nnothing reaches the pipeline; it needs the same permission that manages pipelines and\nevery run is recorded in the audit log.\n\nFrom here: deployment\npipelines covers freeze windows, routing policies, break-glass and rollback reviews\nin full."} {"id":"f687c17692f24406","path":"website/docs/guides/first-query/index.html","url":"https://accessflow.io/docs/guides/first-query/#guide-first-query","anchor":"guide-first-query","title":"What you are building","section":"Guides","order":0,"tokens":163,"text":"AccessFlow Docs > Guides > Run your first governed query > What you are building\n\nA query that a person writes, an AI scores, a human approves, and AccessFlow then runs\nagainst a real database — with the whole sequence recorded. It is the shape every other\npart of the product is built on, so it is worth doing once end to end before you\nconfigure anything else.\n\nAllow about half an hour. You need AccessFlow running (the\ninstall chapter covers Docker Compose, Helm and\nrunning from source) and a database you are willing to point it at — a scratch one is\nideal."} {"id":"b3a6ad0551ecec96","path":"website/docs/guides/first-query/index.html","url":"https://accessflow.io/docs/guides/first-query/","anchor":"","title":"How does a query get approved in AccessFlow?","section":"Guides","order":0,"tokens":209,"text":"AccessFlow Docs > Guides > Run your first governed query > How does a query get approved in AccessFlow?\n\nA user picks a datasource and writes SQL. AccessFlow parses it, checks it against that\nuser's permissions, optionally has an AI model score its risk, and then holds it for\nwhoever the datasource's review plan names as an approver. Once approved it runs through\nthe proxy, under masking, row limits and any row-level rules that apply.\n\nYou will need two accounts. Nobody can approve their own query — not\neven an admin. So this walkthrough uses your admin account as the approver and creates a\nsecond, ordinary account to submit from. That is not a quirk of the demo; it is the rule\nthe whole product rests on."} @@ -445,7 +445,8 @@ {"id":"98112acc14d1fbdc","path":"website/features/database-access-governance/index.html","url":"https://accessflow.io/features/database-access-governance/","anchor":"","title":"Which team is this for","section":"Features","order":0,"tokens":173,"text":"AccessFlow Docs > Features > Database access governance — every query reviewed before it runs > Which team is this for\n\nThe same checkpoint answers a different question depending on who is standing at it — a platform team removing shared credentials, an on-call engineer who needs production access at 03:00, a DBA drowning in review requests, or a privacy team that has to prove erasure happened. Platform engineering, SRE and on-call, DBAs and data platform, and privacy and data protection each get the long version. For where the credentials live and how the audit chain is built, see the security architecture."} {"id":"ffaa9605eb681cec","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"Deployment approval governance — gate every CI/CD release","section":"Features","order":0,"tokens":167,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release\n\nCapabilities\n\nAccessFlow puts a CI/CD release request through the same machinery as a database query —\nAI analysis, routing policies, named approvers, an audit trail — and gives your CI job a\ngate to block on that answers not releasable whenever it is unsure. Shipping to\nproduction is an access decision, and most pipelines record it as a protected-branch rule\nor a chat message, which is neither reviewable nor auditable after the fact.\n\nGet AccessFlow\n\nDeployment governance reference"} {"id":"ad46f113fc11f37c","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"Pipelines, environments and who may trigger them","section":"Features","order":0,"tokens":444,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release > Pipelines, environments and who may trigger them\n\nA 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.\n\nWho 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.\n\nDeleting a pipeline removes its configuration but not its history — past requests, decisions and rollback reviews survive as evidence."} -{"id":"afd9d76e668e0687","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"The gate is fail-closed by construction","section":"Features","order":0,"tokens":605,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release > The gate is fail-closed by construction\n\nA 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.\n\nFrom 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.\n\nThe job then polls one endpoint and branches on one field. A release is releasable only when all of these hold: the request is exactly approved, no freeze window is active, any scheduled time has already passed, and — where the environment requires a change ticket — that ticket has been approved in ServiceNow or Jira. 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.\n\n/deployments/ — 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."} +{"id":"afd9d76e668e0687","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"The gate is fail-closed by construction","section":"Features","order":0,"tokens":739,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release > The gate is fail-closed by construction (part 1 of 2)\n\nA 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.\n\nFrom 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.\n\nThe job then polls one endpoint and branches on one field. A release is releasable only when all of these hold: the request is exactly approved, no freeze window is active, any scheduled time has already passed, and — where the environment requires a change ticket — that ticket has been approved in ServiceNow or Jira. 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.\n\nThe change ticket is opened for you. Give a ServiceNow or Jira channel the deployment submitted trigger, then turn on Change ticket for an environment: each deployment to it gets its own ticket as soon as it enters review or is approved automatically. Emergency break-glass deploys skip the ticket, and are recorded as having skipped it.\n\nFor AccessFlow to see the verdict, add a rule in ServiceNow or Jira that reports status changes back to it. You then choose whether the ticket's answer also decides the deployment, or only unlocks the release once an AccessFlow reviewer has approved it too. Without that rule, a release to that environment never unlocks."} +{"id":"19fb519153cd5e34","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"The gate is fail-closed by construction","section":"Features","order":1,"tokens":180,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release > The gate is fail-closed by construction (part 2 of 2)\n\nTwo limits. AccessFlow never closes the ticket for you. And it records the name of whoever approved the ticket, but cannot match it to an AccessFlow user — so if the person who asked for the deployment must not approve their own change, let only AccessFlow reviewers decide.\n\n/deployments/ — 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."} {"id":"4c55f35e72fd481d","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"Freeze windows, and the emergency exit","section":"Features","order":0,"tokens":392,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release > Freeze windows, and the emergency exit\n\nA 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.\n\nWindows 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.\n\nBreak-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 →"} {"id":"e0f5f6f12280651d","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"What happened after the deploy","section":"Features","order":0,"tokens":225,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release > What happened after the deploy\n\nApproval 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.\n\nA 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."} {"id":"48d57a316eca9b72","path":"website/features/deployment-governance/index.html","url":"https://accessflow.io/features/deployment-governance/","anchor":"","title":"Which version is where","section":"Features","order":0,"tokens":385,"text":"AccessFlow Docs > Features > Deployment approval governance — gate every CI/CD release > Which version is where\n\nA 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.\n\nEach 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.\n\nEnvironments 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."} @@ -461,7 +462,7 @@ {"id":"50137ac8ff946d0b","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"API access governance","section":"Features","order":0,"tokens":100,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > API access governance\n\nThe same pipeline, pointed at outbound REST, SOAP, GraphQL and gRPC calls instead of SQL — because the shared credentials and the missing audit trail are the same problem in a different protocol."} {"id":"70aff9f0593f2d58","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"Governed outbound API calls","section":"Features","order":0,"tokens":230,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > Governed outbound API calls\n\nGovern outbound API calls with the machinery that governs queries. Register a REST, SOAP, GraphQL, or gRPC connector with its base URL, default headers, and an auth method — API key, bearer, basic, OAuth2, custom header, or mTLS — every secret encrypted and never returned. Upload a schema and AccessFlow parses a normalized operation catalog with read/write classification, then shares governed connectivity through per-user permissions. Calls then flow through the same AI scoring, routing, approval, and audit path. API access governance in depth →\n\nREST · SOAP · GraphQL · gRPC · OpenAPI / WSDL / SDL / proto / Postman"} {"id":"07340b1bece0cfcc","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"Deployment approval governance","section":"Features","order":0,"tokens":103,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > Deployment approval governance\n\nA release is an access decision too. The same reviewers, routing policies and audit trail gate a production deploy, behind a gate your CI job blocks on until the release is actually approved."} -{"id":"edc69ad0708d740e","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"Gated CI/CD releases","section":"Features","order":0,"tokens":258,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > Gated CI/CD releases\n\nGate CI/CD deployments behind the same review machinery as a query. Define a pipeline with ordered environments — each with its own required approvals, review plan, and break-glass opt-in — and grant trigger rights per user or group. A CI job opens a request with an API key, the AI analyzer scores it, routing policies set the bar, and reviewers decide in the UI, where nobody can approve their own deployment. The job blocks on a fail-closed gate, and a version matrix shows what each environment ended up running. Deployment approval governance in depth →\n\nGitHub Actions · GitLab CI · Azure Pipelines · Jenkins · CircleCI · Bitbucket · freeze windows · break-glass · rollback tracking · version matrix"} +{"id":"edc69ad0708d740e","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"Gated CI/CD releases","section":"Features","order":0,"tokens":285,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > Gated CI/CD releases\n\nGate CI/CD deployments behind the same review machinery as a query. Define a pipeline with ordered environments — each with its own required approvals, review plan, break-glass opt-in, and an optional ServiceNow or Jira change ticket that must be approved before release — and grant trigger rights per user or group. A CI job opens a request with an API key, the AI analyzer scores it, routing policies set the bar, and reviewers decide in the UI, where nobody can approve their own deployment. The job blocks on a fail-closed gate, and a version matrix shows what each environment ended up running. Deployment approval governance in depth →\n\nGitHub Actions · GitLab CI · Azure Pipelines · Jenkins · CircleCI · Bitbucket · freeze windows · change tickets · break-glass · rollback tracking · version matrix"} {"id":"0312ad5b5c923e43","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"Platform, auth & operations","section":"Features","order":0,"tokens":107,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > Platform, auth & operations\n\nThe parts that hold the rest up: who may sign in, what gets recorded, what you see when you land, and how the whole thing is deployed and declared as code. These have no deep-dive page — the operator docs carry them."} {"id":"343f2e803d5fb548","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"Workforce-ready auth","section":"Features","order":0,"tokens":301,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > Workforce-ready auth\n\nSign in with the accounts your company already runs — SAML 2.0 SSO and OAuth 2.0 / OIDC, with templates for Google, GitHub, Microsoft and GitLab and a generic provider for any other IdP. Roles and group memberships sync on every login, with optional TOTP two-factor. SCIM 2.0 provisioning hands the whole lifecycle to the IdP: a leaver's sessions and standing access grants are revoked the moment they are offboarded. Pipelines and AI agents get service accounts of their own instead of borrowing a person's key: they can never sign in, carry only the role and tools you give them, are rate-limited on their own, and can say which person they act for — without that person being able to approve the result. How authentication is wired →\n\nJWT · SAML · OAuth · SCIM provisioning · IdP group sync · TOTP · Service accounts · Multi-tenant orgs · Per-org quotas"} {"id":"c4c7cd7a789ec7d4","path":"website/features/index.html","url":"https://accessflow.io/features/","anchor":"","title":"Tamper-evident audit & compliance reports","section":"Features","order":0,"tokens":200,"text":"AccessFlow Docs > Features > Database access control, feature by feature — the middle ground between blanket access and a DBA ticket queue > Tamper-evident audit & compliance reports\n\nEvery decision lands in an append-only, cryptographically chained log that cannot be quietly edited after the fact — searchable, exportable, and holding no row data. Pre-built compliance reports cover classified-data access and the DDL/DELETE trail with approver names, export as digitally signed PDF or CSV that verify offline, and are surfaced to a dedicated read-only Auditor role. The chain, the exports and the SIEM sinks →\n\nHMAC-SHA256 chain · signed exports · SIEM streaming · WORM archival"} @@ -477,7 +478,7 @@ {"id":"5b0744d6f38b0bdb","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"How approval works — from submission to execution, observable at every step","section":"Overview","order":0,"tokens":117,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > How approval works — from submission to execution, observable at every step\n\nEvery request follows the same predictable path — submitted, AI-analyzed, approved, executed, audited — whether it is a SQL query, an outbound API call, a grouped request, or a CI/CD deployment waiting on its gate.\n\n01"} {"id":"b039c82e4c21a73d","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"Submit a request","section":"Overview","order":0,"tokens":80,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > Submit a request\n\nSQL written in the built-in editor, an outbound API call, or a deployment triggered from CI — the proxy parses and classifies every request the same way.\n\n02"} {"id":"16cbb8f98983011b","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"AI reviews it","section":"Overview","order":0,"tokens":78,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > AI reviews it\n\nRisk score, anti-pattern flags, missing indexes — API calls and deployment plans get the same scoring. The status moves PENDING_AI → PENDING_REVIEW.\n\n03"} -{"id":"f8caf82270505fa6","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"Humans approve","section":"Overview","order":0,"tokens":106,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > Humans approve\n\nReviewers get paged by Email, Slack, Discord, Telegram, Microsoft Teams, PagerDuty, or webhook — and ServiceNow / Jira tickets open automatically with bi-directional status sync. Multi-stage approval chains run as a state machine.\n\n04"} +{"id":"f8caf82270505fa6","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"Humans approve","section":"Overview","order":0,"tokens":106,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > Humans approve\n\nReviewers get paged by Email, Slack, Discord, Telegram, Microsoft Teams, PagerDuty, or webhook — and ServiceNow / Jira tickets, deployment change tickets included, sync status back. Multi-stage approval chains are a state machine.\n\n04"} {"id":"aac5e11ff96b84f8","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"Execute or release","section":"Overview","order":0,"tokens":79,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > Execute or release\n\nQueries and API calls execute through the proxy's own connections to each datasource; an approved deployment flips its fail-closed CI gate to releasable.\n\n05"} {"id":"ed5ae62df0830b5c","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"Eighteen engines, one governed checkpoint","section":"Overview","order":0,"tokens":286,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > Eighteen engines, one governed checkpoint\n\nPostgreSQL to Snowflake to MongoDB — find your engine and start governing. PostgreSQL is already there; for the rest, Install fetches the driver and SHA-256-verifies it before a single query runs. Using something else? Upload any JDBC driver. All database connectors →\n\nSQL\n\nPostgreSQL\nBuilt in\n\nMySQL\n1-click install\n\nMariaDB\n1-click install\n\nOracle\n1-click install\n\nSQL Server\n1-click install\n\nClickHouse\n1-click install\n\nCustom driver\nUpload any driver\n\nNoSQL\n\nMongoDB\n1-click install\n\nCouchbase\n1-click install\n\nRedis\n1-click install\n\nCassandra\n1-click install\n\nScyllaDB\n1-click install\n\nElasticsearch\n1-click install\n\nOpenSearch\n1-click install\n\nDynamoDB\n1-click install\n\nNeo4j\n1-click install\n\nSnowflake\n1-click install\n\nBigQuery\n1-click install\n\nDatabricks\n1-click install\n\nQuick start"} {"id":"a6ffbe8750b9d52f","path":"website/index.html","url":"https://accessflow.io/","anchor":"","title":"Install AccessFlow — governing queries, API calls and deployments in under five minutes","section":"Overview","order":0,"tokens":308,"text":"AccessFlow Docs > Overview > Governed database, API & deployment access, built for engineers > Install AccessFlow — governing queries, API calls and deployments in under five minutes\n\nPick your platform. The backend initializes its schema on first boot, and the in-app setup wizard creates the first admin. Full install guide →\n\nshell\n\n# zero-config demo — then open http://localhost:5173\ngit clone https://github.com/bablsoft/accessflow.git && cd accessflow\ndocker compose up -d\n\nshell\n\n# keys and the Postgres password are auto-generated on first install\nhelm repo add accessflow https://bablsoft.github.io/accessflow\nhelm install accessflow accessflow/accessflow -n accessflow --create-namespace\n\nshell\n\n# infra, then backend (Java 25) and frontend (Node 24) — open http://localhost:5173\ndocker compose -f backend/docker-compose-dev.yml up -d\nmvn -f backend/pom.xml spring-boot:run\nnpm --prefix frontend install && npm --prefix frontend run dev\n\nUse cases"} diff --git a/help-corpus/manifest.json b/help-corpus/manifest.json index b001cb106..b82ecbf91 100644 --- a/help-corpus/manifest.json +++ b/help-corpus/manifest.json @@ -1,10 +1,10 @@ { "schemaVersion": 1, - "corpusVersion": "f0e3b7ec8b69", - "generatedAt": "2026-10-22T11:54:39.535Z", - "sourceCommit": "b007a2af18ea170974e18beb92f471ca3d34ebdb", - "chunkCount": 608, - "sha256": "f0e3b7ec8b69d6a3a56fec8449ab73da08da9341f6b2596e87a551d8ab498288", + "corpusVersion": "d309bf469cb0", + "generatedAt": "2026-10-23T08:12:16.769Z", + "sourceCommit": "7bc919e2798ca1f2ee0916526b29cf5af8e4b72b", + "chunkCount": 609, + "sha256": "d309bf469cb00cb7a56bad3a1d23b853338c6f95e09d36306a422a7e3549b31b", "quickReferenceSha256": "2eaa3d1f2e024c2049bf0f8a699c2208a88c1e20ad06e9503257fdcf6fea0746", "sources": [ { @@ -213,7 +213,7 @@ "url": "https://accessflow.io/docs/configuration/notifications/", "section": "Reference", "chunks": 6, - "sha256": "7b4a6bed919ad2ff7eea30b90f59edc571c92bacbc39abc16860920a4067f08c" + "sha256": "432112bf26997b1633dc4a35dca1efd2597666f7655708463dc652d96b804117" }, { "path": "website/docs/configuration/review-workflows/index.html", @@ -221,7 +221,7 @@ "url": "https://accessflow.io/docs/configuration/review-workflows/", "section": "Reference", "chunks": 26, - "sha256": "22c91b3d555267d7ae5b5a6b740a277e17c1f29fcf3b6e8a9110c7a1e2e6f6e9" + "sha256": "1930c6b2999267c6e9f121ad23c4a6d6592ed0311b95ead13743622376d6a040" }, { "path": "website/docs/configuration/users-roles/index.html", @@ -261,7 +261,7 @@ "url": "https://accessflow.io/docs/guides/deployment-approval/", "section": "Guides", "chunks": 12, - "sha256": "8d34f57ff26af782149c896343f7170dce952598e8b9e008593334804edea33a" + "sha256": "9151d8bfb759c7e6b29cea588d194c4232341009adf1a00554b6524d87d49ddf" }, { "path": "website/docs/guides/first-query/index.html", @@ -380,8 +380,8 @@ "title": "Deployment approval governance — gate every CI/CD release", "url": "https://accessflow.io/features/deployment-governance/", "section": "Features", - "chunks": 8, - "sha256": "4f21e72c9859b9677e2b4f3492fad889c0d0976ba2ab0f6fb298415e91a25cf1" + "chunks": 9, + "sha256": "fb9445946e3dac83f2177dca35dd5a679484af921e9c202a0212433d6f23b5ba" }, { "path": "website/features/index.html", @@ -389,7 +389,7 @@ "url": "https://accessflow.io/features/", "section": "Features", "chunks": 17, - "sha256": "973cfa594a72d8ea526128afb5f9c3b623b262582f9f3d0af389f020dc248745" + "sha256": "bde2db396556e4c100354352c2936dfe91d73a93b43099c55c35dc0619a6dfae" }, { "path": "website/index.html", @@ -397,7 +397,7 @@ "url": "https://accessflow.io/", "section": "Overview", "chunks": 19, - "sha256": "47f4c131679fca979388d540b8de729d4922b15f0948557f0ee2fca30dd6ec91" + "sha256": "61c04853c135f455f24c8abfb01ac1cf7c305e55f3ecb1d2893fcf85bc6bf715" }, { "path": "website/security/index.html", diff --git a/website/README.md b/website/README.md index 97eec1c22..9cdf27def 100644 --- a/website/README.md +++ b/website/README.md @@ -101,7 +101,7 @@ the right. | [`docs/16-iac.md`](../docs/16-iac.md), [`terraform-provider/`](../terraform-provider/), [`.github/actions/`](../.github/actions/), [`ci-templates/`](../ci-templates/), [`docs/09-deployment.md`](../docs/09-deployment.md) `ACCESSFLOW_BOOTSTRAP_SERVICE_ACCOUNTS_*` (AF-452) | "Infrastructure as Code" hub card (`/features/#platform`) + `/roadmap/`'s available-now **Automation & IaC** group — the homepage `#docs` grid card was deleted, not moved, by [#789](https://github.com/bablsoft/accessflow/issues/789) + the "Infrastructure as Code (Terraform / OpenTofu & CI Actions)" section (`#iac`, incl. `#iac-service-account` — admin-screen issuance vs declared-in-bootstrap, the env vars, the `ADMIN` role default and the declared-key re-import rule — and the HCL example) in [`docs/iac/index.html`](docs/iac/index.html) and the further-reading link on the [`/docs/` hub](docs/index.html) | | [`docs/17-api-governance.md`](../docs/17-api-governance.md), [`docs/03-data-model.md`](../docs/03-data-model.md) `api_connectors` / `api_schemas` / `api_connector_user_permissions`, [`docs/04-api-spec.md`](../docs/04-api-spec.md) "API Access Governance", [`docs/05-backend.md`](../docs/05-backend.md) "API Access Governance (apigov module)" (AF-500) | "Governed outbound API calls" hub card (`/features/#api`) — renamed from "API Access Governance" by [#787](https://github.com/bablsoft/accessflow/issues/787), the phrase now being the `/features/api-access-governance/` spoke's own head term — plus that spoke and the `/use-cases/#api` persona. The homepage `#docs` grid card was deleted, not moved | | [`docs/17-api-governance.md`](../docs/17-api-governance.md) "Dynamic variables" (AF-613), [`docs/03-data-model.md`](../docs/03-data-model.md) `api_connector_variables` / `api_requests.variable_overrides` / `can_override_variables`, [`docs/04-api-spec.md`](../docs/04-api-spec.md) "Connector variables", [`docs/05-backend.md`](../docs/05-backend.md) "Dynamic variables", [`docs/07-security.md`](../docs/07-security.md) dynamic-variable secret bullets, [`docs/09-deployment.md`](../docs/09-deployment.md) `ACCESSFLOW_APIGOV_MAX_VARIABLE_VALUE_BYTES` | the "Values computed per call" block at `/features/api-access-governance/#masking` — the only marketing copy, and note it sits under that section, not `#connectors`; the "Governed outbound API calls" hub card stops at the auth methods + the "Dynamic variables (request signing)" and "Per-request overrides" paragraphs under "API connectors" (`#cfg-api-connectors`) in [`docs/configuration/connectors/index.html`](docs/configuration/connectors/index.html) | -| [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md), [`docs/03-data-model.md`](../docs/03-data-model.md) `deployment_pipelines` / `deployment_environments` / `deployment_freeze_windows` / `deployment_requests`, [`docs/04-api-spec.md`](../docs/04-api-spec.md) "Deployment Governance", [`docs/05-backend.md`](../docs/05-backend.md) "Deployment Governance (deploygov module)", [`docs/07-security.md`](../docs/07-security.md) "Deployment governance security" (epic AF-682) | "Gated CI/CD releases" hub card (`/features/#deployment`) — renamed from "Deployment approval governance" by [#787](https://github.com/bablsoft/accessflow/issues/787), which moved that phrase to the `/features/deployment-governance/` spoke as its head term — plus that spoke, `/`'s "Deployments" pillar card and the `/use-cases/#deploy` persona; the homepage `#docs` grid card was deleted, not moved, **"Deployment pipelines"** section (`/docs/configuration/review-workflows/#cfg-deployment-pipelines`), incl. the environment-labels and version-matrix paragraphs derived from [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md) §9 and [`docs/06-frontend.md`](../docs/06-frontend.md) `DeploymentVersionsPage` (#743), and the environment **Order** / **Database** fields (#877, [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md) §1) described in step 4 of the [deployment-approval guide](docs/guides/deployment-approval/index.html) + matching WebP under [`images/docs/`](images/docs/) (`deployments-list`, `deployment-detail`, `deployment-reviews-queue`, `deployment-rollback-reviews`, `deployment-pipelines-list`, `deployment-pipeline-environments`, `deployment-pipeline-freeze-windows`, `deployment-pipeline-ci`, light + dark) | +| [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md), [`docs/03-data-model.md`](../docs/03-data-model.md) `deployment_pipelines` / `deployment_environments` / `deployment_freeze_windows` / `deployment_requests`, [`docs/04-api-spec.md`](../docs/04-api-spec.md) "Deployment Governance", [`docs/05-backend.md`](../docs/05-backend.md) "Deployment Governance (deploygov module)", [`docs/07-security.md`](../docs/07-security.md) "Deployment governance security" (epic AF-682) | "Gated CI/CD releases" hub card (`/features/#deployment`) — renamed from "Deployment approval governance" by [#787](https://github.com/bablsoft/accessflow/issues/787), which moved that phrase to the `/features/deployment-governance/` spoke as its head term — plus that spoke, `/`'s "Deployments" pillar card and the `/use-cases/#deploy` persona; the homepage `#docs` grid card was deleted, not moved, **"Deployment pipelines"** section (`/docs/configuration/review-workflows/#cfg-deployment-pipelines`), incl. the environment-labels and version-matrix paragraphs derived from [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md) §10 and [`docs/06-frontend.md`](../docs/06-frontend.md) `DeploymentVersionsPage` (#743), and the environment **Order** / **Database** fields (#877, [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md) §1) described in step 4 of the [deployment-approval guide](docs/guides/deployment-approval/index.html) + matching WebP under [`images/docs/`](images/docs/) (`deployments-list`, `deployment-detail`, `deployment-reviews-queue`, `deployment-rollback-reviews`, `deployment-pipelines-list`, `deployment-pipeline-environments`, `deployment-pipeline-freeze-windows`, `deployment-pipeline-ci`, light + dark) | | [`docs/13-mcp.md`](../docs/13-mcp.md) (§1 API keys and service accounts, §3 the twelve tools, §4 limits, errors, the #872 tool allow-list and the unfiltered `tools/list`), [`docs/07-security.md`](../docs/07-security.md) "API-key authentication", [`docs/05-backend.md`](../docs/05-backend.md) "MCP server (mcp module)", [`README.md`](../README.md) MCP bullet, [`docs/06-frontend.md`](../docs/06-frontend.md) "Service accounts admin pages (#875)" | **"AI agents"** topic page ([`ai-agents/index.html`](ai-agents/index.html)) — `#how` (the five-step flow), `#guarantees` (the qa-grid; every item names its enforcement point, and the tool allow-list is the one that runs in the tool wrapper rather than the service layer), `#setup` (Claude Desktop / Claude Code snippets). Says "twelve tools" and "sees twelve tools" deliberately: the allow-list narrows what a service account may *call*, never what `tools/list` advertises | | [`CLAUDE.md`](../CLAUDE.md) "Project at a Glance", [`docs/02-architecture.md`](../docs/02-architecture.md), [`docs/04-api-spec.md`](../docs/04-api-spec.md) (API-key auth, `/queries` lifecycle), [`docs/13-mcp.md`](../docs/13-mcp.md), [`docs/16-iac.md`](../docs/16-iac.md), [`docs/12-roadmap.md`](../docs/12-roadmap.md) (the wire-protocol gateway is **roadmap**, not product), [`connectors/*/connector.json`](../connectors/) `name` (the help-corpus generator fails when a connector is missing from the engine list) | **"Integrations & boundaries"** chapter ([`docs/integrations/index.html`](docs/integrations/index.html)) — `#fit`, `#ways-in`, `#not-a-driver` (incl. the ODBC / ADO.NET Q&A), `#supported`, `#deployment-shape`, `#data`, `#governs`. Written for the in-app help agent as much as for visitors: it is the one page that states what AccessFlow is **not** (no ODBC/JDBC/ADO.NET driver, no database wire protocol, no SaaS) and marks its engine / sign-in / AI-provider lists as **complete**, which is the wording the help prompt keys on to answer "not supported" instead of "I don't know". Never say "connect to AccessFlow" on this page without saying how | | [`docs/16-iac.md`](../docs/16-iac.md) "Deployment gate (AF-694)", [`.github/actions/deployment-gate`](../.github/actions/deployment-gate) + [`deployment-outcome`](../.github/actions/deployment-outcome), [`ci-templates/gitlab/accessflow-deployment.gitlab-ci.yml`](../ci-templates/gitlab/accessflow-deployment.gitlab-ci.yml), [`ci-templates/azure/`](../ci-templates/azure) | **"Deployment gate in CI"** section (`/docs/iac/#iac-deployment-gate`) | @@ -122,7 +122,7 @@ the right. | [`CLAUDE.md`](../CLAUDE.md) *Project at a Glance*, [`docs/05-backend.md`](../docs/05-backend.md), [`docs/07-security.md`](../docs/07-security.md) — the sources the twelve capability cards draw on | **`/features/` hub** ([`features/index.html`](features/index.html)) — the "what can it actually do?" answer: all twelve capabilities at ~90 words each, grouped into `#database`, `#api`, `#deployment` and `#platform`, with each card linking to its deep dive. Since [#789](https://github.com/bablsoft/accessflow/issues/789) cut the homepage's twelve `.feat` cards to three pillar cards, the hub cards (and their `.tag` chip lines) are the canonical capability-by-capability copy — a capability change lands here first, and on `/` only if a pillar card states the changed claim. The sidebar is #782's deliberate replacement for a JS nav dropdown: unlike a dropdown, it is rankable. The head term **database access control** lives here and nowhere else — [#787](https://github.com/bablsoft/accessflow/issues/787) lifted it off `/`. `/` links the hub and — since [#789](https://github.com/bablsoft/accessflow/issues/789) rebuilt its `#features` section as three pillar cards — each spoke directly, with exact-match anchor text | | [`docs/05-backend.md`](../docs/05-backend.md) (proxy pipeline, AI analyzer, routing policies AF-379, JIT access, break-glass AF-385, masking AF-381, row security, discovery AF-623, cost estimate AF-624, request groups AF-501, lifecycle AF-499), [`docs/07-security.md`](../docs/07-security.md), [`docs/08-notifications.md`](../docs/08-notifications.md) (delegation, escalation, ticketing) | **`/features/database-access-governance/` spoke** ([`features/database-access-governance/index.html`](features/database-access-governance/index.html)) — the mechanism the hub summarises, following one query end to end: `#proxy`, `#ai`, `#review`, `#grouping`, `#lifecycle`, plus a closing `#teams` band that hands off to the personas. Primary keyword **database access governance**, which is why `/`'s `#architecture` and `#use-cases` h2s no longer carry that phrase ([#787](https://github.com/bablsoft/accessflow/issues/787)). Keep the honest caveats: approval likelihood is advisory only, and a grouped request has no distributed rollback | | [`docs/17-api-governance.md`](../docs/17-api-governance.md) (connectors + OAuth2 token sourcing §1, schema ingestion §2, the governed-call pipeline §4, masking & classification §5, dynamic variables §6) | **`/features/api-access-governance/` spoke** ([`features/api-access-governance/index.html`](features/api-access-governance/index.html)) — `#connectors`, `#catalog`, `#pipeline`, `#masking`. Primary keyword **API access governance**. Its `#pipeline` section **links** [`use-cases/index.html`](use-cases/index.html) `#api` rather than restating the persona story — that block stays the canonical copy, which is the arrangement [#788](https://github.com/bablsoft/accessflow/issues/788) asked for | -| [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md) (pipelines & grants §1, the CI trigger §2, review and break-glass §4, the gate §5, freeze windows §6, outcome reporting §7, the version matrix and drift §9), [`docs/16-iac.md`](../docs/16-iac.md) + [`ci-templates/`](../ci-templates/) for the CI wrappers | **`/features/deployment-governance/` spoke** ([`features/deployment-governance/index.html`](features/deployment-governance/index.html)) — `#pipelines`, `#gate`, `#freeze`, `#outcome`, `#versions`, `#ci`. Primary keyword **deployment approval governance**. Two claims are easy to get wrong: triggering is a per-pipeline `can_trigger` **grant**, not the `DEPLOYMENT_PIPELINE_MANAGE` permission, and the CI wrappers address a pipeline by id because a trigger-only key may not resolve names | +| [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md) (pipelines & grants §1, the CI trigger §2, review and break-glass §4, the gate §5, freeze windows §6, change tickets §7, outcome reporting §8, the version matrix and drift §10), [`docs/16-iac.md`](../docs/16-iac.md) + [`ci-templates/`](../ci-templates/) for the CI wrappers | **`/features/deployment-governance/` spoke** ([`features/deployment-governance/index.html`](features/deployment-governance/index.html)) — `#pipelines`, `#gate`, `#freeze`, `#outcome`, `#versions`, `#ci`. Primary keyword **deployment approval governance**. Two claims are easy to get wrong: triggering is a per-pipeline `can_trigger` **grant**, not the `DEPLOYMENT_PIPELINE_MANAGE` permission, and the CI wrappers address a pipeline by id because a trigger-only key may not resolve names | | [`docs/20-schema-change-governance.md`](../docs/20-schema-change-governance.md) (authoring §1, the statement gate §2, freeze §3, promotion §6, drift §7, the web UI §8), [`docs/12-roadmap.md`](../docs/12-roadmap.md) `## v2.7` | **Schema change sets** ([#870](https://github.com/bablsoft/accessflow/issues/870), closed out by [#884](https://github.com/bablsoft/accessflow/issues/884)) — the `#schema-changes` section of [`features/deployment-governance/index.html`](features/deployment-governance/index.html), the `#cfg-schema-changes` section of [`docs/configuration/review-workflows/index.html`](docs/configuration/review-workflows/index.html) (the in-app *View docs* target of `/schema-change-sets` and `/schema-drift`, wired on both sides of the `docs.ts` ↔ `app.js` contract), the closing sentence of the homepage `#features` **Deployments** card, and the v2.7 sentence of the [`roadmap/index.html`](roadmap/index.html) `#milestones` paragraph, and two items in the roadmap's available-now grid under **Deployment governance**. Three claims are easy to overstate and must stay plain: there is **no rollback** (a failure part-way leaves the earlier statements applied), promotion needs the DDL grant **for admins too**, and drift cannot see indexes, constraints, defaults or views. Drift configuration is API-only (`PUT /api/v1/schema-drift/configs/{pipelineId}`) — the UI has *Scan now* but no settings form — so the config page says so rather than pointing at a screen that does not exist | | The app itself (real UI labels, routes and error codes, read out of [`frontend/src/locales/en.json`](../frontend/src/locales/en.json) and the pages that use them), plus [`docs/09-deployment.md`](../docs/09-deployment.md) *One-time setup*, [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md), [`docs/16-iac.md`](../docs/16-iac.md), [`docs/08-notifications.md`](../docs/08-notifications.md), [`docs/07-security.md`](../docs/07-security.md), [`docs/14-connectors.md`](../docs/14-connectors.md) and [`docs/17-api-governance.md`](../docs/17-api-governance.md) (AF-773) | The **Guides** section ([`docs/guides/`](docs/guides/)) — a hub plus ten task-oriented manuals, each anchored `guide-*`. These are **verified against code, not against the reference chapters**: where the two disagreed, AF-773 fixed the chapter. Every guide links to the chapter it operationalises, and each of those ten chapters carries a reciprocal *There is a guide for this* callout under its first `h2` — the pair moves together. Sidebar labels are deliberately verb-first ("Add a datasource") so they never collide with the noun-labelled reference chapters ("Datasources") in the shared sidebar | | [`docs/`](../docs/) chapter filenames + H1s (01–19, incl. [`docs/14-connectors.md`](../docs/14-connectors.md), the [`docs/15-engine-sdk.md`](../docs/15-engine-sdk.md) engine-author guide, the [`docs/16-iac.md`](../docs/16-iac.md) IaC guide, the [`docs/17-api-governance.md`](../docs/17-api-governance.md) API-governance guide, and the [`docs/18-deployment-governance.md`](../docs/18-deployment-governance.md) deployment-governance guide, and the [`docs/19-sql-review.md`](../docs/19-sql-review.md) SQL-review chapter, and the [`docs/22-service-accounts.md`](../docs/22-service-accounts.md) service-accounts chapter, and the [`docs/20-schema-change-governance.md`](../docs/20-schema-change-governance.md) schema-change chapter) | The further-reading links inside [`docs/index.html`](docs/index.html), and the footer "Docs" column — which [#791](https://github.com/bablsoft/accessflow/issues/791) repointed at the on-site chapters. Note the four footer labels are copied verbatim from the **docs sidebar TOC**, not from these markdown H1s and not from the chapters' own on-page `h1`s (all three differ): rename a sidebar label and all 22 footers move with it. The targets are `/docs/`, `/docs/install/`, `/docs/configuration/auth/`, `/docs/workflows/` and `/docs/iac/`, leaving exactly one engineering-reference link to `github.com/blob` — [`docs/04-api-spec.md`](../docs/04-api-spec.md), the one of the four with no on-site equivalent, labelled `REST API (GitHub)` so the off-site jump is legible and the name still matches the further-reading list. The homepage `#docs` grid of raw `github.com/blob` cards was **deleted** (not moved) by [#789](https://github.com/bablsoft/accessflow/issues/789); `/docs/` is the docs hub | diff --git a/website/docs/configuration/notifications/index.html b/website/docs/configuration/notifications/index.html index 212184d66..46813248f 100644 --- a/website/docs/configuration/notifications/index.html +++ b/website/docs/configuration/notifications/index.html @@ -64,7 +64,7 @@ "inLanguage": "en", "articleSection": "Configuration", "datePublished": "2026-08-03", - "dateModified": "2026-10-22", + "dateModified": "2026-10-23", "url": "https://accessflow.io/docs/configuration/notifications/", "mainEntityOfPage": "https://accessflow.io/docs/configuration/notifications/", "image": "https://accessflow.io/og-image.png", @@ -269,7 +269,7 @@ Documentation

Notifications.

-

Last updated

+

Last updated

@@ -312,7 +312,7 @@

Where can AccessFlow send review notifications?

  • Microsoft Teams — paste the channel's Incoming Webhook (or Power Automate webhook) URL. Messages render as an Adaptive Card with a View in AccessFlow action button.
  • PagerDuty — paste an Events API v2 integration routing key, pick a default severity (critical / error / warning / info), and choose which triggers page the channel: AI critical-risk query, review timeout, routing-policy escalation, and/or review stalled (nobody decided within the plan’s escalation window). Other events never fire it — a nudge reminder in particular never pages. AccessFlow sends a trigger event with a query-stable dedup key so repeat events fold into one incident.
  • ServiceNow — supply the instance URL plus a username / password allowed to create incidents via the Table API, optionally an assignment group and urgency (1–3), and choose which triggers open an incident: query rejected, review timeout, routing-policy escalation, and/or deployment submitted. Incidents for queries link back onto the query detail page. The deployment submitted trigger opens an incident for each deployment to an environment that requires a change ticket. That incident is the deployment’s change ticket, and it shows on the deployment’s page.
  • -
  • Jira — supply the site URL, an account email + API token, the target project key, and optionally an issue type (default Task); pick the same trigger set. Issues are created via REST v2 with an accessflow label and link back onto the query detail page.
  • +
  • Jira — supply the site URL, an account email + API token, the target project key, and optionally an issue type (default Task); pick the same trigger set. Issues are created via REST v2 with an accessflow label and link back onto the query detail page. With the deployment submitted trigger, the issue is the deployment’s change ticket: it also gets a deployment label and shows on the deployment’s page.
  • Bi-directional status sync (ServiceNow / Jira). Toggle Bi-directional status sync on the channel and set a webhook secret; then point a ServiceNow Business Rule / Jira Automation rule at POST /api/v1/integrations/servicenow/webhook/{channelId} (or …/jira/webhook/{channelId}) sending {"external_id", "status", "resolution", "actor"} signed with X-AccessFlow-Timestamp + X-AccessFlow-Signature: sha256=HMAC-SHA256(secret, "v1:{timestamp}:{body}"). Ticket status updates flow onto the query detail page, and a resolution matching the channel's approve / reject status lists decides a query still awaiting review — attributed to the ticketing system in the audit log. Deployment change tickets are matched too: AccessFlow always keeps the approve / reject verdict on its own link to the ticket, and with sync on it also decides the deployment awaiting review. The ticket's actor cannot be matched to an AccessFlow user, so self-approval of a change ticket is not blocked — keep sync off if you need that guarantee. diff --git a/website/docs/configuration/review-workflows/index.html b/website/docs/configuration/review-workflows/index.html index 718cd0193..4bd2417c0 100644 --- a/website/docs/configuration/review-workflows/index.html +++ b/website/docs/configuration/review-workflows/index.html @@ -64,7 +64,7 @@ "inLanguage": "en", "articleSection": "Configuration", "datePublished": "2026-08-03", - "dateModified": "2026-10-21", + "dateModified": "2026-10-23", "url": "https://accessflow.io/docs/configuration/review-workflows/", "mainEntityOfPage": "https://accessflow.io/docs/configuration/review-workflows/", "image": "https://accessflow.io/og-image.png", @@ -275,7 +275,7 @@ Documentation

    Review workflows.

    -

    Last updated

    +

    Last updated

    @@ -1009,7 +1009,9 @@

    Deployment pipelines

    DEPLOYMENT_PIPELINE_MANAGE permission, held by ADMIN). Each pipeline's settings page carries seven tabs. Alongside General and the CI setup snippet sit Environments (promotion - order, tags, required approvals, review-plan override, break-glass opt-in), + order, tags, required approvals, review-plan override, break-glass opt-in, and whether + releases wait for an approved + change ticket), Versions (which version each environment is running — see below), Permissions (per-user and per-group can_trigger / can_break_glass grants with an optional expiry — a user's effective grant is @@ -1047,10 +1049,10 @@

    Deployment pipelines

    The Environments tab of a deployment pipeline settings page, listing ordered environments with their tags, required approval count, review requirement, review-plan override and break-glass opt-in. -
    Pipeline settings → Environments — promotion order, tags, required approvals, review-plan override and break-glass opt-in, per environment.
    +
    Pipeline settings → Environments — promotion order, tags, required approvals, review-plan override, break-glass opt-in and change-ticket requirement, per environment.

    Environment tags. Each environment can carry short free-form diff --git a/website/docs/guides/deployment-approval/index.html b/website/docs/guides/deployment-approval/index.html index 1556f857a..be556054b 100644 --- a/website/docs/guides/deployment-approval/index.html +++ b/website/docs/guides/deployment-approval/index.html @@ -64,7 +64,7 @@ "inLanguage": "en", "articleSection": "Guides", "datePublished": "2026-09-01", - "dateModified": "2026-10-22", + "dateModified": "2026-10-23", "url": "https://accessflow.io/docs/guides/deployment-approval/", "mainEntityOfPage": "https://accessflow.io/docs/guides/deployment-approval/", "image": "https://accessflow.io/og-image.png", @@ -282,7 +282,7 @@ Guides

    Gate a deployment pipeline.

    -

    Last updated

    +

    Last updated

    @@ -653,7 +653,7 @@

    If the job fails instead

    release waits until AccessFlow hears that the ticket was approved — which only happens through the ticketing system's callback, so set up bi-directional status sync on the - channel. A rejected ticket blocks the release for good; submit a new deployment. + channel. A rejected ticket blocks the release while it stays rejected, and with sync on it also rejects a deployment still awaiting review; then submit a new deployment. none linked yet means no ticket was opened — check that a ServiceNow or Jira channel carries the deployment trigger, or the release will never go out. The deployment's page in AccessFlow shows the same thing: a Change ticket diff --git a/website/features/deployment-governance/index.html b/website/features/deployment-governance/index.html index e161ca663..8cc277aed 100644 --- a/website/features/deployment-governance/index.html +++ b/website/features/deployment-governance/index.html @@ -61,7 +61,7 @@ "articleSection": "Features", "inLanguage": "en", "datePublished": "2026-08-30", - "dateModified": "2026-10-22", + "dateModified": "2026-10-23", "url": "https://accessflow.io/features/deployment-governance/", "mainEntityOfPage": "https://accessflow.io/features/deployment-governance/", "image": "https://accessflow.io/og-image.png", @@ -258,6 +258,9 @@

    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 of these hold: the request is exactly approved, no freeze window is active, any scheduled time has already passed, and — where the environment requires a change ticket — that ticket has been approved in ServiceNow or Jira. 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.

    +

    The change ticket is opened for you. Give a ServiceNow or Jira channel the deployment submitted trigger, then turn on Change ticket for an environment: each deployment to it gets its own ticket as soon as it enters review or is approved automatically. Emergency break-glass deploys skip the ticket, and are recorded as having skipped it.

    +

    For AccessFlow to see the verdict, add a rule in ServiceNow or Jira that reports status changes back to it. You then choose whether the ticket's answer also decides the deployment, or only unlocks the release once an AccessFlow reviewer has approved it too. Without that rule, a release to that environment never unlocks.

    +

    Two limits. AccessFlow never closes the ticket for you. And it records the name of whoever approved the ticket, but cannot match it to an AccessFlow user — so if the person who asked for the deployment must not approve their own change, let only AccessFlow reviewers decide.

    diff --git a/website/features/index.html b/website/features/index.html index 2fd859472..3bee1cc1e 100644 --- a/website/features/index.html +++ b/website/features/index.html @@ -60,7 +60,7 @@ "description": "Database access control, feature by feature — twelve capabilities grouped into database, API and deployment governance, plus the platform underneath.", "inLanguage": "en", "datePublished": "2026-08-30", - "dateModified": "2026-10-21", + "dateModified": "2026-10-23", "url": "https://accessflow.io/features/", "image": "https://accessflow.io/og-image.png", "isPartOf": { "@id": "https://accessflow.io/#website" }, @@ -355,8 +355,8 @@

    Deployment approval governance

    Gated CI/CD releases

    -

    Gate CI/CD deployments behind the same review machinery as a query. Define a pipeline with ordered environments — each with its own required approvals, review plan, and break-glass opt-in — and grant trigger rights per user or group. A CI job opens a request with an API key, the AI analyzer scores it, routing policies set the bar, and reviewers decide in the UI, where nobody can approve their own deployment. The job blocks on a fail-closed gate, and a version matrix shows what each environment ended up running. Deployment approval governance in depth →

    - GitHub Actions · GitLab CI · Azure Pipelines · Jenkins · CircleCI · Bitbucket · freeze windows · break-glass · rollback tracking · version matrix +

    Gate CI/CD deployments behind the same review machinery as a query. Define a pipeline with ordered environments — each with its own required approvals, review plan, break-glass opt-in, and an optional ServiceNow or Jira change ticket that must be approved before release — and grant trigger rights per user or group. A CI job opens a request with an API key, the AI analyzer scores it, routing policies set the bar, and reviewers decide in the UI, where nobody can approve their own deployment. The job blocks on a fail-closed gate, and a version matrix shows what each environment ended up running. Deployment approval governance in depth →

    + GitHub Actions · GitLab CI · Azure Pipelines · Jenkins · CircleCI · Bitbucket · freeze windows · change tickets · break-glass · rollback tracking · version matrix diff --git a/website/images/docs/deployment-detail-dark.webp b/website/images/docs/deployment-detail-dark.webp index 5c6cae2ae..235fe99d8 100644 Binary files a/website/images/docs/deployment-detail-dark.webp and b/website/images/docs/deployment-detail-dark.webp differ diff --git a/website/images/docs/deployment-detail-light.webp b/website/images/docs/deployment-detail-light.webp index 5b7be15d6..1d1a6c78d 100644 Binary files a/website/images/docs/deployment-detail-light.webp and b/website/images/docs/deployment-detail-light.webp differ diff --git a/website/images/docs/deployment-pipeline-environments-dark.webp b/website/images/docs/deployment-pipeline-environments-dark.webp index 8d8660ca1..ee643d96d 100644 Binary files a/website/images/docs/deployment-pipeline-environments-dark.webp and b/website/images/docs/deployment-pipeline-environments-dark.webp differ diff --git a/website/images/docs/deployment-pipeline-environments-light.webp b/website/images/docs/deployment-pipeline-environments-light.webp index 0bc10009f..8420b92fa 100644 Binary files a/website/images/docs/deployment-pipeline-environments-light.webp and b/website/images/docs/deployment-pipeline-environments-light.webp differ diff --git a/website/images/docs/deployments-list-dark.webp b/website/images/docs/deployments-list-dark.webp index d5c6b30e8..7ac90c095 100644 Binary files a/website/images/docs/deployments-list-dark.webp and b/website/images/docs/deployments-list-dark.webp differ diff --git a/website/images/docs/deployments-list-light.webp b/website/images/docs/deployments-list-light.webp index fe7ed224a..ef1dd5f1a 100644 Binary files a/website/images/docs/deployments-list-light.webp and b/website/images/docs/deployments-list-light.webp differ diff --git a/website/index.html b/website/index.html index 95430b722..c7851a8a6 100644 --- a/website/index.html +++ b/website/index.html @@ -68,7 +68,7 @@ "operatingSystem": "Linux, macOS, Windows (Docker)", "softwareVersion": "2.7.0", "datePublished": "2026-04-30", - "dateModified": "2026-10-01", + "dateModified": "2026-10-23", "license": "https://www.apache.org/licenses/LICENSE-2.0", "image": "https://accessflow.io/og-image.png", "screenshot": "https://accessflow.io/images/docs/dashboard-light.webp", @@ -133,7 +133,7 @@ "primaryImageOfPage": "https://accessflow.io/og-image.png", "inLanguage": "en", "datePublished": "2026-05-14", - "dateModified": "2026-10-01" + "dateModified": "2026-10-23" } ] } @@ -365,7 +365,7 @@

    AI reviews it

    03

    Humans approve

    -

    Reviewers get paged by Email, Slack, Discord, Telegram, Microsoft Teams, PagerDuty, or webhook — and ServiceNow / Jira tickets open automatically with bi-directional status sync. Multi-stage approval chains run as a state machine.

    +

    Reviewers get paged by Email, Slack, Discord, Telegram, Microsoft Teams, PagerDuty, or webhook — and ServiceNow / Jira tickets, deployment change tickets included, sync status back. Multi-stage approval chains are a state machine.