cast-ghl-plugin/FUTURE_DEV.md
Head of Product & Engineering 9995027093
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
feat: per-location Cast API key and unified admin config API
Each GHL location can now have its own Cast API key and sender ID stored
in MongoDB. Falls back to global CAST_API_KEY / CAST_SENDER_ID env vars
when not set per-location.

Admin endpoints (all require Authorization: Bearer <INBOUND_API_KEY>):
  GET  /api/admin/locations                        — list all locations
  GET  /api/admin/locations/{locationId}/config    — get location config
  PUT  /api/admin/locations/{locationId}/config    — set sender_id + cast_api_key

Cast API key is masked in GET responses (first 12 chars + "...").
Replaces the /sender-id endpoint deployed in the previous commit.

Also adds FUTURE_DEV.md documenting the migration path to Infisical
for secret management, plus MongoDB security hardening checklist.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-06 14:08:29 +02:00

76 lines
3.4 KiB
Markdown

# Future Development Notes
This document tracks planned improvements, technical debt with business impact, and migration paths
for features currently implemented with known limitations.
---
## Upgrade: Per-location Cast API keys from MongoDB → Infisical
### Current state (Option A)
Per-location Cast API keys (`cast_api_key`) and sender IDs (`sender_id`) are stored in MongoDB
alongside OAuth tokens in the `cast-ghl.oauth_tokens` collection.
**Limitation:** Secret material (Cast API keys) stored in the same database as operational data.
If MongoDB is compromised, all per-location Cast credentials are exposed. MongoDB auth and TLS
mitigate this but do not eliminate the risk.
### Target state (Option C)
Use **[Infisical](https://github.com/Infisical/infisical)** as the secrets backend for per-location
Cast API keys. Infisical is open-source, self-hostable, and provides secret versioning, audit logs,
access policies, and dynamic secrets.
### Migration plan
#### 1. Deploy Infisical (self-hosted or Infisical Cloud)
- Recommended: self-host on the same Vultr instance or a dedicated secrets VM
- Create a project: `cast-ghl-provider`
- Create an environment: `production`
- Create a machine identity (service account) for the bridge service
#### 2. Secret naming convention
Store each location's API key as a secret named:
```
CAST_API_KEY_<locationId>
```
Example: `CAST_API_KEY_q5LZDBHiJ9BsY9Vge5De`
#### 3. Code changes in the bridge
- Add `INFISICAL_CLIENT_ID` and `INFISICAL_CLIENT_SECRET` env vars to config
- Add Infisical Go SDK: `github.com/infisical/go-sdk`
- Create `internal/secrets/infisical.go` — wraps the SDK with a `GetLocationAPIKey(locationID) (string, error)` method and an in-process cache (TTL ~60s to avoid hammering the API)
- In `processOutbound`: call `secrets.GetLocationAPIKey(locationID)` instead of reading from the token record
- The `cast_api_key` field in `TokenRecord` can be kept as a fallback during migration, then removed
#### 4. Admin API changes
- `PUT /api/admin/locations/{locationId}/config` should write the API key to Infisical (not MongoDB)
- `GET` endpoints should read from Infisical and mask the returned value
#### 5. Migration of existing keys
1. For each location with a `cast_api_key` in MongoDB, write it to Infisical via the admin API
2. Verify the bridge reads the key correctly from Infisical
3. Clear `cast_api_key` from MongoDB records
4. Remove the MongoDB field once fully migrated
#### 6. MongoDB security hardening (do regardless of Infisical migration)
- Ensure `MONGO_URI` uses authentication: `mongodb://user:pass@host:27017/cast-ghl?authSource=admin`
- Enable TLS: append `?tls=true&tlsCAFile=/path/to/ca.pem` to the URI
- Restrict MongoDB network access to the bridge server IP only (Vultr firewall rules)
- Enable MongoDB audit logging for the `cast-ghl` database
---
## Other Future Items
### Inbound SMS (2-way) — Cast SIM gateway webhook
- Tracked in CASA-61
- Requires Cast SIM gateway to support outbound webhooks
- Bridge handler stub already exists (`PostInboundMessage` in `internal/ghl/api.go`)
- Need to agree on Cast webhook payload format and DID → locationId mapping
### Token refresh proactive scheduling
- Currently tokens are refreshed on-demand (when expired at webhook time)
- A background goroutine that refreshes tokens 1 hour before expiry would reduce latency on the first webhook after a 24-hour token lifetime