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.
OpenDecree 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 |
Examples by category: feature flags (LaunchDarkly, ConfigCat, Flagsmith), infrastructure config (etcd, Consul, Spring Cloud Config, AWS AppConfig, Azure App Configuration).
* Cloud config services (AWS AppConfig, Azure App Configuration) offer limited validation, but lack schema registries, multi-tenancy, and gRPC streaming β and are vendor-locked.
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.
- 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
INSECURE_LISTEN=1 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/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
# 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 |
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 |
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.

