Skip to content
Feature complete

Slateberry

Most school communication tools compete on more messaging. Slateberry ships less, on purpose: there is no private DM corridor, so a student can never be cornered one-on-one. Everything happens in open, moderated, admin-supervised channels, forums and a shared channel wiki — with live polls, ephemeral channels and a home screen of sixteen rearrangeable widgets.

The app is feature-complete, builds clean and has 82 passing unit tests. The backend Worker and Firebase project are live; the Android app itself has not been released to any store yet.

Slateberry
Android

[In the box]

What it actually does

  • No private DMs by design — every conversation is open and moderated
  • IRC-style channels with edits, reactions, mentions and a sticker palette
  • Channel wiki with Markdown-Lite, [[wikilinks]] and bi-directional backlinks
  • Forums with pinned announcements, locked threads and reply counters
  • Live in-chat polls with real-time results and homepage promotion
  • Sixteen home-screen widgets, including three arcade games and a class pet
Android

By the numbers

Status
Feature complete, unreleased
Kotlin
35,349 lines across 131 files
Worker
371 lines, 9 endpoints
Unit tests
17 suites, 82 tests, 0 failures
Lint
0 errors
Routes
13
Widgets
16
Invite token lifetime
1 hour
Ephemeral retention
24 hours or 7 days
Infrastructure cost
$0 (free tiers)

[Features]

Inside Slateberry

Every item below is implemented in the codebase, not planned for a future release.

Moderated channel chat

Real-time group chat with message editing, reactions, @mentions and a 16-sticker kaomoji palette, plus ephemeral channels with 24-hour or 7-day retention.

Channel wiki with backlinks

A collaborative knowledge base per channel: a Markdown-Lite parser, [[wikilinks]] that auto-cross-reference, and bi-directional backlinks backed by a Firestore index.

Forums

Forum list → thread list → thread view, with pinned announcements, locked threads and reply counters backed by a composite index.

Live polls

In-chat interactive voting with real-time percentage breakdowns, a poll creator sheet, and the option to promote a poll to the homepage.

Sixteen home widgets

Announcements, calendar, polls, forums, class pet, fishbowl, pixel weather and more — each pluggable through one interface with per-widget config sheets and a graceful unknown-widget fallback.

Three arcade minigames

Snake, Flappy and Breakout, each with server-persisted leaderboards. Game logic lives in pure state classes that are unit-tested independently of the UI.

Synthesised music boxes

Three separate piano and music-box synthesizers generate their own audio, so the games ship sound with zero asset weight.

Three-tier moderation

Owner, admin and member roles with kick, ban-with-reason, promote and demote — plus a whole-word, case-insensitive word filter.

Username keys, not directories

Find a classmate with a private @username and a secret 4-digit passcode, validated server-side. Firestore rules make the secret owner-readable only.

Four real themes

Retro light, retro dark, modern light and modern dark. Note: a few themes mentioned in the project's README are not implemented.

[Architecture]

How it is built.

The decisions that shaped the codebase, and what each one buys.

01

Safety by omission

Removing private messaging is a bigger design decision than adding it. With no 1-on-1 corridor there is no private space to abuse, so the hardest class of harassment has nowhere to happen.

02

Privileged writes go through the edge

Invites, membership changes, moderation and OTP verification all run through a Cloudflare Worker with firebase-admin, not from the client. Firestore rules make the invites collection entirely client-inaccessible.

03

Zero dollars is a constraint, not an accident

Expiring messages filter on the client rather than using billed Cloud TTL fields, and a small free tier carries the whole system. The architecture is shaped by the budget.

04

Widgets as one interface

All sixteen widgets implement a single WidgetDefinition contract with a config schema, so a new widget is a data change. An unknown widget id degrades to a stub rather than crashing.

[Who it is for]

Built for

  • Secondary-school students and teachers
  • Schools evaluating a moderated communication layer
  • Classrooms that need admin oversight without surveillance

[Non-negotiables]

The rules we held

  • No 1-on-1 private messaging — the omission is the safety feature
  • No public user directory — classmates use a handle plus a 4-digit secret key
  • Invite tokens expire after one hour
  • MIT licensed, and designed to run at $0 on free tiers

[Stack]

Built with

Kotlin 2.3Jetpack ComposeMaterial 3Firebase AuthCloudflare WorkersFirestoreHaze (frosted glass)CoilGradle 9

Delivered

  • Auth flow, classroom create/join/leave/delete
  • Channel chat with edits, reactions, mentions, stickers, polls
  • Ephemeral channels with client-side TTL filtering
  • Channel wiki with wikilinks and backlinks
  • Forums with pins, locks and reply counts
  • 16 home widgets, 3 minigames, 3 music-box synthesizers
  • 9 Worker endpoints including invites, moderation and OTP

Not done yet

Listed rather than hidden.

  • Release signing and Play Store submission
  • Localisation — the app ships hardcoded English strings today
  • Two of the six themes named in the project README are not implemented

[Timeline]

How it got here.

Where this came from, in order.

    1

    Thesis

    Decide what to remove

    The project started by eliminating 1-on-1 private messaging rather than by adding features, on the grounds that private corridors are where student harassment happens.

    2

    Build

    Chat, wiki and forums

    A channel system with edits and reactions, a per-channel wiki with backlinks, and forum threads with pins and locks — roughly 10,000 lines of Compose.

    3

    Operations

    Move privileged writes to the edge

    Nine Worker endpoints took over invites, membership, moderation and OTP, leaving Firestore rules to deny client access to the sensitive collections.

    4

    Now

    Refactor and harden

    A documented worklog records MainActivity going from 603 to 69 lines, rememberSaveable usage from 4 to 62 call sites, and lint errors from 4 to 0.

[FAQ]

Common questions.

The questions we would expect to be asked.

Why is there no private messaging?

Because a private corridor is exactly where a student gets cornered. With every conversation happening in an open, moderated channel, the hardest class of harassment has nowhere to occur. It is the one thing this app deliberately does not do.

How do students find each other if there is no directory?

With a private @username plus a secret 4-digit passcode, validated server-side and stored in a collection only the owner can read. Invite deep links are the other route, and those tokens expire after an hour.

Is it on the Play Store?

No. The app builds, lints clean and passes 82 unit tests, and the backend is live — but there is no release build and no store listing yet.

The rest of the catalogue

[Get in touch]

Questions about Slateberry?

Want to know more about Slateberry — timelines, availability or how it works under the hood? Send us a note.

Taking on select projects for 2026