Documentation

Install AccessFlow.

Running AccessFlow

Pick one of three modes. Docker Compose is the fastest path to a running instance. Helm is the production-recommended path on Kubernetes. The manual / from-source path is for contributors and for environments where containers aren't available.

Docker Compose

The repo root ships a zero-config demo stack — it pulls the published images from GHCR and starts Postgres + Redis + backend + frontend with insecure demo keys baked in so a fresh clone runs with one command:

shell — demo
git clone https://github.com/bablsoft/accessflow.git
cd accessflow
docker compose up -d
# open http://localhost:5173 — the in-app setup wizard creates the first admin
Demo only. The root docker-compose.yml embeds insecure JWT_PRIVATE_KEY and ENCRYPTION_KEY defaults inline so it works on a fresh clone. Do not deploy this to anything but a sandbox. For real environments, use the production-style compose below or the Helm chart.

For a production-style compose, generate real keys and supply them via .env:

.env — production
# 32-byte hex — AES-256-GCM for datasource credential encryption
ENCRYPTION_KEY=$(openssl rand -hex 32)

# RSA-2048 PEM — JWT RS256 signing key
JWT_PRIVATE_KEY="$(openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 2>/dev/null)"

DB_PASSWORD=change-me
# Password for the dedicated audit-writer role (issue #67). Provisioned by
# deploy/postgres-init/01-audit-role.sql — see "audit_log role separation".
AUDIT_DB_PASSWORD=change-me-audit
CORS_ALLOWED_ORIGIN=https://accessflow.company.com
ACCESSFLOW_PUBLIC_BASE_URL=https://accessflow.company.com

See docs/09-deployment.md → Docker Compose for the full production-style compose (including the optional ollama profile for self-hosted AI).

Structured logs for ELK / OpenSearch. Set ACCESSFLOW_LOGGING_STRUCTURED_FORMAT=logstash (or ecs / gelf) on the backend container and every log line becomes a single JSON object — `traceId` and `spanId` from the Micrometer tracing bridge are top-level fields, so correlation works out of the box. The Spring Boot ASCII banner is hidden by default; set SPRING_MAIN_BANNER_MODE=console to restore it.

Tracing & metrics. Set OTEL_EXPORTER_OTLP_ENDPOINT to your collector's full OTLP/HTTP traces URL (e.g. http://tempo:4318/v1/traces) and AccessFlow exports the proxy-pipeline spans — parse → AI analyze → pool acquire → execute — to Tempo / Jaeger / Honeycomb (sampling via ACCESSFLOW_TRACING_SAMPLING_PROBABILITY; export is off until an endpoint is set). Prometheus metrics are exposed at /actuator/prometheus (unauthenticated for in-cluster scraping — keep /actuator off the public ingress). The Helm chart ships two pre-built Grafana dashboards behind dashboards.enabled=true covering query volume, approval SLAs, AI usage/cost, rejection rates, and connection-pool stats — see examples/values-observability.yaml.

External secrets managers. Datasource credentials can live in HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault instead of the built-in AES layer: enable a provider (ACCESSFLOW_SECRETS_VAULT_ENABLED=true + ACCESSFLOW_SECRETS_VAULT_URI/_TOKEN (or AppRole / Kubernetes auth), ACCESSFLOW_SECRETS_AWS_ENABLED=true (region and credentials from the SDK default chain — env vars, IRSA, instance profile — or explicit _REGION/_ACCESS_KEY_ID/_SECRET_ACCESS_KEY), or ACCESSFLOW_SECRETS_AZURE_ENABLED=true + ACCESSFLOW_SECRETS_AZURE_VAULT_URL (workload/managed identity or an explicit client-secret credential)) and enter a secret reference in a datasource's password field instead of the raw value: vault:<mount>/<path>#<field>, aws:<name-or-arn>[#jsonField], or azure:<secret-name>. AccessFlow resolves the reference through the store at connection time (never caching the value), audits every resolve, and keeps local AES-256-GCM encryption as the default for plain passwords. Full variable reference: docs/09-deployment.md → Secrets Manager.

Kubernetes & Helm

The Helm chart ships at charts/accessflow/ and is published to https://bablsoft.github.io/accessflow. With defaults, no Secrets need to be pre-created — the chart auto-generates the encryption key, the JWT private key, and the PostgreSQL password on first install, and preserves them across helm upgrade:

shell
# 1. add the chart repo
helm repo add accessflow https://bablsoft.github.io/accessflow
helm repo update

# 2. install — chart auto-generates secrets on first run
helm install accessflow accessflow/accessflow \
  --namespace accessflow --create-namespace \
  -f values.yaml

Prefer to manage the secrets yourself (sealed-secrets, External Secrets, Vault, …)? Pre-create them and point config.encryptionKey.existingSecret, config.jwtPrivateKey.existingSecret, and postgresql.auth.existingSecret at your own resources:

shell
kubectl -n accessflow create secret generic accessflow-encryption-key \
  --from-literal=value="$(openssl rand -hex 32)"

kubectl -n accessflow create secret generic accessflow-jwt-key \
  --from-file=value=<(openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048)

# Postgres Secret needs BOTH keys: `password` (AccessFlow user) + `postgres-password` (admin)
kubectl -n accessflow create secret generic accessflow-pg-secret \
  --from-literal=password="$(openssl rand -base64 24)" \
  --from-literal=postgres-password="$(openssl rand -base64 24)"

The chart bundles Bitnami subcharts for Postgres and Redis (toggle off with postgresql.enabled=false / redis.enabled=false to point at external instances). TLS on the Ingress is off by default; set ingress.tls.enabled=true with a secretName and (optionally) a cert-manager annotation to terminate HTTPS. Full values reference: docs/09-deployment.md → Kubernetes & Helm and charts/accessflow/README.md.

Ready-made starting points live under charts/accessflow/examples/ — each file is a minimal override on top of the chart's values.yaml. They split into deployment shapes (cluster-level: replicas, ingress, secrets model) and bootstrap slices (declarative admin config). The intended pattern is one of each, plus your own site-specific overrides.

Deployment shapes:

  • values-minimal.yaml — single-replica demo over plain HTTP.
  • values-production.yaml — HA backend (HPA + PDB + pod anti-affinity), cert-manager-issued TLS, persistent JDBC driver cache.
  • values-external-services.yaml — managed Postgres + Redis (RDS / ElastiCache / …) with every secret managed outside the chart.
  • values-airgapped.yaml — air-gapped: internal registry mirror, offline JDBC drivers, manual TLS Secret.
  • values-backup.yaml — nightly pg_dump backups to a PVC with retention pruning, optional rclone upload (S3/GCS/…), and the one-shot restore Job.

Backup, restore & disaster recovery. Set backup.enabled=true to run a nightly pg_dump CronJob of the AccessFlow database onto a dedicated backups volume (kept across helm uninstall), pruned to backup.retention.keepLast dumps, with an optional rclone step shipping the directory to S3 / GCS / Azure / SFTP. To restore: scale the backend to zero, run helm upgrade --reuse-values --set restore.enabled=true --set restore.dumpFile=<name>.dump (the hook Job re-provisions the audit-writer role and replays the dump with ownership and grants intact), then boot the backend once with ACCESSFLOW_AUDIT_VERIFY_CHAIN_ON_STARTUP=true — every organization's tamper-evident audit chain is re-verified and the outcome logged per organization. Verification needs the same AUDIT_HMAC_KEY / ENCRYPTION_KEY as when the rows were written, so back those Secrets up alongside the dumps. Full runbook (including external-database failover): docs/09-deployment.md → Disaster Recovery.

Bootstrap slices (each declares organization + first admin user and is meant to layer on a deployment shape):

shell
helm install accessflow accessflow/accessflow \
  --namespace accessflow --create-namespace \
  -f charts/accessflow/examples/values-production.yaml \
  -f charts/accessflow/examples/values-bootstrap-oauth2-sso.yaml \
  -f my-site-overrides.yaml

Manual / from source

For contributors and air-gapped builds. Requires JDK 25 and Node.js 24 on the host:

shell
git clone https://github.com/bablsoft/accessflow.git
cd accessflow

# 1. infrastructure — Postgres 18 + Redis 8 + Mailcrab (dev-only compose)
docker compose -f backend/docker-compose-dev.yml up -d

# 2. backend
cd backend
./mvnw spring-boot:run

# 3. frontend (in another shell)
cd frontend
npm install
npm run dev

# open http://localhost:5173

For full coding standards, test commands, and the dev loop, see docs/11-development.md.

Beta / pre-release channel

Pre-release builds are cut for internal testing ahead of a stable release. They are published with a -beta.N / -rc.N version tag and a moving :beta Docker tag — never :latest — so a beta never disturbs production: a plain docker compose up or helm install stays on the last stable release until you opt in.

Internal testing only. Betas carry no upgrade-path guarantees toward the GA they precede. Treat their databases as disposable.

Docker Compose — drop a docker-compose.override.yml next to the demo file (Compose merges it automatically), then docker compose pull && docker compose up -d:

docker-compose.override.yml
services:
  backend:
    image: ghcr.io/bablsoft/accessflow-backend:beta    # or :1.2.0-beta.1
  frontend:
    image: ghcr.io/bablsoft/accessflow-frontend:beta   # or :1.2.0-beta.1

Helm — pre-release chart versions are hidden from default resolution, so opt in with --devel and pin the exact version:

shell
helm repo update
helm install accessflow accessflow/accessflow \
  --version 1.2.0-beta.1 --devel \
  --namespace accessflow --create-namespace

See docs/09-deployment.md → Installing a pre-release / beta build for the full consumer guide.

First-time setup

There are two ways to bring up a brand-new AccessFlow deployment.

Option A — Browser setup wizard

The default. Open the frontend (http://localhost:5173 for the demo stack, or whatever URL serves the SPA in your environment). When no organization exists yet, the app routes to /setup and walks you through:

  1. Create the organization.
  2. Create the first admin user (email + password).
  3. Optional: configure system SMTP so invitation emails work.
  4. Optional: add a first datasource and a default review plan.

After the wizard finishes, log in as the admin and finish configuring the system from the admin pages — see Configuration below.

Option B — Bootstrap via env vars (GitOps)

Set ACCESSFLOW_BOOTSTRAP_ENABLED=true and supply ACCESSFLOW_BOOTSTRAP_* properties for the organization, admin user, review plans, AI configs, datasources, SAML, OAuth2, Langfuse, notification channels, and system SMTP. On every startup the bootstrap module reconciles the declared configuration into the database — declared rows are upserted, omitted rows are left alone.

Reconciliation order. Organization → admin user → notification channels → AI configs → review plans → datasources → SAML → OAuth2 → Langfuse → SMTP. Secret fields (passwords, API keys, webhook secrets) are read from Kubernetes Secrets, never from a ConfigMap. Full property tree: docs/09-deployment.md → Bootstrap configuration.
Multi-replica safety. Bootstrap is safe under replicaCount.backend > 1: every pod races for a Redis-backed bootstrapReconcile lock (the same Redis instance that powers ShedLock and JWT refresh tokens), so exactly one replica performs the upserts per startup wave. The other replicas log an INFO line and keep serving traffic. No additional env vars to configure.
Auditability. Bootstrap upserts are recorded in audit_log with actor_id = NULL and metadata.source = "BOOTSTRAP", so operators can answer "who changed this — me, or a helm upgrade?" from a single source of truth. Restarting with unchanged env vars writes zero new rows (the reconciler caches a SHA-256 fingerprint of each declared spec in bootstrap_state and short-circuits on a match).