SamwiseCampus Open the app
Multi-tenant school operations

One system for the whole school day.

SamwiseCampus runs institutional administration, teaching, attendance, finance, scheduling, records and certificates from a single application — with every school's data resolved on the server, never chosen in the browser.

18
API domains
18
Capabilities
7
Membership types
5
Institution tiers
130+
Protected procedures
Overview

A school is not one user with one screen.

It is an owner, administrators, a bursary, teachers, guardians and learners, each seeing a different slice of the same records — and several schools running side by side in one deployment without ever touching each other's data. That shape is what the system is built around.

Built for institutions

A tenant is an institution with its own campuses, tiers, timezone, currency and branding. A person's relationship to it is a membership — not a column on their user record — so one account can hold several at once and switch between them.

Authorised on the server

Role and institution are resolved from the signed-in session on every protected request. The client never sends a tenant or a privilege level, so it has nothing to tamper with. Eighteen named capabilities decide what each workspace may do.

The whole school day

Admissions through to a signed certificate. Enrolment records, attendance registers, gradebooks, timetables, fee structures, invoices, payment review, documents, messaging and awards all live in one schema and share one audit trail.

Runs without a babysitter

Deterministic builds, migrations applied during deploy, a rehearsed backup and restore drill, health checks and a handover workspace that proves the deployment is ready before anyone depends on it.

Tenancy and safety

The browser does not get a vote.

The central rule of the system, and the one everything else is arranged around: an authenticated person never picks their school or their privilege level. The server works it out and refuses the rest.

  1. A person signs in
    Authentication establishes a session. It does not establish a role — nothing in the credential names a school or a permission.
  2. The server resolves the active membership
    For every protected request, the session is resolved to an active membership, its institution, its access role and its membership type. Suspended, invited, archived and unauthorised contexts are denied or shown a restricted state.
  3. A capability is checked before any data moves
    Each procedure asserts the capability it needs against that resolved membership. Reading the institution, managing membership, writing grades, reviewing payments and signing certificates are separate grants — holding one never implies another.
  4. Queries are scoped to the institution
    Repositories filter by the institution that came from the membership. A record belonging to another school is not merely hidden from the response — it is never selected.

Client-side role gates are navigation, not security. The workspace routes check the role so a teacher is not offered the bursary screens, which keeps the interface honest and the navigation short. They are a courtesy. Every one of those screens is independently protected on the server, so hiding a link is never the thing standing between a person and another school's records.

Workspaces

Eighteen domains, one application.

The product is organised as workspaces rather than screens. Each is a domain of the API with its own capability boundary, its own records and its own audience — and each appears in the navigation only for the people who have the capability to use it.

Foundation

foundation

The institutional dashboard and first-run experience. It answers what a signed-in person may do next, and it is where a new deployment is brought to life.

  • Institution dashboard scoped to the active membership
  • Guided onboarding steps, tracked per person and resumable
  • Access assertions that fail closed for the wrong institution
  • Route performance dashboard with a tunable alert policy
  • Preferred workspace, so a returning user lands where they work

Resilience

resilience

Institutional recovery treated as a measurable practice rather than a promise. A quarterly review is scheduled, activated and evidenced.

  • Recovery posture overview for the institution
  • Quarterly review created, activated and updated on a cadence
  • Evidence recorded against a review, so the claim is auditable

Analytics

analytics

Attendance is the operational heartbeat: rosters, an acceptance preview before anything is committed, single and bulk marking, and a branded register document.

  • Institution summary and attendance acceptance preview
  • Attendance rosters by class, session and date
  • Single and bulk attendance marking
  • Attendance document branding resolved per institution

Reports

reports

The export surface. Reporting data leaves the application as a file a school can keep, mail or hand to an inspector — scoped to the institution that asked.

  • Institution-scoped CSV export of reporting data

Finance and bursary

finance · guardian

Fee structures become invoices, invoices become payment proofs, and proofs are reviewed by a person with the authority to accept them.

  • Fee structures per session and tier
  • Invoice generation against a student and a structure
  • Payment proof submitted by a guardian
  • Pending payments queue with a review decision
  • Receipt reference issued on approval
  • Guardian's own invoice list, isolated to their learners

People and access

membership · profile

Who is in the institution, what they may do, and the invitation that got them there — including teaching assignments that narrow a teacher to specific classes.

  • Invitations issued, listed and revoked before acceptance
  • Invitation accepted by the invited person, not on their behalf
  • Managed memberships with access role changes
  • Deactivation and reactivation, recorded as lifecycle events
  • Per-teacher teaching assignments and their summaries
  • Switching between the contexts one account holds
  • Account profile, avatar and password management

Documents

documents

Files live in managed object storage, not in the database. The application holds the metadata, the ownership and the access decision; the file itself is reached through a short-lived signed reference.

  • Upload, rename and archive against the institution
  • Opening a document via a signed, expiring URL
  • Sharing with a named recipient and listing active shares
  • Revoking a share, which takes effect immediately
  • Recipients opening what was shared with them

Branding

branding

One institution's identity governs every document it produces. A register, a roster or a certificate must not carry another school's marks.

  • Brand profile read and saved per institution
  • Logo uploaded to managed storage
  • Brand resolved into attendance, roster and certificate documents
Attendance

Marked once, counted everywhere.

Attendance is recorded against a roster rather than a free-form list, so the register a teacher marks and the register a head teacher prints are the same object.

Register behaviour. Rosters are resolved per institution, class and session, and a roster without the capability to mark it is a read, not a write.
Step What happens Who
Roster The learners expected in a class for a session and date, resolved for the institution. Teaching staff, administrators
Preview An acceptance preview shows what the mark set will do before it is committed. Teaching staff
Mark A single learner is marked, or the whole register is submitted in bulk. attendance:manage
Brand The institution's brand profile is applied to the register document. Resolved automatically
Export The reporting data leaves as a CSV scoped to the institution. Administrators
Teaching and grading

From enrolment to a grade that means something.

Courses, assignments, submissions and grading schemes, for institutions that grade by continuous assessment and for those that grade on a tertiary scale.

Courses and assignments

Courses are created for an academic session, students are enrolled against them, and assignments are created inside a course. The eligible-student list is resolved per course rather than offered as a directory dump, so a teacher only ever picks from the people who are actually in front of them.

The gradebook

A gradebook is requested for a course and session, submissions are graded against it, and the grading scheme that produced the total is stored alongside the result. A mark without its scheme is a number nobody can defend a year later.

Tertiary grading

Institutions on a tertiary tier can save a grading scheme of their own, which the gradebook and the grading bridge then use. The default scheme is not assumed for them and not forced on anyone else.

Grading bridge

A bridge from recorded grades to issued awards, so a certificate reflects the gradebook rather than a transcription of it. Grades carry grades:manage; courses carry courses:manage. They are separate grants.

Timetable and classes

Rooms, staff, classes — and the learners inside them.

The timetable is built from resources that exist: physical spaces, the staff who teach in them, and the class groups that move between them.

Resources and entries

Timetable resources are registered once and entries are created, updated or cancelled against them. Staff resources and staff profiles let a teacher's own load be described without exposing anyone else's.

Class groups

A class group is a cohort with a roster. Learners are enrolled into it, withdrawn from it or transferred between groups — each transition recorded, so a learner's movement through the institution is reconstructable.

The learner's own view

A learner sees their timetable and nothing else. It is the narrowest workspace in the system by design: the smaller the surface, the smaller the thing that can be got wrong.

Academic reminders

Reminders are activated as an explicit, recorded action rather than a background timer that quietly starts existing. Scheduled work is a declared job, so it survives a restart and can be inspected.

Fees and payments

Two people, two halves of one transaction.

The school sets what is owed; the guardian says what they paid; the bursary decides whether it counts. Three separate grants, so no one of them can complete the loop alone.

Step one

The school defines what is owed

A fee structure is created for an academic session, and an invoice is generated against a student from it. Structures carry fees:manage — a teacher does not have it.

Step two

The guardian declares a payment

A guardian sees only their own learners' invoices and submits proof of payment against one, under payments:submit. They cannot mark it approved, because they do not hold that grant.

Step three

The bursary decides

Pending payments are reviewed and confirmed or rejected under payments:review. An approved payment yields a receipt reference. Reviewing and submitting are deliberately not the same capability.

Certificates and awards

An award you can check without trusting the paper.

Certificates are issued from recorded results, signed by someone with the authority to sign, released to a recipient, and independently verifiable afterwards. A certificate is worth what its verification is worth.

Issuing

A recipient and an award are chosen, suggestions come from the gradebook, and the certificate is previewed before it is issued. Single and batch issue both exist, so a graduating class does not become an evening of clicking.

Correction without erasure

A certificate is voided, not edited, and a corrected one is issued as a separate document. The history of what was issued remains intact, which is the only behaviour that survives an audit.

Release and delivery

A certificate is released to the recipient and can be sent to them. Being eligible to see an award and having it delivered are separate moments, so a document is never in someone's hands before the school meant it to be.

Verification and delegation

A verification path confirms an award against the record it was issued from. Signature authority can be delegated and revoked, and the delegation list is itself visible — so it is knowable who could sign for the institution, and when.

Four capabilities sit here rather than one: issuing, signing, verifying and delegating. An institution can let a registrar issue awards, a head of school sign them, and a verifier check them, without any of those three being able to do the other two.

Records and enrolment

A learner enters the school once, and every step is recorded.

The intake workspace carries a learner from a document on someone's desk to an enrolled person with a guardian attached and a history nobody has to reconstruct.

  1. Intake options resolve
    The tiers, sessions and classes this institution may actually admit into are resolved for the caller — not read from a fixed list.
  2. Documents are attached
    Supporting documents are uploaded and attached to the record, and reached later through a signed reference rather than a public path.
  3. The learner is admitted
    Admission creates the enrolment record, with an admission number and a tier, against the institution and campus.
  4. A guardian is assigned
    The relationship between a guardian and a learner is an explicit assignment, which is what a guardian's access is derived from — not a guess from a surname.
  5. The record stays inspectable
    Roster history and an audit log are queryable for the institution, so the state of the register at any point can be explained rather than remembered.
Documents and sharing

The file is never the thing being protected. The reference is.

Nothing is served from a guessable path. A document is opened by asking the application, which checks the capability, checks the institution, and only then produces a signed reference that expires.

Storage, not database

Files live in S3-compatible object storage. The database holds the metadata: what it is, who it belongs to, which institution it is scoped to, and whether it has been archived.

Signed and short-lived

Every read produces a signed URL with an expiry. Copies of a link differ from one another and none of them works indefinitely, so a link that leaks in a group chat is of limited use.

Sharing is a grant with a name on it

A share names its recipient. It can be listed, and it can be revoked — after which the reference stops resolving. Archiving is separate from deleting, so a document leaves the working set without leaving the record.

Documents that build themselves

Attendance registers, enrolment rosters and certificates are produced as PDFs from the same records the screens read, under the institution's own brand. There is no second copy of the truth in a template.

Communication

Messages that stay with the institution.

A message centre inside the application, governed by notifications:manage. Messages are sent, listed, archived and unarchived — the archive being how a school keeps a working view short without losing anything.

Transactional email — password resets and account notices — is delivered through a dedicated sending domain with its own SPF and DKIM records, so school correspondence is not sent from an address that anyone else can claim.

Institutional branding

The institution's identity governs its paperwork.

A school's register should not go home bearing a default logo. Brand is stored per institution and resolved into every document that institution produces — in a multi-tenant deployment, that is a correctness requirement, not a cosmetic one.

Register documents Enrolment rosters Certificates Logo in managed storage Resolved per institution Governed by documents:write
People and access

Two questions, answered separately.

Who someone is to the school, and how much authority they hold there. A membership type and an access role are different fields on purpose — a bursar and a head teacher are both staff, and they should not be the same grant.

Membership types

What someone is in the institution. This is what determines which workspaces are available to them at all.

Seven membership types, from the institution's own schema.
Type Who it is for Typical surface
Webmaster The institution's technical owner. Branding, handover, resilience, everything below
Admin School administration. People, enrolment records, reports, timetable
Bursary The finance office. Fee structures, pending payments, payment review
Teacher Teaching staff. Attendance, gradebook, courses, own classes
Staff Non-teaching members of staff. Schedules and resources within their role
Student Learners. Own timetable
Guardian Parents and guardians. Own learners' invoices and certificates

Access roles

How much authority a membership carries. Owner, admin, staff and read-only — applied on top of the membership type, and evaluated on the server for every request.

OWNER ADMIN STAFF READ_ONLY

Capabilities

Eighteen named grants. A procedure asserts the one it needs; nothing is implied by holding a neighbouring grant.

The complete capability vocabulary. Each is asserted by the procedures that need it and resolved from the caller's active membership.
Capability What it authorises
institution:read Reading the institution's own profile and settings.
membership:manage Inviting, changing, deactivating and reactivating memberships.
attendance:manage Recording and amending attendance.
courses:manage Creating courses and assigning students to them.
grades:manage Recording and amending grades and grading schemes.
schedule:manage Timetable resources, entries and class groups.
timetable:manage Building and changing the timetable itself.
fees:manage Fee structures and invoice generation.
payments:submit Declaring a payment against an invoice.
payments:review Accepting or rejecting a declared payment.
documents:write Uploading, renaming, archiving and sharing documents.
certificates:issue Issuing and voiding certificates.
certificates:signature Signing a certificate on the institution's behalf.
certificates:verify Verifying an issued certificate.
certificates:delegate Granting or revoking signature authority.
notifications:manage The institution's message centre.
resilience:manage Recovery posture and the quarterly review.
deployment:handover Evidence that the deployment is ready to be depended on.
Membership lifecycle

People arrive, move and leave. The record should too.

A membership is a state machine, not a flag. Every transition is written to a lifecycle event, so the answer to "why can this person not see that?" is a record rather than a conversation.

Membership states and the lifecycle events recorded when they change.
State Meaning Recorded event
Invited An invitation was issued and has not been accepted. INVITED
Active The person has access at the assigned role. Only this state grants access. ACCEPTED
Suspended Access withdrawn without ending the relationship. DEACTIVATED
Archived The membership is closed. DEACTIVATED
Role changed The access role was raised or lowered, recorded with both values. ROLE_CHANGED
Restored A suspended membership is returned to active. REACTIVATED

Invitations are for one person

An invitation is addressed to a specific email, carries an expiry, stores only a hash of its token, and is accepted by the invited person. It can be revoked before that happens. Staff number, job title, admission number and tier travel with it, so accepting it creates the right record rather than a blank one.

Authority to change authority

Not everyone who can manage memberships can promote anyone. Who may assign which role to whom is itself a policy — an administrator cannot create an owner, and an owner cannot be demoted by an administrator. The rule lives on the server beside the capability it qualifies.

One account, several schools

A person who belongs to more than one institution keeps one identity and switches between contexts. The active context is resolved per request, so a stale browser tab cannot be used to act in a school the person has since left.

Teaching assignments narrow a teacher

A teacher can be scoped to specific classes rather than the whole institution. Assignments are set, summarised and removed, which is how a school employs a teacher for one subject without handing them the register for the school.

Architecture

One process, four layers, no surprises.

A React client, an Express server speaking tRPC, a typed data layer on MySQL, and managed object storage. Each layer has one job and does not reach into the next one's.

The layers, and where each one lives in the repository.
Layer Responsibility Where
Web client Role-aware workspaces, accessibility, lazy routes, error recovery, light and dark. client/src/
Application server One Node process serving the built client, the protected tRPC API, the sign-in and reset routes, security headers. server/, api/
Data layer Typed multi-tenant schema, migrations, repositories, transactional school operations. drizzle/, server/*Db.ts
Identity and access Sessions, membership resolution, capability policy, invitation acceptance, auditability. server/_core/, server/authorization.ts
Managed files Object references, signed URLs, sharing and lifecycle for documents. server/storage.ts, server/document*
Operations Deterministic build, migrations on deploy, backup and restore drill, deployment handover, CI. scripts/, deploy/, .github/workflows/

How a request is answered

  1. The client asks
    A typed tRPC call from a workspace screen. It carries a session cookie and the arguments of the call — no tenant, no role.
  2. The session is authenticated
    The signed session is verified. Sessions carry the moment the account's password last changed, and a session issued before that moment is refused — so changing a password ends the old sessions rather than leaving them alive.
  3. The membership is resolved
    The active membership, institution, access role and status are established on the server, from the database, for this request.
  4. The capability is asserted
    The procedure checks the grant it requires. A missing grant is a refusal, and the refusal is what the client receives.
  5. The data is read or written, scoped
    The repository filters by the resolved institution. Input is validated by the schema on the way in, so a malformed or hostile payload never reaches a query.
  6. The typed result returns
    The workspace renders a response whose shape it already knew. If the request fails, the client recovers rather than showing a broken screen.

Data model, in outline

Institutions own campuses, memberships, invitations and their lifecycle events. Enrolment records attach learners to institutions, campuses and tiers. Everything else — attendance, grades, fees, documents, certificates — hangs off those.

Institutions Campuses Memberships Invitations Lifecycle events Teaching assignments Enrolment records Attendance Courses and grades Timetable entries Class groups Fee structures and invoices Documents and shares Certificates and delegations
Security

What the system assumes about its users.

That some of them will be curious, and one of them might be hostile. Everything below follows from that rather than from optimism.

No client-supplied authority

Institution and role are never accepted as input. They are derived, per request, from the authenticated session and the membership row behind it. There is no parameter to tamper with because there is no parameter.

Passwords are not stored

Passwords are held as derived hashes with per-account salt, verified in constant time. Reset tokens are stored only as hashes, expire, and are single-use — a second attempt with the same token is refused.

A session knows when it was made

Each session records the password epoch it was issued under. Changing a password moves the epoch forward and invalidates every session issued before it, on every device, without a session table to keep in step.

Input is validated at the boundary

Every procedure declares the shape it accepts. Values are parsed before they reach a repository, and the data layer uses parameterised queries, so a shape the schema does not describe never travels further than the edge.

Files are never public

Object storage is private. Reads are issued as signed, expiring URLs produced only after the capability and institution have been checked, so possession of a path is not possession of a document.

Secrets stay out of the image

Credentials and connection strings are environment configuration, never source. The application carries no secret it could leak through a response, a log line or a client bundle.

Technology

Chosen for the property that it fails loudly.

TypeScript end to end, a schema that is the source of truth for both the database and the API contract, and a small number of moving parts. A school system is not a place for cleverness nobody can debug at term start.

The stack, and what each piece is responsible for.
Concern Technology Why it is here
Interface React 19, Vite, Tailwind 4 Fast builds, a component model that scales past fifty screens, and a real design system.
Design system Material Design 3, Radix, shadcn Accessible primitives underneath a token-based theme, so light and dark are one implementation.
Routing Wouter A small router with lazy route loading and no ceremony.
API Express 5, tRPC 11, Zod Typed calls with no hand-written client, and validation that is the same code on both sides.
Data Drizzle ORM, MySQL / TiDB A typed schema that generates migrations and the query types from one definition.
Files S3-compatible object storage Documents out of the database, reached only through signed references.
Documents React PDF Registers, rosters and certificates rendered from the same records the screens use.
State and data TanStack Query Caching, retries and request lifecycle handled once rather than per screen.
Quality Vitest, TypeScript, Prettier, CI Tests that run the same way locally and in the pipeline, with a type check gating the deploy.
Hosting Vercel Production deploys from the repository, functions placed in the region nearest the data.
Operations

School data is part of the release, not follow-up work.

A deployment is not finished when the page loads. It is finished when the migrations have run, the restore has been rehearsed, and someone has recorded that it was.

Migrations run with the deploy

Schema changes are applied as part of the production build, and the deployment is stopped if the configuration or the migration is not valid. A release that would run against the wrong schema does not reach the users.

Restore is rehearsed, not assumed

Recovery is a workspace with a quarterly review and recorded evidence, so the institution can show when it last proved it could come back. The drill is a routine, not a paragraph in a policy.

Handover is evidenced

The deployment handover workspace collects the proof that the installation is ready to be depended on — configuration, access, recovery and operations — under deployment:handover, so readiness is a state someone can check.

Functions follow the data

The API runs in the same region as the database, so a query does not cross an ocean to be answered. Region placement is a deployment setting that travels with the project, not a per-environment hope.

Mail is sent from a domain the school owns

Password resets and account notices go out through a dedicated sending domain with SPF, DKIM and DMARC published, so the school's own mail is verifiable and not refused for looking like someone else's.

Quality gates before production

The type check, the test suite and the format check run in CI. A change that breaks a gate does not deploy, and the gate is the same command that runs locally, so there is nothing to disagree about.

FAQ

Questions a school actually asks.

Can one deployment serve several schools?

Yes, and that is the point of the tenancy model. Each institution is a tenant with its own campuses, tiers, branding, memberships and records. A person's access is a membership in one institution, and the server resolves which institution a request is acting in before any data is returned.

The isolation is enforced in the data layer, not in the interface. Hiding a navigation link is a convenience; the query filter is the boundary.

What happens when a teacher leaves?

Their membership is deactivated rather than deleted. Access ends immediately, the lifecycle event records who did it and when, and everything they recorded stays attached to the institution. Reactivating them later restores the same membership rather than creating a second one.

Can a guardian see other children's records?

No. A guardian's access is derived from explicit guardian assignments, and the guardian workspaces are built on those relationships. There is no code path that returns another learner's record to a guardian, so this is a property of the queries rather than a setting someone could get wrong.

Do different school tiers behave differently?

Yes. An institution enables the tiers it operates — creche, nursery, primary, secondary, tertiary — and the application adapts to them. Tertiary institutions, for example, can define their own grading scheme rather than being given a default that does not describe how they assess.

What does "self-contained" mean for a deployment?

The application does not depend on a service the school cannot see or control. One process serves the client and the API; the database and the object storage are the only external dependencies, and both are configuration the operator owns. There is no hidden third-party call in the request path.

How are documents protected?

Files live in private object storage and are never served from a public path. Opening a document asks the application, which checks the capability and the institution, then issues a signed reference that expires. Shares name their recipient and can be revoked, after which the reference stops resolving.

What happens to a certificate issued in error?

It is voided, and a corrected one is issued as a new document. Nothing is edited in place and nothing is deleted, so the record of what was issued remains complete — which is the behaviour that survives an inspection.

Does it work on a phone?

Yes. The client is responsive throughout, with mobile-specific interaction work — quick actions, drawer gestures and touch targets — and it installs to a home screen as a web app. For a teacher marking a register in a classroom, that is the case that matters.

Is there an accessibility story?

Material Design 3 places a floor under contrast, focus states and type scale, and the application honours it in both light and dark schemes: visible focus rings, real landmarks and headings, a skip link, and motion that yields to a reduced-motion preference.