By Bitshock | [email protected]
"The impediment to action advances action. What stands in the way becomes the way." – Marcus Aurelius
Switchgear is a high availability LNURL balancer for enterprise Bitcoin Lightning payment providers. It is designed to scale massive multi-region Lightning Node fleets at five nines uptime.
Professional LNURL load balancing for your enterprise:
- CLN Lightning Node support with gRPC
- LND Lightning Node support with gRPC
- built on CloudFlare's Pingora Load Balancer
- Three balancing algorithms: Round Robin, Random and Consistent (Ketama)
- Node health checks redirect invoice requests to healthy nodes
- Self-healing - if an unhealthy node transitions to healthy, it will automatically start taking invoice requests again
- Weighting can shift invoice requests to preferred nodes
- Liquidity biasing with inbound capacity checks will favor nodes most likely to accept payment
- Timed retries with exponential backoff for spurious node failures
- Partitions bind inbound invoice requests to subset of nodes
- Add nodes in seconds with REST Discovery API - new nodes will automatically start taking invoice requests
- Drop nodes in seconds with REST Discovery API - the balancer will safely direct invoices to remaining healthy nodes
- Publish new LNURLs in seconds with Offer API
bech32
and QR code generation endpoints- Sqlite, MySql and Postgres database support for both Discovery and Offer data stores
- Remote REST stores for both Discovery and Offer
Switchgear is in ALPHA status:
- Critical features are implemented and work as designed
- Integration tests are complete
- APIs may change without warning
See ROADMAP.md for the Switchgear release roadmap.
Failure with a standard LNURL service configuration is highly-probable:
Single LNURL + Lightning Node deployments make Liquidity Bias impossible. Liquidity bias deprioritizes nodes for invoice requests that have low inbound capacity, which make payment success unlikely.
Load balancing multiple LNURL instances with a single fixed Lightning Node will exhaust resources:
- If a node fails, it makes the entire LNURL instance useless
- If a LNURL instance fails, it makes the Lightning Node unreachable
- Duplicating the entire LNURL instance just to attach it to a single Lightning Node raises cost, reducing feasibility of reaching scale
- High latency increases chances of catastrophic retry loop or flooding single node with failover requests
By contrast, every instance of Switchgear in a region can share the Lightning Node fleet, making scale feasible:
- A single Switchgear instance failure does not make a Lightning Node unreachable
- Any Lightning Node failure does not affect the uptime of a Switchgear instance
- Switchgear instances and the Lightning Node fleet size can scale independently, reducing cost
- Health checks are low latency, keeping Lightning Node status accurate
- Internal Backoff runs through entire Backend selection before returning 502 to external balancer, reducing latency and wallet retry cascade
The internal backoff can be entered under two conditions:
- No healthy Lightning Nodes are available
- A Lightning Node spurious failure or node failure between health checks
The Switchgear binary runs all services as well as the CLI admin interface.
cargo install switchgear-server
The docker image is multi-platform for:
- linux/amd64
- linux/arm64
docker pull bitshock/switchgear
swgr service --config {path/to/config/file} {service enabledment list}
The configuration file is in YAML format, and controls settings for all services.
The service enablement list can be any of:
lnurl
- the public LNURL servicediscovery
- the admin discovery serviceoffer
- the offer admin serviceall
- all services
If left empty, all services will be enabled (same as all
).
To run the Docker image:
docker run bitshock/switchgear
The image is configured with a default configuration file path of /etc/swgr/config.yaml
. Mount a volume on top of /etc/swgr
to provide your own configuration file.
Switchgear can be configured by both the REST API and the CLI.
Administration Service endpoints:
https://{host}/discovery
https://{host}/offers
See the Manage Lightning Node Backends with Discovery Service and Manage LNURLs with Offer Service sections for complete REST API.
# Manage Lightning Node Backends
swgr discovery
# Manage LNURLs
swgr offer
See the Manage Lightning Node Backends with Discovery Service and Manage LNURLs with Offer Service sections for complete CLI manual.
To run the CLI administration from Docker:
docker run bitshock/switchgear {cli-options}
All service configuration is controlled by a yaml file passed to the server at startup.
See server/config directory for more configuration examples.
Each service has a root entry the configuration file:
# LNURL Service Configuration
lnurl-service:
# Discovery Service Configuration
discovery-service:
# Offer Service Configuration
offer-service:
# Persistence Settings for Discovery and Offer data
store:
See the service entries below for complete configuration manual.
Shell-style env var expansion is supported anywhere in the yaml configuration file.
Example config.yaml:
lnurl-service:
address: "${MY_LNURL_SERVICE_ADDRESS:-127.0.0.1:8080}"
Run with:
MY_LNURL_SERVICE_ADDRESS=192.168.1.100:8080 swgr service --config ./config.yaml
The configuration would be parsed as:
lnurl-service:
address: "192.168.1.100:8080"
If the env var is unset:
swgr service --config ./config.yaml
The configuration would be parsed as:
lnurl-service:
address: "127.0.0.1:8080"
Optional Liquidity Bias will prioritize nodes with inbound capacity at the moment of selection. If no nodes are in range of the capacity bias, selection will fall back to standard weight selection.
lnurl-service:
selection-capacity-bias: -0.2
Invoice request amount must be less than 20% of total inbound capacity of the node to be favored over other nodes that are not.
lnurl-service:
selection-capacity-bias: 0.1
Invoice request amount may be over inbound capacity by up to 10% of the node and still be favored over other nodes that are not.
Capacity is measured in the same cycle as the Lightning Node health check. It is the sum of inbound capacity for all active channels on the node.
An organization may have a global Offer database. A Switchgear instance may be configured to serve a portion of that database, using partitions. Furthermore, every Lightning Node is configured in Discovery to be bound to a specific partition, insuring payments only land on nodes they belong to.
Each Switchgear instance is configured for the partitions it will serve.
Example:
lnurl-service:
partitions: ["us", "it", "cr"]
Only Offers created in "us", "it" or "cr" partitions will be available on the Switchgear instance, even if the Offer exists the database:
https://example.com/offers/us/{id} - 200 success
https://example.com/offers/it/{id} - 200 success
https://example.com/offers/cr/{id} - 200 success
https://example.com/offers/ca/{id} - 404 not found
Each partition must have Discovery Backends configured for the invoice request to succeed.
See the Manage Lightning Node Backends with Discovery Service and Manage LNURLs with Offer Service sections for creating Discovery Backends and Offers with partitions.
Switchgear itself can be balanced. Balancing multiple switchgear instances within a region:
For multi-region Global Load Balancer deployment, use the full health check to signal to the downstream balancer that a Switchgear instance has no healthy Lightning Nodes:
https://{host}/health/full
The full health check will return 500 if no Lightning Nodes are available.
The regional balancer will forward the failing health status to the global balancer, which will send invoice requests to an alternate region:
Switchgear partitions have predictable URLs. Use partitions and a global Application Load Balancer to map invoice requests to alternate regions that have Switchgear instances configured for the requested partition.
The OpenAPI LNURL Service specification: doc/lnurl-service-openapi.yaml.
The LNURL Service is public facing, and implements the LNURL LUD-06 specification.
See the Manage LNURLs with Offer Service section for complete service manual.
All Switchgear LNURLs are formatted as:
https://{host}/offers/{partition}/{id}
Where:
partition
- the Offer partitionid
- the Offer id (Uuid)
The returned callback is always the LNURL, with the postfix /invoice
:
https://{host}/offers/{partition}/{id}/invoice
The bech32 and QR variants are available with:
https://{host}/offers/{partition}/{id}/bech32
And:
https://{host}/offers/{partition}/{id}/bech32/qr
The QR image is in PNG format.
See server/config directory for more configuration examples.
lnurl-service:
# List of partitions this service will handle
# Partitions allow you to segment different Lightning node groups
partitions: ["default"]
# Network address and port for the LNURL service to bind to
address: "127.0.0.1:8080"
# Frequency in seconds for health checking Lightning node backends (float)
health-check-frequency-secs: 1.0
# Whether to perform health checks in parallel across all backends
parallel-health-check: true
# Number of consecutive successful health checks needed to mark a backend as healthy
health-check-consecutive-success-to-healthy: 1
# Number of consecutive failed health checks needed to mark a backend as unhealthy
health-check-consecutive-failure-to-unhealthy: 1
# Frequency in seconds for updating backend node information (float)
backend-update-frequency-secs: 1.0
# Invoice expiry time in seconds (integer)
invoice-expiry-secs: 180
# Timeout in seconds for Lightning node client connections (float)
ln-client-timeout-secs: 2.0
# List of allowed host headers for incoming requests
# Used for safely generating callback/invoice URLs.
allowed-hosts: ["lnurl.example.com"]
# Backoff configuration for retrying failed operations.
# Backoff is used when Lightning Node invoice request fails.
backoff:
# Type of backoff strategy: "stop" or "exponential"
type: "exponential"
# Optional: Initial interval in seconds for exponential backoff (float)
initial-interval-secs: 1.0
# Optional: Randomization factor (0.0 to 1.0)
randomization-factor: 0.5
# Optional: Multiplier for each retry attempt
multiplier: 2.0
# Optional: Maximum interval between retries in seconds (float)
max-interval-secs: 60.0
# Optional: Maximum total elapsed time before giving up in seconds (float)
max-elapsed-time-secs: 300.0
# Backend selection strategy for load balancing
# Options: "round-robin", "random", or "consistent"
backend-selection: "round-robin"
# For consistent hashing, specify max iterations (only used with "consistent")
# backend-selection:
# type: "consistent"
# max-iterations: 10000
# Optional: Bias factor for capacity-influenced selection
# Negative values are restrictive:
# prefer nodes with capacity higher than requested amount
# Positive values are lenient:
# refer nodes with capacity less than requested amount
selection-capacity-bias: -0.2
# Optional: Allow &comment query param in LNURL invoice request, sized in char len
# Used for Consistent backend selection
comment_allowed: 64,
# Optional: TLS configuration for HTTPS support
tls:
# Path to TLS certificate file
cert-path: "/etc/ssl/certs/lnurl-cert.pem"
# Path to TLS private key file
key-path: "/etc/ssl/certs/lnurl-key.pem"
Consistent uses the optional LNURL comment
query parameter as a hash key, which guarantees the same node will always receive invoice requests for that key. The balancer will move on to the next closest key match if the node becomes unavailable. This is a specific use-case that provides optimized HTLC settlement between cooperating peers for high-frequency transactions.
The OpenAPI Discovery Service specification: doc/discovery-service-openapi.yaml.
The Discovery Service is an administrative service used to manage connections to individual Lightning Nodes.
The service is isolated from the LNURL Service and can be configured to run on any port. The service supports TLS. Access is protected by a bearer token. Do not run this service without TLS enabled if it is exposed to the public internet.
See the Manage Lightning Node Backends with Discovery Service section for complete service manual.
See server/config directory for more configuration examples.
discovery-service:
# Network address and port for the Discovery service to bind to
address: "127.0.0.1:8081"
# Path to the authentication authority certificate/key file
# This file contains the public key used to verify API access
auth-authority: "/etc/ssl/certs/discovery-auth-authority.pem"
# Optional: TLS configuration for HTTPS support
tls:
# Path to TLS certificate file
cert-path: "/etc/ssl/certs/discovery-cert.pem"
# Path to TLS private key file
key-path: "/etc/ssl/certs/discovery-key.pem"
Generate key pairs and tokens for Discovery service authentication:
# Generate a new key pair for token signing
swgr discovery token key --public discovery-public.pem --private discovery-private.pem
# The public key (discovery-public.pem) should be used as the auth-authority in the configuration
# The private key (discovery-private.pem) is used to mint authentication tokens
The OpenAPI Offer Service specification: doc/offer-service-openapi.yaml.
The Offer Service is an administrative service used to manage Offers, which are used to generate LNURLs.
The service is isolated from the LNURL Service and can be configured to run on any port. The service supports TLS. Access is protected by a bearer token. Do not run this service without TLS enabled if it is exposed to the public internet.
See the Manage LNURLs with Offer Service section for complete service manual.
See server/config directory for more configuration examples.
offer-service:
# Network address and port for the Offers service to bind to
address: "127.0.0.1:8082"
# Path to the authentication authority certificate/key file
# This file contains the public key used to verify API access
auth-authority: "/etc/ssl/certs/offer-auth-authority.pem"
# Optional: TLS configuration for HTTPS support
tls:
# Path to TLS certificate file
cert-path: "/etc/ssl/certs/offer-cert.pem"
# Path to TLS private key file
key-path: "/etc/ssl/certs/offer-key.pem"
Generate key pairs and tokens for Offer service authentication:
# Generate a new key pair for token signing
swgr offer token key --public offer-public.pem --private offer-private.pem
# The public key (offer-public.pem) should be used as the auth-authority in the configuration
# The private key (offer-private.pem) is used to mint authentication tokens
Both Discovery and Offer services support multiple storage backends. Configure persistence in the store
section of your configuration file.
Both Discovery and Offer stores support these storage backends:
store:
discover: # or 'offer'
type: "database"
# Database connection URL (SQLite/MySQL/PostgreSQL)
database-url: "connection-url"
# Maximum number of concurrent database connections
max-connections: 5
For database-url
formats, see Database Connection URLs.
Both Discovery and Offer can use a remote http store, making custom integrations straightforward. The store clients connect to the same REST API used for remote administration, making it possible for Switchgear to run headless as well, serving only as a database for other Switchgear instances.
store:
discover: # or 'offer'
type: "http"
# Base URL of the remote service
base-url: "https://service.example.com"
# Timeout in seconds for establishing connection
connect-timeout-secs: 2.0
# Total timeout in seconds for complete request/response
total-timeout-secs: 5.0
# List of trusted CA certificate paths for TLS verification
trusted-roots: ["/etc/ssl/certs/ca.pem"]
# Path to bearer token file for authentication
authorization: "/etc/ssl/certs/auth.token"
Volatile storage, data is lost on restart:
store:
discover: # or 'offer'
type: "memory"
The Discovery store supports an additional file-based storage option:
store:
discover:
type: "file"
# Directory path where backend JSON files will be stored
storage-dir: "/data/discovery_backends"
This stores each backend configuration as a separate JSON file in the specified directory.
store:
# Discovery backend storage
discover:
type: "database"
database-url: "postgres://user:password@localhost:5432/switchgear"
max-connections: 5
# Offer storage (sharing same database)
offer:
type: "database"
database-url: "postgres://user:password@localhost:5432/switchgear"
max-connections: 10
store:
# File-based storage for Discovery
discover:
type: "file"
storage-dir: "/var/lib/switchgear/discovery"
# Database storage for Offers
offer:
type: "database"
database-url: "sqlite:///var/lib/switchgear/offers.db?mode=rwc"
max-connections: 5
store:
# Connect to remote Discovery service
discover:
type: "http"
base-url: "https://discovery.internal:8081"
connect-timeout-secs: 2.0
total-timeout-secs: 5.0
trusted-roots: ["/etc/ssl/certs/internal-ca.pem"]
authorization: "/etc/ssl/certs/discovery.token"
# Local database for Offers
offer:
type: "database"
database-url: "sqlite:///data/offers.db?mode=rwc"
max-connections: 10
Both Discovery and Offer data stores have a database-url
field to configure the database.
sqlite:///path/to/file.db?{options}
See https://www.sqlite.org/uri.html for all connection URL options.
mysql://[host][/database][?properties]
Properties:
Parameter | Default | Description |
---|---|---|
ssl-mode |
PREFERRED |
Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated. See [MySqlSslMode ]. |
ssl-ca |
None |
Sets the name of a file containing a list of trusted SSL Certificate Authorities. |
statement-cache-capacity |
100 |
The maximum number of prepared statements stored in the cache. Set to 0 to disable. |
socket |
None |
Path to the unix domain socket, which will be used instead of TCP if set. |
postgresql://[user[:password]@][host][:port][/dbname][?param1=value1&...]
See https://www.postgresql.org/docs/current/libpq-connect.html for all connection URL options.
The Discovery Service manages Lightning Node backends that connect to Switchgear for invoice requests. The service supports dynamic registration, updates, enablement and removal of CLN and LND nodes.
The Discovery Service can be administered with both REST and the CLI.
To get started quickly, use the CLI to write a new JSON data model template to a file:
# Generate a template backend configuration
swgr discovery new cln-grpc --output cln-backend.json
swgr discovery new lnd-grpc --output lnd-backend.json
The Discovery Service provides a REST API for backend management. All endpoints except /health
require bearer token authentication.
First, generate a token:
# Create a token (expires in 3600 seconds)
# Note: Requires private key from Authentication Setup (see Discovery Service Configuration)
swgr discovery token mint --key discovery-private.pem --expires 3600 --output discovery.token
# Set authorization header for curl commands
export AUTH_TOKEN=$(cat discovery.token)
# Register a CLN node
curl -X POST http://localhost:3001/discovery \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"partition": "default",
"address": {
"publicKey": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
},
"weight": 100,
"enabled": true,
"implementation": {
"type": "clnGrpc",
"url": "https://192.168.1.100:9736",
"domain": "cln-node.local",
"auth": {
"type": "path",
"caCertPath": "/path/to/ca.pem",
"clientCertPath": "/path/to/client.pem",
"clientKeyPath": "/path/to/client-key.pem"
}
}
}'
# Register an LND node
curl -X POST http://localhost:3001/discovery \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"partition": "default",
"address": {
"url": "https://lnd-node.example.com"
},
"weight": 50,
"enabled": true,
"implementation": {
"type": "lndGrpc",
"url": "https://192.168.1.101:10009",
"domain": "lnd-node.local",
"auth": {
"type": "path",
"tlsCertPath": "/path/to/tls.cert",
"macaroonPath": "/path/to/admin.macaroon"
},
"ampInvoice": false
}
}'
curl -X GET http://localhost:3001/discovery/default \
-H "Authorization: Bearer $AUTH_TOKEN"
# By public key
curl -X GET "http://localhost:3001/discovery/default/pk/0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" \
-H "Authorization: Bearer $AUTH_TOKEN"
# By URL (base64 encoded)
curl -X GET "http://localhost:3001/discovery/default/url/aHR0cHM6Ly9sbmQtbm9kZS5leGFtcGxlLmNvbS8" \
-H "Authorization: Bearer $AUTH_TOKEN"
curl -X PUT "http://localhost:3001/discovery/default/pk/0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"weight": 200,
"enabled": false,
"implementation": {
"type": "clnGrpc",
"url": "https://192.168.1.100:9736",
"domain": "cln-node.local",
"auth": {
"type": "path",
"caCertPath": "/path/to/ca.pem",
"clientCertPath": "/path/to/client.pem",
"clientKeyPath": "/path/to/client-key.pem"
}
}
}'
curl -X DELETE "http://localhost:3001/discovery/default/pk/0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" \
-H "Authorization: Bearer $AUTH_TOKEN"
# No authentication required
curl http://localhost:3001/health
The swgr discovery
command provides the same functionality as the REST interface for remote management of Lightning Node backends.
# Create a token (default 3600 seconds)
# Note: Requires a private key generated during Authentication Setup (see Discovery Service Configuration)
swgr discovery token mint --key discovery-private.pem --output discovery.token
# Create a token with custom expiration (86400 seconds = 24 hours)
swgr discovery token mint --key discovery-private.pem --expires 86400 --output discovery-24h.token
# Verify a token
swgr discovery token verify --public discovery-public.pem --token discovery.token
# Generate a template backend configuration
swgr discovery new cln-grpc --output cln-backend.json
swgr discovery new lnd-grpc --output lnd-backend.json
# Set connection parameters (via environment or flags)
export DISCOVERY_STORE_HTTP_BASE_URL="https://discovery.example.com"
export DISCOVERY_STORE_HTTP_AUTHORIZATION="/path/to/discovery.token"
export DISCOVERY_STORE_HTTP_TRUSTED_ROOTS="/path/to/ca.pem"
# List all backends in a partition (simple table format)
swgr discovery ls default
# Get backend details (JSON output)
swgr discovery get default pk/0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 --output backend-details.json
# Get all backends in partition (JSON output)
swgr discovery get default
# Register a new backend from JSON file
swgr discovery post --input cln-backend.json
# Update an existing backend
swgr discovery put default pk/0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 --input updated-backend.json
# Delete a backend
swgr discovery delete default pk/0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Discovery OpenAPI schema: doc/discovery-service-openapi.yaml.
Example CLN backend configuration:
{
"partition": "default",
"address": {
"publicKey": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
},
"weight": 1,
"enabled": true,
"implementation": {
"type": "clnGrpc",
"url": "https://127.0.0.1:9736",
"domain": "localhost",
"auth": {
"type": "path",
"caCertPath": "/path/to/ca.pem",
"clientCertPath": "/path/to/client.pem",
"clientKeyPath": "/path/to/client-key.pem"
}
}
}
Example LND backend configuration:
{
"partition": "default",
"address": {
"publicKey": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
},
"weight": 1,
"enabled": true,
"implementation": {
"type": "lndGrpc",
"url": "https://127.0.0.1:10009",
"domain": "localhost",
"auth": {
"type": "path",
"tlsCertPath": "/path/to/tls.cert",
"macaroonPath": "/path/to/admin.macaroon"
},
"ampInvoice": false
}
}
The Offer service manages Lightning payment offers and their metadata. It provides storage and retrieval of LNURL Pay offers with configurable payment limits and display information.
The Offer Service can be administered with both REST and the CLI.
To get started quickly, use the CLI to write a new JSON data model template to a file:
# Generate a template offer configuration
swgr offer new --output offer-template.json
# Generate a template metadata configuration
swgr offer metadata new --output metadata-template.json
An Offer has two fields that combine to create a unique identifier:
partition
id
(Uuid)
Both fields are used to make the final public LNURL:
https://{host}/offers/{partition}/{id}
The Offer service provides a REST API for offer and metadata management. All endpoints except /health
require bearer token authentication.
First, generate a token:
# Create a bearer token (expires in 3600 seconds)
# Note: Requires private key from Authentication Setup (see Offer Service Configuration)
swgr offer token mint --key offer-private.pem --expires 3600 --output offer.token
# Set authorization header for curl commands
export AUTH_TOKEN=$(cat offer.token)
curl -X POST http://localhost:3002/offers \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"partition": "default",
"id": "6a38ebdd-83ef-4b94-b843-3b18cd90a833",
"maxSendable": 1000000,
"minSendable": 1000,
"metadataId": "88deff7e-ca45-4144-8fca-286a5a18fb1a",
"timestamp": "2024-01-01T00:00:00Z",
"expires": "2024-12-31T23:59:59Z"
}'
curl -X GET http://localhost:3002/offers/default \
-H "Authorization: Bearer $AUTH_TOKEN"
curl -X GET "http://localhost:3002/offers/default/6a38ebdd-83ef-4b94-b843-3b18cd90a833" \
-H "Authorization: Bearer $AUTH_TOKEN"
curl -X PUT "http://localhost:3002/offers/default/6a38ebdd-83ef-4b94-b843-3b18cd90a833" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"maxSendable": 2000000,
"minSendable": 1000,
"metadataId": "88deff7e-ca45-4144-8fca-286a5a18fb1a",
"timestamp": "2024-01-01T00:00:00Z",
"expires": "2024-12-31T23:59:59Z"
}'
curl -X DELETE "http://localhost:3002/offers/default/6a38ebdd-83ef-4b94-b843-3b18cd90a833" \
-H "Authorization: Bearer $AUTH_TOKEN"
curl -X POST http://localhost:3002/metadata \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "88deff7e-ca45-4144-8fca-286a5a18fb1a",
"partition": "default",
"text": "Lightning Payment",
"longText": "Pay for premium services with Lightning Network",
"image": {
"png": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
},
"identifier": {
"email": "[email protected]"
}
}'
curl -X GET http://localhost:3002/metadata/default \
-H "Authorization: Bearer $AUTH_TOKEN"
curl -X GET "http://localhost:3002/metadata/default/88deff7e-ca45-4144-8fca-286a5a18fb1a" \
-H "Authorization: Bearer $AUTH_TOKEN"
curl -X PUT "http://localhost:3002/metadata/default/88deff7e-ca45-4144-8fca-286a5a18fb1a" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Updated Lightning Payment",
"longText": "Pay for premium services with Lightning Network - Updated",
"identifier": {
"email": "[email protected]"
}
}'
curl -X DELETE "http://localhost:3002/metadata/default/88deff7e-ca45-4144-8fca-286a5a18fb1a" \
-H "Authorization: Bearer $AUTH_TOKEN"
# No authentication required
curl http://localhost:3002/health
The swgr offer
command provides the same functionality as the REST interface for remote management of Lightning payment offers and metadata.
# Create a token (default 3600 seconds)
# Note: Requires a private key generated during Authentication Setup (see Offer Service Configuration)
swgr offer token mint --key offer-private.pem --output offer.token
# Create a token with custom expiration (86400 seconds = 24 hours)
swgr offer token mint --key offer-private.pem --expires 86400 --output offer-24h.token
# Verify a token
swgr offer token verify --public offer-public.pem --token offer.token
# Generate a template offer configuration
swgr offer new --output offer-template.json
# Set connection parameters (via environment or flags)
export OFFER_STORE_HTTP_BASE_URL="https://offer.example.com"
export OFFER_STORE_HTTP_AUTHORIZATION="/path/to/offer.token"
export OFFER_STORE_HTTP_TRUSTED_ROOTS="/path/to/ca.pem"
# Get offer details (JSON output)
swgr offer get default 6a38ebdd-83ef-4b94-b843-3b18cd90a833 --output offer-details.json
# Get all offers in partition (JSON output)
swgr offer get default
# Create a new offer from JSON file
swgr offer post --input offer.json
# Update an existing offer
swgr offer put default 6a38ebdd-83ef-4b94-b843-3b18cd90a833 --input updated-offer.json
# Delete an offer
swgr offer delete default 6a38ebdd-83ef-4b94-b843-3b18cd90a833
# Generate a template metadata configuration
swgr offer metadata new --output metadata-template.json
# Get metadata details (JSON output)
swgr offer metadata get default 88deff7e-ca45-4144-8fca-286a5a18fb1a --output metadata-details.json
# Get all metadata in partition (JSON output)
swgr offer metadata get default
# Create new metadata from JSON file
swgr offer metadata post --input metadata.json
# Update existing metadata
swgr offer metadata put default 88deff7e-ca45-4144-8fca-286a5a18fb1a --input updated-metadata.json
# Delete metadata
swgr offer metadata delete default 88deff7e-ca45-4144-8fca-286a5a18fb1a
Offer OpenAPI schema: doc/offer-service-openapi.yaml.
Example offer configuration:
{
"partition": "default",
"id": "6a38ebdd-83ef-4b94-b843-3b18cd90a833",
"maxSendable": 1000000,
"minSendable": 1000000,
"metadataId": "88deff7e-ca45-4144-8fca-286a5a18fb1a",
"timestamp": "1970-01-01T00:00:00Z",
"expires": null
}
Example metadata configuration:
{
"id": "88deff7e-ca45-4144-8fca-286a5a18fb1a",
"partition": "default",
"text": "mandatory offer text",
"longText": "optional long offer text",
"image": {
"png": "base64_encoded_png_data"
},
"identifier": {
"email": "[email protected]"
}
}
Metadata can include images in PNG or JPEG format, base64 encoded:
- PNG:
{"png": "base64_encoded_data"}
- JPEG:
{"jpeg": "base64_encoded_data"}
Metadata identifiers can be:
- Email:
{"email": "[email protected]"}
- Text:
{"text": "[email protected]"}
The switchgear-service crate defines all services and their trait dependencies. See the api
module for trait definitions and data models: service/src/api
PingoraLnBalancer
is the default LnBalancer
implementation. The switchgear-pingora crate holds the complete implementation, plus trait definitions it uses for itself.
The components
module in switchgear-service is a collection self-defined traits and implementations useful for implementing a complete LnBalancer
. The module also holds different implementations of DiscoveryBackendStore
, OfferStore
and OfferMetadataStore
.