Roles, gates, and an audit trail
Switch between counsellor, admin, parent, and reviewer roles to see how the system would gate data visibility. Every action — including this role switch — appends to the audit log below.
Psychologist (Dr. Priya Menon)
Current demo role · stored in a session cookie · in production, this is determined by SSO + DB-side row-level security.
audit_log row.
Production behaviour: the role would gate which students, reports, and dashboards each user sees, enforced by Postgres row-level security policies.
Permission grid role_permissions · 16 cells
Hover any cell for the rationale. Production becomes Postgres row-level security per (role, tenant_id) — same shape, enforced at the database layer rather than the app layer.
| Role | students | reports | sessions | exports | Score |
|---|---|---|---|---|---|
| Psychologist (Dr. Priya Menon) | 4 / 4 | ||||
| Admin (Sandeep Kumar) | 4 / 4 | ||||
| Parent (Nandini Iyer) | 2 / 4 | ||||
| Senior Psychologist (Dr. Priya Menon) | 2 / 4 |
role_permissions table, not from inline if branches. The demo binary still gates writes at the handler level; in production this same data drives Postgres RLS policies so the DB rejects out-of-policy reads before they reach the app. Switching roles writes a role_switch entry to the audit log below.
50 of 148 entries
| When | User | Role | Action | Entity | Details |
|---|---|---|---|---|---|
| today | — | system | llm_call | student#37 | {"input_tokens":676,"latency_ms":10113,"llm_request_sha":"hmac:c54d83b1ea4c","llm_response_sha":"hmac:57414… |
| today | — | system | llm_call | student#34 | {"input_tokens":796,"latency_ms":10614,"llm_request_sha":"hmac:17c33a400752","llm_response_sha":"hmac:6e89d… |
| today | — | system | llm_call | student#17 | {"input_tokens":692,"latency_ms":11596,"llm_request_sha":"hmac:ba2b2c04605a","llm_response_sha":"hmac:c7d86… |
| today | — | system | llm_call | student#49 | {"input_tokens":930,"latency_ms":11142,"llm_request_sha":"hmac:8f16e8b25eb7","llm_response_sha":"hmac:2c53d… |
| today | — | system | llm_call | student#27 | {"input_tokens":950,"latency_ms":10533,"llm_request_sha":"hmac:a9f8b96175d1","llm_response_sha":"hmac:b67c2… |
| today | — | system | llm_call | student#36 | {"input_tokens":951,"latency_ms":11455,"llm_request_sha":"hmac:fb2b344d4b50","llm_response_sha":"hmac:fc0e7… |
| today | — | system | llm_call | student#25 | {"input_tokens":971,"latency_ms":10941,"llm_request_sha":"hmac:10df1ccd36aa","llm_response_sha":"hmac:3bd3b… |
| today | — | system | llm_call | student#39 | {"input_tokens":943,"latency_ms":9868,"llm_request_sha":"hmac:c00c1b54201c","llm_response_sha":"hmac:396fa0… |
| today | — | system | llm_call | student#20 | {"input_tokens":823,"latency_ms":12136,"llm_request_sha":"hmac:642e2c4c41c7","llm_response_sha":"hmac:5e0ef… |
| today | — | system | llm_call | student#10 | {"input_tokens":956,"latency_ms":11393,"llm_request_sha":"hmac:2eba28f6cbb4","llm_response_sha":"hmac:ef210… |
| today | — | system | llm_call | student#24 | {"input_tokens":959,"latency_ms":11042,"llm_request_sha":"hmac:efbee91a9453","llm_response_sha":"hmac:b1d5e… |
| today | — | system | llm_call | student#12 | {"input_tokens":966,"latency_ms":10556,"llm_request_sha":"hmac:1af2ac3fc741","llm_response_sha":"hmac:8fa02… |
| today | — | system | llm_call | student#28 | {"input_tokens":948,"latency_ms":10726,"llm_request_sha":"hmac:71544f31de88","llm_response_sha":"hmac:7a3f9… |
| today | — | system | llm_call | student#29 | {"input_tokens":939,"latency_ms":12081,"llm_request_sha":"hmac:094c2fa72b14","llm_response_sha":"hmac:9bd11… |
| today | — | system | llm_call | student#41 | {"input_tokens":798,"latency_ms":10143,"llm_request_sha":"hmac:a98699d9b127","llm_response_sha":"hmac:d5d87… |
| today | — | system | llm_call | student#32 | {"input_tokens":838,"latency_ms":11824,"llm_request_sha":"hmac:e1c9fe186dc8","llm_response_sha":"hmac:0dc51… |
| today | — | system | llm_call | student#31 | {"input_tokens":812,"latency_ms":11407,"llm_request_sha":"hmac:3d307dc15d1a","llm_response_sha":"hmac:5f5dd… |
| today | — | system | llm_call | student#43 | {"input_tokens":931,"latency_ms":11649,"llm_request_sha":"hmac:0776e6a09b9c","llm_response_sha":"hmac:5a673… |
| today | — | system | llm_call | student#52 | {"input_tokens":477,"latency_ms":11319,"llm_request_sha":"hmac:e4b363419827","llm_response_sha":"hmac:54645… |
| today | — | system | llm_call | student#23 | {"input_tokens":957,"latency_ms":11737,"llm_request_sha":"hmac:847e18bb8338","llm_response_sha":"hmac:31b06… |
| today | — | system | llm_call | student#42 | {"input_tokens":966,"latency_ms":12696,"llm_request_sha":"hmac:db26c3ec7f5a","llm_response_sha":"hmac:9f80c… |
| today | — | system | llm_call | student#16 | {"input_tokens":953,"latency_ms":9111,"llm_request_sha":"hmac:130c30ba2085","llm_response_sha":"hmac:c4d0a5… |
| today | — | system | llm_call | student#18 | {"input_tokens":967,"latency_ms":10734,"llm_request_sha":"hmac:3ddeaae7f14e","llm_response_sha":"hmac:7bb7b… |
| today | — | system | llm_call | student#13 | {"input_tokens":829,"latency_ms":10600,"llm_request_sha":"hmac:2eec135bcec5","llm_response_sha":"hmac:514bd… |
| today | — | system | llm_call | student#35 | {"input_tokens":954,"latency_ms":10199,"llm_request_sha":"hmac:da0f480bb3d1","llm_response_sha":"hmac:16aa5… |
| today | — | system | llm_call | student#15 | {"input_tokens":670,"latency_ms":9964,"llm_request_sha":"hmac:69b0e37e2133","llm_response_sha":"hmac:8809e9… |
| today | — | system | llm_call | student#21 | {"input_tokens":937,"latency_ms":10553,"llm_request_sha":"hmac:c566dce89653","llm_response_sha":"hmac:d2789… |
| today | — | system | llm_call | student#26 | {"input_tokens":953,"latency_ms":10075,"llm_request_sha":"hmac:e0e2e2b2f0a1","llm_response_sha":"hmac:6e1a5… |
| today | — | system | llm_call | student#19 | {"input_tokens":955,"latency_ms":11519,"llm_request_sha":"hmac:c7ac8a700b88","llm_response_sha":"hmac:ee278… |
| today | — | system | llm_call | student#40 | {"input_tokens":956,"latency_ms":10552,"llm_request_sha":"hmac:79473861a0e9","llm_response_sha":"hmac:771e1… |
| today | — | system | llm_call | student#46 | {"input_tokens":947,"latency_ms":14158,"llm_request_sha":"hmac:a7802a21446f","llm_response_sha":"hmac:52771… |
| today | — | system | llm_call | student#30 | {"input_tokens":923,"latency_ms":10698,"llm_request_sha":"hmac:d1dd48a6412b","llm_response_sha":"hmac:df9a5… |
| today | — | system | llm_call | student#51 | {"input_tokens":477,"latency_ms":10808,"llm_request_sha":"hmac:883c5d5a706c","llm_response_sha":"hmac:2a9de… |
| today | — | system | llm_call | student#47 | {"input_tokens":965,"latency_ms":10076,"llm_request_sha":"hmac:54f60033066b","llm_response_sha":"hmac:1b22a… |
| today | — | system | llm_call | student#48 | {"input_tokens":665,"latency_ms":10713,"llm_request_sha":"hmac:5f570accd695","llm_response_sha":"hmac:aa101… |
| today | — | system | llm_call | student#45 | {"input_tokens":685,"latency_ms":10197,"llm_request_sha":"hmac:e626251f1559","llm_response_sha":"hmac:850c4… |
| today | — | system | llm_call | student#38 | {"input_tokens":979,"latency_ms":11381,"llm_request_sha":"hmac:b7b3b860900a","llm_response_sha":"hmac:5a814… |
| today | — | system | llm_call | student#14 | {"input_tokens":927,"latency_ms":9965,"llm_request_sha":"hmac:2e4da5db4aa3","llm_response_sha":"hmac:f1434e… |
| today | — | system | llm_call | student#33 | {"input_tokens":959,"latency_ms":10098,"llm_request_sha":"hmac:bd8f0dff575f","llm_response_sha":"hmac:3e7d2… |
| today | — | system | llm_call | student#44 | {"input_tokens":802,"latency_ms":10751,"llm_request_sha":"hmac:b0ed4412af76","llm_response_sha":"hmac:3c80d… |
| today | — | system | llm_call | student#22 | {"input_tokens":823,"latency_ms":9071,"llm_request_sha":"hmac:8a4763e131b0","llm_response_sha":"hmac:ffd245… |
| today | — | system | llm_call | student#11 | {"input_tokens":931,"latency_ms":10325,"llm_request_sha":"hmac:ee0f67430cc9","llm_response_sha":"hmac:5991d… |
| today | — | system | llm_call | student#9 | {"input_tokens":668,"latency_ms":11394,"llm_request_sha":"hmac:48d3a0b11822","llm_response_sha":"hmac:320ba… |
| today | — | system | llm_call | student#3 | {"input_tokens":938,"latency_ms":10243,"llm_request_sha":"hmac:5653896d772d","llm_response_sha":"hmac:1d1ff… |
| today | — | system | llm_call | student#8 | {"input_tokens":964,"latency_ms":10564,"llm_request_sha":"hmac:b7fe081b0324","llm_response_sha":"hmac:373e2… |
| today | — | system | llm_call | student#2 | {"input_tokens":969,"latency_ms":13397,"llm_request_sha":"hmac:c35e8c64cfd0","llm_response_sha":"hmac:299e6… |
| today | — | system | llm_call | student#6 | {"input_tokens":537,"latency_ms":9686,"llm_request_sha":"hmac:494000f313bc","llm_response_sha":"hmac:ab510c… |
| today | — | system | llm_call | student#5 | {"input_tokens":990,"latency_ms":10763,"llm_request_sha":"hmac:f760b2385d29","llm_response_sha":"hmac:d47cd… |
| today | — | system | llm_call | student#4 | {"input_tokens":963,"latency_ms":10751,"llm_request_sha":"hmac:07ae875f64b4","llm_response_sha":"hmac:70c17… |
| today | — | system | llm_call | student#7 | {"input_tokens":972,"latency_ms":10825,"llm_request_sha":"hmac:80b6f9786468","llm_response_sha":"hmac:891ce… |
- Real authentication — SSO for school admins, magic-link for parents, JWT-based sessions
- Postgres row-level security policies that enforce role gates at the DB layer (not the app layer)
- Per-tenant isolation — schools become tenants, queries auto-scoped via
tenant_id - Audit retention policy — currently append-forever; prod would archive entries older than 90 days to cold storage
- Right-to-be-forgotten flow for student records under DPDP / GDPR compliance
Cookie-based role toggle, audit-everything
Role lives in a signed cookie (demo_role). The button POSTs to /security/role; server sets SET-COOKIE + redirects. No session table needed for the demo.
Every write handler reads the cookie. Viewer attempting a write returns 403 with the audit_log row — never a silent redirect. The denial is itself an audit event.
Every read of student PII, every write, every role flip writes a row into audit_log: actor, action, target, ts, ip. Append-only, never updated.
The viewer below renders the last 100 audit rows. In prod this becomes a tenant-scoped, filterable, exportable trail with retention.