—— capability 6 of 6 · Security

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.

— acting as

Psychologist (Dr. Priya Menon)

Current demo role · stored in a session cookie · in production, this is determined by SSO + DB-side row-level security.

Demo behaviour: switching the role only writes an audit_log row. Production behaviour: the role would gate which students, reports, and dashboards each user sees, enforced by Postgres row-level security policies.
— role × resource matrix

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
Rationale on hover — every ✓/✗ is sourced from the 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.
— audit trail

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…
What this demo does NOT have (and production would):
—— how Security works

Cookie-based role toggle, audit-everything

1
Toggle

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.

2
Gate

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.

3
Audit

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.

4
Review

The viewer below renders the last 100 audit rows. In prod this becomes a tenant-scoped, filterable, exportable trail with retention.