{"id":50697264,"url":"https://github.com/linuxfoundation/lfx-v2-forwards-service","last_synced_at":"2026-06-09T07:30:38.094Z","repository":{"id":361254967,"uuid":"1243934576","full_name":"linuxfoundation/lfx-v2-forwards-service","owner":"linuxfoundation","description":"LFX v2 Platform Forwards Service","archived":false,"fork":false,"pushed_at":"2026-05-29T20:38:22.000Z","size":75,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T21:12:35.555Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/linuxfoundation.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-19T20:08:02.000Z","updated_at":"2026-05-19T20:08:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/linuxfoundation/lfx-v2-forwards-service","commit_stats":null,"previous_names":["linuxfoundation/lfx-v2-forwards-service"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/linuxfoundation/lfx-v2-forwards-service","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-forwards-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-forwards-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-forwards-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-forwards-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linuxfoundation","download_url":"https://codeload.github.com/linuxfoundation/lfx-v2-forwards-service/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-forwards-service/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34096950,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-09T07:30:37.359Z","updated_at":"2026-06-09T07:30:38.086Z","avatar_url":"https://github.com/linuxfoundation.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# lfx-v2-forwards-service\n\nStateless LFX v2 Go microservice that manages email alias forwarding via [forwardemail.net](https://forwardemail.net).\n\nAlias **ownership** (`\u003calias\u003e@\u003cdomain\u003e` → LFID) lives in `lfx-v2-auth-service` as a system-managed Auth0 linked identity. This service only manages the **routing** (`\u003calias\u003e@\u003cdomain\u003e` → `\u003ctarget_email\u003e`) in forwardemail.net.\n\n`lfx-self-serve` orchestrates both services.\n\n---\n\n## NATS subjects\n\nAll subjects are under `lfx.forwards-service.`.\n\n| Subject | Auth | Description |\n|---|---|---|\n| `lfx.forwards-service.check_alias` | None | Check whether an alias exists in forwardemail.net. |\n| `lfx.forwards-service.set_target` | JWT | Create or update forwarding routing for the caller's alias. |\n| `lfx.forwards-service.get_forward` | JWT | Read the current forwarding target for the caller's alias. |\n\nAll request types require a `domain` field. Requests missing or sending an empty `domain` receive a `domain_required` error.\n\n### check_alias\n\n```json\n// request\n{\"alias\": \"johndoe\", \"domain\": \"linux.com\"}\n\n// reply — alias available\n{\"exists\": false, \"alias\": \"johndoe\"}\n\n// reply — alias taken\n{\"exists\": true, \"alias\": \"johndoe\"}\n\n// reply — invalid alias\n{\"error\": \"alias_invalid\"}\n\n// reply — domain not in allowed list\n{\"error\": \"domain_not_allowed\"}\n```\n\n### set_target\n\n```json\n// request\n{\n  \"user\": {\"auth_token\": \"\u003cjwt\u003e\"},\n  \"domain\": \"linux.com\",\n  \"target_email\": \"me@example.com\"\n}\n\n// reply — success\n{\"alias\": \"johndoe\", \"target_email\": \"me@example.com\", \"updated_at\": \"2026-05-28T00:00:00Z\"}\n\n// reply — caller has no identity on this domain\n{\"error\": \"not_found\"}\n\n// reply — domain not in allowed list\n{\"error\": \"domain_not_allowed\"}\n```\n\n### get_forward\n\n```json\n// request\n{\"user\": {\"auth_token\": \"\u003cjwt\u003e\"}, \"domain\": \"linux.com\"}\n\n// reply — found\n{\"found\": true, \"alias\": \"johndoe\", \"target_email\": \"me@example.com\"}\n\n// reply — no identity on this domain\n{\"found\": false}\n\n// reply — domain not in allowed list\n{\"error\": \"domain_not_allowed\"}\n```\n\n### Error codes\n\n| Code | Meaning |\n|---|---|\n| `alias_invalid` | Alias fails format/length/character validation |\n| `alias_reserved` | Alias is on the reserved-name list |\n| `domain_required` | The `domain` field was missing or empty |\n| `domain_not_allowed` | Requested domain is not in the service's configured domain list |\n| `unauthorized` | JWT missing, invalid, or auth-service call failed |\n| `not_found` | Caller has no alias identity on the requested domain |\n| `target_email_invalid` | `target_email` is missing or not a valid email address |\n| `forwardemail_error` | forwardemail.net API call failed |\n\nFull message contracts: [`pkg/api/forwards.go`](pkg/api/forwards.go).\n\n---\n\n## Environment variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `NATS_URL` | Yes | — | NATS server URL |\n| `NATS_CREDENTIALS_FILE` | No | `\"\"` | NKey credentials file path |\n| `FORWARDEMAIL_API_TOKEN` | Yes | — | forwardemail.net basic-auth API token |\n| `FORWARDEMAIL_BASE_URL` | No | `https://api.forwardemail.net` | Override for testing |\n| `FORWARDS_DOMAINS` | Yes | — | Comma-separated allow-list of managed domains. Callers must always send `domain` explicitly. |\n| `FORWARDS_RESERVED_NAMES` | No | `\"\"` | Comma-separated extra reserved alias names |\n| `AUTH0_DOMAIN` | Yes | — | Auth0 tenant hostname for JWKS fetch (e.g. `linuxfoundation-dev.auth0.com`) |\n| `AUTH0_AUDIENCE` | Yes | — | Expected JWT audience |\n| `AUTH_SERVICE_SUBJECT` | No | `lfx.auth-service.user_emails.read` | NATS subject for auth-service |\n| `AUTH_SERVICE_REQUEST_TIMEOUT` | No | `5s` | Timeout for auth-service NATS calls |\n| `LOG_LEVEL` | No | `info` | Logging level (`debug`, `info`, `warn`, `error`) |\n| `OTEL_*` | No | — | Standard OpenTelemetry environment variables |\n\n---\n\n## Local development\n\n### Prerequisites\n\n- Go 1.25+\n- NATS server (or dev cluster via `kubectl port-forward`)\n- A forwardemail.net API token\n- Auth0 domain + audience (dev tenant)\n\n### Build and run\n\n```bash\nmake build\n\nNATS_URL=nats://localhost:4222 \\\nFORWARDEMAIL_API_TOKEN=\u003cyour-token\u003e \\\nAUTH0_DOMAIN=linuxfoundation-dev.auth0.com \\\nAUTH0_AUDIENCE=https://linuxfoundation-dev.auth0.com/api/v2/ \\\nFORWARDS_DOMAINS=linux.com,linuxfoundation.org \\\n./bin/lfx-v2-forwards-service/forwards-service\n```\n\n### Testing with nats CLI\n\n```bash\n# check_alias — no auth required\nnats req lfx.forwards-service.check_alias '{\"alias\":\"johndoe\",\"domain\":\"linux.com\"}'\n\n# set_target — JWT required\nnats req lfx.forwards-service.set_target \\\n  '{\"user\":{\"auth_token\":\"\u003cjwt\u003e\"},\"domain\":\"linux.com\",\"target_email\":\"me@example.com\"}'\n\n# get_forward — JWT required\nnats req lfx.forwards-service.get_forward \\\n  '{\"user\":{\"auth_token\":\"\u003cjwt\u003e\"},\"domain\":\"linux.com\"}'\n\n# missing domain — returns domain_required\nnats req lfx.forwards-service.check_alias '{\"alias\":\"johndoe\"}'\n# reply: {\"error\":\"domain_required\"}\n```\n\n### Tests\n\n```bash\nmake test\n```\n\n### Lint\n\n```bash\nmake lint\n```\n\n### Helm template rendering\n\n```bash\nmake helm-templates\n```\n\n---\n\n## Architecture\n\n```\nlfx-self-serve\n     │\n     ├── NATS r/r ──► lfx-v2-auth-service   (alias ownership in Auth0)\n     │\n     └── NATS r/r ──► lfx-v2-forwards-service\n                           │\n                           ├── NATS r/r ──► lfx-v2-auth-service (user_emails.read)\n                           │\n                           └── HTTPS ──────► api.forwardemail.net\n```\n\nThe service is **stateless** — no database, no KV store, no JetStream.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuxfoundation%2Flfx-v2-forwards-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinuxfoundation%2Flfx-v2-forwards-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuxfoundation%2Flfx-v2-forwards-service/lists"}