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-cache for Salesforce-backed records (6 h stale / 23 h expire / 24 h TTL); member-service-cache for Salesforce sObject REST responses (raw JSON, no soft-TTL); and org-settings for authoritative org access-control settings (no TTL, optimistic locking).
  • Project ID resolution: The ProjectResolver translates v2 project UIDs to Salesforce Project__c.Id values 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.Id without 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 /livez and /readyz endpoints.
  • Request Tracking: Automatic request ID generation and propagation.
  • Structured Logging: JSON-formatted logs with contextual information.

API Endpoints

Project membership

MethodPathDescription
GET/project_memberships/{uid}Get a project membership

Key contacts (nested under project_membership)

MethodPathDescription
GET/project_memberships/{membership_uid}/key_contacts/{uid}Get a key contact
POST/project_memberships/{membership_uid}/key_contactsCreate 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

MethodPathDescription
POST/b2b_orgsCreate 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}/settingsGet org access-control settings (writers, auditors)
PUT/b2b_orgs/{uid}/settingsUpdate org access-control settings (writers, auditors)

Admin

MethodPathDescription
POST/admin/reindexTrigger a full or incremental reindex of cached entities into OpenSearch (requires global org-admin membership)

Utility

MethodPathDescription
GET/readyzReadiness check
GET/livezLiveness check

Note: The legacy /members/* and /memberships/* endpoints return 410 Gone with 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.

FieldValue
Subjectlfx.member.project-id-map.lookup
TransportNATS 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.

FieldValue
Subjectlfx.member.b2b-org-id-map.lookup
TransportNATS 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:

  1. Fork the repository.
  2. 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 -s command to sign your commits.
  3. 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.

VariableDescriptionRequired
SF_INSTANCE_URLSalesforce instance URLYes
SF_CLIENT_IDConnected-app consumer keyYes
SF_CLIENT_SECRETConsumer secret (username/password or client-credentials flow)Conditional
SF_USERNAMESalesforce username (username/password or JWT flow)Conditional
SF_PASSWORDSalesforce password (username/password flow)Conditional
SF_SECURITY_TOKENSecurity token appended to passwordNo
SF_CONSUMER_RSA_PEMPEM-encoded RSA private key (JWT bearer flow)Conditional
SF_API_VERSIONSalesforce 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 (without SF_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.