This repository does two related things:
- LeanACL (the Lean library) — a formal model of an organization-scoped access-control policy: users with per-org roles, resources (
storage/compute/data), optional sensitivity ondata, and permissionsread≤edit≤delete. The executable policy isgrant; permitted access isCan. Theorems inLeanACL/Theorems.leanstate security properties (e.g. sensitive-data lockdown, cross-org edit denial). - LLM evaluation harness (Python +
leanacl_verify) — a small workflow to test whether a large language model can predict allow/deny for a scenario consistently with Lean. The LLM must output structured JSON that includes both its prediction (llm_allowed) and the scenario facts (user,resource,requested_permission). Lean does not trust the LLM: it recomputes access from those facts and checks whetherllm_allowedmatches the formal decision.
If you only care about the proofs and policy, use the Lean library and lake build LeanACL. If you care about model vs. spec alignment, use the bridge and fixtures below.
This project was built during the LeanLang for Verified Autonomy Hackathon (April 17–18 + online through May 1, 2026) at the Indian Institute of Science (IISc), Bangalore. Sponsored by Emergence AI Organized by [Emergence India Labs] (https://east.emergence.ai) in collaboration with IISc Bangalore.
Goal: Treat LeanACL as ground truth and measure whether an LLM’s allow/deny prediction matches it.
Inputs to the model (conceptually):
- A natural-language scenario (orgs, user memberships, resource type/owner/sensitivity, requested permission). In batch mode this lives in a user message built from your prompts JSON file (see
fixtures/leanacl_bridge/input_prompts.json). - A fixed policy summary in the system message (same for every call from
scripts/leanacl_bridge.py). The model must follow this summary when decidingllm_allowed.
Output from the model (machine-readable):
- One JSON object per scenario, constrained by OpenAI structured outputs (
response_format.type = json_schema, strict schema). Required fields includellm_allowed,user,resource,requested_permission, plus metadata stringstrace_id,prompt_version,llm_model. See LeanACL/bridge_schema.md.
What Lean does:
- The verifier (
lake exe leanacl_verify) reads that JSON, decodesUser/Resource/Permission, computes whetherCan user resource requested_permissionholds →lean_allowed, and setsprediction_correct := (llm_allowed == lean_allowed)in the verdict.
So: the LLM predicts; Lean adjudicates; the bridge compares.
For each prompt, scripts/leanacl_bridge.py sends an HTTP POST to {OPENAI_BASE_URL}/chat/completions with a body like:
{
"model": "<from OPENAI_MODEL or --model, default gpt-4o-mini>",
"response_format": {
"type": "json_schema",
"json_schema": { "name": "leanacl_prediction", "strict": true, "schema": { "...": "see leanacl_bridge.py leanacl_prediction_json_schema()" } }
},
"messages": [
{ "role": "system", "content": "<see below>" },
{ "role": "user", "content": "<one prompt string from your prompts file>" }
]
}The user content is exactly the string from each entry in your prompts file (for example the "prompt" field in fixtures/leanacl_bridge/input_prompts.json). Those strings should describe the scenario and ask the model to decide allow/deny and fill the structured fields (including trace_id / prompt_version / llm_model as instructed in the prompt).
This is the full role: system text sent by the bridge (policy summary the model should use when setting llm_allowed):
You are evaluating LeanACL access-control scenarios. Return one structured prediction object. Include the scenario facts and set llm_allowed to your allow/deny decision.
Policy summary: same-org users can read non-sensitive resources. Same-org managers can edit/delete data. Same-org devops can edit/delete non-data resources. Permission delete implies edit and read; edit implies read. Cross-org managers can read non-sensitive resources. Cross-org developers can read non-sensitive data. Cross-org devops can read non-data resources. Sensitive data is deny-override: only same-org managers have access, and they get delete/edit/read.
(Source of truth: openai_prediction_json() in scripts/leanacl_bridge.py.)
Examples live in fixtures/leanacl_bridge/input_prompts.json. Each item is either a string or an object with "prompt": "...". That string is the entire user message for that API call — it should spell out org IDs, user id and memberships, resource facts, requested permission, and any metadata you want echoed back (trace_id, prompt_version, llm_model).
The API returns assistant content that parses to one object matching the strict schema, for example:
{
"trace_id": "prompt-sensitive-manager-delete",
"prompt_version": "input-prompts-v1",
"llm_model": "gpt-4o-mini",
"llm_allowed": true,
"user": { "id": 101, "memberships": [{ "orgId": 1, "role": "manager" }] },
"resource": { "ownerOrg": 1, "resourcetype": "data", "sensitivity": true },
"requested_permission": "delete"
}The bridge then normalizes minor aliases (e.g. write → edit, dev_ops → devops) before calling Lean.
- Read prompts file → one user message per prompt.
- POST to OpenAI with system message + structured JSON schema.
- Parse response → normalize → write temp JSON →
lake exe leanacl_verify …. - Print one JSON line per prompt with
llm_allowed,lean_allowed,llm_verified_by_lean, and fullverdict.
- Lake and Lean 4.29.0 (see
lean-toolchain; elan is typical). - Python 3 (stdlib only; no pip packages required for the bridge).
From this directory:
cd /path/to/LeanACL
lake update # fetches Mathlib and writes lake-manifest.json
lake build LeanACL
lake build leanacl_verify| Command | What it does |
|---|---|
lake build LeanACL |
Builds the LeanACL library (terms, policy, theorems, examples). |
lake build leanacl_verify |
Builds the JSON verifier executable. |
The script calls OpenAI (HTTPS, stdlib urllib) unless you use the offline stub.
| Variable | Required? | Purpose |
|---|---|---|
OPENAI_API_KEY |
Yes for live LLM | Bearer token for OpenAI Chat Completions. |
OPENAI_BASE_URL |
No | API base URL; default https://api.openai.com/v1. |
OPENAI_MODEL or LEANACL_OPENAI_MODEL |
No | Model name; default gpt-4o-mini. |
LEANACL_BRIDGE_LLM_STUB_JSON |
No | Path to a fixed prediction JSON file; when set, skips the network and returns that file for every prompt (tests / CI). |
The file must include llm_allowed (the LLM’s prediction) plus user, resource, and requested_permission. See LeanACL/bridge_schema.md and fixtures/leanacl_bridge/*.json.
python3 scripts/leanacl_bridge.py verify fixtures/leanacl_bridge/allowed_sensitive_manager.json- Prints a JSON verdict to stdout (
llm_allowed,lean_allowed,prediction_correct,reason,grant, …). - Exit code 0 if the LLM prediction matches Lean, 1 if they disagree.
Prompts file: fixtures/leanacl_bridge/input_prompts.json (or your own), shaped as { "prompts": [ ... ] } or a JSON array of prompt strings/objects with a prompt field.
export OPENAI_API_KEY="sk-..."
python3 scripts/leanacl_bridge.py run_prompts fixtures/leanacl_bridge/input_prompts.jsonOptional model override:
python3 scripts/leanacl_bridge.py run_prompts fixtures/leanacl_bridge/input_prompts.json --model gpt-4o-miniEach line of stdout is one JSON object per prompt, including llm_verified_by_lean (true when llm_allowed matches Lean). The process exits non-zero if any prompt fails the pipeline or the prediction is wrong.
export LEANACL_BRIDGE_LLM_STUB_JSON="$(pwd)/fixtures/leanacl_bridge/allowed_sensitive_manager.json"
python3 scripts/leanacl_bridge.py run_prompts fixtures/leanacl_bridge/input_prompts.jsonpython3 -m unittest tests/test_leanacl_bridge.pylake exe leanacl_verify fixtures/leanacl_bridge/allowed_sensitive_manager.json
# or write verdict to a file:
lake exe leanacl_verify fixtures/leanacl_bridge/allowed_sensitive_manager.json /tmp/verdict.json- Full JSON contract: LeanACL/bridge_schema.md.
- Library entrypoint imports: LeanACL.lean.
This project was made possible by:
Emergence AI — Hackathon sponsor Emergence India Labs — Event organizer and research direction
Indian Institute of Science (IISc), Bangalore — Academic partner, hackathon co-design, tutorials, and mentorship
Hackathon Page (https://east.emergence.ai/hackathon-april2026.html) Emergence India Labs (https://east.emergence.ai/) Emergence AI (https://www.emergence.ai/)