When the bridge container fails to start, the deploy script previously
only showed docker compose ps which doesn't include the crash reason.
Now outputs last 30 log lines so pipeline output shows the error.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Trivy flagged golang.org/x/crypto v0.33.0 with HIGH severity
CVE-2025-22869 (DoS in SSH key exchange). Upgraded to v0.35.0
which contains the fix.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
G112 (Slowloris): add ReadHeaderTimeout: 10s to http.Server
G602 (slice bounds): use explicit bounds-safe index for backoff slice
(attempt is guarded but gosec can't prove it statically)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Running as root in a container is a security hazard. Use the existing
nobody user from alpine:3.19 to drop privileges before CMD.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Fix two more json.NewEncoder(w).Encode() calls in oauth_test.go
(lines 53 and 119) that were missed in the previous pass.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- internal/ghl/oauth.go:186: defer func(){ _ = resp.Body.Close() }()
- internal/cast/client_test.go: prefix all json.Decode/Encode calls with _ =
- internal/ghl/oauth_test.go: _ = r.ParseForm(), _, _ = w.Write(...)
golangci-lint exclusion rules in v2 are not suppressing test file errcheck
as expected, so fixes are applied directly in source.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Production fixes:
- cmd/server/main.go: refactor to run() helper to eliminate exitAfterDefer
(os.Exit in main() no longer bypasses deferred s.Close)
- internal/cast/client.go: use _ = resp.Body.Close() (errcheck)
- internal/ghl/api.go: wrap both defers as func(){ _ = resp.Body.Close() }()
Test fixes:
- internal/cast/client_test.go: replace err.(*CastAPIError) type assertions
with errors.As (errorlint)
Config:
- .golangci.yml: use explicit path regex .*_test\\.go and add errorlint
to test-file exclusions
Co-Authored-By: Paperclip <noreply@paperclip.ing>
golangci-lint v2 requires version: "2" at the top level, linters.settings
(not linters-settings), and issues.exclusions.rules (not issues.exclude-rules).
Also removed gosimple and unused which are now merged into staticcheck.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
pem.Decode requires actual newlines. When a PEM key is pasted into a
.env file it is commonly stored as a single line with \n literals.
Normalise these before decoding so both formats work.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
localhost:27017 resolves to the container itself inside Docker.
The mongo service is reachable via its compose service name 'mongo'.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Align Woodpecker CI pipeline with team standard (cast-backend pattern):
- Replace plugins/docker with woodpeckerci/plugin-docker-buildx
- Use git.sds.dev registry; tag with CI_COMMIT_SHA short + latest
- Use team secret names: registry_user/password, deploy_ssh_key
- Add golangci-lint, semgrep, gosec, trivy-fs, trivy-secrets security gates
- Deploy on push to main (not on tag): build-and-push then deploy step
calls bash /opt/cast-ghl-provider/deploy/deploy.sh on server
- Add Telegram notification on success/failure
docker-compose.yaml: add image: git.sds.dev/cast/cast-ghl-provider:latest
(server pulls from registry; build: kept for local dev only)
deploy/deploy.sh: simplified to docker compose pull + up
(build now happens in CI, not on the server)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Update all active config and documentation files to use the correct
production domain hl.cast.ph (not ghl.cast.ph).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- docker-compose.yaml: remove port binding; add VIRTUAL_HOST/LETSENCRYPT_HOST
env vars for nginx-proxy auto-routing; add internal + external proxy networks
- .woodpecker.yml: consolidate build steps into single ci step; add deploy-main
step that builds + deploys on every push to main; keep deploy-tag for
registry-pull deploys on version tags
- deploy/deploy.sh: simplify for manual/emergency use on existing server;
add --from-registry flag for registry pull vs local build
- Remove deploy/setup-server.sh and deploy/nginx/ (not needed on existing server)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
GHL uses RSA + SHA-256 for x-wh-signature, not ECDSA P-256 as documented
in the original task files. Also adds forward-compatible Ed25519 support
for X-GHL-Signature (GHL migration scheduled July 2026): handler checks
X-GHL-Signature first, falls back to x-wh-signature.
- webhook.go: replace ecdsa.VerifyASN1 with rsa.VerifyPKCS1v15; add
verifyEd25519 + verifyIncomingSignature dispatch; update struct fields
- webhook_test.go: regenerate test keys as RSA-2048, sign with PKCS1v15
- CLAUDE.md: correct crypto stack and key implementation notes
- .env.example: clarify GHL_WEBHOOK_PUBLIC_KEY is a static RSA key from docs
Co-Authored-By: SideKx <sidekx.ai@sds.dev>
Complete MVP implementation of the Cast GHL Conversation Provider bridge:
- Go module setup with chi router and mongo-driver dependencies
- Config loading with env var validation and defaults
- MongoDB token store with upsert, get, update, delete operations
- Cast.ph SMS client with 429 retry logic and typed errors
- Phone number normalization (E.164 ↔ Philippine local format)
- GHL OAuth 2.0 install/callback/refresh flow
- GHL webhook handler with ECDSA signature verification (async dispatch)
- GHL API client for message status updates and inbound message stubs
- Multi-stage Dockerfile, docker-compose with MongoDB, Woodpecker CI pipeline
- Unit tests for phone normalization, Cast client, GHL webhook, and OAuth handlers
Co-Authored-By: SideKx <sidekx.ai@sds.dev>