Embedding in Go
Use CrowdControl as a library from inside your service. Zero dependencies, pure stdlib.
Install
go get github.com/mikemackintosh/crowdcontrol
Basic usage
package main
import (
"fmt"
"github.com/mikemackintosh/crowdcontrol"
)
func main() {
eng, err := crowdcontrol.New([]string{"./policies"})
if err != nil {
panic(err)
}
doc := map[string]any{
"user": map[string]any{"name": "alex", "role": "intern"},
"request": map[string]any{"action": "delete"},
"resource": map[string]any{"environment": "production"},
}
results := eng.Evaluate(doc)
for _, r := range results {
fmt.Printf("%s [%s] passed=%v: %s\n",
r.Rule, r.Kind, r.Passed, r.Message)
}
}
With schema validation
import (
"github.com/mikemackintosh/crowdcontrol"
"github.com/mikemackintosh/crowdcontrol/evaluator"
"github.com/mikemackintosh/crowdcontrol/types"
)
schema := &types.Schema{
Fields: map[string]types.FieldType{
"user.name": types.FieldString,
"user.role": types.FieldString,
"request.action": types.FieldString,
"resource.environment": types.FieldString,
},
}
// validate at load time
warnings := evaluator.ValidatePolicy(policies, schema)
if len(warnings) > 0 {
fmt.Print(evaluator.FormatWarnings(warnings))
}
With explain mode
import "github.com/mikemackintosh/crowdcontrol/evaluator"
eng := evaluator.NewFromPolicies(policies,
evaluator.WithExplain(true),
)
results := eng.Evaluate(doc)
// each Result now has a populated Trace field
fmt.Print(evaluator.FormatExplain(results))
Default-deny mode
eng := evaluator.NewFromPolicies(policies,
evaluator.WithDefaultEffect(types.DefaultDeny),
)
Hot-reloading policies
Policies are loaded at New(). To reload, just call New() again and swap the engine. Since the engine has no mutable state beyond compiled rules, this is safe.
var eng atomic.Pointer[crowdcontrol.Engine]
func reload() error {
fresh, err := crowdcontrol.New([]string{"./policies"})
if err != nil {
return err
}
eng.Store(fresh)
return nil
}
func evaluate(doc map[string]any) []types.Result {
return eng.Load().Evaluate(doc)
}