School News Mailboxes · schoolnews.email

Mailboxes for a school’s news and family newsletters

School News Mailboxes is a dedicated address for the office or newsroom that sends family newsletters and student news. The list stays inside our own private system rather than a marketing tool that mines the family list, the adviser or office owns the send, and mail goes out under the school's own name. The address is open to claim.

Example address: [email protected]. Checkout is not open yet — the figures further down are catalog estimates, not a charge, and no mailbox is created or billed.

Send family newsletters from an address the school controls.
The contact list stays in our own private system, not a marketing vendor's.
Built for advisers and offices, with a real reply-to for families.

The argument

Every school communication tool has the same bug, and it is in the data model

A list is a set of people copied out of a roster at some point in the past. It was correct once. Nothing tells you when it stops being correct.

Import the roster in August. In September a family withdraws a permission. In October a second guardian is added to a record. In November a student moves rooms. In December an address starts hard-bouncing. In January someone unsubscribes.

Your list knows about none of it. It is not broken in any way a screen would show you: it opens, it sends, it reports a delivery rate. It is simply answering a question about August.

The whole design here is one sentence: a list address is a query, not a list.

The address itself stores criteria -- a scope, a grade or room anchor, the id of a source group -- and nothing else. When mail is addressed to it, the audience is derived from the roster at that moment, with contacts, consent, and suppression read in the same pass. There is no membership to go stale because there is no membership stored.

This is not a promise about diligence. It is a property of the shape. The type that holds the criteria has no field a person or an address could be written into, so a future change that tried to cache a membership would have nowhere to put it. That is a considerably stronger guarantee than a policy telling everyone not to.

A list imported once drifts further from the truth with every ordinary change during a term, and nothing announces the drift. An address that is re-derived on every send is correct after each of the same changes, without anyone doing anything.One term. The same five ordinary changes.A list imported onceA querya permission withdrawnoff by 1correcta guardian addedoff by 2correcta student changes roomoff by 3correctan address hard-bouncesoff by 4correctan unsubscribeoff by 5correctIllustrative, not a measurement of any school. The point is the direction, and that nothing alerts you to it.
Figure 1. The five ordinary changes any school has in a term. Illustrative rather than a measurement of any school -- the point is the direction of the drift, and that nothing announces it.

And one thing said plainly here rather than buried below: sending is not live yet. This engine is built and it runs, but the mail server we run ourselves has its accept latch off until the endpoint is provisioned in our own network. A send today becomes a durable queued row and is recorded as queued, never as sent. The argument above is about a design that exists; it is not a claim that mail is going out through it this afternoon.

The addresses

Four scopes, and no membership stored behind any of them

These are the four scopes the engine actually models. The local parts shown are illustrative shapes on a placeholder domain; they name no school, no staff member, and no student.

The whole-school address

[email protected]

Scope: school

The one everybody knows and nobody maintains. It has no stored membership at all: there is no roster subset behind it, just the scope itself, and the audience is derived from the current roster at the moment it is resolved.

Which means the question that eats an afternoon every September -- who is actually on the all-school list this year -- does not have an answer to maintain. The list is the school. If the roster is right, the list is right.

apps/api/src/services/roster-distribution-lists.ts:209 (the implicit whole-school list)

A grade address

[email protected]

Scope: grade

A grade address is a scope plus a grade anchor. Nothing more is stored, because nothing more is needed: the guardians of that grade are whoever the roster says they are today.

The type behind it is the part worth reading. It carries a scope, a grade anchor, a room anchor, and the id of the source subset -- and there is no field a resolved person or address could be written into. You cannot freeze a list you have nowhere to put one. That is a stronger guarantee than a policy that says you should not.

apps/api/src/services/roster-distribution-lists.ts:140 (listAddressFor)

A single-room address

[email protected]

Scope: class

A room address narrows a grade further. It is the one a teacher or an office uses for a note that concerns exactly one group of families and should not go to the other four hundred.

The most useful property here is negative: a student who moves rooms in January does not need anybody to remember to move them between two lists, because there are no two lists. There is one query, and it returns a different answer in January than it did in September because the roster does.

apps/api/src/services/roster-distribution-lists.ts:140 (listAddressFor)

A group address

[email protected]

Scope: group

The open scope: a bus route, a club, a season roster, a field-trip cohort. Anything the school already tracks as an explicit group.

An honest note about this one, because it is the kind of detail a vendor usually files off. A bus route is modelled TODAY as a general group with an explicit roster, not as a native route attribute on a student record. A native one would be a schema change with its own migration, and it has deliberately not been invented in order to make this paragraph read better.

apps/api/src/services/roster-distribution-lists.ts:209 (specsFromRosterSubsets)

On every send

6 things that happen again, every single time

Not on a nightly job. Not when someone remembers to re-sync. Each time an address is resolved, all of it runs again from the current roster.

What happens each time a list address is resolved: 6 steps, in order, ending with the mail server being handed a recipient list that has already passed every gate.Every time an address is resolved, all of this happens again1The address map is rebuilt from scratch2The student ids are derived live from the roster3Contacts and consent are read at the same moment4The candidates go through the SAME consent resolver as every other send5An unknown or cross-tenant address resolves to nothing6Only then is the mail server given a recipient list
Figure 2. Drawn from the same array that writes the panels below, so the diagram cannot show a step the text does not describe.
The address map is rebuilt from scratch

Nothing about the address set is cached between lookups. The map of valid addresses is regenerated on every call from the school's currently active list criteria, so a list archived this morning stops resolving this afternoon without a purge job.

apps/api/src/services/roster-distribution-lists.ts:303 (makeDistributionAliasFeed)

The student ids are derived live from the roster

The criteria are turned into a set of ids by asking the roster, right now. This is the step that a frozen list skips, and skipping it is the whole failure.

apps/api/src/services/roster-distribution-lists.ts:256 (resolveListSpecAudience)

Contacts and consent are read at the same moment

Contact details, consent state, and suppression state are read in the same pass rather than assembled from something remembered earlier. A permission withdrawn since the last send is visible to this one.

apps/api/src/services/roster-distribution-lists.ts:256 (resolveListSpecAudience)

The candidates go through the SAME consent resolver as every other send

The list path does not implement its own consent, suppression, or de-duplication logic. It hands the candidates verbatim to the shared resolver every other send already uses.

That is deliberate and it is the difference between a claim and a guarantee: because it is the same function, list membership and the per-send gate cannot disagree. Two implementations of a consent rule always eventually disagree, and the day they do is the day nobody notices.

packages/notifications/src/campaign/consent.ts:103 (consentGate)

An unknown or cross-tenant address resolves to nothing

An address that is not in the freshly built map -- an unknown local part, or an address whose domain belongs to another school -- returns nothing rather than falling back to something broader.

Fail-closed matters most here. The failure mode of a permissive lookup on a school-wide alias is a message going to everyone, which is precisely the incident nobody recovers from politely.

apps/api/src/services/roster-distribution-lists.ts:303 (the feed's null return)

Only then is the mail server given a recipient list

The mail server receives addresses that have already passed every gate. It routes; it does not decide. Keeping the consent decision upstream of the mail server means a routing change can never quietly widen an audience.

apps/api/src/services/roster-distribution-lists.ts:288 (the consume seam)

Step four is the one worth arguing about. A list feature that implemented its own consent check would be faster to write and would eventually disagree with the per-send gate, and the day it did, nobody would find out from a screen. Handing the candidates to the same shared resolver every other send uses is slower to build and is the only version that stays true.

Who may send

Four ways a school can be allowed to send as itself

A sending identity is not usable because somebody typed an address into a settings page. It moves through a state machine -- unverified, verifying, verified, revoked -- and only a verified one may send. Revoked is terminal.

A domain we manage for you Built, switched off

The records for the sending domain are operator-configured and the identity is verified before it may be used.

The verification state machine is built. Whether a given domain has actually been verified is an operator fact about that domain, not something this page can assert for yours.

A verified address on a shared sending service Built, switched off

A single address is verified rather than a whole domain -- the fastest route for a school that cannot get DNS changed quickly.

Opt-in and non-personal only. Mail that carries student information is never routed this way; that separation is the point of running our own server at all.

Delegated access to an existing account Built, switched off

The school authorises sending from an account it already owns; the credential is sealed at rest and never returned to a client.

The identity model and the sealing are built. A delegated identity whose platform application is not wired cannot actually deliver, and the code refuses to report it as usable rather than letting it look ready.

Relay through the school's own server Built, switched off

Mail is handed to a server the school already runs, using a sealed application password.

The transport exists and reports honestly: with no relay host configured it returns not-delivered with a reason, rather than inventing a message id. It never fabricates a success.

The four approaches against the three questions that decide whether one is usable for school mail. Every cell states its verdict in words, so the table reads with the stylesheet removed.
How the school sendsMay carry mail about a studentMust be verified firstAvailable today
A domain we manage for youYesYes -- verified before useBuilt, and switched off
A verified address on a shared sending serviceNo -- non-personal mail onlyYes -- verified before useBuilt, and switched off
Delegated access to an existing accountYesYes -- verified before useBuilt, and switched off
Relay through the school's own serverYesYes -- verified before useBuilt, and switched off

The second row is the one that matters most and the one most likely to be glossed over elsewhere. Mail carrying information about a student is never routed through a shared outside sending service, on any plan, for any reason. That separation is the reason for running our own mail server at all, and it is why the last column reads as it does: the honest answer today is that none of the four is switched on.

apps/api/src/services/marketing-sender-identity.ts:66 (the state machine) · :264 (only verified may send) · :394 (the relay transport, honest-off) · apps/api/src/services/comms-send-transport.ts:61 (the verified-recipient gate applied at the primitive, not the planner)

Records

What the records do, and the one thing nobody can promise

Five pieces of sender authentication, described without the vocabulary that usually surrounds them.

The signature. A private key signs the message so a receiving server can check it was not altered and really came from this domain. The signing path is built; its key store is empty by default, so a message today is sent unsigned and flagged as unsigned rather than sent with a fabricated signature. The distinction is not academic: a fabricated signature is worse than none, because it teaches receiving servers to distrust the domain.

The permitted-senders record. Lists which servers may send for the domain, so a stranger's server cannot claim to be your school. The builder that computes the desired records exists and is named below; publishing them for your domain is an operator step, and this page does not claim it has been done.

The policy record. Tells a receiving server what to do when the two checks above fail, and asks for a report when it happens. The reports are the useful part and the part nobody reads until the first incident.

The return path. A bounce comes back to a per-message envelope address rather than one shared mailbox, so a failure is attributable to a single send rather than smearing across the whole domain's reputation. This piece is built and running.

The brand mark. Some inboxes will show a verified mark beside your mail. Readiness is reported in tiers, and the code will not claim a certified mark from an unsigned image. A certificate is issued by an outside authority against a registered trademark; it cannot be minted in software, and a product that implies otherwise is lying to you about something you can check.

And the thing nobody can promise: inbox placement. Records can be correct, a signature can be real, a domain's history can be clean, and a receiving server may still put a message somewhere you did not want. Any page that implies otherwise is selling you a decision it does not control.

packages/notifications/src/mitto-dkim-signer.ts:123 · mitto-dkim-canonicalize.ts:67 · mitto-dkim-keystore.ts:38 · mitto-smtp-transport.ts:176 · apps/api/src/services/desired-dns-records.ts:34 · packages/notifications/src/bimi.ts:129

What is live

The status board, above the pricing on purpose

Of the 9 things this page describes, 3 are built and running, 4 are built and switched off, and 2 are not built. Putting this after the prices would be the more comfortable order and the less honest one.

The never-freeze list engine

Built and running

The generator, the live resolver, and the lookup seam the mail server reads are built. An address resolves to an audience derived from the roster with a live consent read, every time it is asked.

apps/api/src/services/roster-distribution-lists.ts:209 · :256 · :303

Retry and de-duplication

Built and running

Transient failures are retried on a deterministic backoff and permanent ones are not, and a stable per-message key stops a retry after a slow acceptance from becoming a second copy in a family's inbox.

packages/notifications/src/delivery/retry.ts:110 · delivery/idempotency.ts:45

Delivering mail from our own server

Built, switched off

The server dialogue and the transport are written; the latch that lets it accept a message is off until the endpoint is provisioned in our own network. A send today becomes a durable queued row and is recorded as queued, never as sent.

apps/api/src/services/comms-send-transport.ts:82 · :129

Signing so a receiving server can verify the mail

Built, switched off

The signing path and its canonicalization are built. The key store is empty by default, so a message today is sent unsigned and flagged as unsigned rather than sent with a fabricated signature.

packages/notifications/src/mitto-dkim-signer.ts:123 · mitto-dkim-keystore.ts:38

A verified brand mark beside your mail

Built, switched off

Readiness is reported in tiers and the code refuses to claim a certified mark from an unsigned image. A certificate is issued by an outside authority against a registered trademark and cannot be minted in software, so nothing here pretends to.

packages/notifications/src/bimi.ts:129 (bimiReady)

Charging for a mailbox

Built, switched off

No checkout, no card, no provisioning. The figures below are catalog display prices. Turning billing on is a founder decision and this page does not make it.

apps/marketing/src/mailbox/pricebook.ts (every figure is display-only)

Importing your existing newsletter list

Not built

There is no importer, and it would be the wrong thing to build first anyway: an imported list is exactly the frozen artefact this page argues against. The addresses derive from the roster you already maintain.

no module -- this is an absence, stated as one

AI writing or rewriting a newsletter

Not built

No provider is wired into this surface. A personalization pass exists elsewhere in the codebase, is fail-safe, is barred from transactional kinds by a constant, and does nothing without an in-network provider injected.

packages/notifications/src/ai/personalize.ts:83 · :109

Where AI is, and is not

No AI writes your newsletter. The complete version of that sentence:

A message-personalization pass exists in the codebase. Saying there is no such thing would be false, and letting you infer more than it does would be worse, so here is the whole of it.

It may touch a subject line and an opening line. Not a link, not an amount, not legal text, not a call to action, not the body.

It may never touch a confirmation, a refund, a payout, an authentication link, or a safety block: those are on a constant list of excluded kinds and ship exactly as written, every time. That is structural, not a checkbox somebody could clear.

It only runs when a caller injects a provider that runs inside our own network. Absent provider, out-of-network provider, any error, any guard trip, any detected leak: the original message comes back unchanged.

On this surface, today: no provider is wired in. A newsletter here is what the office wrote, sent as the office wrote it. If that ever changes, this paragraph changes with it and will say so in the same plain words.

packages/notifications/src/ai/personalize.ts:83 (excluded kinds) · :109 (in-network-or-skip) · :184 (the fail-safe pass)

What a mailbox would cost

Catalog figures for planning a budget. Not a price you are charged. Every plan is per mailbox, monthly, with storage included.

Team $2.50 per mailbox / month

Catalog price — not a charge

Includes 5 mailboxes · 25 GB each.

A small set of on-brand school addresses for a department or office.

At the included 5 mailboxes, that is about $12.50 / month at catalog rates — an estimate, never billed.

Organization $2.00 per mailbox / month

Catalog price — not a charge

Includes 25 mailboxes · 50 GB each.

School-wide branded mailboxes with shared routing and archiving.

At the included 25 mailboxes, that is about $50.00 / month at catalog rates — an estimate, never billed.

A single office usually plans against the team tier: a news address, a front-office address, an adviser address, and a spare. A school running its whole staff on branded addresses looks at the organization tier. Both figures are catalog, and neither is a charge.

What is true today

Catalog prices, not a charge

We show these figures so an office can put a number in a budget line, not because you can buy right now.

There is no buy button on this page. There is no signup that bills. Turning a mailbox on is a founder-confirmed step and it is currently off.

  • Prices shown are catalog estimates, not a charge -- checkout is not open yet.
  • Mailbox provisioning is a founder-confirmed step and is currently off; no mailbox is created and no card is billed.
  • A branded address is a professional mailbox, not a claim that any specific school or studio uses it.

Nothing on this page creates a mailbox or moves a cent. When that changes, this page will say so in the same plain words it uses now.

Boundaries

What this is not

A page that only lists what a product does is half a page. These are the six things an office would otherwise reasonably assume, and each one is a no.

Not a newsletter designer

There is no drag-and-drop layout editor and no template gallery. The interesting problem here is who receives the message, not what colour the header is.

Not a marketing automation platform

No lead scoring, no behavioural triggers on a family's reading habits, no engagement leaderboard. Families are not a funnel and a school newsletter is not a campaign.

Not a list importer

There is no upload for an existing contact list, and building one first would contradict the entire argument on this page. The addresses derive from the roster the school already maintains.

Not a website or a news site

This is the mail. Where the stories live, who edits them, and how they are published are different surfaces with their own pages.

Not a live sender yet

The owned mail server's accept latch is off. A send today becomes a durable queued row and is recorded as queued. Nothing reports a delivery that did not happen.

Not a deliverability guarantee

Records can be right and a receiving server can still decide otherwise. We describe what the records do and stop there.

Questions

What an office asks first

Can we send a newsletter today?

No. The owned mail server's accept latch is off until the endpoint is provisioned in our own network. A send becomes a durable queued row and is recorded as queued rather than sent. We would rather say that than show you a tick.

Do we have to upload our family list?

No, and there is nowhere to upload one. The addresses derive from the roster the school already maintains. An uploaded list is exactly the frozen artefact this page argues against.

What happens when a family withdraws a permission?

They are gone from the very next send. The audience is derived at send time with a live consent read, so nobody has to remember to remove them from anything.

A student changed rooms in January. Do we move them between lists?

There are no two lists to move them between. A room address is a query over the current roster, so it returns a different answer in January because the roster does.

Can a family reply to the newsletter?

Yes. It is a real mailbox rather than an unattended address, so a reply reaches the office instead of disappearing into a no-reply account.

What stops somebody guessing an address and mailing the whole school?

An address that is not in the freshly generated map -- an unknown local part, or one whose domain belongs to another school -- resolves to nothing rather than falling back to something broader. That lookup fails closed on purpose.

Who is allowed to send as the school?

Only a sending identity that has reached the verified state. The state machine has four states and revoked is terminal, and an identity whose underlying account or relay is not actually wired is refused rather than reported as ready.

Will mail about a student ever go through an outside sending service?

No. That routing separation is the reason for running our own mail server at all. A shared outside service is opt-in and non-personal only.

Does the unsubscribe link expire?

No, deliberately. The token carries no expiry because a family may click a link in a message from last year, and an unsubscribe that has quietly stopped working is worse than none.

Will a family get two copies if something is retried?

They should not. Each send derives a stable key from the channel, the address, and the rendered message; a success seals the key so a retry returns the earlier result. A failure does not seal it, so genuine failures stay retryable.

Does any of this use AI?

Not on this surface. A personalization pass exists elsewhere in the codebase, is fail-safe, is barred from confirmations and authentication links by a constant, and does nothing without an in-network provider injected. None is wired in here.

Are the prices final?

No, and nobody should plan as if they were. They are catalog figures so a budget conversation can happen. Checkout is not open and nothing is billed.

Asking a question

There is nothing to sign up for here, so the only thing to do is ask something. A real answer comes back, including when the answer is that we have not built the thing you asked about.