API Protos is the shared gRPC/Protocol Buffers definitions package for the Cloud9 multi-jurisdiction banking platform. This repository provides common communication interfaces for all Cloud9 microservices.
Version: 1.0.0 Last Updated: 2025-10-30
- Overview
- Services
- Installation
- Building Maia
- Using Maia in Services
- Development
- Versioning
- Contributing
API Protos provides:
- gRPC Service Definitions - Email and SMS service interfaces
- Protocol Buffers - Strongly-typed message definitions
- Generated Go Code - Auto-generated client and server stubs
- Service Decoupling - Enables microservices to communicate without direct dependencies
┌─────────┐ ┌──────┐ ┌────────┐
│ Hama │─────▶│ Maia │◀─────│ Valar │
│ (Auth) │ │ │ │(Email) │
└─────────┘ └──────┘ └────────┘
│ │
│ gRPC Call │
└──────────────────────────────┘
(no direct dependency)
Benefits:
- ✅ No circular dependencies
- ✅ Independent deployment
- ✅ Type-safe communication
- ✅ Version-controlled contracts
- ✅ Easy testing with mocks
Provides email sending capabilities via Valar service.
Available RPCs:
SendEmail- Send standard HTML/text emailSendTemplateEmail- Send using predefined templateSendVerificationEmail- Send email verification linkSendPasswordResetEmail- Send password reset linkSendWelcomeEmail- Send welcome email to new usersSendTransactionNotification- Send transaction alerts
Example:
import emailv1 "github.com/Cloud9Money/api-protos/proto/email/v1"
client := emailv1.NewEmailServiceClient(conn)
resp, err := client.SendVerificationEmail(ctx, &emailv1.SendVerificationEmailRequest{
To: "user@example.com",
VerificationToken: "abc123",
UserName: "John Doe",
})Provides SMS sending capabilities via Valar service.
Available RPCs:
SendSMS- Send standard SMSSendOTP- Send one-time passwordSendTransactionAlert- Send transaction notificationSendBulkSMS- Send to multiple recipientsVerifyOTP- Verify OTP code
Example:
import smsv1 "github.com/Cloud9Money/api-protos/proto/sms/v1"
client := smsv1.NewSMSServiceClient(conn)
resp, err := client.SendOTP(ctx, &smsv1.SendOTPRequest{
To: "+254712345678",
OtpCode: "123456",
ExpiryMinutes: 5,
Purpose: "login",
Jurisdiction: "KE",
})Required Tools:
- Go 1.23 or later
- Protocol Buffers compiler (
protoc) - protoc-gen-go plugin
- protoc-gen-go-grpc plugin
macOS:
brew install protobufLinux (Debian/Ubuntu):
sudo apt update
sudo apt install -y protobuf-compilerLinux (RHEL/CentOS):
sudo yum install -y protobuf-compilerOr use the install script:
cd scripts
./install-protoc.shmake install-toolsThis installs:
protoc-gen-go- Generates Go structs from protoprotoc-gen-go-grpc- Generates gRPC service stubs
protoc --version # Should show libprotoc 3.x or later
protoc-gen-go --version
protoc-gen-go-grpc --version# Clone the repository
git clone https://github.com/Cloud9Money/api-protos.git
cd protos
# Install Go dependencies
go mod download
# Install protoc plugins
make install-tools
# Generate gRPC code from proto files
make proto# Generate all services
make proto
# Generate specific service
make proto-email
make proto-sms
# Verify proto files are valid
make verify
# Clean generated files
make cleanAfter running make proto, you'll see:
protos/
├── proto/
│ ├── email/
│ │ └── v1/
│ │ ├── email.proto # Proto definition
│ │ ├── email.pb.go # Generated messages
│ │ └── email_grpc.pb.go # Generated gRPC stubs
│ └── sms/
│ └── v1/
│ ├── sms.proto # Proto definition
│ ├── sms.pb.go # Generated messages
│ └── sms_grpc.pb.go # Generated gRPC stubs
Important: Commit the generated .pb.go files to git so consumers don't need to regenerate them.
In your service's go.mod:
module github.com/Cloud9Money/hama
go 1.23
require (
github.com/Cloud9Money/api-protos v1.0.0
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
)Update dependencies:
go mod download
go mod tidyImplement the gRPC server:
package grpc
import (
"context"
emailv1 "github.com/Cloud9Money/api-protos/proto/email/v1"
)
type EmailServer struct {
emailv1.UnimplementedEmailServiceServer
emailService *service.EmailService
}
func NewEmailServer(emailService *service.EmailService) *EmailServer {
return &EmailServer{emailService: emailService}
}
func (s *EmailServer) SendVerificationEmail(
ctx context.Context,
req *emailv1.SendVerificationEmailRequest,
) (*emailv1.SendEmailResponse, error) {
// Call your internal email service
messageID, err := s.emailService.SendVerificationEmail(ctx, req)
if err != nil {
return &emailv1.SendEmailResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &emailv1.SendEmailResponse{
MessageId: messageID,
Success: true,
Status: "sent",
}, nil
}Start gRPC server:
package main
import (
"net"
emailv1 "github.com/Cloud9Money/api-protos/proto/email/v1"
"google.golang.org/grpc"
)
func main() {
lis, _ := net.Listen("tcp", ":50051")
grpcServer := grpc.NewServer()
emailService := service.NewEmailService(/* deps */)
emailServer := grpc.NewEmailServer(emailService)
emailv1.RegisterEmailServiceServer(grpcServer, emailServer)
grpcServer.Serve(lis)
}Create gRPC client:
package clients
import (
"context"
"fmt"
"time"
emailv1 "github.com/Cloud9Money/api-protos/proto/email/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type EmailClient struct {
client emailv1.EmailServiceClient
conn *grpc.ClientConn
}
func NewEmailClient(valarEndpoint string) (*EmailClient, error) {
conn, err := grpc.Dial(
valarEndpoint,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithTimeout(5*time.Second),
)
if err != nil {
return nil, fmt.Errorf("failed to connect to Valar: %w", err)
}
return &EmailClient{
client: emailv1.NewEmailServiceClient(conn),
conn: conn,
}, nil
}
func (c *EmailClient) SendVerificationEmail(
ctx context.Context,
email, token, userName string,
) error {
resp, err := c.client.SendVerificationEmail(ctx, &emailv1.SendVerificationEmailRequest{
To: email,
VerificationToken: token,
UserName: userName,
})
if err != nil {
return fmt.Errorf("gRPC call failed: %w", err)
}
if !resp.Success {
return fmt.Errorf("email send failed: %s", resp.Error)
}
return nil
}
func (c *EmailClient) Close() error {
return c.conn.Close()
}Use the client:
package handlers
func (h *AuthHandler) Register(ctx context.Context, req *RegisterRequest) error {
// Create user, generate token...
// Send verification email
err := h.emailClient.SendVerificationEmail(ctx, req.Email, token, req.Name)
if err != nil {
log.Error("Failed to send verification email", "error", err)
// Don't fail registration if email fails
}
return nil
}protos/
├── proto/ # Protocol Buffer definitions
│ ├── email/
│ │ └── v1/
│ │ ├── email.proto
│ │ ├── email.pb.go (generated)
│ │ └── email_grpc.pb.go (generated)
│ └── sms/
│ └── v1/
│ ├── sms.proto
│ ├── sms.pb.go (generated)
│ └── sms_grpc.pb.go (generated)
├── scripts/ # Helper scripts
│ └── install-protoc.sh
├── Makefile # Build automation
├── go.mod # Go module definition
├── go.sum # Go dependency checksums
├── README.md # This file
└── .gitignore # Git ignore rules
- Edit proto file (
proto/email/v1/email.proto) - Regenerate code:
make proto - Test changes: Update and test consuming services
- Commit everything: Proto file + generated code
- Tag release:
git tag v1.1.0 && git push --tags - Update consumers: Services update to new version
-
Create proto directory:
mkdir -p proto/newservice/v1
-
Create proto file:
syntax = "proto3"; package newservice.v1; option go_package = "github.com/Cloud9Money/api-protos/proto/newservice/v1;newservicev1"; service NewService { rpc DoSomething(Request) returns (Response); }
-
Add to Makefile:
proto-newservice: @protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ proto/newservice/v1/newservice.proto
-
Generate:
make proto-newservice
Validate proto files:
make verifyTest in consuming service:
cd ../hama
go test ./internal/clients/...API Protos follows Semantic Versioning:
- MAJOR (v2.0.0): Breaking changes to proto definitions
- MINOR (v1.1.0): New features (new RPCs, optional fields)
- PATCH (v1.0.1): Bug fixes, documentation
Proto files use version directories (v1, v2) for major API versions:
proto/email/
├── v1/ # Current stable version
│ └── email.proto
└── v2/ # Future version (breaking changes)
└── email.proto
When to create v2:
- Removing fields
- Changing field types
- Removing RPCs
- Major restructuring
Backward-compatible changes (stay in v1):
- Adding new fields (use optional)
- Adding new RPCs
- Adding new services
Check current version:
cd hama
go list -m github.com/Cloud9Money/api-protosUpdate to latest:
go get github.com/Cloud9Money/api-protos@latest
go mod tidyUpdate to specific version:
go get github.com/Cloud9Money/api-protos@v1.2.0
go mod tidy- Create branch:
git checkout -b feature/add-notification-service - Make changes: Edit proto files
- Generate code:
make proto - Test: Verify changes in consuming services
- Commit: Include both proto and generated files
- Create PR: Submit for review
- Tag release: After merge, tag new version
feat(email): add attachment support to SendEmail RPC
fix(sms): correct OTP expiry field type
docs: update README with new service examples
chore: update dependencies
- Proto files follow style guide
- Generated code committed
- Backward compatibility maintained (or version bumped)
- Documentation updated
- Changes tested in at least one consumer service
- go.mod version updated
Solution:
# macOS
brew install protobuf
# Linux
sudo apt install protobuf-compiler
# Or use install script
./scripts/install-protoc.shSolution:
make install-tools
# Ensure $GOPATH/bin is in your $PATH
export PATH="$PATH:$(go env GOPATH)/bin"Solution:
cd <service>
go get github.com/Cloud9Money/api-protos@latest
go mod tidySolution:
make clean
make proto- Protocol Buffers Guide: https://protobuf.dev/
- gRPC Go Tutorial: https://grpc.io/docs/languages/go/quickstart/
- Cloud9 Architecture:
../CLAUDE.md - Valar Service:
../valar/README.md - Hama Service:
../hama/README.md
- Check this README
- Review proto file comments
- Check example code in consuming services
- Open GitHub issue with:
- Proto file excerpt
- Error message
- Steps to reproduce
- Cloud9 Platform Team
Last Updated: 2025-10-30 Version: 1.0.0 License: Proprietary - Cloud9 Money