SACS — Smart Academic Coordination System
A real-time, multi-tenant school coordination platform that runs entirely from the terminal. SACS coordinates schools (tenants), staff, timetables, lesson sessions, a lesson-completion approval workflow, and a deterministic software bell scheduler, streaming every event to authenticated workspaces over WebSockets.
There is no browser frontend: the whole product is exercised through the sacs CLI, which
talks to one shared FastAPI backend exactly like a real client would.
Quick start
pip install sacs # then:
sacs demo # migrate, seed, start the server, open the live dashboard
sacs status # show whether the demonstration is running
sacs demo reset # destroy ONLY the local demo database (requires confirmation)
sacs demo stop # stop the running demonstration
sacs check # verify dependencies and running environment
sacs version
sacs demo is the product. It:
- migrates the database to the latest schema (Alembic) and seeds two schools;
- starts the shared SACS service on a local port;
- authenticates five independently authorized identities against the real API;
- shows a live multi-workspace dashboard (Rich) with keyboard commands for the teacher submit → DOS review → head decide workflow, bell operations, and school administration.
The demonstration persists between restarts (sacs demo again keeps your data; use
sacs demo reset for a clean slate).
Demo identities
| Workspace | Role | What they can do | |
|---|---|---|---|
| Platform | platform@demo.sacs |
platform admin | create/suspend/reactivate schools, health, audit |
| Head | head.northstar@demo.sacs |
school head | final approve/reject, staff & academics, bells |
| DOS | dos.northstar@demo.sacs |
director of studies | review, forward, return requests; timetable |
| Teacher | teacher.northstar@demo.sacs |
teacher | submit completion requests for own lessons |
| Bell | bell.northstar@demo.sacs |
bell monitor | monitor bell events in real time |
(Pair for riverside-institute: head.riverside@demo.sacs, dos.riverside@demo.sacs,
teacher.riverside@demo.sacs, bell.riverside@demo.sacs.)
All demo accounts use the password SacsDemo123!.
The lesson-completion workflow
A teacher submits a completion request for one of their published lessons. The request then moves through a strictly enforced state machine — invalid transitions are rejected server-side:
submitted ──review──> under_dos_review
submitted ──forward─> forwarded_to_head
submitted ──return──> returned_for_correction
under_dos_review ──forward─> forwarded_to_head
under_dos_review ──return──> returned_for_correction
forwarded_to_head ──approve─> approved
forwarded_to_head ──reject──> rejected
returned_for_correction ──submit─> submitted
Every transition records an audit action and the requester-facing history. A secondary-level
request may not be final-approved: request.decide (head) and request.review (DOS) are
deliberately disjoint.
Roles and permissions
| Permission | school_head | dos | teacher | bell_monitor |
|---|---|---|---|---|
school.view |
✓ | ✓ | ✓ | ✓ |
school.settings.manage |
✓ | |||
staff.manage |
✓ | |||
academics.manage |
✓ | ✓ | ||
timetable.manage |
✓ | ✓ | ||
timetable.publish |
✓ | |||
request.submit |
✓ | |||
request.review |
✓ | |||
request.decide |
✓ | |||
bell.view |
✓ | ✓ | ✓ | ✓ |
bell.manage |
✓ | |||
audit.view.school |
✓ |
Platform permissions (platform.schools.manage, platform.schools.suspend,
platform.health.view, platform.audit.view) are exclusive to platform administrators and
never grant academic approval authority.
Bell scheduler
Each school runs BellSchedules with per-period times evaluated in the school's own time
zone (Europe/London, America/New_York, …). A background loop ticks once per second; each
tick records every due-but-unrecorded occurrence with outcome:
- fired — executed inside the grace window (
SACS_BELL_GRACE_SECONDS, default 90); - missed — the window elapsed while the scheduler was offline;
- skipped — the schedule was paused;
- occurrences are keyed
schedule_id + day + period + (start|end)with a unique constraint, so restarts and overlapping ticks can never double-fire the school bell.
Deterministic test clock
In non-production environments the clock can be driven for testing and demonstrations:
POST /api/test/clock/set { "now_utc": "2026-03-02T10:00:30+00:00" }
POST /api/test/bell/tick
POST /api/test/clock/clear
The test clock is hard-disabled in production unless explicitly enabled.
Real-time events
Every state change is written to a transactional outbox in the same database commit as
the change itself, then drained and streamed to the exact recipients allowed server-side by
the event audience rule (platform | school roles | specific users). This guarantees a
persisted change never silently loses its notification, and gives tests a synchronous
delivery path.
Architecture
- API: FastAPI + Pydantic v2, packaged with uvicorn.
- Persistence: SQLAlchemy 2.0 (typed
Mappedmodels); schema only ever created through Alembic migrations — no implicitcreate_all. - Auth: opaque hashed session tokens, PBKDF2 password hashing, per-school memberships.
- Tenant isolation: every school-scoped endpoint resolves the caller's membership and
re-checks the requested
school_idserver-side; a suspended school blocks all its members. - Time handling: all timestamps are UTC; per-school time zones applied only for bell
scheduling and local display (
tzdataincluded on Windows).
Configuration
Everything is configurable through environment variables (see sacs/config.py):
| Variable | Default |
|---|---|
SACS_HOME |
~/.sacs (demo database lives here) |
SACS_DATABASE_URL |
sqlite:///<SACS_HOME>/sacs.db |
SACS_ENV |
demo (demo/dev/prod) |
SACS_HOST / SACS_PORT |
127.0.0.1 / 0 (auto-pick a free port) |
SACS_ENABLE_TEST_CLOCK |
true (false forced in prod) |
SACS_SESSION_TTL_HOURS |
12 |
SACS_BELL_GRACE_SECONDS |
90 |
Deployments can point SACS_DATABASE_URL at PostgreSQL (see the postgres extra) and run
uvicorn sacs.app:create_app --factory.
Development
pip install -e ".[dev]"
pytest # full test suite
pytest tests/test_packaging.py # build wheel/sdist and inspect artifacts
python -m build --no-isolation --outdir dist
Repository layout
sacs/
cli.py Typer CLI + the demo dashboard (five workspaces)
app.py FastAPI application and lifespan tasks
models.py SQLAlchemy models (19 tables)
db.py engines, sessions, Alembic wiring
security.py password + session token handling
rbac.py permission catalog for roles and platform
workflow.py the completion-request state machine
scheduler.py deterministic clock + bell loop and outcomes
events.py connection manager, transactional outbox, dispatcher
seed.py idempotent demo seed (two schools, five identities)
migrations/ Alembic baseline migration
routers/ platform, schools, academics, timetable, requests, bell, realtime, auth
tests/ unit + API + isolation + scheduler + packaging tests
Metadata
Release files for sacs 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sacs-0.1.0.tar.gz | 57.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sacs-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 117.2 kB
Release files / sacs-0.1.0.tar.gz
| Download URL | sacs-0.1.0.tar.gz |
|---|---|
| Size | 57.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9435ac79258c266a5a30e9fb68e038893f8b6ba8f68c3c27d811a97ac51be96b
|
|
BLAKE2b-256 checksum How to use checksums |
5c0eb4b04e839a19da4e883a26df18ff5d9599279ee9615b741c91fcc9988911
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / sacs-0.1.0-py3-none-any.whl
| Download URL | sacs-0.1.0-py3-none-any.whl |
|---|---|
| Size | 60.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
57696798c038beaf163081cb9c8c7f8011c5cc5298f33c02b3c447770405b82d
|
|
BLAKE2b-256 checksum How to use checksums |
cbc617e94b2821bcc8167a088f4e6ff43ff81ffdf3b5ec5b739f9b39b04b29f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency log