SProto is a lightweight, self-hostable registry for managing Protobuf (.proto) file artifacts, inspired by the Buf Schema Registry but with a simpler feature set focused on versioning and artifact storage. It consists of a Go-based server backend and a command-line interface (CLI) client (protoreg-cli).
- Module Versioning: Store immutable, versioned snapshots of Protobuf modules.
- Artifact Storage: Stores
.protofiles as zip archives in an S3-compatible object store (MinIO by default). - Simple API: RESTful API for publishing, fetching, and listing modules and versions.
- CLI Client:
protoreg-clifor easy interaction with the registry from the command line. - Dockerized: Easily deployable using Docker and Docker Compose.
The system comprises the following components:
- Registry Server: A Go application providing the API. It interacts with PostgreSQL for metadata storage and MinIO (or another S3-compatible service) for artifact (zip file) storage.
- Registry Client (
protoreg-cli): A Go CLI tool used by developers to publish proto directories and fetch specific module versions. - PostgreSQL: Stores metadata about modules (namespace, name) and their versions (version string, artifact digest, storage key).
- MinIO (or S3): An S3-compatible object storage server used to store the zipped Protobuf artifacts.
graph LR
Dev[Developer Machine<br/>(Proto Files)] --> CLI(Registry Client<br/>protoreg-cli);
CLI --> Server(Registry Server<br/>Go App in Docker);
Server --> DB[(PostgreSQL<br/>Metadata)];
Server --> S3[(MinIO / S3<br/>Artifacts)];
The easiest way to run the complete system locally is using Docker Compose.
Prerequisites:
- Docker (Install Docker)
- Docker Compose (Usually included with Docker Desktop)
- Go (1.24 or later, required for building the server/CLI locally)
Steps:
- Clone the repository (if you haven't already):
git clone <repository-url> cd SProto
- Start the services:
This command builds the registry server image (if needed) and starts the server, PostgreSQL, and MinIO containers in detached mode.
docker-compose up --build -d
- Verify Services:
- Registry Server API: Should be accessible at
http://localhost:8080. Trycurl http://localhost:8080/health- it should returnOK. - MinIO Console: Access the MinIO web UI at
http://localhost:9090. Login with the default credentialsminioadmin/minioadmin. You should see thesproto-artifactsbucket created automatically. - PostgreSQL: The database is accessible internally to the server container. You can connect using tools like
psqlor a GUI client if needed (uncomment the port mapping indocker-compose.yamlto access from the host).
- Registry Server API: Should be accessible at
- Check Logs (Optional):
docker-compose logs -f
- Stop Services:
docker-compose down
The registry server (registry-server service in docker-compose.yaml) is configured primarily through environment variables. These can be set directly in the docker-compose.yaml file or passed through other means if deploying differently.
| Environment Variable | Default Value | Description |
|---|---|---|
PROTOREG_SERVER_PORT |
8080 |
Port the registry server listens on. |
PROTOREG_DB_DSN |
host=postgres user=postgres password=postgres dbname=sproto port=5432 sslmode=disable |
PostgreSQL Data Source Name. |
PROTOREG_MINIO_ENDPOINT |
minio:9000 |
MinIO server endpoint (use service name within Docker Compose). |
PROTOREG_MINIO_ACCESS_KEY |
minioadmin |
MinIO access key. Change for production! |
PROTOREG_MINIO_SECRET_KEY |
minioadmin |
MinIO secret key. Change for production! |
PROTOREG_MINIO_BUCKET |
sproto-artifacts |
Name of the MinIO bucket to store artifacts. Will be created if it doesn't exist. |
PROTOREG_MINIO_USE_SSL |
false |
Whether to use SSL/TLS when connecting to MinIO. |
PROTOREG_AUTH_TOKEN |
supersecrettoken |
Static bearer token required for publishing. Change for production! |
- Default Credentials: The default
docker-compose.yamluses insecure default credentials (minioadmin/minioadminfor MinIO,postgres/postgresfor PostgreSQL) and a default auth token (supersecrettoken). These MUST be changed for any production or shared deployment. Update the environment variables indocker-compose.yamlor your deployment configuration. - Authentication: Publishing requires a static bearer token (
PROTOREG_AUTH_TOKEN). Ensure this token is kept secret and has sufficient entropy. Consider more robust authentication mechanisms (like OIDC, API Keys per user/team) for production environments if needed (this would require code changes). - Network Exposure: Ensure only necessary ports are exposed to the network. The default
docker-compose.yamlexposes the server (8080) and MinIO UI (9090). Adjust as needed. - S3 Bucket Permissions: If using a managed S3 service, configure bucket policies appropriately to restrict access.
The CLI tool provides commands to interact with the registry.
Building the CLI:
# Build for your current OS/Arch
go build -o protoreg-cli ./cmd/cli
# Optional: Move the binary to a directory in your PATH
# sudo mv protoreg-cli /usr/local/bin/Configuration:
The CLI loads its configuration (Registry URL and API Token) with the following precedence:
- Command-line flags (
--registry-url,--api-token) - Environment variables (
PROTOREG_REGISTRY_URL,PROTOREG_API_TOKEN) - Configuration file (
~/.config/protoreg/config.yamlby default) - Default values (
registry_urldefaults tohttp://localhost:8080)
Global Flags:
--registry-url <url>: Overrides the registry URL.--api-token <token>: Overrides the API token.--config <path>: Specifies a custom config file path.--log-level <level>: Sets the logging level (debug,info,warn,error). Default isinfo.
Commands:
-
configure: Saves configuration settings to the config file.# Save registry URL ./protoreg-cli configure --registry-url http://your-registry.example.com:8080 # Save API token ./protoreg-cli configure --api-token YOUR_SECURE_TOKEN # Save both ./protoreg-cli configure --registry-url http://localhost:8080 --api-token supersecrettoken
-
publish: Zips and uploads a directory as a new module version.- Requires
--moduleand--versionflags. - Requires authentication (API token).
# Usage: ./protoreg-cli publish <directory> --module <namespace/name> --version <semver> ./protoreg-cli publish ./path/to/protos --module mycompany/user --version v1.0.0 - Requires
-
fetch: Downloads and extracts a specific module version.- Requires the
--outputflag.
# Usage: ./protoreg-cli fetch <namespace/module_name> <version> --output <dir> ./protoreg-cli fetch mycompany/user v1.0.0 --output ./downloaded-protos # Files will be extracted to ./downloaded-protos/mycompany/user/v1.0.0/
- Requires the
-
list: Lists modules or versions.# List all modules ./protoreg-cli list # List versions for a specific module ./protoreg-cli list mycompany/user
The server exposes a simple REST API under the /api/v1 base path.
Health Check:
GET /health- Success Response (200 OK):
OK(plain text)
- Success Response (200 OK):
Modules:
-
GET /api/v1/modules- Description: Lists all registered modules with their latest version.
- Success Response (200 OK):
{ "modules": [ { "namespace": "mycompany", "name": "billing", "latest_version": "v1.2.0" }, { "namespace": "mycompany", "name": "user", "latest_version": "v0.1.5" }, { "namespace": "another-org", "name": "common", "latest_version": "" // If no versions published yet } ] } - Error Response (500 Internal Server Error):
{"error": "Failed to retrieve modules"}
-
GET /api/v1/modules/{namespace}/{module_name}- Description: Lists all available versions for a specific module, sorted semantically descending.
- URL Parameters:
namespace: The module's namespace (e.g.,mycompany).module_name: The module's name (e.g.,user).
- Success Response (200 OK):
{ "namespace": "mycompany", "module_name": "user", "versions": [ "v1.0.0", "v0.9.1", "v0.9.0" ] } - Error Response (404 Not Found):
{"error": "Module not found"} - Error Response (500 Internal Server Error):
{"error": "Failed to retrieve module"}or{"error": "Failed to retrieve module versions"}
Artifacts:
-
GET /api/v1/modules/{namespace}/{module_name}/{version}/artifact- Description: Downloads the zipped artifact for a specific module version.
- URL Parameters:
namespace,module_name,version(e.g.,v1.0.0).
- Success Response (200 OK):
Content-Type: application/zipContent-Disposition: attachment; filename="{namespace}_{module_name}_{version}.zip"- Body: The raw zip file content.
- Error Response (404 Not Found):
{"error": "Module version not found"} - Error Response (500 Internal Server Error):
{"error": "Failed to retrieve module version"}or{"error": "Failed to retrieve artifact"}
-
POST /api/v1/modules/{namespace}/{module_name}/{version}- Description: Publishes a new module version artifact.
- URL Parameters:
namespace,module_name,version(e.g.,v1.0.0).
- Headers:
Authorization: Bearer <your-auth-token>(Required)Content-Type: multipart/form-data; boundary=...(Required)
- Form Data:
artifact: The zip file containing the.protofiles for this version.
- Success Response (201 Created):
{ "namespace": "mycompany", "module_name": "user", "version": "v1.0.0", "artifact_digest": "sha256:abcdef123...", // SHA256 hash of the uploaded zip "created_at": "2023-10-27T10:00:00Z" } - Error Response (400 Bad Request):
{"error": "Invalid version format"}or{"error": "Missing artifact file"}or{"error": "Failed to process artifact"} - Error Response (401 Unauthorized):
{"error": "Unauthorized"}(If token is missing or invalid) - Error Response (409 Conflict):
{"error": "Module version already exists"} - Error Response (500 Internal Server Error):
{"error": "Failed to save module metadata"}or{"error": "Failed to upload artifact"}
- Running Tests: Unit tests for the API handlers use
sqlmockfor database interactions. Run them using the standard Go test command:(Note: More comprehensive integration tests involving actual DB/MinIO interactions could be added.)go test ./internal/api/... - Building Server Binary:
go build -o sproto-server ./cmd/server - Building CLI Binary:
go build -o protoreg-cli ./cmd/cli
This project is licensed under the terms of the LICENSE file.