File size: 9,372 Bytes
e28ed64
 
543a85f
 
 
e28ed64
 
543a85f
 
e28ed64
 
543a85f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
---
title: Privilege Desk
emoji: 🏒
colorFrom: blue
colorTo: indigo
sdk: docker
pinned: false
app_port: 8000
base_path: /web
---

# PrivilegeDesk

**Zero-Standing-Privilege Ops Environment for Training AI Agents**

A simulated enterprise access-control environment built on [OpenEnv](https://github.com/meta-pytorch/OpenEnv).  
An AI agent is dropped into a synthetic corporate IAM system and must handle real-world privilege management tasks β€” from reviewing a single access request to conducting a full entitlement audit.

---

## Environment Overview

| Property | Value |
|----------|-------|
| **Domain** | Identity & Access Management (IAM) |
| **Tasks** | 3 (easy β†’ medium β†’ hard) |
| **Tools** | 19 structured tool calls |
| **Reward** | Partial credit, 0.0–1.0, deterministic grading |
| **Generation** | Fully procedural β€” unique episode per seed |
| **API** | OpenEnv-compliant (reset / step / state / grader / tasks / baseline) |

---

## The 3 Tasks

### Task 1 β€” Access Decision (`access_decision`) Β· Easy

> "A new access request has arrived. Review the request, inspect the applicable policy, and decide whether to approve or deny it. If approving, select the correct role and TTL."

- **Steps**: 1–5
- **Grading**: correct decision (40%) + correct role (25%) + correct TTL (20%) + justification category (15%)
- **Baseline difficulty**: ~0.7–0.9 with a well-prompted LLM

### Task 2 β€” JIT Escalation (`jit_escalation`) Β· Medium

> "Process an urgent just-in-time privilege escalation: find the correct approval chain, route the request in order, attach the incident ticket, set the TTL, and activate the grant."

- **Steps**: 3–15
- **Grading**: correct approvers (20%) + routing order (15%) + ticket attached (15%) + role (15%) + TTL (15%) + final decision (20%)
- **Baseline difficulty**: ~0.4–0.6 with a well-prompted LLM

### Task 3 β€” Access Review (`access_review`) Β· Hard

> "Conduct an access review for a user. Identify risky, stale, or over-privileged entitlements and revoke the minimum set β€” without breaking active workflows."

- **Steps**: 5–25
- **Grading**: precision (30%) + recall (30%) + workflow preservation (20%) + policy compliance (10%) + submission (10%)
- **Baseline difficulty**: ~0.2–0.4 with a well-prompted LLM

---

## The World Model

Each episode procedurally generates a complete synthetic enterprise from a seed:

| Entity | Description |
|--------|-------------|
| **Users** | Employees with name, email, department, manager |
| **Resources** | Databases, repos, cloud projects, admin consoles |
| **Policies** | Rules: max role + max TTL + required approvers per resource |
| **Entitlements** | Current user→role→resource assignments |
| **Groups** | Team groups for inherited permissions (Task 3) |
| **Approval Chains** | Ordered approver lists per request (Task 2) |
| **Workflows** | Active pipelines that depend on entitlements (Task 3) |
| **Audit DB** | Historical access events queryable via `audit.query` |
| **Hidden State** | Ground truth β€” generated at reset, used by grader |

---

## API Reference

### Standard OpenEnv Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/reset` | POST | Start a new episode. Pass `task_id` and optional `seed`. |
| `/step` | POST | Execute a tool call. Returns observation + reward + done. |
| `/state` | GET | Current episode metadata (episode_id, step_count). |
| `/schema` | GET | JSON schemas for Action and Observation. |
| `/health` | GET | Health check. |

### Hackathon-Required Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/tasks` | GET | Lists all 3 tasks with schemas and grading weights. |
| `/grader` | POST | Returns 0.0–1.0 episode score with full breakdown. |
| `/baseline` | POST | Runs naive baseline agent on all 3 tasks. |

### Reset Parameters

```json
{
  "task_id": "access_decision",   // "access_decision" | "jit_escalation" | "access_review"
  "seed": 42,                     // optional β€” omit for random episode
  "difficulty_level": 1           // 1-3 (scales entity counts)
}
```

### Step Action Format

```json
{
  "action": {
    "tool_name": "access.decide",
    "arguments": {
      "request_id": "req_000",
      "decision": "approve",
      "role": "viewer",
      "ttl_hours": 4,
      "justification_category": "operational"
    }
  }
}
```

---

## Available Tools (19 total)

| Tool | Arguments | Used In |
|------|-----------|---------|
| `policy.lookup` | `resource_id` | T1, T2 |
| `policy.list` | β€” | T1, T2, T3 |
| `org.get_user` | `user_id` | T1, T2, T3 |
| `org.get_manager` | `user_id` | T2 |
| `org.list_users` | `department?` | T2, T3 |
| `request.view` | `request_id?` | T1, T2 |
| `request.list` | β€” | T2 |
| `approval.route` | `request_id`, `approver_id` | T2 |
| `approval.check_status` | `request_id` | T2 |
| `access.decide` | `request_id`, `decision`, `role`, `ttl_hours`, `justification_category?` | T1 |
| `access.grant` | `request_id` | T2 |
| `access.set_ttl` | `request_id`, `ttl_hours` | T2 |
| `entitlement.list` | `user_id?` | T1, T3 |
| `entitlement.inspect` | `entitlement_id` | T3 |
| `entitlement.revoke` | `entitlement_id`, `reason?` | T3 |
| `audit.query` | `user_id?`, `resource_id?`, `days?` | T3 |
| `group.resolve` | `group_id?` or `user_id?` | T3 |
| `workflow.check_active` | `user_id?`, `entitlement_id?` | T3 |
| `review.submit` | `summary?` | T3 |

---

## Reward Design

### Per-Step Rewards (returned on every `/step`)

- `+0.02–0.05` for discovering new information (policies, entitlements, audit logs)
- `+0.05–0.08` for correctly identifying or revoking a risky entitlement
- `-0.02` for tool errors
- `-0.01` for redundant (repeated) tool calls
- `-0.03` for routing to the wrong approver
- `-0.10` for revoking a workflow-critical entitlement

### Episode Score (returned when `done=True`, accessible via `/grader`)

All task graders return a weighted 0.0–1.0 score with **partial credit**. Binary 0/1 is never used β€” every field is independently gradable.

---

## Local Development

```bash
# Install dependencies
cd privilege_desk
uv sync

# Run server (hot-reload)
uv run server
# OR
uvicorn server.app:app --host 0.0.0.0 --port 8000 --reload

# Test episode generation
uv run python -c "
import sys; sys.path.insert(0, '.')
from pipeline.episode_generator import generate_episode
ws = generate_episode(task_id='access_decision', seed=42)
print('Users:', len(ws['users']), 'Policies:', len(ws['policies']))
"

# Run baseline inference (requires API key)
export API_BASE_URL=https://router.huggingface.co/v1
export HF_TOKEN=hf_...
export MODEL_NAME=meta-llama/Llama-3.3-70B-Instruct
uv run python inference.py

# Validate submission
openenv validate
```

## Docker

```bash
# Build
docker build -t privilege-desk:latest -f server/Dockerfile .

# Run
docker run -p 8000:8000 privilege-desk:latest

# Health check
curl http://localhost:8000/health
```

---

## Architecture

```
privilege_desk/
β”œβ”€β”€ models.py                    # Pydantic Action + Observation
β”œβ”€β”€ pipeline/
β”‚   β”œβ”€β”€ episode_generator.py     # Procedural world generation
β”‚   └── task_templates.py        # 3 task definitions
β”œβ”€β”€ env/
β”‚   β”œβ”€β”€ world_state.py           # Episode lifecycle (reset/step/grade)
β”‚   β”œβ”€β”€ action_router.py         # Tool dispatch + audit log
β”‚   └── tools.py                 # All 19 tool implementations
β”œβ”€β”€ reward/
β”‚   β”œβ”€β”€ grader.py                # Per-task graders (0.0–1.0)
β”‚   └── aggregator.py            # Step reward + episode score
β”œβ”€β”€ server/
β”‚   β”œβ”€β”€ app.py                   # FastAPI app (standard + custom endpoints)
β”‚   └── privilege_desk_environment.py  # OpenEnv Environment subclass
β”œβ”€β”€ inference.py                 # Baseline LLM agent
β”œβ”€β”€ openenv.yaml                 # OpenEnv spec declaration
└── Dockerfile                   # HF Spaces deployment
```

---

## Grading Details

### Task 1: Access Decision
| Component | Weight | What's Checked |
|-----------|--------|----------------|
| Correct approve/deny | 40% | Matches policy: requested role ≀ max_role |
| Correct role | 25% | Exact match to policy max_role (partial credit for lower roles) |
| Correct TTL | 20% | Within Β±2h of policy max_ttl |
| Justification category | 15% | Correct category label |

### Task 2: JIT Escalation  
| Component | Weight | What's Checked |
|-----------|--------|----------------|
| Correct approvers | 20% | Set of routed approver IDs matches required chain |
| Routing order | 15% | Sequential order matches policy chain |
| Ticket attached | 15% | Non-empty ticket_id in request |
| Correct role | 15% | Requested role ≀ policy max_role |
| Correct TTL | 15% | Within Β±2h of policy max_ttl |
| Final grant/deny | 20% | Correctly activated or denied |

### Task 3: Access Review
| Component | Weight | What's Checked |
|-----------|--------|----------------|
| Precision | 30% | Revoked entitlements that were genuinely risky |
| Recall | 30% | Risky entitlements that were caught |
| Workflow preservation | 20% | No active workflows broken |
| Policy compliance | 10% | Remaining entitlements pass policy check |
| Submission | 10% | `review.submit` was called |

---

## License

BSD-style β€” see LICENSE file.