The bridge between you and your users. A universal feedback intake API that collects bug reports and suggestions from web apps and delivers them to an operator inbox.
Named after Veles (Weles), the Slavic god of the underworld, waters, and cattle — a guardian of thresholds. This service sits between product UIs and the people who maintain them.
POST /v1/feedback— validate, abuse-check, notify- Cloudflare Turnstile verification
- Per-app origin and page-host allowlists
- Amazon SES plaintext email delivery (or stdout for local/Docker), with optional Reply-To from the submitter email
- Per-app SES From/To in the apps registry (
fromEmail/notificationEmail) - Dual runtime: AWS Lambda (
provided.al2023arm64) and Docker/net/http - CI on push/PR to
main(vet, race tests, server + Lambda builds, SAM validate)
cp config/apps.example.yaml config/apps.yaml # optional local override (gitignored)
docker compose up --build
curl -s http://localhost:8080/healthzThe Compose image defaults to WELES_TURNSTILE_MODE=skip and stdout notifications for local development only. Do not expose that configuration on the public Internet; set cloudflare mode and provide WELES_TURNSTILE_SSM or WELES_TURNSTILE_SECRET for any shared deployment.
Local defaults skip Turnstile verification and print notifications to stdout. See docs/integration.md for the request contract.
export PATH="$(go env GOPATH)/bin:$PATH"
make run
# WELES_TURNSTILE_MODE=skip WELES_NOTIFIER=stdoutmake test| Document | Purpose |
|---|---|
| docs/integration.md | API contract, Vue/Next examples |
| docs/security.md | Threat model and guarantees |
| docs/deploy.md | SAM deploy, SES verify, CI |
| api/openapi.yaml | OpenAPI 3 description |
| ROADMAP.md | Planned versions |
| CHANGELOG.md | Keep a Changelog |
| Variable | Default | Meaning |
|---|---|---|
WELES_LISTEN_ADDR |
:8080 |
HTTP listen address (server) |
WELES_APPS_CONFIG |
config/apps.example.yaml |
Path to apps allowlist YAML |
WELES_APPS_YAML |
(empty) | Inline apps YAML (overrides file) |
WELES_APPS_SSM |
(empty) | SSM parameter name with apps YAML (used on Lambda; leading / normalized) |
WELES_APPS_RELOAD_SECONDS |
0 (or 300 when SSM is set without override) |
How often to reload apps from SSM |
WELES_TURNSTILE_MODE |
skip |
skip or cloudflare — use cloudflare in any public deploy |
WELES_TURNSTILE_SECRET |
— | Turnstile secret (local/dev); prefer SSM in production |
WELES_TURNSTILE_SSM |
— | SSM SecureString parameter name for the Turnstile secret |
WELES_TRUST_PROXY_XFF |
false |
If true, use first X-Forwarded-For hop for Turnstile remoteip |
WELES_NOTIFIER |
stdout |
stdout or ses |
AWS_REGION |
eu-central-1 |
AWS region |
WELES_MAX_BODY_BYTES |
8192 |
Max JSON body size |
WELES_LOG_LEVEL |
info |
debug / info / warn / error |
WELES_API_STAGE |
(empty) | API Gateway HTTP API stage name; Lambda strips /{stage} from the request path (SAM sets this from StageName) |
Deprecated WELLS_* names are still read as fallback (a warning is logged). WELES_* wins when both are set.
Production app allowlists (real origins/hosts) must not be committed. Use config/apps.example.yaml as the public template and publish real values to SSM (WELES_APPS_SSM) at deploy time. Each app must set notificationEmail (SES To) and fromEmail (SES From). Apps may use different From domains; list every covering SES identity in deploy var SES_IDENTITIES (comma-separated).
MIT — see LICENSE.
