Stop hard-coding fees, limits, and business rules β and redeploying every time one changes.
Define your business-config schema once and get validation, gRPC + REST APIs, type-safe SDKs, an admin UI, and a full audit trail β automatically.
Typed business configuration: the layer between feature flags and infrastructure config.
Alpha Software β OpenDecree is under active development. APIs, proto definitions, configuration formats, and behavior may change without notice between versions. Not recommended for production use yet.
See the Roadmap for what is stable today and the path to a 1.0 stable release.
Write a schema once:
# payments.decree.schema.yaml
fields:
payments.fee:
type: number
constraints:
min: 0.0
max: 1.0
payments.retries:
type: integer
constraints:
min: 1
max: 10
payments.enabled:
type: boolGet typed, validated reads in every language:
// Go
client := grpctransport.NewConfigClient(conn, grpctransport.WithSubject("myapp"))
fee, _ := client.GetFloat(ctx, tenantID, "payments.fee") // float64
retries, _ := client.GetInt(ctx, tenantID, "payments.retries") // int64
enabled, _ := client.GetBool(ctx, tenantID, "payments.enabled") // bool# Python
with ConfigClient("localhost:9090", subject="myapp") as client:
fee = client.get(tenant_id, "payments.fee", float)
retries = client.get(tenant_id, "payments.retries", int)
enabled = client.get(tenant_id, "payments.enabled", bool)// TypeScript
const client = new ConfigClient("localhost:9090", { subject: "myapp" });
const fee = await client.get(tenantId, "payments.fee", Number);
const retries = await client.get(tenantId, "payments.retries", Number);
const enabled = await client.get(tenantId, "payments.enabled", Boolean);OpenDecree manages business-oriented configuration β approval rules, fee structures, settlement windows, feature parameters: the config that lives between your infrastructure settings and your application code. It sits between feature flags (release toggles) and infrastructure config (low-level key-value), purpose-built for typed business configuration.
| Capability | Feature Flags | Infrastructure Config* | OpenDecree |
|---|---|---|---|
| Typed values | bool / variant only | strings only | β native types (int, number, string, bool, time, duration, url, json) |
| Schema validation | β | β | β constraints + JSON Schema, enforced on every write |
| Multi-tenant | limited (segments) | β | β first-class, with field-level locking |
| Audit trail | limited | β | β full who/what/when/why history |
| Real-time updates | β SDK polling/SSE | β watch APIs | β gRPC streaming subscriptions |
| Admin UI | β (vendor-hosted) | β | β open-source admin GUI |
| Versioning + rollback | limited | β | β every change versioned, rollback to any state |
* Cloud config services (AWS AppConfig, Azure App Configuration) offer limited validation, but lack schema registries, multi-tenancy, and gRPC streaming β and are vendor-locked. Examples by category: feature flags (LaunchDarkly, ConfigCat, Flagsmith); infrastructure config (etcd, Consul, Spring Cloud Config, AWS AppConfig, Azure App Configuration).
What makes OpenDecree unique: no other open-source tool combines schema-first typed configuration with native multi-tenancy, field-level locking, gRPC streaming, and versioned rollback in a single Go binary.
Beyond the table, OpenDecree also includes import/export of portable YAML schemas and configs, optimistic concurrency (checksum-validated read-modify-write), and first-class null support (null and empty string are distinct values).
# go install names the server binary "server" after its directory; the canonical
# name is decree-server, matching the release archives and Docker image.
go install github.com/opendecree/decree/cmd/server@latest
mv "$(go env GOPATH)/bin/server" "$(go env GOPATH)/bin/decree-server"
go install github.com/opendecree/decree/cmd/decree@latest
# Start with in-memory storage β zero dependencies
INSECURE_LISTEN=1 STORAGE_BACKEND=memory HTTP_PORT=8080 decree-server
# Open http://localhost:8080/docs for Swagger UI
curl -H "x-subject: admin" http://localhost:8080/v1/schemasTLS required in production.
INSECURE_LISTEN=1disables TLS for local development only. See Transport Security (TLS) for production setup.
git clone https://github.com/opendecree/decree.git
cd decree
# Start the full stack (PostgreSQL + Redis + migrations + service)
docker compose up -d --wait service
# gRPC at localhost:9090, REST/JSON at localhost:8080# Go install
go install github.com/opendecree/decree/cmd/decree@latest
# Homebrew (macOS / Linux) β coming in beta
brew install opendecree/tap/decree
# Download binary
# https://github.com/opendecree/decree/releases/latest| SDK | Language | Protocol | Status | Version | Install |
|---|---|---|---|---|---|
| decree | go get github.com/opendecree/decree/sdk/grpctransport@latest |
||||
| decree-python | pip install opendecree |
||||
| decree-typescript | npm install @opendecree/sdk |
||||
| REST API | Any | β | curl |
The core SDK modules require Go 1.22+ and have zero external dependencies. The grpctransport module (default transport) requires Go 1.24+ because google.golang.org/grpc pins that version β if you need a Go 1.22-compatible transport, π or comment on #90 to signal demand.
// configclient β application runtime reads and writes
client := grpctransport.NewConfigClient(conn, grpctransport.WithSubject("myapp"))
val, _ := client.GetInt(ctx, tenantID, "payments.retries")
// configwatcher β live typed values with auto-reconnect
w := grpctransport.NewWatcher(conn, tenantID, grpctransport.WithSubject("myapp"))
fee := w.Float("payments.fee", 0.01)
w.Start(ctx)
fmt.Println(fee.Get()) // always freshgo get github.com/opendecree/decree/sdk/grpctransport@latest # gRPC transport (pulls in configclient, etc.)
go get github.com/opendecree/decree/sdk/configclient@latest # core client (no gRPC dependency)
go get github.com/opendecree/decree/sdk/adminclient@latest # admin operations
go get github.com/opendecree/decree/sdk/configwatcher@latest # live config watcher
go get github.com/opendecree/decree/sdk/tools@latest # diff, docgen, validate, seed, dumpGo API docs on pkg.go.dev: grpctransport Β· configclient Β· adminclient Β· configwatcher Β· tools
from opendecree import ConfigClient
with ConfigClient("localhost:9090", subject="myapp") as client:
retries = client.get("tenant-id", "payments.retries", int)
with client.watch("tenant-id") as watcher:
fee = watcher.field("payments.fee", float, default=0.01)
print(fee.value) # always freshDocs: decree-python
import { ConfigClient } from "@opendecree/sdk";
const client = new ConfigClient("localhost:9090", { subject: "myapp" });
const retries = await client.get("tenant-id", "payments.retries", Number);
const watcher = client.watch("tenant-id");
const fee = watcher.field("payments.fee", Number, { default: 0.01 });
await watcher.start();
console.log(fee.value); // always freshDocs: decree-typescript
Runnable examples in examples/ β each is a standalone Go module you can copy into your own project.
| Example | What it shows |
|---|---|
| quickstart | Connect and read typed values |
| feature-flags | Live feature toggles with configwatcher |
| live-config | HTTP server with hot-reloadable config |
| multi-tenant | Same schema, different tenant values |
| optimistic-concurrency | Safe concurrent updates with CAS |
| schema-lifecycle | Create, publish, and manage schemas |
| environment-bootstrap | Bootstrap from a single YAML file |
| config-validation | Offline validation (no server needed) |
cd examples && make setup # start server + seed data
cd quickstart && go run . # run any examplego install github.com/opendecree/decree/cmd/decree@latest
decree schema list
decree schema import --publish decree.schema.yaml # import + auto-publish
decree tenant create --name acme --schema payroll-service --schema-version 1
decree config set acme payments.fee 0.5% # use tenant name or UUID
decree config get-all acme
decree config versions acme
decree config rollback acme 2
decree watch acme # live stream
decree lock set acme payments.currency # lock field
decree audit query --tenant acme --since 24h
# Power tools
decree seed fixtures/billing.yaml # bootstrap from a fixture
decree dump acme > backup.yaml # full tenant backup
decree diff acme 1 2 # diff two config versions
decree diff --old v1.yaml --new v2.yaml # diff two files
decree docgen payroll-service # use schema name or UUID
decree validate --schema decree.schema.yaml --config decree.config.yamlGlobal flags: --server, --subject, --role, --output table|json|yaml, --wait, --wait-timeout
Use --wait in Docker/Kubernetes init containers to wait for the server to be ready:
decree seed examples/seed.yaml --server decree:9090 --wait --wait-timeout 60sThe entire gRPC API is also available as REST/JSON (via grpc-gateway). Set HTTP_PORT to enable:
# Version check
curl http://localhost:8080/v1/version
# List schemas
curl -H "x-subject: admin" http://localhost:8080/v1/schemas
# Create a schema
curl -X POST http://localhost:8080/v1/schemas \
-H "Content-Type: application/json" \
-H "x-subject: admin" \
-d '{"name":"payments","fields":[{"path":"fee","type":7}]}'
# Get config
curl -H "x-subject: admin" http://localhost:8080/v1/tenants/{id}/configOpenAPI spec: docs/api/openapi.swagger.json
flowchart LR
subgraph Clients
direction TB
SDKs["π§ SDKs\nGo Β· Python Β· TypeScript"]
CLI[">_ CLI\ndecree"]
UI["π₯οΈ Admin UI\ndecree-ui"]
Direct["π Direct\ncurl, custom"]
end
SDKs & CLI -->|gRPC| GW
UI & Direct -->|REST| GW
GW{{"π Gateway\nauth Β· routing"}}
subgraph Server["βοΈ OpenDecree"]
SS["π SchemaService\nschemas, tenants"]
CS["π ConfigService\nread, write, subscribe"]
AS["π AuditService\nhistory, usage"]
end
GW --> SS & CS & AS
subgraph Backends["πΎ Pluggable Backends"]
Storage[("Storage")]
Cache[("Cache")]
PubSub[("Pub/Sub")]
end
SS & CS & AS --> Storage
CS --> Cache
CS <--> PubSub
Single binary exposing three gRPC services + REST/JSON gateway. All external dependencies (storage, cache, pub/sub) are behind Go interfaces β swap implementations via STORAGE_BACKEND=memory for zero-dependency evaluation or testing. Deploy with ENABLE_SERVICES to control which services run on each instance.
| Variable | Description | Default |
|---|---|---|
GRPC_PORT |
gRPC listen port | 9090 |
HTTP_PORT |
REST/JSON gateway port (disabled if empty) | disabled |
STORAGE_BACKEND |
postgres or memory |
postgres |
DB_WRITE_URL |
PostgreSQL primary connection string | required if postgres |
DB_READ_URL |
PostgreSQL read replica connection string | DB_WRITE_URL |
REDIS_URL |
Redis connection string | required if postgres |
ENABLE_SERVICES |
Services to enable: schema, config, audit |
all |
LOG_LEVEL |
debug, info, warn, error |
info |
INSECURE_LISTEN |
Set to 1 to accept plaintext gRPC (local dev only) |
disabled |
TLS_CERT_FILE |
Path to PEM server certificate | required unless INSECURE_LISTEN=1 |
TLS_KEY_FILE |
Path to PEM server private key | required unless INSECURE_LISTEN=1 |
TLS_CLIENT_CA_FILE |
CA bundle for client certificates (enables mTLS) | disabled |
RATE_LIMIT_ENABLED |
Set to false to disable rate limiting |
true |
RATE_LIMIT_ANON_RPS |
Requests per second for unauthenticated callers | 10 |
RATE_LIMIT_AUTHED_RPS |
Requests per second per authenticated tenant | 100 |
RATE_LIMIT_SUPERADMIN_RPS |
Requests per second per superadmin (0 = unlimited) |
0 |
RATE_LIMIT_BURST |
Token bucket burst size (all role classes) | 10 |
JWT is opt-in. By default, the service uses metadata-based auth:
| Variable | Description | Default |
|---|---|---|
JWT_JWKS_URL |
JWKS endpoint β enables JWT validation | disabled |
JWT_ISSUER |
Expected JWT issuer | optional |
Without JWT, pass identity via gRPC metadata headers:
x-subject(required) β actor identityx-roleβsuperadmin(default),admin, oruserx-tenant-idβ required for non-superadmin roles
| Variable | Description |
|---|---|
OTEL_ENABLED |
Master switch β initializes SDK + slog trace correlation |
OTEL_TRACES_GRPC |
gRPC server spans |
OTEL_TRACES_DB |
PostgreSQL query spans |
OTEL_TRACES_REDIS |
Redis command spans |
OTEL_METRICS_GRPC |
gRPC request count/latency |
OTEL_METRICS_DB_POOL |
Connection pool gauges |
OTEL_METRICS_CACHE |
Cache hit/miss counters |
OTEL_METRICS_CONFIG |
Config write counter + version gauge |
OTEL_METRICS_SCHEMA |
Schema publish counter |
Standard OTel variables (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME) are respected by the SDK.
The API is defined in Protocol Buffers under proto/, published to BSR at buf.build/opendecree/decree. Three gRPC services:
- SchemaService β create, version, and manage config schemas and tenants
- ConfigService β read/write typed config values, subscribe to changes, version management
- AuditService β query change history and usage statistics
Values use a TypedValue oneof β integer, number, string, bool, timestamp, duration, url, json β with null support.
The schema YAML format authors write to define their config shape is documented in Schema Format. The corresponding JSON Schema 2020-12 meta-schemas for editor IntelliSense and CI validation are published at:
https://schemas.opendecree.dev/schema/v0.1.0/decree-schema.jsonβ validates*.decree.schema.yamlhttps://schemas.opendecree.dev/schema/v0.1.0/decree-config.jsonβ validates*.decree.config.yaml
All endpoints that accept a tenant or schema ID also accept the name slug β the server resolves automatically. Use UUIDs or human-readable names interchangeably.
Generate client stubs in any language from BSR, or use the official SDKs:
| Repo | Package | |
|---|---|---|
| Go | this repo | github.com/opendecree/decree/sdk/* |
| Python | decree-python | opendecree |
| TypeScript | decree-typescript | @opendecree/sdk |
The Codecov badge reflects business logic coverage. CI also enforces a per-module coverage ratchet via scripts/check-coverage.sh β absolute floors defined in coverage-thresholds.json that can only move up.
Both Codecov and the ratchet exclude the same categories of infrastructure code:
| Excluded | Reason |
|---|---|
*/store_pg.go |
PostgreSQL store implementations β thin DB wrappers, tested via e2e |
cache/redis.go, pubsub/redis.go |
Redis wrappers β thin wrappers, tested via e2e |
*.gen.go, internal/storage/dbstore/ |
sqlc/protobuf-generated code |
The exclude list is documented in scripts/check-coverage.sh (COVERAGE_EXCLUDES).
Want to see OpenDecree in action without reading docs? The demos repo has self-contained, runnable examples β from a 5-minute quickstart to production patterns. Each demo runs with a single docker compose up.
See CONTRIBUTING.md for development setup, build instructions, and contribution guidelines.
Apache License 2.0 β see LICENSE for details.

