Five acceptance gates
Discovery, direction, design, development and launch each carry their own state, deadline and unambiguous acceptance history.
A self-hosted client portal for project stages, acceptance, consolidated feedback rounds, scope changes and decision history.
N3XFlow turns scattered email and chat threads into a premium Project Room: the client always sees the next action, the team controls scope, and every comment, acceptance and commercial decision stays with the right stage.
Data on your server · single-use email links · encrypted SMTP · no application telemetry
Managed hosting by N3X
N3X can deploy and maintain this application, from initial setup and SSL to updates, monitoring and backups.
Discovery, direction, design, development and launch each carry their own state, deadline and unambiguous acceptance history.
Up to three shared responses per stage instead of an endless stream of conflicting messages.
Reviewers may add items, but only the designated decision maker accepts stages and additional cost.
Defects, questions and scope changes stay distinct, while each change order includes price, timing, validity and a decision.
The worker tracks review windows and records precisely whether a stage was accepted explicitly or deemed accepted after the deadline.
Clients do not create passwords. Tokens are hashed, single-use, valid for 15 minutes and consumed only after confirmation.
The audit trail records actor, time, project, event and context for material decisions.
Install with a dedicated icon, iOS support, bottom navigation and a safe offline screen that never stores project content.
DOCKER COMPOSE
One configuration starts the portal, worker and PostgreSQL. The owner configures agency branding and SMTP in the authenticated panel, keeping secrets out of Compose.
DOCKER CONFIGURATOR
Set the domain, bind address, port and image version. The configurator prepares a ready-to-use Docker Compose file and startup commands without manually managing secrets.
The domain must point to your reverse proxy. Do not include https:// or a path.
0.0.0.0 provides LAN and proxy access. Use 127.0.0.1 when the reverse proxy runs on the same host.
The application port available to your reverse proxy. The default is 8792.
Keep latest for the newest release or pin a specific version.
name: n3xflow
x-app-environment: &app-environment
N3XFLOW_DATABASE_PASSWORD_FILE: /run/n3xflow/database_password
N3XFLOW_TRUST_PROXY_HOPS: ${N3XFLOW_TRUST_PROXY_HOPS:-0}
N3XFLOW_DEV_MAGIC_LINKS: ${N3XFLOW_DEV_MAGIC_LINKS:-0}
TZ: Europe/Warsaw
x-app-security: &app-security
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
tmpfs:
- /tmp:size=64m,mode=1777
- /app/.next/cache:size=128m,mode=0700,uid=1001,gid=1001
services:
bootstrap:
image: alpine:3.22
restart: "no"
command:
- /bin/sh
- -ec
- |
umask 077
if [ ! -s /run/n3xflow/database_password ]; then
od -An -N32 -tx1 /dev/urandom | tr -d ' \n' > /run/n3xflow/database_password
fi
if [ ! -s /run/n3xflow/settings_key ]; then
od -An -N32 -tx1 /dev/urandom | tr -d ' \n' > /run/n3xflow/settings_key
fi
chmod 0444 /run/n3xflow/database_password /run/n3xflow/settings_key
volumes:
- runtime_secrets:/run/n3xflow
networks:
- backend
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
web:
image: ghcr.io/nexitpl/n3xflow:latest
pull_policy: always
restart: unless-stopped
depends_on:
bootstrap:
condition: service_completed_successfully
postgres:
condition: service_healthy
environment: *app-environment
volumes:
- runtime_secrets:/run/n3xflow:ro
ports:
# Keep behind a firewall until the owner account exists; publish through HTTPS.
- "0.0.0.0:8792:3000"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/api/health"]
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
networks:
- frontend
- backend
<<: *app-security
worker:
image: ghcr.io/nexitpl/n3xflow:latest
pull_policy: always
restart: unless-stopped
depends_on:
bootstrap:
condition: service_completed_successfully
postgres:
condition: service_healthy
web:
condition: service_healthy
environment: *app-environment
volumes:
- runtime_secrets:/run/n3xflow:ro
command: ["node", "dist/worker.cjs"]
networks:
- backend
<<: *app-security
postgres:
image: postgres:17-alpine
restart: unless-stopped
depends_on:
bootstrap:
condition: service_completed_successfully
environment:
POSTGRES_DB: n3xflow
POSTGRES_USER: n3xflow
POSTGRES_PASSWORD_FILE: /run/n3xflow/database_password
volumes:
- postgres_data:/var/lib/postgresql/data
- runtime_secrets:/run/n3xflow:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n3xflow -d n3xflow"]
interval: 5s
timeout: 5s
retries: 12
networks:
- backend
networks:
frontend:
backend:
internal: true
volumes:
postgres_data:
runtime_secrets:
mkdir -p n3xflow
cd n3xflow
# Save the copied configuration as docker-compose.yml
docker compose up -d
docker compose ps
curl -fsS http://127.0.0.1:8792/api/health