A small, embeddable policy system for making runtime decisions via a compact, language-agnostic wire format.
- Policy data is encoded with FlatBuffers.
- There’s a C runtime that evaluates policies against a host-provided context and executes registered actions.
- Go helpers generate policy buffers (and can emit a C header containing an embedded policy buffer for static inclusion).
The library is intended to be used by auto_inject and other projects as a standalone component.
- Compact, versionable policy representation (FlatBuffers schema in fbs-schema/)
- Tri-state boolean evaluation with short-circuiting (TRUE, FALSE, ABSTAIN)
- Composable rules via AND, OR, NOT over a tree of evaluators
- String, signed numeric, and unsigned numeric evaluators
- Pluggable action handlers (execute after rule evaluation)
- C library (libpolicies.a) with clean public headers
- Go tools and examples to build policy buffers and embed them in C
c/include/policies/*.h: Public C API (actions, eval context, evaluators, error codes, etc.)src/: Evaluator engine, context, "wire" mappings, and FlatBuffers integrations (C/flatcc)examples/: Minimal programs showing how to register evaluators/actions and evaluate policy buffersMakefile: Builds the static library and examples
go/schema/: Go code generated from the FlatBuffers schemaexample_writer/: Programmatic examples for building .bin policy buffersexample_generate_c_header_buffer/: Emits a C header with an embedded policy buffer (for static inclusion)example_json_to_hardcoded_injector_policies/: Converts a JSON policy description into a FlatBuffer (and a C header)Makefile: Regenerates Go schema and runs examples
fbs-schema/: FlatBuffers schema definitions (actions, evaluators, nodes, policies)Makefile: Multi-language orchestrator for C and Go
You’ll need the FlatBuffers toolchains and compilers:
- C toolchain (clang/gcc) and ar
- flatcc (for generating C readers): https://github.com/dvidelabs/flatcc
- flatc (for generating Go schema): https://github.com/google/flatbuffers
- Go (per go.mod, Go 1.23.x)
- Optional: clang-format for formatting (used by
make fmt)
From the policies/ directory:
- Build both C and Go parts:
make all
- Build C library only:
make -C c all
- Build Go examples (regenerates Go schema):
make -C go examples
- Build all examples (C + Go):
make examples
- Clean:
make clean
C build outputs:
- Static library:
c/lib/libpolicies.a - Policy debugger:
c/lib/basic-debugger - Generated C schema headers:
c/src/generated/(viaflatcc)
Go examples output:
- Binary buffers: under example-specific
out/ - Optional C headers with embedded buffers (see below)
High-level steps:
- Initialize the evaluation context with
plcs_eval_ctx_init(). - Optionally register custom evaluator functions with:
plcs_eval_ctx_register_str_evaluator(func, plcs_string_evaluators id)plcs_eval_ctx_register_num_evaluator(func, plcs_numeric_evaluators id)plcs_eval_ctx_register_unum_evaluator(func, plcs_numeric_evaluators id)If you do not register, built-in default evaluators are used.
- Set context parameters your policy relies on:
plcs_eval_ctx_set_str_eval_param(plcs_string_evaluators id, const char* value)plcs_eval_ctx_set_num_eval_param(plcs_numeric_evaluators id, long value)plcs_eval_ctx_set_unum_eval_param(plcs_numeric_evaluators id, unsigned long value)
- Register action handlers using:
plcs_eval_ctx_register_action(plcs_action_function_ptr, plcs_actions id)Action signature:plcs_errors (*plcs_action_function_ptr)(plcs_evaluation_result res, char* values[], size_t value_len, const char* description, int action_id)
- Load a policy buffer (from disk, mmap, or embedded array) and call:
plcs_errors plcs_evaluate_buffer(const uint8_t* buffer)
Examples to review under c/examples/:
basic-reader.c: reads a binary flat buffer policy file into memory and parses itmmap-reader.c: mmaps a binary flat buffer policy file and parses ithardcoded-header-reader.c: uses an embedded C header buffer and parses ithardcoded_cmdline_evaluator.c: reads a binary flat buffer policy file or a hardcoded header buffer and parses it (multiple evalutaors are used)
Build all C examples:
make -C c examples
Notes:
- If you do not register custom evaluator functions, the library falls back to built-in defaults:
- String:
exact/prefix/suffix/contains - Numeric (signed/unsigned):
==, >, >=, <, <=
- String:
- You still must provide the context parameter values (
plcs_eval_ctx_set_*_eval_param) for evaluators referenced by the policy.
Create policies programmatically and save them as FlatBuffers:
- Run all Go examples (also regenerates Go schema):
make -C go examples
Examples:
go/example_writer: emits out/*.bin - these are binary flat buffer policy files that can be used with the C examples.go/example_generate_c_header_buffer: emits out/buffer.bin and out/buffer.h (C header withconst uint8_t hardcoded_policies[])go/example_json_to_hardcoded_injector_policies: converts a JSON file (skips.json by default) to out/hardcoded.h and hardcoded.bin
Include the generated header in your C app or examples (see c/examples/hardcoded-header-reader.h).
Schema lives in fbs-schema/:
- plcs_actions: action_ids.fbs, actions.fbs
- Evaluators: evaluator_ids.fbs, evaluators.fbs
- Nodes and composition: nodes.fbs, boolean_operation.fbs
- Policy container: policy.fbs (root type: Policies)
Policy structure (high level):
- Policies: vector of Policy
- Policy:
- description: string
- rules: NodeTypeWrapper (a tree of evaluator nodes and/or composite boolean nodes)
- actions: [Action] (executed after evaluation)
Tri-state plcs_evaluation_result:
- TRUE, FALSE, ABSTAIN ("don’t care")
Boolean composition:
- AND: FALSE dominates; TRUE if all TRUE; ABSTAIN propagates if no decisive result
- OR: TRUE dominates; FALSE if all FALSE; ABSTAIN propagates if no decisive result
- NOT: inverts TRUE/FALSE, ABSTAIN remains ABSTAIN
Evaluators:
- String: StrEvaluator with id, cmp, value
- Numeric (signed): NumEvaluator with id, cmp, value: long
- Numeric (unsigned): UNumEvaluator with id, cmp, value: ulong
plcs_actions:
- Each Action has action: ActionId, description, values: [string]
- C action handler signature:
plcs_errors (*plcs_action_function_ptr)(plcs_evaluation_result res, char* values[], size_t value_len, const char* description, int action_id)
Key public headers (under c/include/policies):
eval_ctx.h- Initialize/reset:
plcs_eval_ctx_init,plcs_eval_ctx_reset - Register evals/actions:
plcs_eval_ctx_register_* - Set parameters:
plcs_eval_ctx_set_*_eval_param - Accessors:
plcs_eval_ctx_get_*(primarily used by the engine and your custom evals) - Error tracking:
plcs_eval_ctx_get_last_error,plcs_eval_ctx_peek_last_error, per-evaluator/action error setters
- Initialize/reset:
evaluator.hplcs_evaluate_buffer(const uint8_t *buffer)— evaluate all policies from a FlatBuffers buffer
evaluator_types.h- Enums for comparator types and evaluator IDs
action.hplcs_actionsenum,plcs_action_function_ptr
evaluation_result.hplcs_evaluation_resultenum andplcs_evaluation_result_to_string(...)
error_codes.hplcs_errorsenum (e.g.,DD_ESUCCESS,DD_EIX_OVERFLOW, etc.)
- ABSTAIN is expected and not an error; it often means "insufficient context" and can be fine depending on boolean composition.
- Call
plcs_eval_ctx_init()once per process before using the API; subsequent calls return-DD_EINITIZLIED. - If you rely on default evaluators, remember to set the corresponding context parameters for any evaluators referenced by the policies.
- Action handlers should return
DD_ESUCCESSunless they truly failed;plcs_evaluate_buffersums error codes across policies.
dd-policy-engine is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.