Committee API
May 4, 2026 · View on GitHub
This directory contains the Committee API service. The service provides comprehensive committee management functionality including:
- It serves HTTP requests via Traefik to perform CRUD operations on committee data
- Manages committee base information (name, category, description, settings, etc.)
- Handles committee settings including voting configurations, member management, and access controls
- Manages committee members including their roles, voting status, organization details, and appointment information
- Integrates with project services to ensure committee-project relationships
Applications with a BFF should use the REST API with HTTP requests to perform the needed operations on committees, while other resource API services can communicate with this service as needed.
This service contains the following API endpoints:
-
/committeesPOST: create a new committee with base information and settingsGET /{uid}: retrieve committee base information by UID (includes public data like name, category, description, voting settings, etc.)PUT /{uid}: update committee base informationDELETE /{uid}: delete a committee by UID
-
/committees/{uid}/settingsGET: retrieve committee settings by committee UID (includes sensitive data like writers, auditors, business email requirements)PUT: update committee settings
-
/committees/{uid}/membersPOST: add a new member to a committee (requires email and other member details)GET /{member_uid}: retrieve a specific committee member by member UIDPUT /{member_uid}: replace an existing committee member (requires complete resource with all fields)DELETE /{member_uid}: remove a member from a committee
NATS Messaging Interface
In addition to HTTP endpoints, this service provides NATS messaging capabilities for inter-service communication. Other LFX services can send requests via NATS subjects to retrieve committee data.
Supported NATS Subjects
| Subject | Purpose | Request Format | Response Format |
|---|---|---|---|
lfx.committee-api.get_name | Get committee name by UID | Committee UID (string) | Committee name (string) |
lfx.committee-api.list_members | List all members of a committee | Committee UID (string) | JSON array of committee members |
Usage Examples
Get Committee Name
# Send request with committee UID as message data
nats request lfx.committee-api.get_name "061a110a-7c38-4cd3-bfcf-fc8511a37f35"
# Response: "Technical Steering Committee"
List Committee Members
# Send request with committee UID as message data
nats request lfx.committee-api.list_members "061a110a-7c38-4cd3-bfcf-fc8511a37f35"
# Response: JSON array of CommitteeMember objects
Error Handling
NATS message responses follow this format:
- Success: Direct data response (string for name, JSON for members)
- Error: JSON object with error message:
{"error": "error description"}
Common error scenarios:
- Invalid UUID format:
{"error": "invalid UUID format"} - Committee not found:
{"error": "committee with UID <uid> not found"} - Committee has no members:
[](empty array for list_members)
File Structure
├── design/ # Goa design files
│ ├── committee.go # Goa committee service specification
│ └── type.go # Goa data types and models
├── service/ # Service implementation (presentation layer)
│ ├── committee_service.go # Committee and member service implementation
│ ├── error.go # Error handling utilities
│ └── providers.go # Dependency injection providers
├── main.go # Application startup and dependency injection
├── http.go # HTTP server setup and configuration
└── README.md # This documentation
# Dependencies from internal/ packages:
# - internal/service/ # Business logic and use case orchestration (committee & member operations)
# - internal/domain/ # Domain models, ports, and business rules (committee & member entities)
# - internal/infrastructure/ # Infrastructure implementations (NATS storage, Auth, Messaging)
# - internal/middleware/ # HTTP middleware components
Architecture
This service follows clean architecture principles with clear separation of concerns:
Layers
-
Presentation Layer (
cmd/committee-api/)committeeServicesrvcstruct implements the Goa-generated service interface- HTTP endpoint handlers for committee operations (
service/committee_service.go) - HTTP server setup and configuration (
http.go) - Dependency injection and startup (
main.go)
-
Service/Use Case Layer (
internal/service/)CommitteeWriterorchestrates committee creation, updates, and deletionCommitteeReaderorchestrates committee data retrieval operationsCommitteeMemberWriterorchestrates committee member creation, updates, and deletionMessageHandlerprocesses committee-related events and notifications- Contains business logic for committee and member operations
- Validates business rules and coordinates between domain and infrastructure
-
Domain Layer (
internal/domain/)- Domain models (
model/) - committee base, settings, and member entities - Port interfaces (
port/) - committee and member reader/writer interfaces - Business rules and domain-specific validation for committees and members
- Domain models (
-
Infrastructure Layer (
internal/infrastructure/)- NATS storage implementation (
nats/) - JWT authentication implementation (
auth/) - Mock implementations for testing (
mock/) - Messaging infrastructure
- NATS storage implementation (
Key Benefits
- Storage Independence: Can switch from NATS to PostgreSQL without changing business logic
- Testability: Each layer can be tested in isolation using comprehensive mocks
- Maintainability: Clear separation of concerns and dependency direction
- Scalability: Support for committee hierarchies, complex organizational structures, and member management
- Integration: Seamless integration with project services, member data, and external authentication systems
Indexer Contract
This service indexes committee data into the indexer service, making it searchable via the query service.
Create and update indexer messages include an IndexingConfig that provides the metadata controlling how the document is stored, searched, and access-checked in the index. Delete messages omit IndexingConfig — only the object ID is needed to remove the document. For the full field reference and message format details, see the indexer service client guide.
For the data schemas, tags, access control values, parent references, and fulltext fields for all resource types — see docs/indexer-contract.md.
Development
Prerequisites
- Go: the service is built with the Go programming language [Install]
- Kubernetes: used for deployment of resources [Install]
- Helm: used to manage kubernetes applications [Install]
- NATS: used to communicate with other LFX V2 services [Install]
- GOA Framework: used for API code generation
GOA Framework
Follow the GOA installation guide to install GOA:
go install goa.design/goa/v3/cmd/goa@latest
Verify the installation:
goa version
Building and Development
1. Generate Code
The service uses GOA to generate API code from the design specification. Run the following command to generate all necessary code:
make apigen
# or directly run the "goa gen" command
goa gen github.com/linuxfoundation/lfx-v2-committee-service/cmd/committee-api/design
This command generates:
- HTTP server and client code
- OpenAPI specification
- Service interfaces and types
- Transport layer implementations
2. Set up resources and external services
The service relies on some resources and external services being spun up prior to running this service.
-
NATS service: ensure you have a NATS server instance running and set the
NATS_URLenvironment variable with the URL of the serverexport NATS_URL=nats://lfx-platform-nats.lfx.svc.cluster.local:4222 -
NATS key-value bucket: once you have a NATS service running, you need to create buckets used by the committee service.
# if using the nats cli tool nats kv add committees --history=20 --storage=file --max-value-size=10485760 --max-bucket-size=1073741824 nats kv add committee-settings --history=20 --storage=file --max-value-size=10485760 --max-bucket-size=1073741824 nats kv add committee-members --history=20 --storage=file --max-value-size=10485760 --max-bucket-size=1073741824
3. Export environment variables
| Environment Variable Name | Description | Default | Required |
|---|---|---|---|
| PORT | the port for http requests to the committee service API | 8080 | false |
| NATS_URL | the URL of the nats server instance | nats://localhost:4222 | false |
| LOG_LEVEL | the log level for outputted logs | info | false |
| LOG_ADD_SOURCE | whether to add the source field to outputted logs | false | false |
| JWKS_URL | the URL to the endpoint for verifying ID tokens and JWT access tokens | false | |
| AUDIENCE | the audience of the app that the JWT token should have set - for verification of the JWT token | lfx-v2-committee-service | false |
| JWT_AUTH_DISABLED_MOCK_LOCAL_PRINCIPAL | a mocked auth principal value for local development (to avoid needing a valid JWT token) | false |
4. Development Workflow
-
Make design or implementation changes: Edit files in the
design/directory for design changes, and edit the other files for implementation changes. -
Regenerate code: Run
make apigenafter design changes -
Build the service:
make build -
Run the service:
make run # or run with debug logs enabled make debug # or run with the go command to set custom flags # -bind string interface to bind on (default "*") # -d enable debug logging (default false) # -p string listen port (default "8080") go runOnce the service is running, make a request to the
/livezendpoint to ensure that the service is alive.curl http://localhost:8080/livezYou should get a 200 status code response with a text/plain content payload of
OK. -
Run tests:
make test # or run go test to set custom flags go test . -v -
Lint the code
# From the root of the directory, run megalinter (https://megalinter.io/latest/mega-linter-runner/) to ensure the code passes the linter checks. The CI/CD has a check that uses megalinter. npx mega-linter-runner . -
Docker build + K8
# Build the dockerfile (from the root of the repo) docker build -t lfx-v2-committee-service:<release_number> .Using default values (suitable for a shared/staging cluster with existing NATS buckets and Heimdall):
# Install the helm chart using default values make helm-install # Update an already-installed chart make helm-installUsing a local values override (recommended for local development — lets you customise image tag, disable auth, etc.):
# 1. Copy the example local values file (only needed once) cp charts/lfx-v2-committee-service/values.local.example.yaml \ charts/lfx-v2-committee-service/values.local.yaml # 2. Edit values.local.yaml to suit your environment, then install: make helm-install-local # Preview what Kubernetes manifests will be rendered without installing: make helm-templates-localvalues.local.yamlis gitignored — your local overrides will never be committed.# Check that the REST API is accessible by hitting the `/livez` endpoint (you should get a response of OK if it is working): # # Note: replace the hostname with the host from ./charts/lfx-v2-committee-service/ingressroute.yaml curl http://lfx-api.k8s.orb.local/livez
Authorization with OpenFGA
When deployed via Kubernetes, the committee service uses OpenFGA for fine-grained authorization control. The authorization is handled by Heimdall middleware before requests reach the service.
Configuration
OpenFGA authorization is controlled by the openfga.enabled value in the Helm chart:
# In values.yaml or via --set flag
openfga:
enabled: true # Enable OpenFGA authorization (default)
# enabled: false # Disable for local development only
Local Development
For local development without OpenFGA:
- Set
openfga.enabled: falsein your Helm values - All requests will be allowed through (after JWT authentication)
- Warning: Never disable OpenFGA in production environments
Add new API endpoints
Note: follow the Development Workflow section on how to run the service code
- Update design files: Edit the committee design file in
design/committee.goto include specification of the new endpoint with all of its supported parameters, responses, and errors, etc. - Regenerate code: Run
make apigenafter design changes to generate the new Goa interfaces and types - Implement code: Implement the new endpoint in
service/following the existing patterns. Add the necessary business logic to the use case layer ininternal/service/if needed. Include comprehensive tests for the new endpoint. - Update heimdall ruleset: Ensure that
/charts/lfx-v2-committee-service/templates/ruleset.yamlhas the route and method for the endpoint set so that authentication is configured when deployed. If the endpoint modifies data (PUT, DELETE, PATCH), consider adding OpenFGA authorization checks in the ruleset for proper access control
Adding New Business Logic
For complex committee operations that require multiple steps or external service integration:
- Extend Use Cases: Add new methods to the
CommitteeWriterinterface ininternal/service/ - Update Domain Models: Modify committee models in
internal/domain/model/if new data structures are needed - Extend Port Interfaces: Update port interfaces in
internal/domain/port/to support new storage or external service operations - Implement Infrastructure: Add concrete implementations in
internal/infrastructure/for new external service integrations - Add Tests: Create comprehensive unit tests with mocks for all new components