diff --git a/.env.example b/.env.example index 3f62ac7..5899e50 100644 --- a/.env.example +++ b/.env.example @@ -25,6 +25,10 @@ FORGECRM_JWT_SECRET= FORGECRM_ENCRYPTION_KEY= # Any random string; you paste the same value into Meta's webhook setup. META_WEBHOOK_VERIFY_TOKEN= +# Encrypts Instagram tokens and the Social section's AI keys. Any long random +# string: openssl rand -hex 32. Saving an Instagram credential is refused while +# it is empty. ⚠ Same rule as FORGECRM_ENCRYPTION_KEY: back it up, never change it. +SOCIAL_ENCRYPTION_KEY= # ─── Database ──────────────────────────────────────────────────────────────── # With the bundled compose stack, leave the host as the service name. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3265d9f..dc07412 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,29 @@ re-run and leaves `.env` and your database alone. Pin to a specific release with ## [Unreleased] +## [3.1.1] — 2026-10-26 + +### Fixed + +- **Instagram could not be connected on a fresh install.** The Social section refuses to save + Instagram credentials without `SOCIAL_ENCRYPTION_KEY`, and nothing generated it, so every new install + needed a hand edit of `.env` that no page mentioned. The installer and `generate-secrets.sh` now + generate it with the other secrets; re-run `./install.sh` on an existing install to fill it in. + An existing value is never changed. +- The installer's closing "Next steps" named `Admin Settings → WhatsApp Accounts`, a menu that no + longer exists. It now says `Settings → WhatsApp → Accounts`, and adds where to connect Instagram. +- **The installer hung forever, silently, on Alpine Linux** (and in `docker:dind`) right after + "install name". BusyBox's `lsof` ignores its arguments and reports success, so every port looked + taken and the search for a free one never ended. It now uses `netstat` there, and stops with a clear + message if a hundred ports in a row read as busy. + +### Documentation + +- **The printable installation guide is rewritten for this release.** The previous PDF dated from + before 1.0.0 and sent people to Admin Settings → Domain, which 3.0.0 removed. New: the download + bundle, a chapter on connecting Instagram, and domain checks that use `curl`. Its HTML source is now + in `docs/guide/`, and `scripts/make-guide.sh` rebuilds the PDF from it. + ## [3.1.0] — 2026-10-26 **Fresh installs work again.** Every new install of 3.0.0 stopped while pulling images, because the @@ -400,7 +423,8 @@ Published as multi-architecture images (`linux/amd64` and `linux/arm64`) at - Playwright end-to-end tests are not run in CI — they need the full stack and browser binaries, so run them locally against a live install. -[Unreleased]: https://github.com/Forgemind-git/ForgeGrowth-OSS/compare/v3.1.0...HEAD +[Unreleased]: https://github.com/Forgemind-git/ForgeGrowth-OSS/compare/v3.1.1...HEAD +[3.1.1]: https://github.com/Forgemind-git/ForgeGrowth-OSS/compare/v3.1.0...v3.1.1 [3.1.0]: https://github.com/Forgemind-git/ForgeGrowth-OSS/compare/v3.0.0...v3.1.0 [3.0.0]: https://github.com/Forgemind-git/ForgeGrowth-OSS/compare/v1.0.1...v3.0.0 [2.0.0]: https://github.com/Forgemind-git/ForgeGrowth-OSS/compare/v1.0.1...v2.0.0 diff --git a/README.md b/README.md index 8984611..757383a 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,8 @@ follow-up?"*. ### Already on a server with Docker? One command. ```bash -bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.0/scripts/install.sh)" -- \ - --version v3.1.0 --domain crm.example.com --yes +bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.1/scripts/install.sh)" -- \ + --version v3.1.1 --domain crm.example.com --yes ``` Swap `crm.example.com` for your domain. That single command is the entire install: it pulls the @@ -238,7 +238,7 @@ Two ways in, same result. Pick either. Everything in one file, with a folder for each operating system. -1. Download **[`forge-growth-v3.1.0.zip`](https://github.com/Forgemind-git/ForgeGrowth-OSS/raw/main/dist/forge-growth-v3.1.0.zip)** +1. Download **[`forge-growth-v3.1.1.zip`](https://github.com/Forgemind-git/ForgeGrowth-OSS/raw/main/dist/forge-growth-v3.1.1.zip)** (~145 KB). It is in this repository under [`dist/`](./dist), and also attached to the [releases page](https://github.com/Forgemind-git/ForgeGrowth-OSS/releases/latest) — two hosts, so one of them being down or rate-limiting you is not the end of the install. @@ -264,8 +264,8 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowt **On a server with a real domain**, pinned to a release and asking nothing at all: ```bash -bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.0/scripts/install.sh)" -- \ - --version v3.1.0 --domain crm.example.com +bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.1/scripts/install.sh)" -- \ + --version v3.1.1 --domain crm.example.com ``` --- diff --git a/SECURITY.md b/SECURITY.md index a7e90f9..d4c0159 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -17,7 +17,7 @@ re-run and leaves `.env` and your database alone: ```bash ./install.sh # move to the current release -./install.sh --version v3.1.0 # or pin to a specific one +./install.sh --version v3.1.1 # or pin to a specific one ``` ## Reporting a vulnerability diff --git a/backend/package-lock.json b/backend/package-lock.json index 164289b..c9722d8 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "forgecrm-backend", - "version": "3.1.0", + "version": "3.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "forgecrm-backend", - "version": "3.1.0", + "version": "3.1.1", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.99.0", diff --git a/backend/package.json b/backend/package.json index 113cea9..992448a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "forgecrm-backend", - "version": "3.1.0", + "version": "3.1.1", "license": "MIT", "description": "ForgeChat backend API for WhatsApp chat viewer", "main": "src/index.js", diff --git a/dist/forge-growth-v3.1.0.zip b/dist/forge-growth-v3.1.1.zip similarity index 50% rename from dist/forge-growth-v3.1.0.zip rename to dist/forge-growth-v3.1.1.zip index b268f75..f193ba8 100644 Binary files a/dist/forge-growth-v3.1.0.zip and b/dist/forge-growth-v3.1.1.zip differ diff --git a/docs/ForgeGrowth-Installation-Guide.pdf b/docs/ForgeGrowth-Installation-Guide.pdf index 21d2e1e..3338509 100644 Binary files a/docs/ForgeGrowth-Installation-Guide.pdf and b/docs/ForgeGrowth-Installation-Guide.pdf differ diff --git a/docs/guide/installation-guide.html b/docs/guide/installation-guide.html new file mode 100644 index 0000000..50b7df4 --- /dev/null +++ b/docs/guide/installation-guide.html @@ -0,0 +1,593 @@ + + +
+ +Every way to install it — a server from published images or the download bundle, a + server from source, a Mac or Windows laptop — then attaching a domain and connecting WhatsApp and + Instagram. Every command can be copied as-is.
+| Repository | github.com/Forgemind-git/ForgeGrowth-OSS |
| Release | v3.1.1 |
| 0 | Before you start — what the machine needs |
| 1 | Server, from published images — the usual route; nothing is compiled |
| 2 | Attaching a domain — both kinds of server |
| 3 | Connecting WhatsApp — Meta credentials and the webhook |
| 4 | Connecting Instagram — the Social section |
| 5 | Server, from source — git clone, build locally |
| 6 | Local install — macOS |
| 7 | Local install — Windows |
| 8 | ngrok — WhatsApp against a laptop |
| 9 | Upgrading, backup, removal |
| 10 | When it goes wrong — real errors and what they mean |
| Situation | Read |
|---|---|
| Installing on a customer's server | 1 → 2a → 3 and/or 4 |
| Server already runs Traefik, nginx or another site | 1 → 2b → 3 and/or 4 |
| You intend to change the code | 5 |
| Trying it on your own laptop | 6 or 7, then 8 |
| Already installed, moving to a newer version | 9 |
Everything runs in containers. You do not install Node, PostgreSQL, Redis or MinIO on the machine.
+| Requirement | Detail |
|---|---|
| Docker | with the Compose v2 plugin. docker compose version must work — note the space, not docker-compose |
| Shell | Linux, macOS, or Windows through WSL2 / Git Bash. There is no PowerShell installer |
| RAM | 2 GB only if building from source (section 5). The published-image route has no build step |
| Disk | ~3 GB. The first download is about 350 MB |
| Ports | One for the web UI (8080 by default; the installer moves to the next free one). Ports 80 and 443 as well, if this machine is to obtain its own HTTPS certificate |
docker compose version +docker run --rm hello-world+
If the second command prints a greeting, Docker works. If either fails, fix that before continuing — +every failure after this point will otherwise be blamed on the installer.
+ +The installer makes one directory — forge-growth — holding a handful of small files and no source code:
forge-growth/ + .env your settings and every generated secret + .forgegrowth-install which version this install is pinned to + docker-compose.yml downloaded; overwritten on every upgrade + caddy/Caddyfile only used when this machine handles HTTPS itself + up.sh down.sh start and stop + install.sh a copy of the installer, for upgrades+
Your data is not in that folder. It lives in Docker volumes, which is why ./down.sh is safe
+and docker compose down -v destroys everything:
docker volume ls | grep forgegrowth+ + +
The normal way to install on a customer's machine. Nothing is cloned, nothing is compiled, and every secret is generated for you.
+ +bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.1/scripts/install.sh)" -- \ + --version v3.1.1 --domain crm.example.com --yes+
That is the whole install. It pulls the images, generates every secret, obtains a Let's Encrypt
+certificate, starts the stack, fetches the public URL to prove it answers, and prints the admin
+password once. --yes means it asks nothing.
Both halves of the pin matter: the URL fixes which script runs, and --version
+fixes what that script then downloads and pulls. The choice is remembered, so a later bare re-run
+cannot drift onto a newer build.
bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.1/scripts/install.sh)" -- \ + --version v3.1.1 --url https://crm.example.com --port 8080 --yes+
Use --url, not --domain. It tells the installer HTTPS is terminated
+elsewhere, so it does not start a second web server to fight over the ports. Then follow section 2b.
bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.1/scripts/install.sh)" -- \ + --version v3.1.1 --url http://203.0.113.10:8080 --yes+
https://. Add a domain later (section 2) —
+no reinstall is needed.For handing an install to someone who will not paste a command from the internet. Download
+forge-growth-v3.1.1.zip from the releases page, unzip it, open the folder for the
+operating system (linux, macos or windows), then:
chmod +x *.sh +./install.sh+
The installer continues in that folder — what you unzipped is the install — and the bundle +stays on its own version. It still needs the internet the first time, to pull the images.
+ +| Flag | Meaning |
|---|---|
--domain <host> | Serve HTTPS here, certificate included. Needs ports 80 + 443 free |
--url <origin> | Public address when something else terminates HTTPS. Include the port if people use one |
--port <n> | Host port for the UI (default 8080, or the next free one) |
--version <ref> | Pin the downloaded files and the image tag together. v3.1.1 and 3.1.1 both work |
--dir <path> | Install here instead of ./forge-growth |
--admin-email <addr> | First admin account (default admin@<your domain>) |
--admin-password <pw> | First admin password (default: generated and printed) |
--tls-email <addr> | Certificate contact. internal self-signs, for a domain with no public DNS |
--source | Build from a checkout rather than pulling images |
--yes, -y | Never ask. With no address flag: this machine's public address, or localhost |
The installer prints the URL, the admin email and the password. To read the password again:
+cd forge-growth +grep '^BOOTSTRAP_ADMIN_PASSWORD=' .env+ +
| Command | Does |
|---|---|
./up.sh | Start, then verify the public URL really answers |
./down.sh | Stop. Keeps all data |
./install.sh | Upgrade in place |
docker compose logs -f backend | Follow the backend log |
docker compose ls | List every install on this machine |
docker compose down -v | Deletes all data. Take a dump first |
Which instructions apply depends on one thing: does this install own ports 80 and 443, or does something else? The installer worked it out and recorded the answer.
+cd forge-growth +grep '^TLS_MODE=' .env+
| Value | Meaning | Follow |
|---|---|---|
TLS_MODE=caddy | This install owns the ports and obtains its own certificate | 2a |
TLS_MODE=proxy | Something else owns them; that thing must route the domain here | 2b |
cd forge-growth +./install.sh --domain crm.example.com
Your existing proxy must accept the hostname and forward it to this install's web
+container on port 80. First tell the install its public address, so the API accepts requests from it:
cd forge-growth +./install.sh --url https://crm.example.com+
Traefik, running in Docker on the same host. Create an overlay file outside the install
+directory — docker-compose.yml is re-downloaded on every upgrade, so labels added there are
+eventually lost:
services: + web: + networks: [default, proxy_default] + labels: + - traefik.enable=true + - traefik.docker.network=proxy_default + # ONE service, named explicitly on every router below. + - traefik.http.services.forgegrowth.loadbalancer.server.port=80 + - "traefik.http.routers.forgegrowth.rule=Host(`crm.example.com`)" + - traefik.http.routers.forgegrowth.entrypoints=websecure + - traefik.http.routers.forgegrowth.tls=true + - traefik.http.routers.forgegrowth.tls.certresolver=myresolver + - traefik.http.routers.forgegrowth.service=forgegrowth +networks: + proxy_default: + external: true+
Save it as /opt/forge-growth-proxy.yml, replace proxy_default with the network
+Traefik is on, and myresolver with your certificate resolver. Find the network with:
docker inspect <traefik-container> \
+ --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}'
+Wire it in through .env — never an exported shell variable — then restart:
cd forge-growth +echo 'COMPOSE_FILE=docker-compose.yml:/opt/forge-growth-proxy.yml' >> .env +./up.sh+
loadbalancer.server.port for each router makes
+Traefik unable to choose, so it discards every router on that container and each hostname returns 404.
+No certresolver behind a CDN. If the domain is proxied by Cloudflare or similar, the ACME
+challenge can never succeed, and its retries are rate-limited per account — enough to stop every other
+domain on the server from renewing. Use tls=true with no resolver and let the CDN supply the
+certificate.nginx, running outside Docker
+server {
+ listen 443 ssl;
+ server_name crm.example.com;
+ ssl_certificate /etc/letsencrypt/live/crm.example.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/crm.example.com/privkey.pem;
+ client_max_body_size 100M; # media uploads; nginx defaults to 1M
+ location / {
+ proxy_pass http://127.0.0.1:8080; # WEB_PORT from .env
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 600s;
+ }
+}
+
+A page that loads in your browser shows the interface was served. It does not show the API behind it +is reachable — and that is the half which breaks sign-in. Check both:
+curl -s -o /dev/null -w '%{http_code}\n' https://crm.example.com/
+200
+curl -s https://crm.example.com/api/auth/me
+{"error":"Unauthorized"}
+| Result | Meaning |
|---|---|
200, then {"error":"Unauthorized"} | Working. The proxy reaches this install, and the API answers |
| 404 on the first command | The proxy has no route for the domain — usually a typo in the host rule, or COMPOSE_FILE missing from .env |
| An HTML page from the second command | Something else is answering — another app, or another install on this machine |
| A certificate error | Routing is right; the certificate is missing or still being issued |
Could not resolve host | DNS |
./up.sh runs the first check for you every time it starts the stack.
Attach a WhatsApp Business number. This needs an https:// address, so finish section 2 first.
At developers.facebook.com → your app → WhatsApp → API Setup:
+| Value | Where |
|---|---|
| Phone number ID | API Setup, under the number |
| WhatsApp Business Account ID | Same screen |
| App ID | App dashboard header |
| Access token | The one on API Setup expires in 24 hours — create a System User token in Business Settings for anything permanent |
Settings → WhatsApp → Accounts → Add account. Paste the display number (digits only, no
++ and no spaces), the Phone Number ID, the WABA ID, the App ID and the token. The token is
+encrypted at rest with AES-256-GCM.
cd forge-growth +grep '^META_WEBHOOK_VERIFY_TOKEN=' .env+ +
This is the exact request Meta sends when you press Verify and save. Running it yourself turns a +slow, silent failure into a one-second answer:
+cd forge-growth
+TOKEN=$(grep '^META_WEBHOOK_VERIFY_TOKEN=' .env | cut -d= -f2-)
+curl -s "https://crm.example.com/api/webhook/whatsapp?hub.mode=subscribe&hub.verify_token=$TOKEN&hub.challenge=42"
+42
+If it prints 42, Meta will verify successfully. Anything else — fix it here first.
WhatsApp → Configuration → Webhook → Edit:
+| Field | Value |
|---|---|
| Callback URL | https://crm.example.com/api/webhook/whatsapp |
| Verify token | the META_WEBHOOK_VERIFY_TOKEN from step 3 |
Save, then under Webhook fields → Manage, subscribe to messages. That single field covers +messages, delivery statuses, echoes and template events.
+/api/webhook. A mismatch gives a silent 404 at verification time and no useful message from Meta.processed and records_extracted=1.The Social tab — Instagram DM automations, AI agents, the inbox and analytics — runs on a Meta app of its own. This also needs an https:// address.
cd forge-growth
+grep -c '^SOCIAL_ENCRYPTION_KEY=.' .env
+1
+The installer generates it. If this prints 0 — an install made before 3.1.1 — re-run
+./install.sh, which fills it in and keeps everything else. Without it, the app refuses to
+save Instagram credentials.
| Setting | Value |
|---|---|
| Instagram OAuth redirect | https://crm.example.com/api/social/instagram/oauth/callback |
| Webhook callback (Instagram) | https://crm.example.com/api/social/instagram/webhook |
| Privacy policy URL | Your own business's privacy policy page |
Settings → Instagram → Connection → App credentials. Enter the App ID and secret, the +Instagram App ID and secret, and a webhook verify token of your choosing — paste the same token into +the app's webhook setup. Then connect each Instagram account from the same screen.
+ +Posts, insights and comments sync in the background, which is off until you switch it on:
+cd forge-growth +echo 'SOCIAL_JOBS_ENABLED=1' >> .env +./up.sh+ +
Use this only if you intend to change the code. It builds the images on the machine, which needs about 2 GB of free memory and a few minutes.
+git clone https://github.com/Forgemind-git/ForgeGrowth-OSS.git +cd ForgeGrowth-OSS +./scripts/install.sh --domain crm.example.com+
The same flags apply as in section 1. Without a domain:
+./scripts/install.sh --yes+
| Command | Does |
|---|---|
./scripts/up.sh | Start and verify the public URL |
./scripts/up.sh --build | Rebuild from the checkout, then start |
./scripts/down.sh | Stop, keeping data |
git pull && ./scripts/install.sh | Upgrade |
./scripts/uninstall.sh --purge | Remove, including all data |
git pull alone moves the files on disk and nothing else, which looks exactly like a feature
+failing to arrive.cd backend && REQUIRE_DB=1 npm test && npm run lint +cd frontend && npm run test:unit && npm run lint+
Without REQUIRE_DB=1, the database-backed third of the backend tests quietly skip when no database is reachable.
Works on both Apple Silicon and Intel. The published images carry linux/amd64 and linux/arm64, so nothing is emulated.
Install Docker Desktop, OrbStack or Colima, start it, then confirm:
+docker compose version+
cd ~ +bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.1/scripts/install.sh)" -- \ + --version v3.1.1 --yes+
--yes means "localhost, ask me nothing". It creates ~/forge-growth and serves on
+port 8080 — or the next free port, which it tells you.
cd ~/forge-growth +grep -E '^(BOOTSTRAP_ADMIN_EMAIL|BOOTSTRAP_ADMIN_PASSWORD)=' .env+
Open the URL it printed and sign in with those.
+cd ~/forge-growth +./up.sh # start +./down.sh # stop, keeping data +docker compose logs -f backend # follow the log +open ~/forge-growth # reveal in Finder (Cmd+Shift+. shows .env)+ + +
The installer is a shell script, so it runs inside a Unix shell. WSL2 is the recommended route; Git Bash also works.
+In PowerShell as Administrator, then reboot when prompted:
+wsl --install+
Open Ubuntu from the Start menu and create your Linux user.
+Install Docker Desktop, then enable Settings → Resources → WSL integration for your distro. Confirm from inside the Ubuntu terminal:
+docker compose version+
cd ~ +bash -c "$(curl -fsSL https://raw.githubusercontent.com/Forgemind-git/ForgeGrowth-OSS/v3.1.1/scripts/install.sh)" -- \ + --version v3.1.1 --yes+
~/forge-growth. A location under /mnt/c/... is dramatically slower and confuses file-watching. cd ~ first, every time.cd ~/forge-growth +grep -E '^(BOOTSTRAP_ADMIN_EMAIL|BOOTSTRAP_ADMIN_PASSWORD)=' .env+
Open the printed URL in your normal Windows browser — WSL2 forwards localhost.
Run explorer.exe . from the Ubuntu terminal, or type this in Explorer's address bar:
\\wsl$\Ubuntu\home\<your-user>\forge-growth+ + +
Meta will not accept a localhost or plain-HTTP callback, so a laptop needs a public HTTPS address borrowed from a tunnel.
brew install ngrok # macOS; on Windows install it inside WSL2 +ngrok config add-authtoken <your-token> # dashboard.ngrok.com → Your Authtoken+
dashboard.ngrok.com → Domains → New Domain. The free tier includes one. Without it the URL changes on +every restart and the Meta webhook has to be re-pointed each time.
+Use the port your install actually reported, and leave that terminal running:
+ngrok http --url=your-name.ngrok-free.app 8080+
curl -s -o /dev/null -w '%{http_code}\n' https://your-name.ngrok-free.app/
+200
+cd ~/forge-growth
+TOKEN=$(grep '^META_WEBHOOK_VERIFY_TOKEN=' .env | cut -d= -f2-)
+curl -s "https://your-name.ngrok-free.app/api/webhook/whatsapp?hub.mode=subscribe&hub.verify_token=$TOKEN&hub.challenge=42"
+42
+This works before any WhatsApp account is configured — the check falls back to the token in
+.env — which separates "my tunnel is wrong" from "my credentials are wrong".
The tunnel is for Meta. Keep opening the app at the localhost address the installer printed.
| Field | Value |
|---|---|
| Callback URL | https://your-name.ngrok-free.app/api/webhook/whatsapp |
| Verify token | the value from step 5 |
| Subscribe to | messages |
Add your own phone under API Setup → To → Manage phone number list; a test number only messages
+people on that list. Then watch every delivery arrive in ngrok's inspector at
+http://127.0.0.1:4040 — it shows each request Meta makes with the full body, and lets you replay them.
Re-running the installer is the upgrade. From the install directory:
+cd forge-growth +./install.sh # stays on the version it is pinned to +./install.sh --version v3.1.1 # move deliberately, and stay there+
It re-downloads the compose file and scripts, pulls the images, applies any new database migrations
+at start-up, and restarts. .env is never overwritten, and secrets are kept. For a source
+install: git pull && ./scripts/install.sh.
cd forge-growth +grep -E '^(FORGECRM_ENCRYPTION_KEY|SOCIAL_ENCRYPTION_KEY|POSTGRES_PASSWORD)=' .env+
POSTGRES_PASSWORD is applied only when the database is first created, so a regenerated
+.env cannot open an existing database. Lose any of them and the failure names the wrong
+thing — an authentication error that reads like a network problem.cd forge-growth +docker compose exec -T postgres pg_dump -U forgegrowth forgegrowth \ + | gzip > backup-$(date +%F).sql.gz+
cd forge-growth +docker compose down -v # stops and DELETES all data +cd .. && rm -rf forge-growth+ + +
An install from before 3.1.0. The official MinIO image has been removed from Docker Hub; 3.1.0 and +later use a maintained build of the same server. Move to a current release — the Media Library is kept:
+./install.sh --version v3.1.1+ +
An image with no build for your processor. Compose pulls concurrently and reports one failure against
+every service, so the message names postgres, redis, minio and
+caddy — all innocent. Check the two images this project publishes:
docker manifest inspect ghcr.io/forgemind-git/forgegrowth-web:latest | grep architecture+
To carry on regardless, run everything under emulation: export DOCKER_DEFAULT_PLATFORM=linux/amd64.
Instagram on an install made before 3.1.1. Re-run ./install.sh; it generates the key and changes nothing else.
The login cookie is marked Secure and the site is being served over http://, so
+the browser silently discards it. Reach the site over https://. If HTTPS is terminated by a
+proxy this stack cannot see, set the flag explicitly:
cd forge-growth +echo 'COOKIE_SECURE=true' >> .env +./up.sh+ +
Nothing is routing the domain to the install. From inside the stack nothing is wrong, which is why
+container health cannot detect it. Check that COMPOSE_FILE is in .env, then run the checks in section 2c.
Not an error — the installer moved to the next free port and told you which. Find what holds a port:
+lsof -nP -iTCP:8080 -sTCP:LISTEN # macOS +ss -lntp | grep 8080 # Linux+ +
Usually a memory ceiling set too low — a container that reaches its limit is killed and restarted, +which looks like a container that never finishes starting.
+cd forge-growth +docker compose ps +docker compose logs --tail 50 backend+
Raise the ceiling in .env — BACKEND_MEM_LIMIT first; a busy install wants 1g.
Installs on one machine are separated by their project name and nothing else. Check which directory owns which:
+docker compose ls +grep '^COMPOSE_PROJECT_NAME=' forge-growth/.env+
up.sh refuses to start when another directory already owns this install's name — the reason to use it rather than raw Compose.
cd forge-growth +grep '^BOOTSTRAP_ADMIN_PASSWORD=' .env # admin password +grep '^META_WEBHOOK_VERIFY_TOKEN=' .env # WhatsApp webhook verify token +grep '^TLS_MODE=' .env # caddy = owns 80/443, proxy = does not +grep '^WEB_PORT=' .env # which port the UI is on +docker compose ps # container states +docker compose logs -f backend # follow the backend +docker compose ls # every install on this machine +docker volume ls | grep forgegrowth # where the data lives+ + + diff --git a/docs/install.md b/docs/install.md index 95c5d91..648c855 100644 --- a/docs/install.md +++ b/docs/install.md @@ -14,8 +14,9 @@ New here? [`README.md`](../README.md) has the short version. This page is the fu > Installing on someone else's machine, or walking a non-technical person through it? > [`ForgeGrowth-Installation-Guide.pdf`](./ForgeGrowth-Installation-Guide.pdf) is the printable -> version — every route end to end, plus attaching a domain, connecting WhatsApp, and what the -> confusing errors actually mean. +> version — every route end to end, plus attaching a domain, connecting WhatsApp and Instagram, and +> what the confusing errors actually mean. Its source is [`guide/installation-guide.html`](./guide/installation-guide.html); +> `./scripts/make-guide.sh` regenerates it. ## Contents @@ -111,7 +112,7 @@ Download **`forge-growth-