
Most embedded analytics work treats authorization as the whole privacy question: decide which rows a viewer may reach, filter to those rows, return the result. Education reporting adds a second question after that one is answered.
A teacher is allowed to see their own class. A class of six with one student in a demographic group produces an average that names that student to anyone who can read a second chart. The row filter was correct and the output still disclosed a person.
The Aggregate Is the Disclosure, Not the Row
Under FERPA, agencies and institutions reporting data derived from education records are responsible for protecting personally identifiable information in those reports. Connecticut's education department states the operating consequence directly: "If any cell is ≤ 5, the value is suppressed. This includes a total" (CSDE Data Suppression Guidelines, checked 4 September 2026).
Read that as a product requirement rather than as a policy note. A count can be authorized and still be forbidden to display, which means the decision sits between the query result and the rendered chart, and it depends on the value returned rather than on the identity asking.
That is the part most embedded stacks have nowhere to put. Row-level security answers "may this viewer see these rows"; nothing in the chart layer answers "is this permitted number too small to show".
The Threshold Is Your Customer's Setting, Not Your Default
It is tempting to ship a constant. The law removes that option.
Under ESSA, "states must specify a single value for the minimum number of students needed to provide statistically sound data for all students and for each subgroup", and the same report records that "ESSA prohibits IES from recommending any specific minimum number of students in a subgroup" (IES, Best Practices for Determining Subgroup Size, checked 4 September 2026). The federal government was asked for a number and was forbidden from giving one.
So the values differ in practice: Connecticut suppresses at five or fewer, and other agencies publish their own floors. For a platform selling into more than one jurisdiction the threshold is a per-tenant configuration with the same standing as the tenant filter itself, and a shipped default is a guess about somebody else's legal obligation.
Hiding One Cell Does Not Hide It
The rule that catches teams late is the second one, and it is arithmetic rather than policy. From the same guidelines: "If a cell is ≤ 5 and only one value is suppressed in a row or column, the next highest value in that row or column is also suppressed. If there are multiple occurrences of this value, randomly suppress one occurrence. This is referred to as complementary suppression."
The reason is subtraction. If a column shows a total and every entry but one, the hidden entry is the difference, so a table that suppresses honestly and totals honestly can publish the number it just hid.
This is why suppression cannot be a display filter. Whether a cell is safe depends on the other cells in its row and column and on which totals are shown, so the rule has to run over the whole result set in one place — the same place the tenant boundary is enforced, and the place every path reads from.
Percentages Are Suppressed on Different Grounds Than Counts
A rate can be unsafe when its inputs look fine. The same guidelines suppress a statistic when any of three conditions holds: "the count associated with the statistic has been previously suppressed", "the numerator is ≤ 5", or "the denominator is < 20".
The third condition has no privacy motive at all. A pass rate computed on twelve students is not disclosive so much as unreliable, and the rule refuses to print it for that reason.
For a product this means two different rules over the same figure, and a dashboard that applies only the privacy one will publish percentages the customer's own reporting standard forbids.
Where the Rule Has to Reach
A suppression rule that runs in one component and not the others is a rule that leaks at the seam. The list below is the same delivery surface our multi-tenant analytics architecture piece treats for tenant scope, applied to a second constraint:
- The interactive chart and the table behind it.
- Drill-through, where a permitted aggregate opens the records that compose it.
- CSV and PDF export, which is where small counts most often escape.
- Scheduled reports and alerts, which run when nobody is looking at the threshold.
- The API, if customers query it directly.
- Caches keyed before suppression was applied.
Test it the way you test the tenant boundary: build a class small enough to trip the rule, then try every one of those paths and count how many return the number.
What to Settle Before the First District Contract
- Whose threshold applies when a district, its state and your default disagree, and where that value is stored.
- Whether complementary suppression runs on rows, columns or both, and what the user sees in a suppressed cell.
- Whether a suppressed cell is visibly suppressed or silently absent, since an empty cell and a hidden cell teach the reader different things.
- Which surfaces are covered today, stated as a list rather than as a claim, because the ones you cannot name are the ones that are not covered.
The last one is the honest test. A vendor that can name the covered surfaces has implemented the rule; a vendor that answers "we support FERPA" has answered a different question.
Where to go next
- Multi-tenant analytics architecture: the tenant contract this constraint sits on top of.
- Row-level security: what a row policy does and does not decide.
- Interactive dashboard features: drill paths and the state a viewer is allowed to reach.
- Healthcare dashboard guide: the other vertical where the record itself is regulated.
- Embedded analytics complete guide: the evaluation path from data audit to pilot.
Try the Rule Against a Real Small Cohort
Build a class small enough to trip a suppression threshold, then check the chart, the drill path, the export and the schedule.


