[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"article-body-one-number-per-viewer":3},"\nThere is a quiet way to leak information that never shows anyone a\ndocument: show them a number. A regional compliance manager asks the\nsystem how many open investigations exist. The engine counts\neverything, 86, then dutifully hides the case rows she is not cleared\nfor, listing her 11. She sees both figures. She now knows 75\ninvestigations exist that someone decided she should not see, and\nsince her own region accounts for the 11, she knows the rest sit\nsomewhere beyond her scope.\n\nNo file was opened. The permissions all held. The disclosure travelled\nthrough the count.\n\n*If a total includes rows the viewer cannot see, the total is the\nleak.*\n\n## Filter-then-count, not count-then-filter\n\nThe failure has one root: the aggregate was computed over everything\nand the filtering applied afterwards, to the presentation. Every\nvariant of afterwards is the same bug. Hiding rows after totalling,\nredacting documents after retrieval, trimming a chart after the\nrollup. In each case the sensitive population has already shaped what\nthe viewer receives.\n\nThe correct order is absolute. The viewer's entitlements are applied\nfirst, inside the query, at the data layer, and every downstream\nnumber, list, ranking and answer is computed within that boundary.\nHer honest total is 11. A colleague with broader scope asks the\nidentical question and honestly receives 86. One number, per viewer,\nand neither number betrays the other's existence.\n\nThree design consequences follow, and they are where implementations\nusually fail:\n\n- **Every aggregate is scoped**: counts, sums, averages, trend lines\n  and the totals used for pagination. A pager reading \"1 of 8\" above\n  a filtered list of 11 rows is the same leak in a smaller font; the\n  eight pages were computed over the full 86.\n- **Sort order and ranking are scoped.** A \"top risks\" list computed\n  over everything and then trimmed still ordered itself using data the\n  viewer cannot see.\n- **There is exactly one path.** If a filtered endpoint and an\n  unfiltered legacy endpoint both exist, the unfiltered one will\n  eventually serve a viewer it should not. The predicate lives in one\n  place, and everything queries through it.\n\n> Security that is applied to the presentation can be forgotten once.\n> Security that is a property of the query cannot be skipped at all.\n\n## Why AI raises the stakes\n\nRetrieval-augmented assistants industrialise this problem. An\nassistant with a service account's broad access, answering questions\nfrom users with narrow access, is an exfiltration tool with a chat\ninterface: it will happily summarise documents the asker could never\nhave opened, and the summary launders the breach. The public\nconversation about enterprise AI security focuses on prompts and\njailbreaks; the plumbing question, whose permissions does the\nretrieval run under, matters more and is checked less.\n\nThe standard has to be that the assistant sees exactly what the asker\nsees. Permissions from the source systems, enforced at retrieval, per\nquery, per viewer. If you cannot see it at the source, you cannot see\nit, or feel its shape through a total, in the answer.\n\nThis is also, not incidentally, a trust property rather than only a\nsafety one. An answer computed inside your entitlements is an answer\nyou can repeat in a meeting, file in a report and defend to an\nauditor. An answer computed over the whole estate and trimmed for you\nis a number nobody can account for.\n\nProphesee's Nexus engine enforces viewer scope at the data layer for\nevery count, ranking and cited answer, entirely on-premises. Test it\nagainst your own permission model. [Start here](/contact).\n",1786799034545]