Schema-driven business configuration management for multi-tenant services.
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.
OpenDecree manages business-oriented configuration — approval rules, fee structures, settlement windows, feature parameters — the kind of config that lives between your infrastructure settings and your application code.
Feature flag tools (LaunchDarkly, ConfigCat, Flagsmith) focus on boolean/multivariate flags for release management — not structured business configuration with schemas.
Infrastructure config tools (etcd, Consul, Spring Cloud Config) are low-level key-value stores without typed schemas, validation, or multi-tenancy.
Cloud config services (AWS AppConfig, Azure App Configuration) offer some validation but lack schema registries, gRPC APIs, real-time subscriptions, and are vendor-locked.
What makes OpenDecree unique:
No existing open-source tool combines a schema-first approach to typed configuration with native multi-tenancy, constraint validation, field-level locking, gRPC streaming, and versioned rollback — all in a single Go binary.
- Typed values — native proto types (integer, number, string, bool, timestamp, duration, url, json) with wire-level type safety
- Schema validation — constraints (min/max, pattern, enum, JSON Schema) enforced on every write
- Multi-tenancy — apply schemas to tenants with role-based access and field-level locking
- Versioned configs — every change creates a version; rollback to any previous state
- Real-time subscriptions — gRPC streaming pushes changes to consumers instantly
- Audit trail — full history of who changed what, when, and why
- Import/Export — portable schemas and configs in YAML format
- Optimistic concurrency — safe read-modify-write with checksum validation
- Null support — null and empty string are distinct values
| 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 60sgo install github.com/opendecree/decree/cmd/server@latest
# Start with in-memory storage — zero dependencies
STORAGE_BACKEND=memory HTTP_PORT=8080 decree-server
# Open http://localhost:8080/docs for Swagger UI
# All requests need x-subject header:
curl -H "x-subject: admin" http://localhost:8080/v1/schemasgit 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
# No JWT required — metadata auth is the defaultThe 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
# Set auth identity
export DECREE_SUBJECT=admin@example.com
# Create and publish a schema
decree schema import --publish examples/config-validation/decree.schema.yaml
# Create a tenant and set config
decree tenant create --name acme --schema <schema-id> --schema-version 1
decree config set <tenant-id> payments.fee "0.5%"
decree config get-all <tenant-id>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 |
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.
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 |
Coverage badges reflect business logic only — infrastructure wrappers that are tested at the integration/e2e level are excluded from the calculation:
| Excluded | Reason |
|---|---|
store_pg.go |
PostgreSQL store implementations — thin DB wrappers, tested via e2e |
redis.go |
Redis cache/pubsub implementations — thin wrappers, tested via e2e |
storage/dbstore/ |
sqlc-generated query code |
storage/postgres.go |
Interface definitions only |
telemetry/ |
OpenTelemetry provider wiring boilerplate |
Run ./scripts/coverage.sh to calculate the server coverage, or ./scripts/coverage.sh -v for a per-function breakdown.
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.

