Cube Access: Individual Exceptions
This guide is for whoever maintains the cube_access_individual_exceptions tab
in the Cube access spreadsheet — granting a specific person access beyond what
their role or department normally provides. For the engineering design behind
this sheet, see
2026-07-29-cube-access-individual-exceptions-redesign.md.
For how to add or edit a Google Sheets source in general, see the
Google Sheets & Forms guide.
Identify people by their Google address
Every row is keyed on google_email — the person's KIPP Google address, the
same one they sign in to Cube with. Not their employee number.
That is deliberate: it lets you grant access to a contractor or anyone else who has a KIPP Google login but no employment record. Those people appear in no HR feed, so there is nothing to look an employee number up from, and before this they could not be granted access at all.
Two things to get right:
- Use the address exactly as they sign in, and nothing else — not a personal address, not an alias. Capitalization and stray spaces are fine; they are cleaned up automatically.
- The account has to exist and be active. A typo, or the address of someone whose account has been deleted, grants nothing at all rather than failing loudly at the moment you type it. A validation check catches typos on the next pipeline run.
requested_by and approved_by are still employee numbers — those are staff
approving the grant, not the person receiving it.
Students and staff are separate columns
A row says how far it reaches for each kind of data, in the same words the role sheet uses:
additional_student_location_scope— student data.additional_staff_location_scope— staff data.
Both take network, region, school, or none. They are independent, so you
can widen one without the other: student = school with staff = none gives
someone student data at one school and no extra staff visibility at all.
No cell in this sheet is ever left blank. Every column takes a word or a date, so a request form can make each field required and nobody has to work out whether an empty cell was deliberate.
When you set both, they must be the same word. A row carries a single
additional_location_name, so it cannot be a region for staff and a school for
students at the same time. To give someone different breadth per kind of data,
use two rows.
network is the widest value. There is no all — all belongs to
staff_department_scope, which is a different question (which departments, not
which locations).
additional_location_name: a real name, or all, or none
Three cases, and the word always matches how much the row actually grants:
| this row's scope | write |
|---|---|
region or school |
the exact region or school name |
network |
all |
none on both axes |
none |
all and none look like they ought to be one shared "not applicable" value,
and they are not — they are opposites. A network row reaches every
location, so all is literally true. A row that only changes a visibility
setting reaches no location, so none is literally true. A single
placeholder would be wrong on whichever of the two it didn't describe.
Validation checks all three. A real school name on a network row fails — it
reads as granting that one school while actually granting everything. An all
left behind on a row that names a real school fails. So does a visibility-only
row still claiming a location.
A contradictory row also grants nothing in the meantime, rather than granting the wider of the two readings. Validation reports the mistake on the next pipeline run; the grant itself is dropped as soon as the sheet is read. Fix the row and it starts working — no cleanup needed for the window in between.
One row per additional location
Each row can grant one additional location — a whole network, a named
region, or a named school. If someone needs access to two schools, add two
rows with the same Google address, one additional_location_name each — never
try to combine two locations into a single row.
Example — access to two additional schools:
| google_email | additional_student_location_scope | additional_staff_location_scope | additional_location_name | status |
|---|---|---|---|---|
example.one@apps.teamschools.org |
school | school | KIPP BOLD Academy | active |
example.one@apps.teamschools.org |
none | school | KIPP THRIVE Academy | active |
This person gets staff access at both schools, but student data only at KIPP
BOLD Academy — the second row's additional_student_location_scope is none.
There is no "All" option
List every additional region or school as its own row — there is no shortcut
value that means "all of them." If someone genuinely needs access across the
entire network, set the scope columns to network on a single row instead
of listing every region.
Example — three additional regions (instead of one row saying "all regions"):
| google_email | additional_student_location_scope | additional_staff_location_scope | additional_location_name |
|---|---|---|---|
example.two@apps.teamschools.org |
region | region | KIPP Cooper Norcross Academy |
example.two@apps.teamschools.org |
region | region | KIPP Miami |
example.two@apps.teamschools.org |
region | region | KIPP TEAM and Family Schools Inc. |
Example — literally everything (one row, not one per region):
| google_email | additional_student_location_scope | additional_staff_location_scope | additional_location_name |
|---|---|---|---|
example.three@apps.teamschools.org |
network | network | all |
The two things a row can do
A row can do either or both of the following. A row that does neither is inert (it exists in the sheet but grants nothing).
- Grant a location (
additional_student_location_scope/additional_staff_location_scope/additional_location_name) — this is additive. It adds the named location on top of the person's normal access; it never takes away anything they already have. - Override a sensitive-field visibility setting (
staff_department_scope,staff_pii_scope,staff_compensation_scope,staff_observations_scope,staff_benefits_scope) — this replaces the person's normal setting for that field.
If a person has multiple rows for their location grants, put any visibility
overrides on only one of those rows and write inherit on the other rows —
the sheet will fail validation if two of a person's active rows each set any
override column. It does not matter whether the two rows agree, or whether they
set the same column: a second row carrying any value other than inherit in any
of the five columns fails the check.
inherit versus none on the override columns
Each of the five override columns takes one of two "do nothing much" values, and they are opposites. Read this twice:
inheritleaves the person's normal setting alone. This is almost always what you want, and it is what goes on every override column you are not deliberately changing.nonereplaces their normal setting with "see nothing", which takes away access their role would otherwise give them.
Both are spelled out on purpose. An empty cell used to mean inherit, which
made "I did not touch this" and "I meant to revoke this" impossible to tell
apart by looking at the sheet.
none is a real tool — it is how you revoke someone's visibility below their
role's default — just rarely what you want.
Granting staff access takes three columns, not one
additional_staff_location_scope on its own gets someone the staff
directory — the roster, employment and work-contact fields. It does not
get them personal emails, cell numbers, birth dates, or demographics. Those live
behind a second gate.
The staff directory is not scoped to the locations you named. It carries no
row filter at all, so any staff-axis grant — even school naming one school —
shows the whole network's roster. The location you name still scopes the
sensitive fields below, and it still scopes student data on the other axis. This
is deliberate: work-directory information is already internally public across
KTAF. Treat a staff-axis grant as "this person may see the staff directory," not
as "at this school."
Sensitive staff fields need all three of these on the same person:
| column | write |
|---|---|
additional_staff_location_scope |
network, region, or school |
staff_pii_scope |
all_in_scope or teaching_staff |
staff_department_scope |
all |
Miss any one of the three and the person sees no sensitive fields at all. Two of the three is the easy mistake, and it does not fail loudly at the moment you type it — the next pipeline run turns a data test red instead.
staff_department_scope is the one people forget. For someone with no
employment record it starts at none, so unless you write all on their row
the department half of the gate stays shut. own_group does not work for them
either: with no job, they are in no department, so "their own group" is empty.
Someone who already has an employment record usually has a department setting
from their role, so for them a location grant plus staff_pii_scope is often
enough — but write staff_department_scope explicitly if you are unsure.
What a contractor sees with no grants at all
A row that grants no location and overrides no setting leaves a contractor seeing nothing — not even the staff directory. That is deliberate. The directory is open to employees because the network publishes it to staff; a person with no employment record reaches it only when you give them a staff location above.
A grant survives an employee's last day
"No employment record" means no current work assignment, so someone whose assignment has ended reads as a contractor from that day on. Their role-based access stops immediately — that comes off the work assignment — but any live row they still have in this sheet keeps granting, until their Google account is suspended.
Offboarding usually suspends the account days or weeks after the termination date, so plan for the gap rather than relying on it:
- Set
expiry_dateto the person's known end date when you create the grant. A grant that expires on its own cannot be forgotten. - Revoke the row as part of offboarding for anyone leaving sooner than their
expiry_date. Revocation is not instant — see Lifecycle below for how long it takes. - Ask IT to suspend the Google account promptly. For someone with no current work assignment, that is what actually ends their Cube access.
A pipeline warning
(test_cube_access_individual_exceptions_grant_reaches_a_viewer) fires on live
rows that reach nobody, which catches the opposite case — an account already
suspended while the row is still marked active. It does not catch this one.
Lifecycle: status, grant_date, expiry_date
status—active,expired, orrevoked. Onlyactiverows (that have also reached theirgrant_dateand haven't passed theirexpiry_date) actually apply.grant_date— the date the row starts applying. Required; use today's date for "right away." A future date means the grant doesn't take effect until that day arrives.expiry_date— the date the row stops applying. Required; write9999-12-31for a grant that never expires. Prefer a real date — access that expires on its own cannot be forgotten about.- To end a grant early, set
statustorevokedrather than deleting the row — this keeps the row for audit history while making it stop applying.
Revocation is not instant. Budget for the next midnight Eastern. Two things sit between the edit and the effect:
- The sheet reaches Cube only after the pipeline rebuilds the staging table that reads it. That runs on a data-change trigger, typically within minutes, occasionally hours.
- Cube caches each viewer's resolved access until the next midnight Eastern. A person who has already run a query today keeps the access they had when it was resolved, even once the sheet change has landed.
So a revocation reaches someone who has not queried today almost as soon as the pipeline runs, and someone who has by the next midnight ET at the latest.
To force it sooner, ask the data team to redeploy Cube Cloud. The cache lives in the server process, so a redeploy drops it and every viewer re-resolves on their next query. That is the only lever — there is no per-person cache clear. For an urgent removal (a termination, a compromised account), have IT suspend the Google account as well: for a contractor it is an explicit gate in the model — a suspended account stops resolving to a viewer entirely — and for anyone it stops them signing in.
Example — a row that has already expired, alongside a still-active one:
| google_email | additional_location_name | status | expiry_date |
|---|---|---|---|
example.four@apps.teamschools.org |
KIPP Sunrise Academy | active | 2025-06-30 (past) |
example.four@apps.teamschools.org |
KIPP Seek Academy | active | 2026-12-31 |
Only the KIPP Seek Academy grant is currently in effect — the Sunrise row is
kept for history but doesn't grant anything once its expiry_date has passed.
Audit columns
business_justification, requested_by, approved_by, and notes are for
your own record-keeping — they document why a grant was made and who approved
it, but nothing in the pipeline reads them to decide access. Fill them in on
every row so the sheet stays a usable audit trail on its own.
Example — a fully filled-in row:
| google_email | additional_student_location_scope | additional_staff_location_scope | additional_location_name | business_justification | requested_by | approved_by | grant_date | expiry_date | status | notes |
|---|---|---|---|---|---|---|---|---|---|---|
example.five@apps.teamschools.org |
region | region | KIPP Cooper Norcross Academy | Covering the Camden data audit through September. | 034521 | 011200 | 2026-07-01 | 2026-09-01 | active | Requested by Finance leadership. |