Access
Who can open what, and what happens when the system cannot tell
Five controls that exist and run in the system this mailbox will live inside, plus one honest row about the mailbox itself. Each is named with the module that implements it, so none of this has to be taken on trust.
Two chips, and what they mean
Platform — a control that is built and running in the wider system this mailbox will live inside. Describing it is describing the house, not the room.
Mailbox — a statement about the address itself. Mailbox provisioning is a founder-confirmed step and is currently off, so every mailbox-scoped claim on this site is about what is built, never about a running inbox.
Every mechanism named on this site carries one of the two. That is not decoration: a platform control quoted as if it were a mailbox feature is a true sentence with its scope removed, and that is the most common way a page like this stops being honest.
Access control is mostly a set of small refusals. Almost nothing in it is interesting on its own, and the interesting part is only visible at the edges: what the system does when a grant is incomplete, when an actor cannot be identified, or when a link between two things has not been established. Those are the paragraphs worth reading below.
The controls
6 mechanisms, each with the module that implements it
- A closed list of permissions
- Scope, and failing closed
- Reading records leaves a trace
- A trail that shows if it was edited
- One way in for writing the trail
- What the mailbox itself will do
A closed list of permissions
Seven named permissions, and no eighth that a string could invent
Platform Built and running
The permissions an administrator can hold are a closed list of seven, declared once: managing users, managing roles, viewing the audit trail, managing billing, managing settings, viewing support, and managing support. A guard checks membership of that list rather than comparing strings, so a typo is not silently a new permission that nothing grants and nothing denies.
Closed lists are unglamorous and they are most of what access control actually is. The failure they prevent is the one where a feature ships checking canManageUser while every grant in the system says manage_users, and the check therefore passes for nobody or, worse, is skipped.
packages/shared/src/admin-rbac.ts:58 (ADMIN_PERMISSIONS) · :70 (isAdminPermission)
Scope, and failing closed
A grant is bound to a tenancy level, and an unbound grant matches nothing
Platform Built and running
Holding a permission is not enough; a grant is also bound to a level -- platform, district, or school -- and to a concrete identifier at that level. A check has to match both.
The interesting behaviour is at the edges, and it is fail-closed at every one of them. A district or school grant with no identifier bound to it is treated as unbound and matches nothing at all, rather than matching everything. A narrower actor can never contain a broader target. And a district-level actor is only allowed to reach a school when the caller has already established that the school belongs to that district -- the engine will not invent the link to make a check succeed.
Every one of those is a decision to answer "no" when the honest answer is "I cannot tell". That is the property worth paying for in this kind of code and it is invisible from a feature list.
packages/shared/src/admin-rbac.ts:84 (ADMIN_SCOPE_LEVELS) · :137 (scopeContains)
Reading records leaves a trace
An access to personal data is recorded, and an unattributed read is still recorded
Platform Built and running
Access to personal data writes an audit record. The detail that says something about the design is the sentinel for an unattributed actor: when a read cannot be tied to a person, the module records it against a reserved identifier rather than skipping the record.
The alternative -- no actor, no row -- makes the exact accesses you would most want to find afterwards the ones that leave no trace. A gap in an audit trail is worse than an ugly entry in it.
packages/db/src/pii-access-audit.ts:50 (PiiAccessAuditArgs) · :44 (UNATTRIBUTED_PII_ACTOR)
A trail that shows if it was edited
The safety audit ledger is hash-chained, so a quiet edit stops verifying
Platform Built and running
For the highest-sensitivity resources, entries are chained: each one is canonicalised and hashed together with the hash of the entry before it, starting from a fixed genesis value. A verifier can walk the chain and report exactly two kinds of break -- a link whose recorded previous hash does not match, and an entry whose hash does not recompute.
What this buys is narrow and worth stating precisely: it makes tampering DETECTABLE, not impossible. Someone with enough access can still rewrite history; they cannot do it without the chain failing verification afterwards. Any product that describes a feature like this as making records unalterable is describing something that does not exist.
apps/api/src/domain/safety-audit-ledger-hashchain.ts:144 (computeSafetyEntryHash) · :194 (verifySafetyChain)
One way in for writing the trail
Recording is a service with an interface, not a line copied into each caller
Platform Built and running
Writing an audit entry goes through one declared interface with one repository-backed implementation behind it. That sounds like housekeeping and it is the reason the trail is uniform: a caller cannot invent its own slightly different record shape, and a test can substitute the interface without a database.
The failure it avoids is an audit trail that is complete in the three places someone remembered and empty in the fourth.
apps/api/src/services/audit-service.ts:29 (AuditService) · :55 (RepoAuditService)
What the mailbox itself will do
An access-controlled inbox on a system we run -- and it is not provisioned yet
Mailbox Built, latched off
Here is the honest statement about the address rather than the platform. A Castra mailbox is intended to be a real inbox with controlled access, run on our own private system, for the staff who sit closest to sensitive correspondence.
Provisioning is a founder-confirmed step and it is currently off. No mailbox exists to apply any of the controls above to, and no message is being sent or received on this domain today. Every row on this page carrying the platform chip is describing the system the mailbox will live inside, and this row is the one that tells you the room is not built yet.
That distinction is the whole reason this site puts a scope chip on every claim. Without it, the four rows above read as features of your inbox, and they are not.
mailbox/brands.ts:502-533 (the brand's own promise) · mailbox/pricebook.ts (honest-off)
The precise version of a claim people overstate
Detectable is not the same as impossible, and the difference is the honest part
A hash-chained audit trail is a genuinely good mechanism and it is routinely described as something it is not. Here is the exact claim.
Each entry is canonicalised and hashed together with the hash of the entry before it. A verifier walking the chain can report exactly two kinds of failure: an entry whose recorded previous hash does not match the one actually before it, and an entry whose own hash does not recompute from its contents. Both mean the same thing to a reader: something changed after it was written.
What that does NOT mean is that records cannot be altered. Anyone with sufficient access to the store can alter them. What they cannot do is alter them and leave the chain verifying, so the alteration is discoverable afterwards by anyone who runs the check.
The reason to be this pedantic is that the overstated version -- "tamper-proof", "immutable" -- is the version a buyer repeats to their own leadership, and it is the version that becomes a problem for them rather than for us.
apps/api/src/domain/safety-audit-ledger-hashchain.ts:126 (canonicalizeSafetyEntry) · :144 · :163 (the two break reasons) · :194
Built and not built
The table with two rows against us
Product categories, not named vendors. The last two rows are the ones a buyer most needs, and both go the wrong way for us.
| Question a buyer should ask | A general business suite | A school communications app | This product |
|---|---|---|---|
| An admin grant with no tenant bound to it | Often treated as global | Depends on configuration | Matches nothing -- fail closed |
| An access nobody can be attributed for | No row written | Written sometimes | Written against a reserved identifier |
| A quiet edit to the audit trail | Undetectable | Detectable if logs are shipped elsewhere | Chain stops verifying |
| Certification badges displayed | Several | Several | None -- and none claimed |
| A mailbox you can actually use today | Yes | Yes | No -- provisioning is off |
The fourth row is worth sitting with. Having no badges is presented here as a virtue, and for some buyers it is simply a disqualification -- a procurement process may require a certification we do not have. That is a real reason to choose something else, and it is better read here than discovered later.
Elsewhere on this site
The rest of it
Overview
Who this address is for, and what a security claim can honestly mean when a product is not provisioned.
Read it →How long things stay
A schedule a build can fail on, an unsigned floor that becomes a hold, and what none of it says about your mail.
Read it →What we will not say
The badge wall we do not have, the words we refuse, and the questions we answer with 'we do not know yet'.
Read it →Plans
Catalog figures for planning a budget. Checkout is not open and nothing here bills.
Read it →