Examples
Policy gate for Git
Build a fail-closed policy gate in front of the Git command line.maelys-git is a C wrapper around the git binary. Before any git command
runs, it evaluates a Datalog policy to decide whether the operation is allowed.
This wrapper runs before git is exec'd, so git hooks and --no-verify are
irrelevant to denied operations: a denied operation never reaches git at all.
This tutorial builds the wrapper in five levels of increasing rigour, with one page per level. Start at Level 1, then extend the same wrapper through the remaining levels.
The programming examples have synchronized C / Python tabs. Both languages use the current opaque consumer API: C includes <maelys/datalog.h> and <maelys/datalog_builders.h>; Python imports maelys_datalog. Follow the quickstart setup for Maelys Datalog 0.11.1. The Datalog rules are shared. These are assembly fragments: Git argument normalization and repository discovery are application code, not services provided by the binding. Neither version is a complete security boundary against a user who can bypass the wrapper.
Follow the five levels
Why not git hooks
git hook (.git/hooks/) maelys-git
──────────────────────────────────────────────────────────────
Shell scripts, not versioned Datalog in .maelys/git/policy.dl
Bypassable via --no-verify Enforced before git runs
One hook per event One policy for all operations
No audit trail Audit record per decision
Hard to test Testable with input facts
Silent conflicts Explicit deny rules + stratified negationThe full picture
Level 1 argv/env → EDB → solve → allow/deny → execv(real_git, argv)
Level 2 deny codes stable, human messages in C lookup table
Level 3 rich EDB: files, classes, tree state; C extracts, policy decides
Level 4 .maelys/git/manifest.json + policy.dl versioned in repo + SHA verify
Level 5 audit record per decision: deny code, policy hash, witness summaryThis is the exact pattern Maelys uses for agent orchestration:
observe context → project into facts → apply deterministic policy
→ produce decision → emit an audit record → execute only if allowedWhat you will learn
Learning objectives
The wrapper turns operating-system context into a policy decision- Normalize before reasoning
The C wrapper converts commands, refspecs, paths, identities, and repository state into a small declared vocabulary. Policy evaluates canonical facts, never ambiguous command-line strings.
- Keep policy outputs stable
Rules derive machine-stable decision codes; human messages live outside policy and may evolve independently. The same separation keeps audits durable.
- Context extraction is a security boundary
Every required fact must be established and every insertion result checked. A silently missing operation, identity, flag, or commit message could otherwise turn a denial into an authorization.
- Authorization stays explicit and fail-closed
Unknown subcommands, incomplete push syntax, unnormalizable refspecs, missing facts, and solver errors all deny. The wrapper executes only after the expected allow fact exists.
- Decision, execution, and audit remain separate
The solver decides once; the wrapper records policy identity, decision code, and witness summary; then execv starts an absolute trusted Git binary only for an allowed request.
Decision ruleObserve operating-system context, normalize it into declared facts, solve once, query an explicit decision, record the evidence, and execute only on ALLOW.
Where to go next
- maelys-pty — the same pattern applied to an interactive shell with forkpty and seccomp/execve tracing.
- Document access — ABAC for document ownership, sharing, and sensitivity levels.
- Manifest loading in C and Python — verified packages and loading permissions.
- Rulesets — full policy language reference.