Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

76 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Blade

An API Gateway built in Go using Gin, ported from the .NET YARP-based gateway.

In fact, it's not the next generation gateway, it's the prev generation. Because Solar Network is built by pure Go at the v2, and migrated to .NET at v3, now we planned to move some core services to Go again.

Features

  • Reverse Proxy Routing - Routes requests to backend microservices
  • Health Monitoring - Background health checks every 10 seconds
  • Service Discovery - Redis-backed leased instance registry over gRPC
  • Readiness Gating - Returns 503 if core services are unhealthy
  • CORS Support - Allows all origins with custom headers
  • Special Routes - Fully configurable route system via routes
  • Route Transforms - Strips service prefix, adds /api prefix
  • Maintenance Mode - Returns 503 globally or per configured services

Configuration

Edit configs/config.toml:

siteUrl = "https://solian.app"

[cache]
serializer = "JSON"

[services]
ring = { http = "http://ring:5000", grpc = "ring:5001" }
pass = { http = "http://pass:5000", grpc = "pass:5001" }
drive = { http = "http://drive:5000", grpc = "drive:5001" }
sphere = { http = "http://sphere:5000", grpc = "sphere:5001" }
develop = { http = "http://develop:5000", grpc = "develop:5001" }
insight = { http = "http://insight:5000", grpc = "insight:5001" }
zone = { http = "http://zone:5000", grpc = "zone:5001" }
messager = { http = "http://messager:5000", grpc = "messager:5001" }
wallet = { http = "http://wallet:5000", grpc = "wallet:5001" }
ideask = { http = "http://ideask:5000", grpc = "ideask:5001" }

[endpoints]
serviceNames = ["ring", "pass", "drive", "sphere", "develop", "insight", "zone", "messager", "wallet", "ideask"]
coreServiceNames = ["ring", "pass", "drive", "sphere"]

[rateLimit]
requestsPerMinute = 120
burstAllowance = 10

[health]
checkIntervalSeconds = 10

[discovery]
enabled = true
prefix = "blade:discovery"
leaseSeconds = 30
leaderLeaseSeconds = 15
registrationToken = "replace-with-a-service-secret"

[server]
port = "6000"
readTimeout = 60
writeTimeout = 60

[websocket]
enabled = true
path = "/ws"
keepAliveSeconds = 60
maxMessageBytes = 4096
allowedDeviceAlternatives = ["watch"]

[maintenance]
enabled = false
mode = "full" # "full" or "service"
services = [] # used when mode = "service"

Maintenance Mode

[maintenance]
enabled = true
mode = "full" # blocks all proxied requests with 503
[maintenance]
enabled = true
mode = "service" # blocks only listed services
services = ["sphere", "drive"]

Legacy key maintaince is also supported for backward compatibility.

Environment Variables

Variable Description Default
CONFIG_PATH Path to config file configs/config.toml
GIN_MODE debug or release debug
ZEROLOG_PRETTY Enable pretty logging false

Service Discovery

When discovery.enabled is set, Blade requires cache.redisUrl and a non-empty discovery.registrationToken. Services register an HTTP/gRPC endpoint through DyServiceDiscoveryService/Register, then renew the lease before it expires. Only the Redis-elected Blade replica probes registered HTTP endpoints; all replicas read the same healthy instance set for proxy routing. Send the service secret as authorization: Bearer <registrationToken> for registration, renewal, and deregistration calls.

Configured [services] targets remain a fallback until that service has a registered instance, allowing incremental migration.

Special Routes Configuration

The gateway supports fully configurable special routes:

[[routes]]
path = "/.well-known/openid-configuration"
service = "pass"
target = "/auth/.well-known/openid-configuration"
prefix = false

[[routes]]
path = "/activitypub"
service = "sphere"
target = "/activitypub"
prefix = true           # true for wildcard matching
Field Description
path Source path to match (e.g., /ws, /.well-known/openid-configuration)
service Target service name
target Path on the backend service
prefix If true, match path as prefix (e.g., /activitypub/**)

Build & Run

Local

# Build
go build -o gateway ./cmd/main.go

# Run
./gateway

# Or with custom config
CONFIG_PATH=./configs/config.toml ./gateway

Docker

# Build
docker build -t dyson-gateway .

# Run
docker run -p 6000:6000 dyson-gateway

# Run with custom config
docker run -p 6000:6000 -v ./config.toml:/app/configs/config.toml dyson-gateway

Endpoints

Endpoint Description
GET /health Gateway health status
/<service>/** Proxied to backend service (e.g., /ring/**ring:5000/api/**)
/ws Native WebSocket gateway (configurable via websocket.path)
/.well-known/* .well-known endpoints (configurable via routes)
/activitypub/** ActivityPub (configurable via routes)
/swagger/<service>/** Swagger docs → service
gRPC DyServiceDiscoveryService Register, renew, remove, and resolve service instances

WebSocket Authentication Notes

Current implementation follows DysonTokenAuthHandler behavior:

  • Token extraction order: tk query, Authorization header (Bearer, AtField, AkField), AuthToken cookie
  • Token validation: remote gRPC call to DyAuthService/Authenticate using the configured websocket.authService target
  • Request IP is forwarded to auth service as ip_address

Request Flow

Client Request
    ↓
Rate Limiter (120 req/min/IP)
    ↓
Readiness Middleware (503 if core services unhealthy)
    ↓
Reverse Proxy (routes based on path)
    ↓
Backend Service

About

The next generation API gateway designed for Solar Network

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages