Nix-powered Protocol Buffers with declarative code generation and comprehensive developer tooling
Features β’ Quick Start β’ Languages β’ Examples β’ Documentation
Bufrnix provides a declarative, reproducible way to generate Protocol Buffer code for multiple languages through Nix flakes. It eliminates the complexity of managing protoc plugins, dependencies, and build environments by leveraging Nix's deterministic package management.
π See the quick start guide for a quick introduction to Bufrnix.
- π― Why Bufrnix?
- β¨ Key Features
- π Quick Start
- π Comprehensive Examples
- π Language Support
- π‘ Examples
- π οΈ Development Environment
- βοΈ Configuration Reference
- π§ Advanced Usage
- π€ Contributing
- π License
Protocol Buffer tooling has traditionally suffered from dependency hell, network dependencies, and non-reproducible builds. While Buf's remote plugin system simplifies initial setup, it introduces critical limitations that become deal-breakers for production teams:
π Network Dependency Friction
- Remote plugins require constant internet connectivity, breaking offline development
- Corporate firewalls and air-gapped environments can't access remote plugin execution
- Network latency and rate limiting slow down development workflows
- Timeout errors (
context deadline exceeded
) and service interruptions disrupt CI/CD pipelines - Geographic latency affects teams in regions distant from Buf's servers
π Security and Compliance Concerns
- Proprietary Protocol Buffer schemas must be sent to external servers for processing
- Financial services, healthcare, and government contractors can't share sensitive API definitions
- Intellectual property concerns prevent many organizations from using remote execution
- Compliance requirements (SOX, HIPAA, FedRAMP) demand local processing of technical specifications
- Supply chain security policies prohibit external dependency on third-party infrastructure
β‘ Technical Limitations
- 64KB response size limits cause silent failures with large generated outputs (affects protoc-gen-grpc-swift and other plugins)
- Plugins requiring file system access or multi-stage generation cannot function remotely
- "All" strategy requirement prevents efficient directory-based generation optimizations
- Custom plugins require expensive Pro/Enterprise subscriptions
- Plugin ecosystem growth is bottlenecked by centralized approval processes
- Cross-plugin dependencies (like protoc-gen-gotag modifying generated Go code) are impossible
π Reproducibility Challenges
- Network variability introduces non-determinism in generated code
- Plugin version updates can break existing workflows without warning
- Cache invalidation and remote infrastructure changes affect build consistency
- Migration between plugin versions often requires extensive code modifications
- Alpha-to-stable transitions have caused breaking changes requiring full codebase updates
- Remote caching can mask non-deterministic plugin behavior until production
π Local, Deterministic Execution
# All plugins execute locally with dependencies managed by Nix
languages.go = {
enable = true;
grpc.enable = true; # No network calls, no timeouts
validate.enable = true; # Full plugin ecosystem available
# Exact plugin versions cryptographically pinned
grpc.package = pkgs.protoc-gen-go-grpc; # v1.3.0 always
};
π Complete Privacy and Control
- All processing happens on your machines - schemas never leave your environment
- No external dependencies for code generation workflows
- Full control over plugin versions, updates, and security patches
- Compliance-friendly for regulated industries (SOX, HIPAA, FedRAMP)
- Supply chain integrity through cryptographic verification
β‘ Performance and Flexibility
- 60x faster builds in some cases (20 minutes β 20 seconds in CI)
- No artificial size limits (64KB) or plugin capability restrictions
- Support for custom plugins, multi-stage generation, and complex workflows
- Plugin chaining and file system access work seamlessly
- Directory-based generation strategies for optimal performance
- Parallel execution across multiple languages and plugins
π― True Reproducibility
# Same inputs = identical outputs, always
config = {
languages.go.grpc.package = pkgs.protoc-gen-go-grpc; # Exact version pinned
# Cryptographic hashes ensure supply chain integrity
# Content-addressed storage prevents version drift
# Hermetic builds with no external state
};
π Developer Experience
- Offline-first: Development continues without internet connectivity
- Zero setup:
nix develop
provides complete toolchain in seconds - Type-safe configuration: Catch errors before generation runs
- Multi-language: Generate for 8+ languages simultaneously from one config
- Plugin ecosystem: Access to all community plugins, not just Buf-approved ones
Teams using Bufrnix report:
- Eliminated "works on my machine" problems with reproducible Nix environments
- Simplified CI/CD pipelines with deterministic, cacheable builds
- Improved security posture by keeping sensitive schemas internal
- Faster iteration without network latency and rate limiting
- Better compliance with local processing requirements
- Cost savings by eliminating Pro/Enterprise subscriptions for custom plugins
- Increased developer productivity with offline-capable workflows
Bufrnix doesn't compete with Buf - it complements the Protocol Buffer ecosystem by addressing different use cases:
Buf excels at:
- Schema management and breaking change detection
- Collaborative protobuf development with buf.build registry
- Getting started quickly with zero local setup
- Managed plugin ecosystem with security guarantees
- Remote code generation for simple workflows
Bufrnix excels at:
- Local, offline-first development workflows
- Complex multi-language, multi-plugin scenarios
- Regulated environments with compliance requirements
- High-performance build pipelines at scale
- Custom plugin development and integration
- Supply chain security with cryptographic verification
The hybrid approach many teams adopt:
- Use Buf for schema validation, breaking change detection, and collaboration
- Use Bufrnix for actual code generation in production environments
- Combine both for comprehensive Protocol Buffer development workflows
This pattern maximizes the benefits of both tools while avoiding their respective limitations.
Choose Bufrnix if you need:
- Offline development capabilities
- Corporate firewall/air-gapped environment support
- Sensitive schema privacy and compliance
- Custom or community plugins not in Buf's registry
- Reproducible builds with version pinning
- High-performance local execution
- Multi-language code generation workflows
Buf's remote plugins work well for:
- Quick experimentation and getting started
- Simple, single-language projects
- Teams comfortable with external processing
- Workflows fitting within remote plugin limitations
Bufrnix doesn't replace Buf - it extends the Protocol Buffer ecosystem with local, reproducible alternatives for teams that need them.
π Multi-language Support | 13+ languages: Go, JS/TS, Python, Java, C++, C#, Kotlin, Scala, Dart, PHP, Swift, C, and documentation generation |
π§ Rich Plugin Ecosystem | gRPC, Connect, gRPC-Web, gRPC-Gateway, Twirp, and validation |
π¦ Zero Setup | All dependencies managed through Nix |
π― Declarative Configuration | Type-safe configuration with clear error messages |
π Reproducible Builds | Same output across all machines and CI/CD |
β‘ Developer Experience | Comprehensive tooling, formatting, linting, and language servers included |
- Nix with flakes enabled
- That's it! All other dependencies are managed by Nix
Create a flake.nix
in your project root:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
bufrnix.url = "github:conneroisu/bufr.nix";
bufrnix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { nixpkgs, bufrnix, ... }:
let
system = "x86_64-linux"; # or your system
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = bufrnix.lib.mkBufrnixPackage {
inherit (pkgs) lib;
inherit pkgs;
config = {
root = ./.;
protoc = {
files = ["./proto/user/v1/user.proto"];
};
languages.go = {
enable = true;
outputPath = "gen/go";
grpc.enable = true;
};
};
};
};
}
// proto/user/v1/user.proto
syntax = "proto3";
package user.v1;
option go_package = "example.com/user/v1;userv1";
service UserService {
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
message CreateUserRequest {
string name = 1;
string email = 2;
}
message CreateUserResponse {
User user = 1;
}
message GetUserRequest {
string id = 1;
}
message GetUserResponse {
User user = 1;
}
message User {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}
nix run
Generated code will appear in gen/go/user/v1/
:
user.pb.go
- Protocol Buffer messagesuser_grpc.pb.go
- gRPC service definitions
Generate code for multiple languages simultaneously:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
bufrnix.url = "github:conneroisu/bufr.nix";
bufrnix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { nixpkgs, bufrnix, ... }: {
packages.default = bufrnix.lib.mkBufrnixPackage {
inherit (nixpkgs.legacyPackages.x86_64-linux) lib pkgs;
config = {
root = ./.;
protoc = {
sourceDirectories = ["./proto"];
includeDirectories = ["./proto"];
files = [
"./proto/user/v1/user.proto"
"./proto/product/v1/product.proto"
];
};
# Go with full gRPC ecosystem
languages.go = {
enable = true;
outputPath = "gen/go";
options = ["paths=source_relative"];
grpc.enable = true;
gateway.enable = true; # HTTP/JSON transcoding
validate.enable = true; # Message validation
connect.enable = true; # Modern Connect protocol
};
# Dart for Flutter applications
languages.dart = {
enable = true;
outputPath = "lib/proto";
packageName = "my_app_proto";
grpc.enable = true;
};
# JavaScript for web and Node.js
languages.js = {
enable = true;
outputPath = "src/proto";
packageName = "my-proto";
es.enable = true; # Modern ECMAScript modules
connect.enable = true; # Connect-ES for modern RPC
grpcWeb.enable = true; # Browser-compatible gRPC
twirp.enable = true; # Twirp RPC framework
};
# PHP with Twirp support
languages.php = {
enable = true;
outputPath = "gen/php";
namespace = "MyApp\\Proto";
twirp.enable = true;
};
# Swift for iOS/macOS applications
languages.swift = {
enable = true;
outputPath = "Sources/Generated";
packageName = "MyAppProto";
};
};
};
};
}
For Go projects requiring the full ecosystem:
languages.go = {
enable = true;
outputPath = "internal/proto";
packagePrefix = "github.com/myorg/myproject";
options = [
"paths=source_relative"
"require_unimplemented_servers=false"
];
# Core gRPC support
grpc = {
enable = true;
options = [
"paths=source_relative"
"require_unimplemented_servers=false"
];
};
# HTTP/JSON gateway for REST APIs
gateway = {
enable = true;
options = [
"paths=source_relative"
"generate_unbound_methods=true"
];
};
# Message validation
validate = {
enable = true;
options = ["lang=go"];
};
# Modern Connect protocol
connect = {
enable = true;
options = ["paths=source_relative"];
};
};
For modern web applications:
languages.js = {
enable = true;
outputPath = "src/generated";
packageName = "@myorg/proto";
# Modern ECMAScript modules
es = {
enable = true;
options = [
"target=ts" # Generate TypeScript
"import_extension=.js" # ES module extensions
];
};
# Connect-ES for type-safe RPC
connect = {
enable = true;
options = [
"target=ts"
"import_extension=.js"
];
};
# gRPC-Web for browser compatibility
grpcWeb = {
enable = true;
options = [
"import_style=typescript"
"mode=grpcwebtext"
];
};
};
Language | Status | Plugins & Features | Output |
---|---|---|---|
Go | β Full | protoc-gen-go , gRPC, Connect, Gateway, Validation, VTProtobuf, Struct Transformer |
Standard Go packages with comprehensive ecosystem |
JavaScript/TypeScript | β Full | ES modules, gRPC-Web, Connect-ES, Twirp, ts-proto, Protovalidate | Modern JS/TS with type definitions |
Python | β Full | Standard protoc, gRPC, mypy, betterproto, type stubs | Python packages with optional typing |
Dart | β Full | protoc-gen-dart , gRPC support |
Flutter/server Dart classes with type safety |
PHP | β Full | Standard protoc, Twirp, Async, Laravel, Symfony, gRPC, RoadRunner | PSR-4 compatible classes with framework integration |
Java | β Full | protoc-gen-java , gRPC, Protovalidate |
Standard Java classes with build system integration |
C++ | β Full | protoc-gen-cpp , gRPC, CMake helpers |
Native C++ classes with CMake integration |
Swift | β Full | protoc-gen-swift |
iOS/macOS Swift classes with SwiftProtobuf |
C# | β Full | protoc-gen-csharp , gRPC |
.NET compatible classes with gRPC support |
Kotlin | β Full | protoc-gen-kotlin , gRPC, Connect |
JVM Kotlin classes with modern RPC support |
Scala | β Full | protoc-gen-scala , gRPC |
Scala classes with functional programming patterns |
C | β Full | protobuf-c, nanopb | Embedded-friendly C implementations |
Documentation | β Full | HTML/SVG generation, MDX templates | Rich documentation output formats |
Explore complete working examples in the examples/
directory:
cd examples/simple-flake
nix develop
go run main.go
- Basic gRPC server and client
- User management service
- Error handling and validation
cd examples/dart-example
nix develop
dart pub get
dart run lib/main.dart
dart test
- Complex protobuf messages with all field types
- Complete gRPC client implementation
- Comprehensive test suite
cd examples/js-example
nix develop
npm install && npm run build && npm start
- Multiple JavaScript output formats
- Connect-ES and gRPC-Web clients
- TypeScript integration
π PHP Twirp Example
cd examples/php-twirp
nix develop
composer install
php -S localhost:8080 -t src/
- Twirp RPC server and client
- PSR-4 autoloading
- JSON-over-HTTP communication
π Swift Example
cd examples/swift-example
nix develop
bufrnix_init
bufrnix
swift build && swift run
- Protocol Buffer messages for iOS/macOS
- Type-safe Swift code generation
- SwiftProtobuf integration
- Nix with flakes enabled
- That's it! All other dependencies are managed by Nix
# Clone the repository
git clone https://github.com/conneroisu/bufr.nix.git
cd bufrnix
# Enter development environment
nix develop
# Available commands:
dx # Edit flake.nix
lint # Run Nix linting (statix + deadnix)
nix fmt # Format all files (Nix, Markdown, TypeScript, YAML)
The documentation is built with Astro and uses MDX format for enhanced content capabilities:
cd doc
nix develop
bun install
bun run dev # http://localhost:4321
bun run build # Build static site
config = {
# Required: Root directory containing proto files
root = "./proto";
# Protoc configuration
protoc = {
sourceDirectories = ["./proto"]; # Where to find .proto files
includeDirectories = ["./proto"]; # Include path for imports
files = [ # Optional: specific files to compile
"./proto/user/v1/user.proto" # If empty, compiles all .proto files
];
};
# Debug configuration
debug = {
enable = false; # Enable debug output
verbosity = 1; # 1-3, higher = more verbose
logFile = ""; # Empty = stdout, or path to file
};
# Language configurations (see language-specific docs)
languages = { ... };
};
Each language module supports:
enable
: Boolean to enable/disable the languageoutputPath
: Where to place generated files (relative to root)options
: Array of options passed to the base protoc plugin- Plugin-specific configuration (e.g.,
grpc.enable
,connect.enable
)
See the Language Modules Documentation for complete details.
protoc = {
includeDirectories = [
"./proto"
"${pkgs.protobuf}/include" # Include well-known types
"${googleapis}/google/api" # Google API protos
];
};
# .github/workflows/protobuf.yml
name: Generate Protobuf Code
on: [push, pull_request]
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v22
with:
extra_nix_config: |
experimental-features = nix-command flakes
- run: nix build
- run: git diff --exit-code # Ensure generated code is up-to-date
Generate different outputs for different environments:
{
packages = {
# Production build with optimizations
prod = bufrnix.lib.mkBufrnixPackage {
inherit (pkgs) lib;
inherit pkgs;
config = {
languages.go = {
enable = true;
options = ["paths=source_relative" "Mgrpc/service_config/service_config.proto=google.golang.org/grpc/serviceconfig"];
};
};
};
# Development build with validation and debugging
dev = bufrnix.lib.mkBufrnixPackage {
inherit (pkgs) lib;
inherit pkgs;
config = {
debug.enable = true;
languages.go = {
enable = true;
validate.enable = true;
gateway.enable = true;
};
};
};
};
}
We welcome contributions! Here's how you can help:
- Fork and clone the repository
- Make changes to language modules or core functionality
- Test with the example projects
- Update documentation as needed
- Submit a pull request
- Create a new module in
src/languages/
- Add configuration options to
src/lib/bufrnix-options.nix
- Create a working example in
examples/
- Update documentation
See the Language Modules README for details.
This project is licensed under the MIT License - see the LICENSE file for details.
- Protocol Buffers - Google's language-neutral data serialization
- Nix - Reproducible package management and builds
- Connect - Modern, type-safe RPC framework
- gRPC - High-performance RPC framework
- Twirp - Simple RPC framework for service-to-service communication
Questions? Check out the documentation, browse the examples, or open an issue!
Made with β€οΈ by the Bufrnix community