HomeroomBook a demo

homeroom.one

One account for everyone in the district

A student, a teacher, a counsellor, a bus driver and a district administrator each get ONE Homeroom account. That account reaches every module the district has turned on, it moves with the person when they change buildings, and it is the account the district can point at its own identity provider or use as an identity provider for something else.

Homeroom speaks OpenID Connect and SAML 2.0 in BOTH directions -- as the thing you sign in to, and as the thing that signs you in elsewhere. The endpoints are registered in the running API. The rest of this page names them, says what each one answers, and says plainly what each one does before a district configures it.

The idea

One person, one account, for as long as they are in the district

Not one login per module. Not one login per building. One account, scoped by role, that the district owns.

A K-12 district is not one organisation with one directory. It is a set of buildings, each with its own front office habits, sharing a roster that changes every August and every time a family moves. The default outcome of that shape is an account per building per system: a gradebook login, a library login, a cafeteria terminal login, a photo-day portal login, and a spreadsheet somewhere that maps them together badly.

Homeroom is built the other way round. The person is the record. The role is a property of that person within a scope, and the scope is a school or a district, both of which are real entities in the schema rather than a text field. What a person may see is decided from the role and the scope at the moment of the request, not from which of five logins they happened to use.

The role union is closed

There are eighteen coarse roles, and they are a TYPE, not a string column. Adding a nineteenth fails the build until someone consciously classifies it.

Source: packages/shared/src/roles.ts -- the UserRole union and the minor-held classifier keyed on the full union. An unrecognised role FAILS CLOSED to minor-held.

The district is a real entity

A school carries a required district foreign key with ON DELETE RESTRICT. The hierarchy an account is scoped by lives in the database, not in application code.

Source: packages/db/prisma/schema.prisma -- model District, School.districtId, and the school-by-district index. Transcribed; marketing cannot import the schema.

Three of five capability domains are deniable

Denial is lifted into the type system, so a wrong-domain call is a compile error rather than a silent no-op that reads clean.

Source: packages/shared/src/roles.ts -- the five capability domains and the deniable-domain type over the three that can be denied.

An act-as session names the human behind it

The audit record carries an impersonated-by column precisely so an act-as action stays attributable to the operator, not just to the account they borrowed.

Source: packages/db/prisma/schema.prisma -- model AuditLog. See #boundaries for what that record is NOT.

The seams

Two protocols, both directions, named endpoint by endpoint

A district technology office does not want a paragraph about interoperability. It wants the metadata URL and the grant types. Here they are.

Homeroom as an OpenID Connect provider

Homeroom mints identity tokens for other systems. The flow is authorization_code with PKCE; the code is single-use and short-lived and carries a consumed stamp, so a replayed code is refused rather than quietly honoured. Consent is gated twice: a per-client column that defaults FALSE, and a global switch above it.

MethodPathWhat it answersBefore a district configures it
GET /.well-known/openid-configuration The discovery document: issuer, endpoints, supported response and grant types. Answers. The issuer field is empty until an issuer is configured.
GET /.well-known/jwks.json The public signing keys, rotation-aware. The public half only. Answers. It never touches the private key.
GET /oauth2/authorize The authorization endpoint. Requires a live Homeroom session. Answers, and refuses without a session.
POST /oauth2/authorize/consent The consent decision, when the interactive consent screen is in play. Registered behind the global consent gate.
POST /oauth2/token Code exchange and refresh. Accepts form-urlencoded, as the specification requires. Answers.
GET /oauth2/userinfo The claims for the bearer of an access token. Answers.
POST /v1/oauth2/clients Register a relying party. Tenant-owned by district, school or partner. Answers, for an administrator in scope.

Homeroom as a SAML 2.0 identity provider

The reverse seam. A district registers a service provider, Homeroom issues it a signed assertion for the live session, and the browser posts that assertion to the service provider. The service-provider registry is a per-school table whose enabled column defaults FALSE -- registered but off until an administrator turns it on, which is the safe default and is the schema comment word for word.

MethodPathWhat it answersBefore a district configures it
GET /v1/auth/saml-idp/metadata Our identity-provider metadata for one registered service provider: entity ID, sign-on location, signing certificate. This is the file district technology staff feed to the other system. Answers 501 saml_idp_not_configured until the signing configuration is set.
GET /v1/auth/saml-idp/:sp/sso Issues the signed assertion for the live session and auto-posts it to the service provider. Answers 501 saml_idp_not_configured until the signing configuration is set.

Two details worth stating because they are the ones a security office asks about. The metadata endpoint requires a session BEFORE it resolves the service provider, so an out-of-scope caller gets 401 unauthenticated rather than a 404 that would leak whether the service provider exists. And a bare metadata request with no service provider named is a 400, because there is no single global sign-on endpoint in this version -- the sign-on location is per service provider.

Rostering, so the accounts exist in the first place

Single sign-on answers who someone is. It does not answer who exists. Homeroom carries a consent-gated OneRoster provider surface, registered on the same protected path as the two above, so a district can read organisations, users and students out of Homeroom against the same tenancy walls rather than through an export. It is read-only in this version, and this page says read-only rather than implying a write-back.

The state today

Registered, answering, and inert until a district configures it

This section is the present tense of the latch. It describes what the serving path does with the configuration it has right now.

Every endpoint in the tables above is registered by a plugin the API registers at boot. That is a measurement, not a plan: the two registrar functions are imported and then CALLED with app.register in the API composition root. An import is presence; the register call is the wire, and only the wire is being cited.

What is NOT configured, stated as plainly as the configuration file states it:

  • The identity-provider signing configuration is empty by default. With any part of it unset the SAML identity provider is inert: both endpoints answer 501 with the error code saml_idp_not_configured. That refusal is the first statement in both handler bodies, above every other check.
  • The OpenID Connect issuer identifier defaults to the empty string. The discovery document is served either way; its issuer field is empty until a district or the platform sets one.
  • The two upstream sign-in seams -- an OpenID Connect provider a district already runs -- are each gated by a client identifier and secret that default to empty. The configuration file comment on both says the same thing: empty means the provider is off.
  • The per-school service-provider registry column enabled defaults to FALSE, so registering a service provider does not switch it on. Someone with the authority has to.
  • The interactive consent screen sits behind a global switch above the per-client consent column, so consent cannot be silently skipped by a client that would rather not ask.

Read that list as the design and not as an apology. An identity provider that ships with a signing key already in it is a defect. Every one of these defaults is off because the safe state for an identity seam is off, and turning it on is a deliberate act by a named administrator whose act lands in the audit record.

What we do NOT claim

We do not claim your district can complete a federated sign-in today without doing configuration work with us. Nobody can, and a page implying otherwise would be selling you an afternoon you would then spend finding out different.

Basis: the five default-empty settings listed above, read in apps/api/src/config.ts.

What we DO claim

The protocol work is done and it is in the running API. What remains for a given district is configuration, certificate exchange and a test sign-in -- an afternoon of work between two people who know their own systems.

Basis: the register calls in the API composition root and the handler bodies for all nine endpoints named in #protocols.

The reader

This page is written for one person

The district technology director, or the one systems administrator who is that person without the title.

You are the one who gets the ticket when a long-term substitute cannot get into the gradebook on their first morning. You are the one who has to answer, in writing, what happens to a departing employee account inside twenty-four hours. You are the one who knows which of your systems still has a shared password taped inside a cabinet in the front office of one building, and roughly how long that has been true.

Everyone else in the district experiences identity as a login box. You experience it as an inventory. So this page is an inventory: named endpoints, named defaults, named refusals. The sections below say what it costs to not have this, what you are likely to object to, and where the boundary of the claim is.

  • If you already run an identity provider, the part you want is the upstream seam in #protocols and the objection about not wanting a second directory in #objections.
  • If you do not run one, the part you want is Homeroom as an identity provider -- the SAML half -- because that is the path where Homeroom becomes the account other systems trust.
  • If your question is who may do what once someone is signed in, that is a different page in this family, and #family names it rather than duplicating it here.

The cost

What an account-per-system district pays, without ever seeing an invoice for it

None of these are our figures. They are the shapes of the problem, and you can price them against your own district in an afternoon.

The costs of fragmented identity in a school district are real and they are almost never on a budget line, which is exactly why they persist. They show up as time, as risk, and as the quiet erosion of a control everyone believes is in place.

  • AUGUST. Every system that holds its own account list has to be reconciled against the new roster separately. The work scales with the number of systems times the number of buildings, and it lands in the two weeks when you have the least slack.
  • OFFBOARDING. A departing employee is disabled in the systems someone remembers. The measure of your exposure is not the systems on the checklist; it is the ones that are not, and you find out which those are during an incident.
  • THE SHARED LOGIN. Wherever per-person accounts are expensive to create, a building creates one account and shares it. Every audit record produced by that account names nobody. The record still exists; it just stopped being evidence.
  • PASSWORD RESETS. The help-desk cost is visible. The hidden cost is the pattern it teaches: staff who reset often choose passwords they can guess back, and staff who cannot get in borrow a colleague credential, which is the shared login again with extra steps.
  • THE MAPPING SPREADSHEET. Somewhere there is a file that maps a person to their account in each system. It is among the most sensitive files in the district and it is maintained by hand.

One account per person does not delete these costs by existing. It moves them into one place where you can see all of them at once, where the roster is the source rather than a thing to reconcile against, and where a single disable is a single disable.

The hard questions

The five objections a competent technology office raises, answered without dodging

If an answer below is a hedge, it is a hedge because the honest answer is a hedge, and it says which.

We already run an identity provider. We do not want a second one.

Then do not run a second one. The upstream seam exists exactly for this: Homeroom accepts an OpenID Connect provider that a district already operates, and the sign-in you already own stays the sign-in. In that configuration Homeroom is a relying party, not a directory.

The reason the identity-provider half also exists is that the reverse case is common in K-12: a district has systems that need an identity source and no appetite to run one. Both halves are in the same API and a district uses whichever half matches its shape. Using one does not oblige you to use the other.

What happens to our accounts if we stop using Homeroom?

The account list is a roster and the roster is readable. The rostering surface named in #protocols is a standards-based read of organisations, users and students, which means the exit path is the same interface as the integration path rather than a support request. That is deliberate.

The honest limit: a signed assertion is not portable and neither is a session. Federation you configured against Homeroom as the identity provider has to be reconfigured against whatever replaces it. No identity provider can hand that over, and one that implied otherwise would be describing something else.

Students are minors. What are you doing with their identity data?

The classifier that decides whether a role is minor-held is keyed on the FULL role union and fails closed: an unrecognised role is treated as minor-held, which denies rather than permits. Adding a role to the platform fails the build until someone classifies it. That is a mechanism in the shared package, and this page describes it rather than being it.

This page describes a mechanism and never a legal conclusion. What a given law entitles a family to, or requires of your district, is a question for your counsel, and any vendor page that answers it for you is doing something other than helping you.

How is this different from the district operations page in this family?

That page is about AUTHORISATION: which role may see which record, who may act as whom, what the audit record holds, what a procurement office receives. It begins after someone is signed in.

This page is about AUTHENTICATION and about the account itself: how a person proves who they are, which protocols carry that proof across a boundary, and why the account outlives the building. The two pages share no section and neither restates the other.

Your own page says most of this is switched off. Why are you selling it?

We are not selling a switched-on federation. We are describing a built protocol surface and the configuration it needs, so that you can decide whether the remaining work is an afternoon or a project. That distinction matters more to you than to us, so it is on the page rather than in a follow-up call.

The alternative -- a page that describes the finished state and lets you discover the defaults during implementation -- would read better and would be worse. #honest-state is on this page on purpose, and it is not going to be quietly softened later.

The boundary

What one account is NOT

A capability page that only lists capabilities is not honest. This is the other list.

  • NOT AN AUTOMATED JUDGEMENT. No model and no scoring decides who gets in or what they may see. Access is decided by role and scope, both of which a human set. There is no artificial-intelligence feature on this seam and this page is not claiming one.
  • NOT A TAMPER-EVIDENT LEDGER. A named set of compliance ledgers in this platform is append-only and refused against update and delete at the database level. The GENERAL audit record is NOT in that set. It is a record. Calling it a tamper-evident ledger would be a claim the enforcement does not carry, so this page calls it a record.
  • NOT A COMPLIANCE GUARANTEE. Nothing here certifies your district against any framework. The page describes mechanism; certification is between your district, its counsel and its auditors.
  • NOT A PHOTO SURFACE. This page never captures, delivers or sells a student photograph. That is why the photo-privacy line is absent from the footer here rather than decorating a page that has not earned it.
  • NOT A PURCHASE. There is no price, no plan, no tier and no checkout on this page. Money is off across this surface, and the only commercial action available is a conversation.
  • NOT A MAILBOX. This page provisions no mailbox and sends no mail. The address in the next section is a link your own mail client opens.
  • NOT A CLAIM ABOUT YOUR ENVIRONMENT. Every statement above is about this repository and the API it builds. What is running in front of your district on a given day is a deployment question, and the answer belongs to whoever can read that deployment, not to a marketing page.

The family

Where this address sits, and why it is its own page rather than a redirect

Homeroom owns several addresses. Each one worth a page has a reason to exist that the others do not cover.

This address used to serve a shared template: the same page with a different domain name at the top, pointing at the flagship. That was honest about being a pointer and dishonest about being a site. It has been replaced, because the platform holds an identity seam worth its own page and this is the address that names it.

  • homeroom.software -- the flagship. The whole platform: records, publications, picture day, the health office, communications. This page is one seam of it and does not restate it.
  • homeroom.management -- district operations. Roles, audit, vendor access, procurement. Authorisation, where this page is authentication.
  • homeroom.family -- written for a parent or guardian rather than an institution.
  • homeroom.cloud -- hosting and infrastructure.

Same house, same typography, same navigation. Different reader, different verb, different evidence in every section. That is what family-similar is supposed to mean, and it is the test this page had to pass before it was allowed to exist separately.

The next step

One concrete thing a district does next, depending on which half it needs

Neither of these is a form that mails you a brochure.

IF YOU RUN AN IDENTITY PROVIDER and want Homeroom to accept it: send the discovery URL for your OpenID Connect provider and the redirect URI you want registered. The seam is configuration on our side; there is nothing for you to build.

IF YOU NEED HOMEROOM TO BE THE IDENTITY PROVIDER for another system: send that system service-provider metadata -- its entity ID and assertion-consumer URL. It gets registered as a service-provider row for your school, and it stays disabled until one of your administrators enables it, because that column defaults to FALSE and we are not going to quietly change that for convenience.

Either way the conversation starts the same: one message describing which of your systems needs to trust which, and roughly how many people are on the other side of it.

That address opens your own mail client. This page sends nothing and stores nothing you type, because there is nothing on it to type into.

Short answers

The questions that do not need a whole section

Each answer is the true one at the length it deserves.

Which grant type does the OpenID Connect provider use?

Authorization code with PKCE. The default challenge method is S256, the code is single-use and short-lived, and a consumed code carries a stamp so a replay is refused.

Where is the signing key stored?

The identity-provider signing private key is configuration-only and is never a database column. The service-provider registry table holds no certificate and no private key. The public keys are served, and only the public half.

Can a service provider skip the consent screen?

Not on its own. Consent is gated by a per-client column that defaults FALSE and by a global switch above it, so a client cannot decide unilaterally to stop asking.

Does a key or a client reach across districts?

No. Tenancy is anchored at the district on the credential record itself, and the schema comment on that column states in its own words that a key may never reach across districts.

Is the rostering surface read-write?

Read-only in this version. It reads organisations, users and students, behind consent and the same tenancy walls as everything else.

What does the SAML metadata endpoint return before configuration?

501, with the error code saml_idp_not_configured. That refusal is the first statement in the handler, before the request is even parsed.

For the record

How every claim on this page was taken, and what would make it wrong

A page that cites itself is easier to correct than a page that asserts.

Every structural claim above was read at the bytes in the Homeroom repository, at a named commit, with a search that carried an explicit path list rather than a bare tree walk. The registration claims cite the CALL that registers each plugin, not the import of it, because an import is presence and only the call is the wire.

This page is served by the marketing application, which is deliberately isolated: it cannot import the shared package or the database schema. So every field name, default and refusal quoted here is a TRANSCRIPTION cited to its source, and it is a DESCRIPTION of the enforcement rather than the enforcement itself. If one of those sources changes and this file does not, this page is wrong until someone fixes it. That is a real failure mode and it is stated here rather than left for a reader to find.

There are no adoption figures on this page. There is no district count, no customer name, no testimonial and no percentage, because we would have to make one up to put it here.