LFX V2 Member Service
May 25, 2026 · View on GitHub
This repository contains the source code for the LFX v2 platform member service.
Overview
The LFX v2 Member Service is a RESTful API service that provides membership data within the Linux Foundation's LFX platform. It exposes endpoints for querying project memberships and key contacts, write endpoints for managing B2B orgs (create, update, access-control settings) and key contacts (create, update, delete), and an admin reindex endpoint for pushing data into OpenSearch. Data is sourced directly from Salesforce via SOQL queries, with a per-record NATS Key-Value cache to minimise round-trips.
File Structure
├── .github/ # Github files
│ └── workflows/ # Github Action workflow files
├── charts/ # Helm charts for running the service in kubernetes
├── cmd/ # Services (main packages)
│ └── member-api/ # Member service API code
│ ├── design/ # API design specifications (Goa)
│ ├── service/ # Service implementation
│ ├── main.go # Application entry point
│ └── http.go # HTTP server setup
├── gen/ # Generated code from Goa design
├── internal/ # Internal service packages
│ ├── domain/ # Domain logic layer (business logic)
│ │ ├── model/ # Domain models and entities
│ │ └── port/ # Repository and service interfaces
│ ├── service/ # Service logic layer (use cases)
│ ├── infrastructure/ # Infrastructure layer
│ │ ├── nats/ # NATS KV cache and project RPC
│ │ ├── salesforce/ # Salesforce SOQL client and repositories
│ │ ├── project/ # ProjectResolver (UID ↔ slug ↔ SFID)
│ │ └── mock/ # Mock implementations for testing
│ └── middleware/ # HTTP middleware components
└── pkg/ # Shared packages
Key Features
- RESTful API: Endpoints for querying project memberships and key contacts, write endpoints for B2B orgs and key contacts, org access-control settings (writers/auditors), and an admin reindex endpoint.
- Salesforce-backed: All membership data is fetched directly from Salesforce via SOQL queries; no PostgreSQL dependency at runtime.
- NATS KV cache: Three buckets —
membership-cachefor Salesforce-backed records (6 h stale / 23 h expire / 24 h TTL);member-service-cachefor Salesforce sObject REST responses (raw JSON, no soft-TTL); andorg-settingsfor authoritative org access-control settings (no TTL, optimistic locking). - Project ID resolution: The
ProjectResolvertranslates v2 project UIDs to SalesforceProject__c.Idvalues by chaining a NATS RPC to the project-service and a SOQL lookup, backed by the same KV cache. - NATS RPC endpoint: Exposes a request/reply subject so other services can
resolve a v2 project UID to a Salesforce
Project__c.Idwithout querying Salesforce or PostgreSQL directly. - Clean Architecture: Follows hexagonal architecture with clear separation of domain, service, and infrastructure layers.
- Authorization: JWT-based authentication with Heimdall middleware integration and OpenFGA fine-grained access control.
- Health Checks: Built-in
/livezand/readyzendpoints. - Request Tracking: Automatic request ID generation and propagation.
- Structured Logging: JSON-formatted logs with contextual information.
API Endpoints
Project membership
| Method | Path | Description |
|---|---|---|
| GET | /project_memberships/{uid} | Get a project membership |
Key contacts (nested under project_membership)
| Method | Path | Description |
|---|---|---|
| GET | /project_memberships/{membership_uid}/key_contacts/{uid} | Get a key contact |
| POST | /project_memberships/{membership_uid}/key_contacts | Create a key contact |
| PUT | /project_memberships/{membership_uid}/key_contacts/{uid} | Update a key contact |
| DELETE | /project_memberships/{membership_uid}/key_contacts/{uid} | Remove a key contact |
B2B orgs
| Method | Path | Description |
|---|---|---|
| POST | /b2b_orgs | Create a B2B org from a Salesforce Account SFID |
| PUT | /b2b_orgs/{uid} | Partial update of a B2B org |
| GET | /b2b_orgs/{uid} | Get a B2B org |
| GET | /b2b_orgs/{uid}/settings | Get org access-control settings (writers, auditors) |
| PUT | /b2b_orgs/{uid}/settings | Update org access-control settings (writers, auditors) |
Admin
| Method | Path | Description |
|---|---|---|
| POST | /admin/reindex | Trigger a full or incremental reindex of cached entities into OpenSearch (requires global org-admin membership) |
Utility
| Method | Path | Description |
|---|---|---|
| GET | /readyz | Readiness check |
| GET | /livez | Liveness check |
Note: The legacy
/members/*and/memberships/*endpoints return410 Gonewith a hint pointing to the replacement paths above.
NATS API
In addition to the HTTP API, the service handles NATS request/reply subjects that allow other services to resolve identifiers without querying Salesforce or this service's HTTP layer directly.
Project ID Map Lookup
Resolves a v2 project UUID to its corresponding Salesforce Project__c.Id.
Resolution is cached in the membership-cache KV bucket; a cache miss triggers
a NATS RPC to the project-service (to obtain the project slug) followed by a
Salesforce SOQL query.
| Field | Value |
|---|---|
| Subject | lfx.member.project-id-map.lookup |
| Transport | NATS core request/reply |
Request body (JSON):
{"project_uid": "<v2 project UUID>"}
Response — success (HTTP 200 equivalent):
{"project_sfid": "<Salesforce Project__c.Id>"}
Response — not found:
{"error": "project not found"}
Response — bad request:
{"error": "project_uid is required"}
The reply is always valid JSON. Check for the presence of the "error" key to
detect failure.
B2B Org ID Map Lookup
Resolves a Salesforce Account SFID to its v2 b2b_org UUID. The mapping is
deterministic (pure base-62 transform via pkg/sfuuid) — no Salesforce or KV
round-trip is required.
| Field | Value |
|---|---|
| Subject | lfx.member.b2b-org-id-map.lookup |
| Transport | NATS core request/reply |
Request body (JSON):
{"b2b_org_sfid": "<Salesforce Account.Id>"}
Response — success:
{"b2b_org_uid": "<v2 b2b_org UUID>"}
Response — not found / invalid SFID:
{"error": "b2b org not found"}
Response — bad request:
{"error": "b2b_org_sfid is required"}
Response — malformed JSON:
{"error": "invalid request body"}
The reply is always valid JSON. Check for the presence of the "error" key to
detect failure.
Development
To contribute to this repository:
- Fork the repository.
- Commit your changes to a feature branch in your fork. Ensure your commits
are signed with the Developer Certificate of Origin
(DCO).
You can use the
git commit -scommand to sign your commits. - Submit your pull request.
Building
make apigen # Generate Goa API code
make build # Build the member-api binary
make test # Run tests
Running Locally
# With NATS and Salesforce credentials
export NATS_URL=nats://localhost:4222
export SF_INSTANCE_URL=https://linuxfoundation.my.salesforce.com
export SF_CLIENT_ID=<connected-app-client-id>
export SF_CLIENT_SECRET=<connected-app-client-secret>
./bin/member-api
# With mock data (no NATS or Salesforce required)
REPOSITORY_SOURCE=mock AUTH_SOURCE=mock ./bin/member-api
# With mock auth only (NATS + Salesforce still required)
export JWT_AUTH_DISABLED_MOCK_LOCAL_PRINCIPAL=local-dev-user
./bin/member-api
Salesforce Credentials
The service reads credentials from environment variables. A pre-existing
Kubernetes Secret is required in production; see the Helm chart values.yaml
salesforce.secrets stanza for configuration.
| Variable | Description | Required |
|---|---|---|
SF_INSTANCE_URL | Salesforce instance URL | Yes |
SF_CLIENT_ID | Connected-app consumer key | Yes |
SF_CLIENT_SECRET | Consumer secret (username/password or client-credentials flow) | Conditional |
SF_USERNAME | Salesforce username (username/password or JWT flow) | Conditional |
SF_PASSWORD | Salesforce password (username/password flow) | Conditional |
SF_SECURITY_TOKEN | Security token appended to password | No |
SF_CONSUMER_RSA_PEM | PEM-encoded RSA private key (JWT bearer flow) | Conditional |
SF_API_VERSION | Salesforce REST API version (default: v60.0) | No |
At least one complete authentication flow must be configured:
- JWT bearer:
SF_USERNAME+SF_CONSUMER_RSA_PEM - Username/password:
SF_USERNAME+SF_PASSWORD+SF_CLIENT_SECRET - Client-credentials:
SF_CLIENT_SECRET(withoutSF_USERNAME)
License
Copyright The Linux Foundation and each contributor to LFX.
This project's source code is licensed under the MIT License. A copy of the
license is available in LICENSE.
This project's documentation is licensed under the Creative Commons Attribution
4.0 International License (CC-BY-4.0). A copy of the license is available in
LICENSE-docs.