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>
3.4 KiB
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 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_IDandINFISICAL_CLIENT_SECRETenv vars to config - Add Infisical Go SDK:
github.com/infisical/go-sdk - Create
internal/secrets/infisical.go— wraps the SDK with aGetLocationAPIKey(locationID) (string, error)method and an in-process cache (TTL ~60s to avoid hammering the API) - In
processOutbound: callsecrets.GetLocationAPIKey(locationID)instead of reading from the token record - The
cast_api_keyfield inTokenRecordcan be kept as a fallback during migration, then removed
4. Admin API changes
PUT /api/admin/locations/{locationId}/configshould write the API key to Infisical (not MongoDB)GETendpoints should read from Infisical and mask the returned value
5. Migration of existing keys
- For each location with a
cast_api_keyin MongoDB, write it to Infisical via the admin API - Verify the bridge reads the key correctly from Infisical
- Clear
cast_api_keyfrom MongoDB records - Remove the MongoDB field once fully migrated
6. MongoDB security hardening (do regardless of Infisical migration)
- Ensure
MONGO_URIuses authentication:mongodb://user:pass@host:27017/cast-ghl?authSource=admin - Enable TLS: append
?tls=true&tlsCAFile=/path/to/ca.pemto the URI - Restrict MongoDB network access to the bridge server IP only (Vultr firewall rules)
- Enable MongoDB audit logging for the
cast-ghldatabase
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 (
PostInboundMessageininternal/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