feat: Initialize telecom platform with core services and infrastructure

- Add Go-based API server and carrier connector services
- Add Rust-based charging engine and packet gateway services
- Add Docker Compose configuration for local development
- Add comprehensive documentation (API, Architecture, Deployment)
- Add development scripts for Redis, MongoDB, and environment setup
- Add workspace configuration for Go modules and pnpm
- Remove unnecessary documentation files from web-dashboard
- Add project build configuration (Makefile, Cargo.toml)

This commit establishes the foundational microservices architecture for the sovereign Telecom-as-a-Service platform.
This commit is contained in:
nutcas3
2026-04-03 19:55:38 +03:00
parent a69c6a125a
commit b69b7ec087
26 changed files with 2573 additions and 6 deletions
+80
View File
@@ -0,0 +1,80 @@
# Go
*.exe
*.exe~
*.dll
*.so
*.dylib
*.test
*.out
/bin/
/dist/
go.work.sum
vendor/
# Rust
/target/
**/*.rs.bk
*.pdb
Cargo.lock
# Node/TypeScript
node_modules/
.next/
out/
dist/
.turbo/
.vercel/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
.pnpm-store/
# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
*.env
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Build artifacts
*.o
*.a
*.so
*.dylib
*.elf
*.wasm
# OS
.DS_Store
Thumbs.db
.AppleDouble
.LSOverride
# Database
*.db
*.sqlite
*.sqlite3
# Logs
logs/
*.log
# Testing
coverage/
.nyc_output/
# Temporary
tmp/
temp/
.tmp/
+65
View File
@@ -0,0 +1,65 @@
[workspace]
members = [
"apps/charging-engine",
"apps/packet-gateway",
]
resolver = "2"
[workspace.package]
version = "1.0.0"
authors = ["TaaS Team <[email protected]>"]
edition = "2021"
rust-version = "1.94"
license = "MIT"
[workspace.dependencies]
# Async runtime
tokio = { version = "1.40", features = ["full"] }
async-std = "1.13"
# eBPF with Aya (for packet-gateway)
aya = { version = "0.13", features = ["async_tokio"] }
aya-ebpf = "0.1"
aya-log = "0.2"
aya-log-ebpf = "0.1"
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# Database
redis = { version = "0.27", features = ["tokio-comp", "connection-manager", "cluster-async"] }
# Web framework (for charging-engine)
axum = { version = "0.7", features = ["macros"] }
tower = "0.5"
tower-http = { version = "0.5", features = ["cors", "trace"] }
hyper = "1.5"
# Networking
bytes = "1.7"
prost = "0.13"
# Logging and tracing
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
# Error handling
anyhow = "1.0"
thiserror = "2.0"
# Configuration
dotenv = "0.15"
config = "0.14"
# Time handling
chrono = { version = "0.4", features = ["serde"] }
# Utilities
uuid = { version = "1.10", features = ["v4", "serde"] }
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 TaaS Platform Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+152
View File
@@ -0,0 +1,152 @@
# Master Makefile - Telecom Platform
# Orchestrates builds across Go, Rust, and TypeScript
.PHONY: all build-ui build-rust build-go clean test dev help install-deps
# Colors for terminal output
CYAN := \033[0;36m
GREEN := \033[0;32m
YELLOW := \033[1;33m
RESET := \033[0m
help: ## Show this help message
@echo "$(CYAN)Telecom Platform - Available Commands:$(RESET)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(RESET) %s\n", $$1, $$2}'
all: build-ui build-rust build-go ## Build all components
install-deps: ## Install all dependencies
@echo "$(CYAN)Installing Go dependencies...$(RESET)"
@cd apps/api-server && go mod download
@cd apps/carrier-connector && go mod download
@echo "$(CYAN)Installing Rust dependencies...$(RESET)"
@cargo fetch
@echo "$(CYAN)Installing Node.js dependencies...$(RESET)"
@pnpm install
@echo "$(GREEN)All dependencies installed!$(RESET)"
# TypeScript - Frontend & SDK
build-ui: ## Build Next.js dashboard and TypeScript SDK
@echo "$(CYAN)Building TypeScript projects...$(RESET)"
@pnpm install
@pnpm -r run build
@echo "$(GREEN)TypeScript build complete!$(RESET)"
dev-ui: ## Start Next.js development server
@pnpm --filter web-dashboard dev
lint-ui: ## Lint TypeScript code
@pnpm -r run lint
# Rust - Data Plane & Charging Engine
build-rust: ## Build Rust components (release mode)
@echo "$(CYAN)Building Rust projects...$(RESET)"
@cargo build --release
@echo "$(GREEN)Rust build complete!$(RESET)"
build-rust-dev: ## Build Rust components (debug mode)
@cargo build
test-rust: ## Run Rust tests
@cargo test --workspace
lint-rust: ## Run Rust linter (clippy)
@cargo clippy --workspace -- -D warnings
# Go - BSS API & Carrier Connector
build-go: ## Build Go services
@echo "$(CYAN)Building Go projects...$(RESET)"
@mkdir -p dist
@cd apps/api-server && go build -o ../../dist/api-server
@cd apps/carrier-connector && go build -o ../../dist/carrier-connector
@echo "$(GREEN)Go build complete!$(RESET)"
test-go: ## Run Go tests
@go test ./apps/...
lint-go: ## Run Go linter
@golangci-lint run ./apps/...
fmt-go: ## Format Go code
@go fmt ./apps/...
# Development
dev: ## Start development environment (info only)
@echo "$(CYAN)To start the development environment:$(RESET)"
@echo ""
@echo "$(YELLOW)Terminal 1 - API Server:$(RESET)"
@echo " ./dist/api-server"
@echo ""
@echo "$(YELLOW)Terminal 2 - Carrier Connector:$(RESET)"
@echo " ./dist/carrier-connector"
@echo ""
@echo "$(YELLOW)Terminal 3 - Charging Engine:$(RESET)"
@echo " ./target/release/charging-engine"
@echo ""
@echo "$(YELLOW)Terminal 4 - Web Dashboard:$(RESET)"
@echo " make dev-ui"
@echo ""
# Testing
test: test-go test-rust ## Run all tests
@echo "$(GREEN)All tests passed!$(RESET)"
# Linting
lint: lint-go lint-rust lint-ui ## Run all linters
# Formatting
fmt: fmt-go ## Format all code
# Cleaning
clean: ## Remove all build artifacts
@echo "$(CYAN)Cleaning build artifacts...$(RESET)"
@rm -rf dist/
@rm -rf target/
@pnpm -r exec rm -rf .next dist node_modules
@find . -name "*.o" -type f -delete
@find . -name "*.a" -type f -delete
@echo "$(GREEN)Clean complete!$(RESET)"
clean-all: clean ## Remove all artifacts including dependencies
@rm -rf node_modules
@rm -rf apps/*/node_modules
@rm -rf libs/*/node_modules
@cd apps/api-server && rm -rf vendor/
@cd apps/carrier-connector && rm -rf vendor/
# Docker
docker-build: ## Build Docker images for all services
@echo "$(CYAN)Building Docker images...$(RESET)"
@docker build -f deployments/docker/api-server.Dockerfile -t taas-api-server .
@docker build -f deployments/docker/carrier-connector.Dockerfile -t taas-carrier-connector .
@docker build -f deployments/docker/charging-engine.Dockerfile -t taas-charging-engine .
@docker build -f deployments/docker/web-dashboard.Dockerfile -t taas-web-dashboard .
@echo "$(GREEN)Docker images built!$(RESET)"
docker-up: ## Start all services with docker-compose
@docker-compose up -d
docker-down: ## Stop all services
@docker-compose down
# Kubernetes
k8s-deploy: ## Deploy to Kubernetes
@kubectl apply -f deployments/kubernetes/
k8s-delete: ## Delete Kubernetes resources
@kubectl delete -f deployments/kubernetes/
# Database setup
db-setup: ## Set up MongoDB and Redis
@echo "$(CYAN)Setting up databases...$(RESET)"
@bash scripts/setup-mongodb.sh
@bash scripts/setup-redis.sh
@echo "$(GREEN)Database setup complete!$(RESET)"
# Free5GC integration
free5gc-setup: ## Set up free5GC (requires separate installation)
@echo "$(YELLOW)Please install free5GC separately.$(RESET)"
@echo "Visit: https://free5gc.org/guide/"
.DEFAULT_GOAL := help
+6
View File
@@ -0,0 +1,6 @@
MONGODB_URI=mongodb://localhost:27017/free5gc
REDIS_URI=redis://localhost:6379
API_PORT=8000
GIN_MODE=debug
JWT_SECRET=change-me-in-production
LOG_LEVEL=info
+13
View File
@@ -0,0 +1,13 @@
module github.com/taas-platform/telecom-platform/apps/api-server
go 1.26
require (
github.com/gin-gonic/gin v1.12.0
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/rs/zerolog v1.35.0
go.mongodb.org/mongo-driver v1.17.9
go.opentelemetry.io/otel v1.43.0
)
+232
View File
@@ -0,0 +1,232 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/joho/godotenv"
"github.com/rs/zerolog"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var (
logger zerolog.Logger
dbClient *mongo.Client
)
func main() {
// Initialize logger
logger = zerolog.New(os.Stdout).With().
Timestamp().
Str("service", "api-server").
Logger()
// Load environment variables
if err := godotenv.Load(); err != nil {
logger.Info().Msg("No .env file found, using system environment")
}
// Connect to MongoDB
mongoURI := getEnv("MONGODB_URI", "mongodb://localhost:27017/free5gc")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var err error
dbClient, err = mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
if err != nil {
logger.Fatal().Err(err).Msg("Failed to connect to MongoDB")
}
defer dbClient.Disconnect(context.Background())
// Verify connection
if err := dbClient.Ping(ctx, nil); err != nil {
logger.Fatal().Err(err).Msg("Failed to ping MongoDB")
}
logger.Info().Msg("Connected to MongoDB successfully")
// Initialize Gin router
router := setupRouter()
// Start server
port := getEnv("API_PORT", "8000")
logger.Info().Str("port", port).Msg("Starting API server")
if err := router.Run(":" + port); err != nil {
logger.Fatal().Err(err).Msg("Failed to start server")
}
}
func setupRouter() *gin.Engine {
// Set Gin mode
if getEnv("GIN_MODE", "debug") == "release" {
gin.SetMode(gin.ReleaseMode)
}
router := gin.Default()
// Health check
router.GET("/health", healthCheck)
// API v1 routes
v1 := router.Group("/v1")
{
// eSIM management
v1.POST("/esims", createESIM)
v1.GET("/esims/:id", getESIM)
v1.GET("/esims/:id/usage", getESIMUsage)
v1.DELETE("/esims/:id", deleteESIM)
// Subscriber management
v1.GET("/subscribers", listSubscribers)
v1.GET("/subscribers/:imsi", getSubscriber)
}
return router
}
// Health check endpoint
func healthCheck(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "healthy",
"service": "api-server",
"timestamp": time.Now().Unix(),
})
}
// POST /v1/esims - Create new eSIM
func createESIM(c *gin.Context) {
var req struct {
DataPlan string `json:"data_plan" binding:"required"`
CountryCode string `json:"country_code" binding:"required"`
Carrier string `json:"carrier,omitempty"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Generate IMSI (simplified - use proper allocation in production)
imsi := fmt.Sprintf("208930%09d", time.Now().UnixNano()%1000000000)
// Using Go 1.26's new(expr) for optional pointer fields
esim := gin.H{
"esim_id": uuid.New().String(),
"imsi": imsi,
"iccid": generateICCID(imsi),
"activation_code": fmt.Sprintf("LPA:1$smdp.example.com$%s", uuid.New().String()),
"status": "provisioned",
"data_plan": req.DataPlan,
"country_code": req.CountryCode,
"created_at": time.Now(),
}
logger.Info().
Str("esim_id", esim["esim_id"].(string)).
Str("imsi", imsi).
Msg("eSIM created")
c.JSON(http.StatusCreated, esim)
}
// GET /v1/esims/:id - Get eSIM details
func getESIM(c *gin.Context) {
esimID := c.Param("id")
// TODO: Implement actual database lookup
esim := gin.H{
"esim_id": esimID,
"imsi": "208930000000001",
"iccid": "8933123456789012345",
"status": "active",
"data_plan": "1GB",
"created_at": time.Now().Add(-24 * time.Hour),
}
c.JSON(http.StatusOK, esim)
}
// GET /v1/esims/:id/usage - Get eSIM usage statistics
func getESIMUsage(c *gin.Context) {
esimID := c.Param("id")
usage := gin.H{
"esim_id": esimID,
"data_used": 250 * 1024 * 1024, // 250 MB in bytes
"data_limit": 1024 * 1024 * 1024, // 1 GB in bytes
"sessions": []gin.H{
{
"started_at": time.Now().Add(-2 * time.Hour),
"ended_at": time.Now().Add(-1 * time.Hour),
"bytes_up": 10 * 1024 * 1024,
"bytes_down": 50 * 1024 * 1024,
},
},
"updated_at": time.Now(),
}
c.JSON(http.StatusOK, usage)
}
// DELETE /v1/esims/:id - Terminate eSIM
func deleteESIM(c *gin.Context) {
esimID := c.Param("id")
logger.Info().Str("esim_id", esimID).Msg("eSIM terminated")
c.JSON(http.StatusOK, gin.H{
"message": "eSIM terminated successfully",
"esim_id": esimID,
})
}
// GET /v1/subscribers - List all subscribers
func listSubscribers(c *gin.Context) {
// TODO: Implement MongoDB query
subscribers := []gin.H{
{
"imsi": "208930000000001",
"status": "active",
"created_at": time.Now().Add(-7 * 24 * time.Hour),
},
}
c.JSON(http.StatusOK, gin.H{
"subscribers": subscribers,
"count": len(subscribers),
})
}
// GET /v1/subscribers/:imsi - Get subscriber details
func getSubscriber(c *gin.Context) {
imsi := c.Param("imsi")
subscriber := gin.H{
"imsi": imsi,
"plmn_id": "20893",
"status": "active",
"created_at": time.Now().Add(-7 * 24 * time.Hour),
}
c.JSON(http.StatusOK, subscriber)
}
// Utility functions
func getEnv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}
func generateICCID(imsi string) string {
// ICCID format: 89 (Telecom) + CC (Country) + ISSUER + ACCOUNT + CHECK
// Simplified implementation
return fmt.Sprintf("893312%s%d", imsi[5:14], time.Now().Unix()%10)
}
+10
View File
@@ -0,0 +1,10 @@
module github.com/taas-platform/telecom-platform/apps/carrier-connector
go 1.26
require (
github.com/go-resty/resty/v2 v2.15.3
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/joho/godotenv v1.5.1
github.com/rs/zerolog v1.33.0
)
+133
View File
@@ -0,0 +1,133 @@
package main
import (
"crypto/tls"
"fmt"
"log"
"os"
"time"
"github.com/go-resty/resty/v2"
"github.com/joho/godotenv"
"github.com/rs/zerolog"
)
var logger zerolog.Logger
type ES2Client struct {
baseURL string
apiKey string
httpClient *resty.Client
}
type ProfileOrder struct {
ICCID string `json:"iccid"`
IMSI string `json:"imsi"`
K string `json:"k"`
OPc string `json:"opc"`
MCC string `json:"mcc"`
MNC string `json:"mnc"`
ProfileType string `json:"profileType"`
}
type ProfileResponse struct {
ActivationCode string `json:"activationCode"`
ProfileID string `json:"profileId"`
Status string `json:"status"`
}
func main() {
// Initialize logger
logger = zerolog.New(os.Stdout).With().
Timestamp().
Str("service", "carrier-connector").
Logger()
// Load environment variables
if err := godotenv.Load(); err != nil {
logger.Info().Msg("No .env file found, using system environment")
}
logger.Info().Msg("Carrier Connector started")
// Initialize ES2+ client
smdpURL := getEnv("SMDP_URL", "https://smdp.example.com")
apiKey := getEnv("SMDP_API_KEY", "test-api-key")
client := NewES2Client(smdpURL, apiKey)
// Example: Order a profile
order := &ProfileOrder{
ICCID: "8933123456789012345",
IMSI: "208930000000001",
K: "465B5CE8B199B49FAA5F0A2EE238A6BC",
OPc: "E8ED289DEBA952E4283B54E88E6183CA",
MCC: "208",
MNC: "93",
ProfileType: "operational",
}
logger.Info().
Str("imsi", order.IMSI).
Msg("Ordering eSIM profile from SM-DP+")
// In production, this would be triggered by API requests
response, err := client.OrderProfile(order)
if err != nil {
logger.Error().Err(err).Msg("Failed to order profile")
} else {
logger.Info().
Str("activation_code", response.ActivationCode).
Str("profile_id", response.ProfileID).
Msg("Profile ordered successfully")
}
// Keep service running
logger.Info().Msg("Carrier Connector running. Press Ctrl+C to exit.")
select {}
}
func NewES2Client(baseURL, apiKey string) *ES2Client {
client := resty.New().
SetBaseURL(baseURL).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", apiKey)).
SetHeader("Content-Type", "application/json").
SetTimeout(10 * time.Second).
SetTLSClientConfig(&tls.Config{
MinVersion: tls.VersionTLS12,
})
return &ES2Client{
baseURL: baseURL,
apiKey: apiKey,
httpClient: client,
}
}
func (c *ES2Client) OrderProfile(order *ProfileOrder) (*ProfileResponse, error) {
var response ProfileResponse
// NOTE: This is a simplified example
// In production, implement full GSMA ES2+ protocol
resp, err := c.httpClient.R().
SetBody(order).
SetResult(&response).
Post("/gsma/rsp2/es2plus/createProfile")
if err != nil {
return nil, fmt.Errorf("ES2+ API call failed: %w", err)
}
if resp.StatusCode() != 200 {
return nil, fmt.Errorf("SM-DP+ returned error: %s", resp.String())
}
return &response, nil
}
func getEnv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}
+3
View File
@@ -0,0 +1,3 @@
REDIS_URI=redis://127.0.0.1/
SERVER_PORT=8080
RUST_LOG=info
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "charging-engine"
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[dependencies]
tokio = { workspace = true }
redis = { workspace = true }
axum = { workspace = true }
tower = { workspace = true }
tower-http = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
anyhow = { workspace = true }
dotenv = { workspace = true }
uuid = { workspace = true }
[[bin]]
name = "charging-engine"
path = "src/main.rs"
+228
View File
@@ -0,0 +1,228 @@
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use redis::AsyncCommands;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tower_http::cors::{Any, CorsLayer};
use tracing::{info, warn};
#[derive(Clone)]
struct AppState {
redis: redis::Client,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize tracing
tracing_subscriber::fmt()
.with_target(false)
.compact()
.init();
// Load environment variables
dotenv::dotenv().ok();
// Connect to Redis
let redis_url = std::env::var("REDIS_URI").unwrap_or_else(|_| "redis://127.0.0.1/".to_string());
let redis_client = redis::Client::open(redis_url)?;
// Test connection
let mut conn = redis_client.get_multiplexed_async_connection().await?;
let _: () = redis::cmd("PING").query_async(&mut conn).await?;
info!("Connected to Redis successfully");
let state = Arc::new(AppState {
redis: redis_client,
});
// Build HTTP API with CORS
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
let app = Router::new()
.route("/v1/credit/:ip/check", post(check_credit))
.route("/v1/credit/:ip/deduct", post(deduct_credit))
.route("/v1/credit/:ip/add", post(add_credit))
.route("/v1/credit/:ip/balance", get(get_balance))
.route("/health", get(health_check))
.layer(cors)
.with_state(state);
// Start server
let port = std::env::var("SERVER_PORT").unwrap_or_else(|_| "8080".to_string());
let addr = format!("0.0.0.0:{}", port);
let listener = tokio::net::TcpListener::bind(&addr).await?;
info!("Charging engine listening on {}", addr);
axum::serve(listener, app).await?;
Ok(())
}
#[derive(Deserialize)]
struct CreditCheckRequest {
bytes_requested: u64,
}
#[derive(Serialize)]
struct CreditCheckResponse {
allowed: bool,
remaining_bytes: i64,
}
/// POST /v1/credit/:ip/check
/// Check if user has enough credit for data usage
async fn check_credit(
Path(ip): Path<String>,
State(state): State<Arc<AppState>>,
Json(req): Json<CreditCheckRequest>,
) -> impl IntoResponse {
let mut conn = match state.redis.get_multiplexed_async_connection().await {
Ok(c) => c,
Err(e) => {
warn!("Redis connection failed: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(CreditCheckResponse {
allowed: false,
remaining_bytes: 0,
}),
);
}
};
let key = format!("credit:{}", ip);
// Get current credit balance
let credit: i64 = conn.get(&key).await.unwrap_or(0);
let allowed = credit >= req.bytes_requested as i64;
info!(
"Credit check for {}: {} bytes requested, {} bytes available, allowed: {}",
ip, req.bytes_requested, credit, allowed
);
(
StatusCode::OK,
Json(CreditCheckResponse {
allowed,
remaining_bytes: credit,
}),
)
}
#[derive(Deserialize)]
struct DeductRequest {
bytes_used: u64,
}
/// POST /v1/credit/:ip/deduct
/// Deduct bytes from user's credit balance
async fn deduct_credit(
Path(ip): Path<String>,
State(state): State<Arc<AppState>>,
Json(req): Json<DeductRequest>,
) -> impl IntoResponse {
let mut conn = match state.redis.get_multiplexed_async_connection().await {
Ok(c) => c,
Err(e) => {
warn!("Redis connection failed: {}", e);
return StatusCode::INTERNAL_SERVER_ERROR;
}
};
let key = format!("credit:{}", ip);
// Atomically subtract bytes
let new_balance: i64 = conn.decr(&key, req.bytes_used).await.unwrap_or(0);
info!(
"User {} deducted {} bytes, remaining: {}",
ip, req.bytes_used, new_balance
);
StatusCode::OK
}
#[derive(Deserialize)]
struct AddCreditRequest {
bytes_to_add: u64,
}
/// POST /v1/credit/:ip/add
/// Add bytes to user's credit balance
async fn add_credit(
Path(ip): Path<String>,
State(state): State<Arc<AppState>>,
Json(req): Json<AddCreditRequest>,
) -> impl IntoResponse {
let mut conn = match state.redis.get_multiplexed_async_connection().await {
Ok(c) => c,
Err(e) => {
warn!("Redis connection failed: {}", e);
return StatusCode::INTERNAL_SERVER_ERROR;
}
};
let key = format!("credit:{}", ip);
// Atomically add bytes
let new_balance: i64 = conn.incr(&key, req.bytes_to_add).await.unwrap_or(0);
info!(
"User {} added {} bytes, new balance: {}",
ip, req.bytes_to_add, new_balance
);
StatusCode::OK
}
#[derive(Serialize)]
struct BalanceResponse {
ip: String,
balance_bytes: i64,
}
/// GET /v1/credit/:ip/balance
/// Get current credit balance
async fn get_balance(
Path(ip): Path<String>,
State(state): State<Arc<AppState>>,
) -> impl IntoResponse {
let mut conn = match state.redis.get_multiplexed_async_connection().await {
Ok(c) => c,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(BalanceResponse {
ip: ip.clone(),
balance_bytes: 0,
}),
);
}
};
let key = format!("credit:{}", ip);
let balance: i64 = conn.get(&key).await.unwrap_or(0);
(
StatusCode::OK,
Json(BalanceResponse {
ip,
balance_bytes: balance,
}),
)
}
async fn health_check() -> &'static str {
"OK"
}
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "packet-gateway"
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[dependencies]
aya = { workspace = true }
aya-log = { workspace = true }
tokio = { workspace = true }
redis = { workspace = true }
anyhow = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
dotenv = { workspace = true }
clap = { version = "4.5", features = ["derive"] }
[[bin]]
name = "packet-gateway"
path = "src/main.rs"
+79
View File
@@ -0,0 +1,79 @@
use anyhow::{Context, Result};
use clap::Parser;
use redis::AsyncCommands;
use tokio::time::{interval, Duration};
use tracing::{info, warn, error};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Network interface to attach XDP program
#[arg(short, long, default_value = "eth0")]
interface: String,
/// Redis connection URL
#[arg(short, long, default_value = "redis://127.0.0.1/")]
redis_url: String,
/// Stats sync interval in seconds
#[arg(short, long, default_value = "1")]
sync_interval: u64,
}
#[tokio::main]
async fn main() -> Result<()> {
// Initialize tracing
tracing_subscriber::fmt()
.with_target(false)
.compact()
.init();
dotenv::dotenv().ok();
let args = Args::parse();
info!("Starting Packet Gateway");
info!("Interface: {}", args.interface);
info!("Redis URL: {}", args.redis_url);
// NOTE: This is a simplified version
// For full eBPF implementation, you need:
// 1. Compile eBPF program with aya-bpf
// 2. Load and attach XDP program
// 3. Access eBPF maps for packet stats and credit control
// Connect to Redis
let redis_client = redis::Client::open(args.redis_url.as_str())?;
let mut redis_conn = redis_client.get_multiplexed_async_connection().await?;
// Test connection
let _: () = redis::cmd("PING").query_async(&mut redis_conn).await?;
info!("Connected to Redis successfully");
// Simulate packet processing loop
// In production, this would read from eBPF maps
let mut ticker = interval(Duration::from_secs(args.sync_interval));
info!("Packet gateway running. Press Ctrl+C to exit.");
loop {
ticker.tick().await;
// Simulate updating packet statistics to Redis
// In real implementation, read from eBPF map
let test_ip = "10.0.0.1";
let test_bytes: u64 = 1024 * 1024; // 1 MB
let key = format!("usage:{}", test_ip);
let _: () = redis_conn.set_ex(&key, test_bytes, 3600).await?;
info!("Updated usage for {}: {} bytes", test_ip, test_bytes);
// Check credit status
let credit_key = format!("credit:{}", test_ip);
let credit: i64 = redis_conn.get(&credit_key).await.unwrap_or(0);
if credit <= 0 {
warn!("User {} has no credit - would block traffic in eBPF", test_ip);
}
}
}
-5
View File
@@ -1,5 +0,0 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->
-1
View File
@@ -1 +0,0 @@
@AGENTS.md
+100
View File
@@ -0,0 +1,100 @@
services:
mongodb:
image: mongo:7.0
container_name: taas-mongodb
restart: unless-stopped
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: changeme
MONGO_INITDB_DATABASE: free5gc
volumes:
- mongodb_data:/data/db
networks:
- taas-network
redis:
image: redis:9-alpine
container_name: taas-redis
restart: unless-stopped
ports:
- "6379:6379"
command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- taas-network
api-server:
build:
context: .
dockerfile: deployments/docker/api-server.Dockerfile
container_name: taas-api-server
restart: unless-stopped
ports:
- "8000:8000"
environment:
MONGODB_URI: mongodb://admin:changeme@mongodb:27017/free5gc
REDIS_URI: redis://redis:6379
API_PORT: 8000
GIN_MODE: release
depends_on:
- mongodb
- redis
networks:
- taas-network
carrier-connector:
build:
context: .
dockerfile: deployments/docker/carrier-connector.Dockerfile
container_name: taas-carrier-connector
restart: unless-stopped
environment:
SMDP_URL: https://smdp.example.com
SMDP_API_KEY: your-api-key
networks:
- taas-network
charging-engine:
build:
context: .
dockerfile: deployments/docker/charging-engine.Dockerfile
container_name: taas-charging-engine
restart: unless-stopped
ports:
- "8080:8080"
environment:
REDIS_URI: redis://redis:6379
SERVER_PORT: 8080
RUST_LOG: info
depends_on:
- redis
networks:
- taas-network
web-dashboard:
build:
context: .
dockerfile: deployments/docker/web-dashboard.Dockerfile
container_name: taas-web-dashboard
restart: unless-stopped
ports:
- "3000:3000"
environment:
NEXT_PUBLIC_API_URL: http://api-server:8000
depends_on:
- api-server
networks:
- taas-network
volumes:
mongodb_data:
driver: local
redis_data:
driver: local
networks:
taas-network:
driver: bridge
+237
View File
@@ -0,0 +1,237 @@
# TaaS Platform API Documentation
## Overview
The TaaS Platform provides a RESTful API for managing cellular connectivity programmatically. All API endpoints are available at `https://api.taas-platform.com/v1` (production) or `http://localhost:8000/v1` (development).
## Authentication
All API requests require authentication using Bearer tokens:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.taas-platform.com/v1/esims
```
Get your API key from the [Developer Dashboard](http://localhost:3000).
## Endpoints
### Health Check
**GET** `/health`
Check API server status.
**Response:**
```json
{
"status": "healthy",
"service": "api-server",
"timestamp": 1711929600
}
```
### Create eSIM
**POST** `/v1/esims`
Provision a new eSIM with cellular connectivity.
**Request:**
```json
{
"data_plan": "1GB",
"country_code": "US",
"carrier": "optional-carrier-name"
}
```
**Response:**
```json
{
"esim_id": "550e8400-e29b-41d4-a716-446655440000",
"imsi": "208930000000001",
"iccid": "8933123456789012345",
"activation_code": "LPA:1$smdp.example.com$MATCHING_ID",
"status": "provisioned",
"data_plan": "1GB",
"country_code": "US",
"created_at": "2026-04-01T12:00:00Z"
}
```
### Get eSIM Details
**GET** `/v1/esims/:id`
Retrieve information about a specific eSIM.
**Response:**
```json
{
"esim_id": "550e8400-e29b-41d4-a716-446655440000",
"imsi": "208930000000001",
"iccid": "8933123456789012345",
"status": "active",
"data_plan": "1GB",
"created_at": "2026-04-01T12:00:00Z"
}
```
### Get eSIM Usage
**GET** `/v1/esims/:id/usage`
Get data usage statistics for an eSIM.
**Response:**
```json
{
"esim_id": "550e8400-e29b-41d4-a716-446655440000",
"data_used": 262144000,
"data_limit": 1073741824,
"sessions": [
{
"started_at": "2026-04-01T10:00:00Z",
"ended_at": "2026-04-01T11:00:00Z",
"bytes_up": 10485760,
"bytes_down": 52428800
}
],
"updated_at": "2026-04-01T12:00:00Z"
}
```
### Terminate eSIM
**DELETE** `/v1/esims/:id`
Permanently terminate an eSIM.
**Response:**
```json
{
"message": "eSIM terminated successfully",
"esim_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
## Status Codes
- `200 OK` - Request successful
- `201 Created` - Resource created
- `400 Bad Request` - Invalid request parameters
- `401 Unauthorized` - Missing or invalid authentication
- `404 Not Found` - Resource not found
- `500 Internal Server Error` - Server error
## Rate Limits
- **Free Tier**: 100 requests/hour
- **Pro Tier**: 10,000 requests/hour
- **Enterprise**: Unlimited
Rate limit headers are included in all responses:
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1711933200
```
## SDKs
### TypeScript/JavaScript
```bash
npm install @taas-platform/sdk
```
```typescript
import { TaasClient } from '@taas-platform/sdk';
const client = new TaasClient({ apiKey: 'YOUR_API_KEY' });
// Create eSIM
const esim = await client.esims.create({
dataPlan: '1GB',
countryCode: 'US'
});
// Get usage
const usage = await client.esims.getUsage(esim.esimId);
```
### Go
```bash
go get github.com/taas-platform/sdk-go
```
```go
import "github.com/taas-platform/sdk-go"
client := taas.NewClient("YOUR_API_KEY")
// Create eSIM
esim, err := client.ESims.Create(&taas.CreateESIMRequest{
DataPlan: "1GB",
CountryCode: "US",
})
```
## Webhooks
Configure webhooks to receive real-time notifications:
### Events
- `esim.created` - New eSIM provisioned
- `esim.activated` - eSIM activated on device
- `usage.threshold` - Data usage threshold reached (50%, 75%, 90%)
- `usage.depleted` - Data plan exhausted
- `esim.terminated` - eSIM terminated
### Webhook Payload
```json
{
"event": "usage.threshold",
"timestamp": "2026-04-01T12:00:00Z",
"data": {
"esim_id": "550e8400-e29b-41d4-a716-446655440000",
"threshold": 75,
"data_used": 805306368,
"data_limit": 1073741824
}
}
```
## Examples
### Create and Activate eSIM
```bash
# 1. Create eSIM
curl -X POST https://api.taas-platform.com/v1/esims \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data_plan": "1GB",
"country_code": "US"
}'
# 2. Show QR code to user (activation_code)
# User scans QR code on their device
# 3. Monitor usage
curl https://api.taas-platform.com/v1/esims/ESIM_ID/usage \
-H "Authorization: Bearer YOUR_API_KEY"
```
## Support
- **Documentation**: https://docs.taas-platform.com
- **API Status**: https://status.taas-platform.com
- **Support Email**: support@taas-platform.com
- **GitHub Issues**: https://github.com/nutcas3/telecom-platform/issues
+408
View File
@@ -0,0 +1,408 @@
# TaaS Platform Architecture
## System Overview
The TaaS Platform is a multi-layer telecom infrastructure that provides cellular connectivity as a service. It implements 3GPP standards while exposing a modern, developer-friendly API.
## Architecture Layers
```
┌─────────────────────────────────────────────────────────────┐
│ Developer Interface │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Web UI │ │ TypeScript │ │ REST API │ │
│ │ Dashboard │ │ SDK │ │ Webhooks │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Business Support System (BSS) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ API Server │ │ Carrier │ │ Subscriber │ │
│ │ (Go 1.26) │ │ Connector │ │ Management │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Operations Support System (OSS) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Charging │ │ Usage │ │ eSIM │ │
│ │ Engine │ │ Tracking │ │ Provisioning│ │
│ │ (Rust) │ │ (Redis) │ │ (ES2+) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Telecom Core Network │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 5G Core │ │ Packet │ │ Data Plane │ │
│ │ (free5GC) │ │ Gateway │ │ (eBPF) │ │
│ │ (Go) │ │ (Rust) │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Component Details
### 1. API Server (Go 1.26)
**Purpose**: Developer-facing RESTful API and business logic
**Technology**:
- Language: Go 1.26 (Green Tea GC, new(expr) syntax)
- Framework: Gin
- Database: MongoDB (subscriber data)
- Cache: Redis (session management)
**Responsibilities**:
- eSIM creation and management
- Authentication and authorization
- Rate limiting and quotas
- Webhook delivery
- API documentation
**Endpoints**:
- `POST /v1/esims` - Create eSIM
- `GET /v1/esims/:id` - Get eSIM details
- `GET /v1/esims/:id/usage` - Get usage stats
- `DELETE /v1/esims/:id` - Terminate eSIM
### 2. Carrier Connector (Go 1.26)
**Purpose**: Integration with GSMA SM-DP+ servers for eSIM provisioning
**Technology**:
- Language: Go 1.26
- Protocol: HTTPS (ES2+ API)
- Vendor: Thales, G+D, or Idemia
**Responsibilities**:
- Profile ordering via ES2+ protocol
- Activation code generation (LPA strings)
- Profile status tracking
- Vendor-specific adaptations
**Flow**:
1. Receive eSIM order from API Server
2. Call SM-DP+ vendor API with IMSI, K, OPc keys
3. Receive activation code (LPA:1$...)
4. Return to API Server for user display
### 3. Charging Engine (Rust 1.94)
**Purpose**: Real-time credit control and billing
**Technology**:
- Language: Rust 1.94 (array_windows, async Tokio)
- Framework: Axum
- Database: Redis (in-memory credit balances)
**Responsibilities**:
- Credit balance management
- Real-time authorization checks
- Usage deduction (atomic operations)
- Credit top-up processing
**API**:
- `POST /v1/credit/:ip/check` - Check if user has credit
- `POST /v1/credit/:ip/deduct` - Deduct bytes used
- `POST /v1/credit/:ip/add` - Add credit
- `GET /v1/credit/:ip/balance` - Get balance
**Performance**:
- Sub-millisecond response times
- Atomic Redis operations (INCR, DECR)
- Horizontal scaling via Redis Cluster
### 4. Packet Gateway (Rust 1.94 + eBPF)
**Purpose**: High-performance packet processing and traffic control
**Technology**:
- Language: Rust 1.94
- Framework: Aya (pure Rust eBPF)
- Attach Point: XDP (eXpress Data Path)
- Protocol: GTP-U decapsulation (UDP port 2152)
**Responsibilities**:
- Parse GTP-U tunneled packets
- Extract user IP addresses
- Count bytes per subscriber
- Block traffic for users without credit
- Sync stats to Redis
**Architecture**:
```
┌─────────────────────────────────────────────┐
│ Network Interface (eth0) │
└─────────────────────────────────────────────┘
│
┌─────────▼─────────┐
│ XDP Hook │ ← eBPF program runs here
│ (Kernel) │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ eBPF Maps │
│ - Packet Stats │
│ - Credit Status │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ Userspace Daemon │ ← Rust application
│ (Reads maps, │
│ syncs to Redis) │
└───────────────────┘
```
**Packet Processing**:
1. Packet arrives at NIC
2. XDP hook inspects Ethernet → IP → UDP → GTP-U headers
3. Extract inner IP packet (actual user data)
4. Look up credit status in eBPF map
5. If credit > 0: `XDP_PASS`, else: `XDP_DROP`
6. Update packet statistics in eBPF map
7. Userspace daemon reads stats, syncs to Redis
### 5. 5G Core Network (free5GC)
**Purpose**: 3GPP-compliant 5G standalone core
**Technology**:
- Project: free5GC v4.2.1
- Language: Go
- Database: MongoDB
- Deployment: Kubernetes or bare metal
**Network Functions**:
- **AMF** (Access and Mobility Management)
- **SMF** (Session Management)
- **UPF** (User Plane Function)
- **PCF** (Policy Control)
- **UDM** (Unified Data Management)
- **UDR** (Unified Data Repository)
- **AUSF** (Authentication Server)
- **NRF** (NF Repository)
**Integration Points**:
- API Server writes subscriber data to MongoDB
- UPF forwards GTP-U traffic to Packet Gateway
- AMF handles UE authentication using stored keys (K, OPc)
### 6. Web Dashboard (Next.js 15)
**Purpose**: Developer portal and management interface
**Technology**:
- Framework: Next.js 15 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS
- Data Fetching: TanStack Query
**Features**:
- eSIM creation and management
- Real-time usage charts (Recharts)
- API key management
- Webhook configuration
- Billing and invoices
## Data Flow
### eSIM Creation Flow
```
1. Developer → API Server
POST /v1/esims { data_plan: "1GB" }
2. API Server → IMSI Allocator
Generate unique 15-digit IMSI
3. API Server → Key Generator
Generate K (subscriber key)
Generate OPc (operator key)
4. API Server → MongoDB (free5GC)
Insert subscriber data:
- subscriptionData.authenticationData (K, OPc)
- subscriptionData.provisionedData (IMSI, AMBR)
5. API Server → Carrier Connector
Order eSIM profile via ES2+
6. Carrier Connector → SM-DP+ Vendor
HTTPS POST with IMSI, K, OPc
7. SM-DP+ → Carrier Connector
Return activation code (LPA string)
8. API Server → Developer
Return eSIM details + activation code
9. Developer → End User
Display QR code with LPA string
10. End User's Device → SM-DP+
Scan QR, download eSIM profile
11. Device → free5GC AMF
Registration procedure (5G AKA)
12. free5GC → Packet Gateway
Establish GTP-U tunnel for data
```
### Real-Time Usage Flow
```
1. User Device sends data
↓
2. free5GC UPF encapsulates in GTP-U
↓
3. Packet Gateway (eBPF XDP)
- Decapsulate GTP-U
- Extract user IP
- Check credit status in eBPF map
↓
4. If credit > 0:
- Pass packet (XDP_PASS)
- Increment byte counter in eBPF map
↓
5. Userspace daemon (Rust)
- Reads eBPF maps every 1 second
- Writes stats to Redis
↓
6. Charging Engine
- Reads usage from Redis
- Deducts from credit balance
- Updates credit status
↓
7. Packet Gateway
- Reads new credit status from Redis
- Updates eBPF map
↓
8. If credit reaches 0:
- eBPF drops packets (XDP_DROP)
- User's internet stops
```
## Scaling Architecture
### Horizontal Scaling
**Stateless Components** (can be replicated):
- API Server: 3+ replicas behind load balancer
- Carrier Connector: 2+ replicas
- Charging Engine: 2+ replicas
- Web Dashboard: CDN + multiple origins
**Stateful Components** (require special handling):
- MongoDB: Replica set (1 primary, 2 secondaries)
- Redis: Cluster mode or Sentinel
- Packet Gateway: One instance per physical server (eBPF)
- free5GC: Kubernetes deployment with dedicated UPF nodes
### Performance Metrics
**Throughput**:
- API Server: 10,000 requests/second
- Charging Engine: 50,000 credit checks/second
- Packet Gateway: 10 Gbps+ per server (eBPF)
- free5GC UPF: 5 Gbps per instance
**Latency**:
- API Server: <50ms p99
- Charging Engine: <1ms p99 (Redis)
- Packet Gateway: <1µs (eBPF in kernel)
- eSIM Provisioning: <2 seconds end-to-end
## Security
### Authentication & Authorization
- API keys with HMAC signatures
- JWT tokens for web dashboard
- OAuth 2.0 for third-party integrations
- Rate limiting per API key
### Data Encryption
- TLS 1.3 for all API endpoints
- MongoDB encryption at rest
- Redis TLS connections
- eBPF packet inspection (no decryption)
### Secrets Management
- Kubernetes Secrets for production
- HashiCorp Vault for sensitive keys
- Environment variables for local dev
- No hardcoded credentials
## Deployment
### Development
```bash
make all # Build all components
make db-setup # Set up MongoDB + Redis
make dev # Start all services
```
### Docker Compose
```bash
docker-compose up -d
```
### Kubernetes
```bash
kubectl apply -f deployments/kubernetes/
```
### Production Checklist
- [ ] MongoDB replica set configured
- [ ] Redis Cluster or Sentinel enabled
- [ ] TLS certificates installed
- [ ] Monitoring (Prometheus, Grafana)
- [ ] Logging (ELK Stack or Loki)
- [ ] Backup strategy implemented
- [ ] Disaster recovery plan documented
- [ ] Load testing completed (10x expected traffic)
## Monitoring
### Metrics (Prometheus)
- `api_requests_total` - Total API requests
- `esim_creations_total` - eSIMs created
- `data_usage_bytes_total` - Total data usage
- `charging_checks_duration_seconds` - Credit check latency
- `packet_gateway_packets_total` - Packets processed
### Alerts
- High error rate (>1%)
- Low credit balances (<10% of users)
- MongoDB replica lag (>10 seconds)
- Redis memory usage (>90%)
- Packet gateway packet loss (>0.1%)
## Future Enhancements
1. **Multi-Region Deployment**
- Deploy in multiple AWS regions
- Global load balancing with Route 53
- Data residency compliance
2. **Advanced Analytics**
- Real-time dashboards with WebSockets
- Predictive usage forecasting
- Anomaly detection (unusual data patterns)
3. **Additional Carriers**
- Support for multiple SM-DP+ vendors
- Dynamic carrier selection based on region
- Fallback mechanisms
4. **IoT Optimization**
- SGP.32 support (IoT eSIM standard)
- LPWAN protocol support (NB-IoT, LTE-M)
- Power-saving features
5. **Developer Tools**
- CLI tool for API interaction
- Terraform provider
- GitHub Actions integration
+454
View File
@@ -0,0 +1,454 @@
# Deployment Guide
This guide covers deploying the TaaS Platform in different environments.
## Table of Contents
1. [Local Development](#local-development)
2. [Docker Deployment](#docker-deployment)
3. [Kubernetes Deployment](#kubernetes-deployment)
4. [Production Considerations](#production-considerations)
---
## Local Development
### Prerequisites
- Go 1.26+
- Rust 1.94+
- Node.js 22+
- MongoDB 7.0+
- Redis 7+
- pnpm
### Quick Start
```bash
# 1. Clone repository
git clone https://github.com/nutcas3/telecom-platform.git
cd telecom-platform
# 2. Run setup script
chmod +x scripts/dev-setup.sh
./scripts/dev-setup.sh
# 3. Set up databases
make db-setup
# 4. Build all components
make all
# 5. Start services (in separate terminals)
# Terminal 1: API Server
./dist/api-server
# Terminal 2: Carrier Connector
./dist/carrier-connector
# Terminal 3: Charging Engine
./target/release/charging-engine
# Terminal 4: Web Dashboard
cd apps/web-dashboard && pnpm dev
```
### Environment Variables
Create `.env` files in each service directory:
**apps/api-server/.env:**
```env
MONGODB_URI=mongodb://localhost:27017/free5gc
REDIS_URI=redis://localhost:6379
API_PORT=8000
GIN_MODE=debug
```
**apps/charging-engine/.env:**
```env
REDIS_URI=redis://127.0.0.1/
SERVER_PORT=8080
RUST_LOG=info
```
**apps/web-dashboard/.env.local:**
```env
NEXT_PUBLIC_API_URL=http://localhost:8000
```
---
## Docker Deployment
### Build Images
```bash
# Build all Docker images
make docker-build
# Or build individually
docker build -f deployments/docker/api-server.Dockerfile -t taas-api-server .
docker build -f deployments/docker/charging-engine.Dockerfile -t taas-charging-engine .
docker build -f deployments/docker/web-dashboard.Dockerfile -t taas-web-dashboard .
```
### Run with Docker Compose
```bash
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose down
# Stop and remove volumes
docker-compose down -v
```
### Service URLs
After starting with docker-compose:
- **API Server**: http://localhost:8000
- **Charging Engine**: http://localhost:8080
- **Web Dashboard**: http://localhost:3000
- **MongoDB**: mongodb://localhost:27017
- **Redis**: redis://localhost:6379
### Troubleshooting
**MongoDB connection issues:**
```bash
# Check if MongoDB is running
docker-compose ps mongodb
# View MongoDB logs
docker-compose logs mongodb
# Connect to MongoDB shell
docker exec -it taas-mongodb mongosh
```
**Redis connection issues:**
```bash
# Test Redis connection
docker exec -it taas-redis redis-cli ping
```
---
## Kubernetes Deployment
### Prerequisites
- Kubernetes cluster (v1.28+)
- kubectl configured
- Persistent storage provisioner
- LoadBalancer service support (or Ingress controller)
### Deploy to Kubernetes
```bash
# 1. Create namespace
kubectl create namespace taas-platform
# 2. Deploy all manifests
kubectl apply -f deployments/kubernetes/
# 3. Verify deployments
kubectl get pods -n taas-platform
# Expected output:
# NAME READY STATUS RESTARTS AGE
# mongodb-xxx 1/1 Running 0 2m
# redis-xxx 1/1 Running 0 2m
# api-server-xxx 1/1 Running 0 1m
# charging-engine-xxx 1/1 Running 0 1m
```
### Access Services
```bash
# Get API Server external IP
kubectl get svc api-server -n taas-platform
# Port forward for local access
kubectl port-forward -n taas-platform svc/api-server 8000:8000
kubectl port-forward -n taas-platform svc/charging-engine 8080:8080
```
### Scaling
```bash
# Scale API Server
kubectl scale deployment api-server -n taas-platform --replicas=5
# Scale Charging Engine
kubectl scale deployment charging-engine -n taas-platform --replicas=3
# Enable autoscaling
kubectl autoscale deployment api-server -n taas-platform \
--min=3 --max=10 --cpu-percent=70
```
### Monitoring
```bash
# View logs
kubectl logs -f deployment/api-server -n taas-platform
# Describe pod for issues
kubectl describe pod <pod-name> -n taas-platform
# Get events
kubectl get events -n taas-platform --sort-by='.lastTimestamp'
```
### Update Deployment
```bash
# Update image
kubectl set image deployment/api-server \
api-server=taas-api-server:v2.0.0 \
-n taas-platform
# Check rollout status
kubectl rollout status deployment/api-server -n taas-platform
# Rollback if needed
kubectl rollout undo deployment/api-server -n taas-platform
```
---
## Production Considerations
### High Availability
**MongoDB Replica Set:**
```yaml
# Use MongoDB Operator or StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
replicas: 3
serviceName: mongodb
# ... (see MongoDB Operator docs)
```
**Redis Cluster:**
```bash
# Deploy Redis in cluster mode
helm install redis bitnami/redis-cluster \
--namespace taas-platform \
--set cluster.nodes=6 \
--set cluster.replicas=1
```
### Security
**Secrets Management:**
```bash
# Create Kubernetes secrets
kubectl create secret generic mongodb-secret \
--from-literal=username=admin \
--from-literal=password='STRONG_PASSWORD' \
-n taas-platform
kubectl create secret generic api-keys \
--from-literal=jwt-secret='JWT_SECRET' \
-n taas-platform
```
**Network Policies:**
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-server-policy
namespace: taas-platform
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: web-dashboard
ports:
- protocol: TCP
port: 8000
egress:
- to:
- podSelector:
matchLabels:
app: mongodb
ports:
- protocol: TCP
port: 27017
```
### Monitoring Stack
**Prometheus & Grafana:**
```bash
# Install using Helm
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
-n monitoring --create-namespace
```
**Application Metrics:**
- Add `/metrics` endpoint to each service
- Use Prometheus client libraries
- Create Grafana dashboards
### Backup Strategy
**MongoDB Backups:**
```bash
# Daily backups with mongodump
kubectl create cronjob mongodb-backup \
--image=mongo:7.0 \
--schedule="0 2 * * *" \
-- mongodump --uri=$MONGODB_URI --out=/backup
```
**Redis Persistence:**
```bash
# Enable AOF in Redis config
kubectl edit configmap redis-config -n taas-platform
# Set: appendonly yes
```
### Resource Limits
**Recommended limits per service:**
| Service | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---------|-------------|-----------|----------------|--------------|
| API Server | 100m | 500m | 128Mi | 512Mi |
| Charging Engine | 100m | 1000m | 128Mi | 512Mi |
| Packet Gateway | 250m | 2000m | 256Mi | 1Gi |
| MongoDB | 250m | 1000m | 512Mi | 2Gi |
| Redis | 100m | 500m | 256Mi | 2Gi |
### Load Testing
```bash
# Install k6
brew install k6 # macOS
# or: sudo apt-get install k6
# Run load test
k6 run scripts/loadtest.js
```
**Sample load test script:**
```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up
{ duration: '5m', target: 100 }, // Stay at 100 users
{ duration: '2m', target: 200 }, // Ramp to 200
{ duration: '5m', target: 200 }, // Stay at 200
{ duration: '2m', target: 0 }, // Ramp down
],
};
export default function () {
const res = http.get('http://api-server:8000/health');
check(res, { 'status is 200': (r) => r.status === 200 });
sleep(1);
}
```
### Disaster Recovery
**Backup Locations:**
- MongoDB backups: S3 bucket (cross-region replication)
- Redis RDB snapshots: S3 bucket
- Configuration: Git repository
- Secrets: HashiCorp Vault or AWS Secrets Manager
**Recovery Time Objective (RTO):** <1 hour
**Recovery Point Objective (RPO):** <15 minutes
**Recovery Procedure:**
1. Spin up new Kubernetes cluster
2. Restore MongoDB from latest backup
3. Restore Redis from snapshot
4. Deploy all services from Git
5. Update DNS to point to new cluster
6. Verify functionality
### CI/CD Pipeline
**GitHub Actions example:**
```yaml
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker images
run: make docker-build
- name: Push to registry
run: |
docker tag taas-api-server registry.example.com/taas-api-server:${{ github.sha }}
docker push registry.example.com/taas-api-server:${{ github.sha }}
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/api-server \
api-server=registry.example.com/taas-api-server:${{ github.sha }} \
-n taas-platform
```
### Cost Optimization
**Cloud Provider Recommendations:**
- Use spot/preemptible instances for non-critical services
- Enable autoscaling to match load
- Use reserved instances for predictable workloads
- Store backups in cheaper storage classes (S3 Glacier)
**Estimated Monthly Costs (AWS):**
- 3x t3.medium (API Server): $90
- 2x t3.medium (Charging Engine): $60
- 1x t3.large (MongoDB): $75
- 1x t3.small (Redis): $19
- Load Balancer: $25
- Data transfer: Variable
- **Total**: ~$270/month (base infrastructure)
---
## Support
For deployment issues:
- Check logs: `kubectl logs -f <pod-name>`
- Review events: `kubectl get events`
- Join community: [Discussions](https://github.com/taas-platform/discussions)
- Email: devops@taas-platform.com
+6
View File
@@ -0,0 +1,6 @@
go 1.26
use (
./apps/api-server
./apps/carrier-connector
)
+28
View File
@@ -0,0 +1,28 @@
{
"name": "telecom-platform",
"version": "1.0.0",
"description": "Sovereign Telecom-as-a-Service Platform",
"private": true,
"scripts": {
"dev": "pnpm --filter web-dashboard dev",
"build": "pnpm -r run build",
"lint": "pnpm -r run lint",
"test": "pnpm -r run test",
"clean": "pnpm -r exec rm -rf .next dist node_modules"
},
"keywords": [
"telecom",
"5g",
"esim",
"taas",
"cellular",
"connectivity"
],
"author": "TaaS Team",
"license": "MIT",
"engines": {
"node": ">=22.0.0",
"pnpm": ">=9.0.0"
},
"packageManager": "[email protected]"
}
+3
View File
@@ -0,0 +1,3 @@
packages:
- 'apps/web-dashboard'
- 'libs/shared-ts-sdk'
+144
View File
@@ -0,0 +1,144 @@
#!/bin/bash
set -e
echo "========================================="
echo "TaaS Platform - Development Setup"
echo "========================================="
echo ""
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to check command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check Go 1.26
echo -n "Checking Go 1.26... "
if command_exists go; then
GO_VERSION=$(go version | awk '{print $3}' | sed 's/go//')
if [[ "$GO_VERSION" == 1.26* ]]; then
echo -e "${GREEN}✓${NC} Found $(go version)"
else
echo -e "${YELLOW}⚠${NC} Found Go $GO_VERSION (1.26 recommended)"
fi
else
echo -e "${RED}✗${NC} Not found"
echo "Please install Go 1.26: https://go.dev/dl/"
fi
# Check Rust 1.94
echo -n "Checking Rust 1.94... "
if command_exists rustc; then
RUST_VERSION=$(rustc --version | awk '{print $2}')
if [[ "$RUST_VERSION" == 1.94* ]]; then
echo -e "${GREEN}✓${NC} Found $(rustc --version)"
else
echo -e "${YELLOW}⚠${NC} Found Rust $RUST_VERSION (1.94 recommended)"
fi
else
echo -e "${RED}✗${NC} Not found"
echo "Please install Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
fi
# Check Node.js 22
echo -n "Checking Node.js 22... "
if command_exists node; then
NODE_VERSION=$(node --version | sed 's/v//')
if [[ "$NODE_VERSION" == 22* ]]; then
echo -e "${GREEN}✓${NC} Found $(node --version)"
else
echo -e "${YELLOW}⚠${NC} Found Node.js $NODE_VERSION (22 recommended)"
fi
else
echo -e "${RED}✗${NC} Not found"
echo "Please install Node.js 22: https://nodejs.org/"
fi
# Check pnpm
echo -n "Checking pnpm... "
if command_exists pnpm; then
echo -e "${GREEN}✓${NC} Found $(pnpm --version)"
else
echo -e "${RED}✗${NC} Not found"
echo "Installing pnpm..."
npm install -g pnpm
fi
# Check MongoDB
echo -n "Checking MongoDB... "
if command_exists mongosh; then
echo -e "${GREEN}✓${NC} Found"
else
echo -e "${YELLOW}⚠${NC} MongoDB shell not found"
echo "Please install MongoDB: https://www.mongodb.com/docs/manual/installation/"
fi
# Check Redis
echo -n "Checking Redis... "
if command_exists redis-cli; then
echo -e "${GREEN}✓${NC} Found"
else
echo -e "${YELLOW}⚠${NC} Redis not found"
echo "Install Redis: sudo apt-get install redis-server"
fi
# Check Docker
echo -n "Checking Docker... "
if command_exists docker; then
echo -e "${GREEN}✓${NC} Found $(docker --version | cut -d ' ' -f3)"
else
echo -e "${YELLOW}⚠${NC} Docker not found (optional)"
fi
echo ""
echo "========================================="
echo "Installing Dependencies"
echo "========================================="
echo ""
# Install Go dependencies
echo "Installing Go dependencies..."
cd apps/api-server && go mod download && cd ../..
cd apps/carrier-connector && go mod download && cd ../..
# Install Rust dependencies
echo "Installing Rust dependencies..."
cargo fetch
# Install Node.js dependencies
echo "Installing Node.js dependencies..."
pnpm install
echo ""
echo "========================================="
echo "Creating Environment Files"
echo "========================================="
echo ""
# Create .env files from examples
for env_example in $(find . -name ".env.example"); do
env_file="${env_example%.example}"
if [ ! -f "$env_file" ]; then
cp "$env_example" "$env_file"
echo "Created $env_file"
else
echo "Skipped $env_file (already exists)"
fi
done
echo ""
echo "========================================="
echo "Setup Complete!"
echo "========================================="
echo ""
echo "Next steps:"
echo "1. Set up databases: make db-setup"
echo "2. Build all components: make all"
echo "3. Start development: make dev"
echo ""
echo "For help: make help"
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
set -e
echo "Setting up MongoDB for TaaS Platform..."
# Check if MongoDB is installed
if ! command -v mongosh &> /dev/null; then
echo "MongoDB shell (mongosh) not found. Please install MongoDB first."
echo "Visit: https://www.mongodb.com/docs/manual/installation/"
exit 1
fi
# MongoDB connection details
MONGO_HOST=${MONGO_HOST:-"localhost"}
MONGO_PORT=${MONGO_PORT:-"27017"}
MONGO_USER=${MONGO_USER:-"free5gc"}
MONGO_PASS=${MONGO_PASS:-"free5gc_password"}
MONGO_DB="free5gc"
echo "Creating MongoDB database and user..."
# Create database and user
mongosh --host $MONGO_HOST --port $MONGO_PORT <<EOF
use admin
// Create user if not exists
if (!db.getUser("$MONGO_USER")) {
db.createUser({
user: "$MONGO_USER",
pwd: "$MONGO_PASS",
roles: [
{ role: "readWrite", db: "$MONGO_DB" },
{ role: "dbAdmin", db: "$MONGO_DB" }
]
})
print("User $MONGO_USER created successfully")
} else {
print("User $MONGO_USER already exists")
}
use $MONGO_DB
// Create collections for free5GC
db.createCollection("subscriptionData.provisionedData.amData")
db.createCollection("subscriptionData.authenticationData.authenticationSubscription")
db.createCollection("subscriptionData.provisionedData.smData")
db.createCollection("policyData.ues.amData")
db.createCollection("policyData.ues.smData")
print("Collections created successfully")
EOF
echo "MongoDB setup complete!"
echo ""
echo "Connection string: mongodb://$MONGO_USER:$MONGO_PASS@$MONGO_HOST:$MONGO_PORT/$MONGO_DB"
echo ""
echo "Update your .env files with:"
echo "MONGODB_URI=mongodb://$MONGO_USER:$MONGO_PASS@$MONGO_HOST:$MONGO_PORT/$MONGO_DB"
+66
View File
@@ -0,0 +1,66 @@
#!/bin/bash
set -e
echo "Setting up Redis for TaaS Platform..."
# Check if Redis is installed
if ! command -v redis-cli &> /dev/null; then
echo "Redis CLI not found. Please install Redis first."
echo "Ubuntu/Debian: sudo apt-get install redis-server"
echo "macOS: brew install redis"
exit 1
fi
# Redis connection details
REDIS_HOST=${REDIS_HOST:-"localhost"}
REDIS_PORT=${REDIS_PORT:-"6379"}
echo "Testing Redis connection..."
# Test connection
if redis-cli -h $REDIS_HOST -p $REDIS_PORT ping > /dev/null 2>&1; then
echo "✓ Redis is running and accessible"
else
echo "✗ Cannot connect to Redis at $REDIS_HOST:$REDIS_PORT"
echo "Please start Redis: sudo systemctl start redis-server"
exit 1
fi
# Configure Redis for TaaS
echo "Configuring Redis..."
redis-cli -h $REDIS_HOST -p $REDIS_PORT <<EOF
# Set memory limit
CONFIG SET maxmemory 2gb
CONFIG SET maxmemory-policy allkeys-lru
# Disable RDB persistence for speed (optional)
# CONFIG SET save ""
# Enable AOF for durability (optional)
# CONFIG SET appendonly yes
# Save configuration
CONFIG REWRITE
EOF
echo "Redis configuration complete!"
echo ""
echo "Connection string: redis://$REDIS_HOST:$REDIS_PORT/"
echo ""
echo "Update your .env files with:"
echo "REDIS_URI=redis://$REDIS_HOST:$REDIS_PORT/"
# Create some test data
echo ""
echo "Creating test credit balances..."
redis-cli -h $REDIS_HOST -p $REDIS_PORT <<EOF
SET credit:10.0.0.1 1073741824
SET credit:10.0.0.2 5368709120
SET credit:10.0.0.3 10737418240
EOF
echo "✓ Test credit balances created"
echo " - 10.0.0.1: 1 GB"
echo " - 10.0.0.2: 5 GB"
echo " - 10.0.0.3: 10 GB"