{"id":42767641,"url":"https://github.com/linuxfoundation/lfx-v2-voting-service","last_synced_at":"2026-04-02T18:46:26.985Z","repository":{"id":334302058,"uuid":"1139437894","full_name":"linuxfoundation/lfx-v2-voting-service","owner":"linuxfoundation","description":"LFX v2 Platform Voting Service","archived":false,"fork":false,"pushed_at":"2026-03-30T01:39:46.000Z","size":767,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-30T05:06:56.571Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","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":"CONTRIBUTING.md","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-01-22T00:42:47.000Z","updated_at":"2026-03-30T01:39:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/linuxfoundation/lfx-v2-voting-service","commit_stats":null,"previous_names":["linuxfoundation/lfx-v2-voting-service"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/linuxfoundation/lfx-v2-voting-service","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-voting-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-voting-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-voting-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-voting-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linuxfoundation","download_url":"https://codeload.github.com/linuxfoundation/lfx-v2-voting-service/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-voting-service/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31313329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-01-29T21:16:18.051Z","updated_at":"2026-04-02T18:46:26.974Z","avatar_url":"https://github.com/linuxfoundation.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LFX V2 Voting Service\n\nA proxy service for the ITX voting system with LFXv2 authentication and API patterns.\n\n## Overview\n\nThis service provides a proxy layer between LFXv2 clients and the legacy ITX voting service. It handles:\n\n- **Authentication**: Validates JWT tokens from LFXv2 clients using Heimdall\n- **Service-to-Service Auth**: Uses service account tokens for ITX API calls\n- **Terminology Translation**: Maps LFXv2 \"vote\" terminology to ITX \"poll\" terminology\n- **ID Schema Translation**: Bidirectional mapping between LFXv2 UUIDs and LFXv1 Salesforce IDs via NATS\n- **Event Processing**: Real-time sync of v1 voting data to v2 indexer and FGA (see [Event Processing](docs/event-processing.md))\n- **Error Handling**: Provides consistent error responses following LFXv2 patterns\n- **Logging**: Structured logging with request tracking\n\n## Architecture\n\n- **Framework**: [Goa v3](https://goa.design/) - Design-first API development\n- **Authentication**: JWT validation via Heimdall JWKS\n- **Proxy Pattern**: HTTP client with service account authentication\n- **Clean Architecture**: Domain-driven design with clear layer separation\n- **Middleware Stack**: Authorization → Request ID → Request Logger\n\n### Project Structure\n\n```text\nlfx-v2-voting-service/\n├── api/voting/v1/design/    # Goa DSL API definitions\n├── charts/                   # Helm chart for Kubernetes deployment\n│   └── lfx-v2-voting-service/\n│       ├── templates/        # Kubernetes manifests + Heimdall ruleset\n│       ├── values.yaml       # Default Helm values\n│       └── values.local.example.yaml  # Local dev values template\n├── cmd/voting-api/           # Application entry point\n│   ├── eventing/             # Event processing handlers\n│   ├── service/              # Request/response converters\n│   ├── api.go                # Goa interface implementation (votes)\n│   ├── api_votes.go          # Vote handler helpers\n│   ├── api_vote_responses.go # Vote response handler implementations\n│   └── main.go               # App startup and wiring\n├── docs/                     # Documentation\n│   ├── api-contracts.md      # LFXv2 ↔ ITX field mappings and examples\n│   ├── event-processing.md   # NATS event flow and operational guide\n│   ├── glossary.md           # Voting-service-specific terms (ITX, SFID, v1/v2, FGA roles)\n│   └── itx-proxy-implementation.md  # Proxy architecture deep-dive\n├── gen/                      # Generated Goa code (never edit directly)\n├── internal/\n│   ├── domain/               # Interfaces and domain models\n│   │   └── models/           # Shared domain model types\n│   ├── service/              # Business logic layer\n│   │   ├── vote_service.go\n│   │   └── vote_response_service.go\n│   ├── infrastructure/       # External system integrations\n│   │   ├── auth/             # JWT authentication (Heimdall)\n│   │   ├── eventing/         # NATS event publisher\n│   │   ├── idmapper/         # NATS-based v1↔v2 ID mapping\n│   │   └── proxy/            # ITX HTTP client\n│   ├── middleware/           # HTTP middleware (auth, request ID, logger)\n│   └── log/                  # Logging configuration\n└── pkg/\n    ├── constants/            # Shared constants\n    ├── models/itx/           # ITX request/response model types\n    └── utils/                # Utility functions\n```\n\n## Getting Started\n\n### Prerequisites\n\n- Go 1.24 or later\n- Goa CLI: `go install goa.design/goa/v3/cmd/goa@latest`\n- ITX service account credentials (see [Getting Dev Credentials](CONTRIBUTING.md#getting-dev-credentials))\n- **[lfx-platform Helm chart](https://github.com/linuxfoundation/lfx-v2-helm/tree/main/charts/lfx-platform)** — provides NATS and Heimdall for local development (optional: can be bypassed with env flags)\n\n### Installation\n\n```bash\n# Clone the repository\ngit clone https://github.com/linuxfoundation/lfx-v2-voting-service.git\ncd lfx-v2-voting-service\n\n# Install dependencies\nmake deps\n\n# Generate API code from Goa design\nmake apigen\n\n# Build the service\nmake build\n```\n\n### Configuration\n\nConfigure the service using environment variables:\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `PORT` | HTTP server port | `8080` |\n| `LOG_LEVEL` | Logging level (`debug`, `info`, `warn`, `error`) | `debug` |\n| `LOG_ADD_SOURCE` | Add source file/line to logs (`true`, `false`) | `false` |\n| `JWKS_URL` | Heimdall JWKS endpoint | `http://heimdall:4457/.well-known/jwks` |\n| `AUDIENCE` | JWT audience claim | `lfx-v2-voting-service` |\n| `JWT_AUTH_DISABLED_MOCK_LOCAL_PRINCIPAL` | Any non-empty string disables JWT validation and is used as the mock principal identity | `\"\"` (disabled) |\n| `ITX_BASE_URL` | ITX API base URL | `https://api.dev.itx.linuxfoundation.org/` |\n| `ITX_AUTH0_DOMAIN` | Auth0 domain for ITX M2M auth | `linuxfoundation-dev.auth0.com` |\n| `ITX_CLIENT_ID` | OAuth2 client ID for ITX | **(required)** |\n| `ITX_CLIENT_PRIVATE_KEY` | RSA private key in PEM format for JWT assertion | **(required)** |\n| `ITX_AUDIENCE` | OAuth2 audience for ITX | `https://api.dev.itx.linuxfoundation.org/` |\n| `NATS_URL` | NATS server URL for v1/v2 ID mapping | `nats://nats:4222` |\n| `ID_MAPPING_DISABLED` | Disable ID mapping (use for local dev without NATS) | `false` (set to `true` to disable) |\n\n### Event Processing\n\n- `EVENT_PROCESSING_ENABLED` - Enable/disable event processing (default: true)\n- `EVENT_CONSUMER_NAME` - JetStream consumer name (default: voting-service-kv-consumer)\n- `EVENT_STREAM_NAME` - JetStream stream name (default: KV_v1-objects)\n- `EVENT_FILTER_SUBJECT` - NATS subject filter (default: $KV.v1-objects.\u003e)\n\nSee [Event Processing Documentation](docs/event-processing.md) for details.\n\n### Local Infrastructure (NATS + Heimdall)\n\nThe service depends on NATS (for ID mapping and event processing) and Heimdall (for JWT validation). The easiest way to run both locally is with the [lfx-platform Helm chart](https://github.com/linuxfoundation/lfx-v2-helm/tree/main/charts/lfx-platform):\n\n```bash\nkubectl create namespace lfx\n\n# Latest version (may include breaking changes):\nhelm install -n lfx lfx-platform \\\n  oci://ghcr.io/linuxfoundation/lfx-v2-helm/chart/lfx-platform\n\n# Pinned version (recommended for reproducible local setup):\nhelm install -n lfx lfx-platform \\\n  oci://ghcr.io/linuxfoundation/lfx-v2-helm/chart/lfx-platform \\\n  --version \u003cversion\u003e\n```\n\nFor available versions, see the [lfx-v2-helm releases](https://github.com/linuxfoundation/lfx-v2-helm/releases).\n\nThis deploys NATS, Heimdall, Traefik, OpenFGA, and other platform services into your local Kubernetes cluster. Once running, the default `NATS_URL` in [.env.example](.env.example) (`nats://lfx-platform-nats.lfx.svc.cluster.local:4222`) will connect automatically.\n\nIf you prefer to skip NATS and Heimdall entirely, the defaults in [.env.example](.env.example) already set `ID_MAPPING_DISABLED=true`, `EVENT_PROCESSING_ENABLED=false`, and `JWT_AUTH_DISABLED_MOCK_LOCAL_PRINCIPAL=test-user@example.com` so no cluster is required.\n\n### Running Locally\n\nCopy the example environment file and fill in your ITX credentials (see [Getting Dev Credentials](CONTRIBUTING.md#getting-dev-credentials)):\n\n```bash\ncp .env.example .env\n# edit .env and set ITX_CLIENT_ID and ITX_CLIENT_PRIVATE_KEY\n```\n\nThen source it and run:\n\n```bash\nsource .env \u0026\u0026 make run\nmake debug # or run with debug log level\n```\n\nThe defaults in [.env.example](.env.example) already set `JWT_AUTH_DISABLED_MOCK_LOCAL_PRINCIPAL=test-user@example.com`, `ID_MAPPING_DISABLED=true`, and `EVENT_PROCESSING_ENABLED=false` so you don't need a running Heimdall or NATS instance for local development.\n\nThe service will start on `http://localhost:8080`\n\n### Running with Docker\n\n```bash\n# Build the Docker image\ndocker build -t lfx-v2-voting-service .\n\n# Run the container\ndocker run -p 8080:8080 \\\n  -e ITX_CLIENT_ID=\u003cyour-client-id\u003e \\\n  -e ITX_CLIENT_PRIVATE_KEY=\"$(cat /path/to/your/private-key.pem)\" \\\n  lfx-v2-voting-service\n```\n\n### Deploying with Helm\n\nThe service includes a Helm chart for Kubernetes deployment.\n\n#### Prerequisites: Kubernetes Secret\n\nRegardless of which install method you use, first create the `lfx-v2-voting-service` secret in the `lfx` namespace. The `ITX_CLIENT_ID` and `ITX_CLIENT_PRIVATE_KEY` values are in 1Password under the **LFX V2** vault, in the note **LFX V2 Voting Service Env Vars**.\n\n```bash\n# Create the namespace if it doesn't exist\nkubectl create namespace lfx\n\nkubectl create secret generic lfx-v2-voting-service -n lfx \\\n  --from-literal=ITX_CLIENT_ID=\"\u003cclient-id-from-1password\u003e\" \\\n  --from-file=ITX_CLIENT_PRIVATE_KEY=/path/to/your/private.key\n```\n\n#### Option 1: Install from GHCR (no local build needed)\n\nUse this when you just want to run the service without making code changes:\n\n```bash\nmake helm-install\n```\n\n#### Option 2: Install from Local Build (for active development)\n\nUse this when making code changes and testing them in Kubernetes. The chart uses `pullPolicy: Never` and pulls from the local image `linuxfoundation/lfx-v2-voting-service`.\n\nFirst, copy the example local values file and fill in any overrides you need:\n\n```bash\ncp charts/lfx-v2-voting-service/values.local.example.yaml \\\n   charts/lfx-v2-voting-service/values.local.yaml\n```\n\nThen build the image and install. Re-run `make docker-build` whenever you want to pick up new changes:\n\n```bash\nmake docker-build\nmake helm-install-local\n```\n\n#### Preview Helm Templates\n\n```bash\n# With default values\nmake helm-templates\n\n# With local values override\nmake helm-templates-local\n```\n\n#### Uninstall\n\n```bash\nmake helm-uninstall\n```\n\n## API Documentation\n\n### API Design Files\n\nThe API is defined using [Goa DSL](https://goa.design/) (Design-first approach):\n\n- **API Design**: [`api/voting/v1/design/voting.go`](api/voting/v1/design/voting.go) - Service and method definitions\n- **Type Definitions**: [`api/voting/v1/design/types.go`](api/voting/v1/design/types.go) - Request/response types\n\n### Generating OpenAPI/Swagger Documentation\n\nGenerate OpenAPI 3.0 specification and Swagger UI:\n\n```bash\n# Generate API code and OpenAPI spec\nmake apigen\n\n# The generated files will be in:\n# - gen/http/openapi.json  - OpenAPI 3.0 JSON spec\n# - gen/http/openapi.yaml  - OpenAPI 3.0 YAML spec\n# - gen/http/openapi3.json - OpenAPI 3.0 JSON spec (alternative)\n# - gen/http/openapi3.yaml - OpenAPI 3.0 YAML spec (alternative)\n```\n\n### Viewing API Documentation\n\nTo view the interactive Swagger UI documentation:\n\n1. **Generate the OpenAPI spec**: `make apigen`\n2. **Start the service**: `make run`\n3. **Access Swagger UI**: Import `gen/http/openapi.yaml` into [Swagger Editor](https://editor.swagger.io/)\n\nOr browse the deployed docs directly:\n\n- **Dev**: [lfx-api.dev.v2.cluster.linuxfound.info/docs](https://lfx-api.dev.v2.cluster.linuxfound.info/docs/#/)\n- **Production**: [lfx-api.v2.cluster.lfx.dev/docs](https://lfx-api.v2.cluster.lfx.dev/docs/#/)\n\n### Available Endpoints\n\nThe service provides the following endpoints:\n\n#### Health Checks\n\n- `GET /health` - Service health status\n- `GET /livez` - Kubernetes liveness probe\n- `GET /readyz` - Kubernetes readiness probe\n\n#### Vote Management (Polls)\n\n- `POST /votes` - Create a new vote/poll\n- `GET /votes/{vote_uid}` - Get vote/poll details\n- `PUT /votes/{vote_uid}` - Update a vote/poll (only when status is \"disabled\")\n- `DELETE /votes/{vote_uid}` - Delete a vote/poll (only when status is \"disabled\")\n- `POST /votes/{vote_uid}/extend` - Extend a vote/poll end time\n- `POST /votes/{vote_uid}/enable` - Enable a vote/poll for voting\n- `POST /votes/{vote_uid}/bulk_resend` - Bulk resend vote emails to recipients\n- `GET /votes/{vote_uid}/results` - Get aggregated vote results\n\n#### Vote Responses (Ballot Submissions)\n\n- `POST /vote_responses` - Submit a vote response\n- `GET /vote_responses/{vote_response_uid}` - Get vote response details\n- `PUT /vote_responses/{vote_response_uid}` - Update a vote response\n- `POST /vote_responses/{vote_response_uid}/resend` - Resend vote email\n\nFor detailed request/response schemas, authentication requirements, and examples, refer to the generated OpenAPI specification or the Goa design files.\n\n## Development\n\n### Running Tests\n\n```bash\nmake test\n```\n\n### Regenerating API Code\n\nAfter modifying files in `api/voting/v1/design/`:\n\n```bash\nmake apigen\n```\n\n### Adding New Endpoints\n\n1. **Define the endpoint** in `api/voting/v1/design/voting.go`\n2. **Add types** in `api/voting/v1/design/types.go` if needed\n3. **Regenerate code**: `make apigen`\n4. **Add proxy method** in `internal/infrastructure/proxy/client.go`\n5. **Add service method** in `internal/service/voting_service.go`\n6. **Add converters** in `cmd/voting-api/service/` (request and response)\n7. **Implement API handler** in `cmd/voting-api/api.go`\n\n### Code Style\n\n- Follow [Effective Go](https://golang.org/doc/effective_go.html)\n- Use `gofmt` for formatting\n- All exported functions/types must have comments\n- Domain errors use the `ErrorType` enum pattern\n\n## Monitoring and Observability\n\n### Logging\n\nThe service uses structured logging (`slog`) with the following context:\n\n- Request ID (X-REQUEST-ID header)\n- HTTP method and path\n- User agent and remote address\n- JWT principal (user ID)\n- Response status and duration\n\n### Metrics\n\nHealth check endpoints are available for monitoring:\n\n- `/health` - Returns 200 if service is running\n- `/livez` - Kubernetes liveness probe\n- `/readyz` - Kubernetes readiness probe\n\n## Security\n\n- **JWT Authentication**: All endpoints require valid JWT tokens from Heimdall\n- **OAuth2 M2M Authentication**: ITX calls use OAuth2 client credentials flow with JWT assertion (private key) for enhanced security, with automatic token caching and renewal\n- **HTTPS**: Use HTTPS in production (configure via reverse proxy)\n- **Secrets Management**: Store `ITX_CLIENT_ID` and `ITX_CLIENT_PRIVATE_KEY` securely (e.g., Kubernetes secrets, AWS Secrets Manager)\n\n## License\n\nMIT License - See [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nPlease open an issue or pull request on GitHub for details on our code of conduct and the process for submitting pull requests.\n\n## Support\n\nFor issues and questions:\n\n- Create an issue in the GitHub repository\n- Contact the LFX team at \u003csupport@linuxfoundation.org\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuxfoundation%2Flfx-v2-voting-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinuxfoundation%2Flfx-v2-voting-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuxfoundation%2Flfx-v2-voting-service/lists"}