File size: 5,396 Bytes
83d6851
 
 
 
 
 
 
 
 
 
 
 
 
6cd5bf4
83d6851
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6cd5bf4
83d6851
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6cd5bf4
83d6851
 
 
6cd5bf4
83d6851
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# WhatsApp Service Gateway

The main FastAPI application (port **7860**) is the **public API gateway**. It
proxies every request under `/api/whatsapp/*` to the internal WhatsApp service
(a Go/whatsmeow application), which is not exposed publicly.

```text
Client
  |  HTTP
  v
Main FastAPI :7860
  |  /api/whatsapp/...
  v
WhatsApp Service (internal, e.g. http://localhost:8231)
  |
  v
Client
```

## Route mapping

The gateway forwards the **full path, query string, HTTP method and body**
unchanged. All routes require the main application's API key
(`Authorization: Bearer <API_KEY>`).

| Client request | WhatsApp service call |
|---|---|
| `GET /api/whatsapp/server/ok` | `GET /server/ok` (health) |
| `POST /api/whatsapp/instance/create` | `POST /instance/create` (admin) |
| `GET /api/whatsapp/instance/status` | `GET /instance/status` |
| `POST /api/whatsapp/send/text` | `POST /send/text` |
| `DELETE /api/whatsapp/instance/delete/:id` | `DELETE /instance/delete/:id` |

No path transformation is applied: after the `/api/whatsapp` prefix is stripped,
the rest of the path is appended to `WHATSAPP_SERVICE_URL`.

## Deployment model

Single Docker container (Hugging Face Spaces style): the Go binary is compiled
in a multi-stage `whatsapp-build` stage and embedded in the image at
`/app/whatsapp-service/server`. `start.sh` launches it as a sibling process and
`uvicorn` is the primary process. The gateway reaches it over loopback
(`http://127.0.0.1:8231`).

A crash of the WhatsApp service does **not** take the main app down: the gateway
returns sanitized `502`/`503` responses until it recovers (restart loop every
5s, degraded mode).

## Gateway behaviour

- **Auth:** the gateway requires the main app's Bearer API key. The WhatsApp
  service's own `apikey` header is forwarded as-is when provided by the client
  (per-instance token); otherwise the configured
  `WHATSAPP_SERVICE_GLOBAL_API_KEY` is injected (admin routes).
- **Headers:** hop-by-hop headers and the gateway's `Authorization`/`Cookie` are
  never forwarded. Response headers are whitelisted — internal
  `Server`/`content-encoding`/hop-by-hop headers never reach the client.
- **Errors:** connection failures → `502`; timeouts → `504`; disabled service →
  `503`. Upstream 4xx/5xx responses are passed through unchanged. Error bodies
  never contain internal hostnames, URLs, exceptions or stack traces.
- **Payments/streaming:** responses are streamed back to the client (media/QR
  downloads are not buffered).

## Configuration

| Variable | Default | Description |
|---|---|---|
| `WHATSAPP_SERVICE_ENABLED` | `false` | Enable the gateway routes. `start.sh` auto-enables it when the binary exists. |
| `WHATSAPP_SERVICE_URL` | `http://localhost:8231` | Base URL of the internal WhatsApp service. |
| `WHATSAPP_SERVICE_TIMEOUT` | `30` | Read/write/pool timeout for upstream calls (seconds). |
| `WHATSAPP_SERVICE_CONNECT_TIMEOUT` | `5` | Connect timeout for upstream calls (seconds). |
| `WHATSAPP_SERVICE_GLOBAL_API_KEY` | _(empty)_ | The WhatsApp service `GLOBAL_API_KEY`, injected as the default `apikey` header. |
| `WHATSAPP_SERVICE_PORT` | `8231` | Port `start.sh` uses to launch the embedded binary (`SERVER_PORT`). |
| `WHATSAPP_SERVICE_BINARY` | `/app/whatsapp-service/server` | Path to the embedded binary. |

### WhatsApp service env vars (centralized in the main app)

There is exactly **one** configuration source: the main application's `.env`
(or the deployment platform's environment). The Go service **never reads a
`.env` of its own**`start.sh` / the Docker image inject its variables as
process environment, and `--dev` only optionally loads a local `.env` if one
exists. Full reference: the WhatsApp section of `.env.example`.

Values shared with the main app use the same names (`SUPABASE_URL`,
`REDIS_URL`, ...). A few names are mapped automatically by `start.sh` so you
only configure one value:

| Centralized setting | Injected into Go service as |
|---|---|
| `SUPABASE_SERVICE_ROLE_KEY` | `SUPABASE_SERVICE_KEY` |
| `WHATSAPP_SERVICE_GLOBAL_API_KEY` | `GLOBAL_API_KEY` |
| `WHATSAPP_SERVICE_PORT` | `SERVER_PORT` |

Other Go-service settings configured centrally: `SUPABASE_DB_URL`,
`DATABASE_SAVE_MESSAGES`, `CLIENT_NAME`, `CONNECT_ON_STARTUP`, `DEBUG_ENABLED`,
`LOG_TYPE`, `WEBHOOK_FILES`, `OS_NAME`, `WHATSAPP_VERSION_*`, `MINIO_*`,
`AMQP_*`, `NATS_*`, `WEBHOOK_URL`, `PROXY_*`, `API_AUDIO_CONVERTER*`,
`EVENT_IGNORE_*`, `QRCODE_MAX_COUNT`, `CHECK_USER_EXISTS`, `LOG_*`.

> Configure `WHATSAPP_SERVICE_GLOBAL_API_KEY` (equivalently `GLOBAL_API_KEY`)
> once — it feeds both the gateway's default `apikey` header and the Go
> service's authentication.

## Health checks

The main app's `GET /health` now includes a non-fatal `whatsapp_service` block:

```json
{"configured": true, "reachable": true, "status": "ok"}
```

The WhatsApp service's own liveness endpoint is exposed through the gateway at
`GET /api/whatsapp/server/ok`.

## Testing

```bash
python -m pytest tests/test_whatsapp_gateway.py -v
```

Covers: gateway auth, forwarding (method/path/query/body/headers), apikey
injection vs pass-through, upstream 4xx/5xx passthrough, 502/503/504 handling,
response-header sanitization and the `/health` integration.

A full stack test:

```bash
docker build -t agentdeck-backend:test .
# run with the WhatsApp env vars above + WHATSAPP_SERVICE_ENABLED=true
```