A simple, ergonomic Go library for building services that integrate with Lyre-Server.
go get github.com/LyrinoxTechnologies/liblyre-svcpackage main
import (
"fmt"
"log"
"github.com/LyrinoxTechnologies/liblyre-svc"
)
func main() {
// Create service
svc, err := liblyresvc.New(liblyresvc.Config{
ServiceID: "my-service",
ServiceName: "My Service",
ServiceType: "backend",
Description: "Example service",
Secret: "my-shared-secret",
ServerURL: "ws://localhost:36623/ws",
Endpoints: []string{"echo", "greet"},
})
if err != nil {
log.Fatal(err)
}
// Register handlers
svc.Handle("echo", func(req *liblyresvc.Request) *liblyresvc.Response {
return req.Success(req.Payload)
})
svc.Handle("greet", func(req *liblyresvc.Request) *liblyresvc.Response {
name, _ := req.Payload["name"].(string)
if name == "" {
return req.Error("name is required")
}
return req.Success(map[string]interface{}{
"greeting": fmt.Sprintf("Hello, %s!", name),
})
})
// Print the configure command for the admin
fmt.Println("Run this to configure Lyre-Server:")
fmt.Println(svc.ConfigureCommand())
// Connect and run
if err := svc.Connect(); err != nil {
log.Fatal(err)
}
defer svc.Close()
log.Println("Service running...")
if err := svc.Run(); err != nil {
log.Fatal(err)
}
}Before your service can connect, an admin must register it in Lyre-Server's config.yaml.
The library provides a helper method to generate the configuration command:
svc, _ := liblyresvc.New(config)
// Get the command to run (plain text secret - for development)
fmt.Println(svc.ConfigureCommand())
// Output: lyre-service-configure add --id "my-service" --name "My Service" --type "backend" --secret "my-secret" --endpoint "echo" --endpoint "greet"
// Get the command with hashed secret (recommended for production)
cmd, _ := svc.ConfigureCommandHashed()
fmt.Println(cmd)fmt.Println(svc.ServiceConfigYAML())
// Output:
// - id: "my-service"
// name: "My Service"
// type: "backend"
// description: "Example service"
// secret: "$2a$10$..."
// endpoints:
// - "echo"
// - "greet"svc.Handle("endpoint-name", func(req *liblyresvc.Request) *liblyresvc.Response {
// Access request data
userID := req.FromUser // User ID if from client
serviceID := req.FromService // Service ID if from another service
msgID := req.MessageID // Unique message ID
// Access payload fields
name := req.Payload["name"].(string)
// Return success
return req.Success(map[string]interface{}{
"result": "value",
})
// Or return error
return req.Error("something went wrong")
return req.Errorf("invalid value: %v", value)
})Handle all unmatched endpoints:
svc.Handle("*", func(req *liblyresvc.Request) *liblyresvc.Response {
log.Printf("Unknown endpoint: %s", req.Endpoint)
return req.Errorf("unknown endpoint: %s", req.Endpoint)
})// Generate a cryptographically secure secret
secret, err := liblyresvc.GenerateSecret()
// secret = "a1b2c3d4e5f6..." (64 hex characters)
// Hash it for use in config.yaml
hash, err := liblyresvc.HashSecret(secret)
// hash = "$2a$10$..."- Use bcrypt-hashed secrets in config.yaml - The
ConfigureCommandHashed()method does this automatically - Use TLS - Connect via
wss://in production - Rotate secrets periodically - Update both the service and config.yaml
liblyresvc.Config{
// Required
ServiceID: "my-service", // Must match config.yaml
Secret: "shared-secret", // Must match config.yaml
ServerURL: "ws://host:port/ws",
// Optional
ServiceName: "My Service",
ServiceType: "backend", // "backend", "cli", "webapp"
Description: "Service description",
Endpoints: []string{"ep1", "ep2"},
// Tuning
HeartbeatInterval: 30 * time.Second,
ReconnectDelay: 5 * time.Second,
Logger: customLogger,
}svc.Handle("risky", func(req *liblyresvc.Request) *liblyresvc.Response {
result, err := doSomethingRisky()
if err != nil {
return req.Errorf("operation failed: %v", err)
}
return req.Success(result)
})// Handle signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
log.Println("Shutting down...")
svc.Close()
}()
svc.Run()