Circulation.software · The record under the library desk · 2026
The school library still runs on a scanner, a stamp, and the librarian’s memory. This is the record underneath.
The catalog and its physical copies. A check-out that refuses to over-loan or step in front of a waiting reader. A return that assesses an exact-cents overdue fine as a record — never a payment taken online. A FIFO hold queue. A fine ledger where a waived fine posts nothing. And an interlibrary-loan request with a real, auditable lifecycle. Every decision on this page is a small, deterministic classifier you could read in source. There is no AI in it, and it takes no card.
The desk today — where the record actually lives
The overdue book is chased by hand. The hold queue is a sticky note. The fine ledger is an envelope in a drawer.
A school library is a real circulation system running on informal memory. The scanner knows a barcode; it does not know that the one copy everybody wants already has three readers waiting, or that this patron is over the school’s loan cap, or that today’s return is one day short of a fine. Those rules live in the librarian’s head, and they break the moment the librarian is out sick and a parent volunteer is at the desk.
The enterprise integrated library systems solve this, and they are priced and shaped for a public library system or a university — a procurement, a migration, a training week. The consumer apps solve the opposite problem (a reading list on your phone) and know nothing about a physical copy, a hold queue, or a fine. The middle — a school-shaped circulation record that a librarian AND a volunteer can both operate correctly, that enforces the queue and the caps itself, and that treats a child’s reading list as confidential by default — is empty. That is where circulation.software sits.
Catalog & OPAC — the record of what the library holds
Catalog a title, add its physical copies, and find them by keyword.
A title carries a classification (Dewey, Library of Congress, or a local scheme), a call number, and an ISBN that is normalized at the boundary — hyphens and spaces stripped, the ISBN-10 check character upper-cased. A malformed ISBN is stored as entered, on purpose, so a typo’d record is still found by the same typo rather than vanishing. A librarian can paste a MARC record by hand when they have one; nothing here requires a vendor feed.
Titles and copies are distinct
One title, many physical copies, each with its own barcode, accession, shelf location, and cost basis. Circulation happens against a copy; the catalog is about the title.
Keyword search (OPAC)
A flat keyword search across the catalog for the desk and for patrons — find the title, see which copies are on the shelf right now.
Six-state copy lifecycle
Every copy is in exactly one named state, and the set is closed — so a copy is never mysteriously “somewhere.”
- available
- checked_out
- on_hold_shelf
- lost
- withdrawn
- in_repair
Circulation — the rules the desk should not have to remember
A check-out that refuses in a fixed, readable order — and a renewal that never jumps the queue.
The check-out decision is a single pure classifier evaluated in a fixed order. First, the copy has to be on the shelf. Then a ready hold for a DIFFERENT patron blocks the loan — the reader who has been waiting gets the copy, not whoever happens to be at the desk. Only then does the patron’s own policy posture apply: over the school’s outstanding-fine ceiling, or at or over the active-loan cap, and the loan is refused. An unset cap means no limit on that axis — a school that does not set a loan cap does not accidentally block everyone.
copy_not_available— the copy is checked out, lost, withdrawn, or in repair.held_for_other— a ready hold for another patron sits on this copy. Respect the queue.patron_blocked— the patron is at/over the fine ceiling or at/over the loan cap.
Renewal is the same discipline. A renewal is refused when the loan is not out (not_active), when
another patron is waiting on the title (hold_queue_exists — a renewal may never step in front
of a waiting reader), or at the school’s renewal cap (renewal_cap_reached). The rule set is the
same whether a librarian or a volunteer is at the desk, because the record enforces it, not the person.
Student self-service, deny-first
A self-linked student can check a copy out or in for themselves. The patron is always derived on the server from the signed-in student’s own record — a patron id in the request body is discarded. No self-link, no self-service: a clean refusal, never a default to someone else.
Every loan has a due date
The due date is UTC-anchored day math from the check-out date and the loan period. A malformed date fails closed to the check-out date rather than opening a silent infinite loan.
Holds & fines — a queue that holds, a fine that is a record
The hold queue is FIFO. The fine is exact integer cents. Nothing here charges a card.
A hold puts a patron in line for a title; when a copy comes back it goes on the hold shelf for the next reader in the queue, and that ready hold is exactly what blocks a walk-up check-out of the same copy. The queue is the point: it is enforced at the moment of loan, not remembered afterward.
An overdue fine is computed, not guessed: whole overdue days times the school’s per-day rate, capped at the policy ceiling, in integer cents — never a float, so a fine is always an exact number of pennies. A book due today is not yet overdue: there is grace through the due day. And the whole calculation fails closed — a bad rate, a bad cap, or a malformed date yields a zero fine, never an accidental one.
A fine is a RECORD, not a checkout
There is no online payment on this site and no card field anywhere. A fine settles at the desk as a ledger entry under a deterministic, idempotent key — a replay books the same row as a no-op, never a double charge. Money is deliberately off on this surface.
Waivers post nothing
A fine can be waived for an FRL-eligible patron (equity), for grace, or by an administrator. A waived fine moves no money and is audited without a patron name in the record.
Fine ceilings gate borrowing, not people
A patron over the outstanding-fine ceiling is blocked from a new loan — a policy the school sets, applied by the record, and cleared the moment the fine is settled or waived.
Reading records — the most sensitive datum in a library
A child’s reading list is confidential by default. The record refuses to surface it unless two gates clear.
The list of titles a patron has borrowed is the most sensitive thing a library holds. The intellectual-freedom posture that libraries already keep treats it as confidential, and a minor’s reading record is a FERPA-adjacent education record. circulation.software encodes that as the default: a per-patron reading-history read is surfaced only when the requester is the authorized record-holder for that student AND a consent gate clears. An ambiguous or missing posture returns nothing. The store never even reads the title list until the gate has passed.
Default-DENY, not opt-out
The safe state is no answer. A future caller cannot accidentally default-open a child’s reading list; surfacing it requires both the record-holder check and the consent gate to return true.
Role-walled and tenant-isolated
Reading-record access is walled to a librarian role at the student’s own school, and every library record is tenant-isolated at the database. A district evaluating the product cannot read another’s patrons.
Audit without PII
Actions such as a fine waiver are recorded to the audit log without a patron name — the record proves what happened without re-exposing who it happened to.
This is FERPA-AWARE architecture — confidential-by-default reading records, role-walled and consent-gated. It is not a certification and not legal advice: your district’s counsel makes the compliance determination.
Acquisitions & interlibrary loan — the record around the shelf
An acquisition has a lifecycle. An interlibrary-loan request has a real state machine — not yet a live network.
Intake is modeled as config, not money: add a vendor, then move an acquisition through requested, ordered, received, and cancelled. It is the paper trail of what the library is buying, kept next to the catalog it feeds.
Interlibrary loan is a genuine two-sided request state machine: a request has a lifecycle a librarian can see and audit. What it is NOT, today, is a live inter-district borrowing network with real fulfilment across schools — that is planned, and the page says planned rather than dressing the state machine up as a network that is already on.
Why it is correct — the guards, in plain words
The parts that matter are small, pure, and fail closed.
Pure classifiers
Check-out, renewal, and the fine calculation are pure functions of their inputs — no IO, no hidden state, no model. They are exhaustive over a closed set of outcomes, so every case maps to a defined result.
Fail-closed arithmetic
A bad clock, a malformed date, a negative rate or cap all resolve to the safe answer: no fine, not overdue, no infinite loan. The failure direction is chosen on purpose.
Idempotent money records
A fine settlement books under a deterministic key; a retry is a no-op against the same row. There is no path to a double charge because there is no online charge at all.
Tenant-walled by construction
Every library record is isolated per school at the database. The consent chokepoint and the reading-record gate sit on top of that, not instead of it.
What is built — the honest boundary
Now, planned, and not-claimed — on one page, so nothing implies more than it is.
Every row below is one of three things and says which: shipped and running (Now), a real intention that is not on yet (Planned), or a thing this product deliberately is NOT (Not claimed). Money and AI are in the not-claimed column on purpose.
Catalog a title (Dewey / LoC / other, call number, ISBN normalized at the boundary, optional manual MARC paste), add physical copies, keyword search.
The pure classifier refuses in a fixed order -- copy not on the shelf, a ready hold for another patron, or a patron policy block (over the fine ceiling / over the loan cap). It never jumps the queue.
A renewal is refused when the loan is not out, when another patron is waiting on the title, or at the school’s renewal cap. A renewal may never step in front of a waiting reader.
Whole overdue days × the per-day rate, capped at the policy ceiling, in integer cents -- never a float. A book due today is not yet overdue (grace through the due day). Fail-closed to zero on a bad clock.
available, checked_out, on_hold_shelf, lost, withdrawn, in_repair -- a closed enum, so a copy can never be in a state the desk cannot name.
An FRL-eligible patron’s overdue fine can auto-waive for equity; a grace or admin waiver clears it too. A waived fine posts no money and is audited without a patron name.
A per-patron reading history is surfaced only when the requester is the authorized record-holder AND the consent gate clears. An ambiguous or missing posture returns nothing.
A self-linked student can check a copy out or in for themselves; the patron is always server-derived from the caller’s own record, never anything the client sent. No self-edge means a clean refusal.
Add a vendor and move an acquisition through requested → ordered → received → cancelled. Non-student-coupled config; no money moves here.
The ILL REQUEST already moves through a two-sided state machine in the platform. Turning that into a live inter-district borrowing network -- real fulfilment across schools -- is planned, and is not on today.
A book fair is not re-implemented here. It rides the platform’s existing order / order_line commerce ledger; circulation.software models only the fair window and the SKU pointer. The live-sale wiring is planned, not claimed on this host.
A scanning workflow that walks the shelf and reconciles it against the catalog is on the roadmap. It is described here as planned, never as shipped.
This site does not take a card. A fine settles at the desk as a ledger record; there is no way to settle a fine with a card here, and no card processor is wired to this surface.
No AI reading recommendations, no AI cataloging, no AI collection analytics, no AI anything. Every verdict on this page is a deterministic classifier you can read in source.
The architecture is FERPA-aware -- reading records are treated as confidential and default-DENY -- but nothing here is a certification, a compliance stamp, or legal advice.
Where it sits — structurally, not by naming anyone
Between the enterprise library system and the reading-list app, shaped for a school.
The enterprise integrated library systems are complete and are built for a public-library or university procurement — powerful, and heavy for a school with one librarian and a shelf of volunteers. The consumer reading apps are light and know nothing about a physical copy, a hold queue, or a fine. circulation.software is the school-shaped middle: it enforces the queue and the caps itself, treats a child’s reading record as confidential by default, and takes no money online. This describes the categories, not any single vendor — no competitor is named on this site.
Questions worth asking a circulation system
Straight answers — especially about money, AI, and a child’s reading record.
Can a student pay an overdue fine online through this site?
No. There is no card field and no online payment on circulation.software. An overdue fine is a record in exact integer cents; it is settled at the desk and booked to the school’s ledger, or it is waived (which posts nothing). Money is deliberately off on this surface.
Does anything here use AI?
No. There is no AI in the product -- no recommendations, no cataloging, no analytics. Every check-out, renewal, and fine decision is a small, pure, deterministic classifier written in plain source you could read line by line.
Is a check-out ever allowed to jump the hold queue?
No. The check-out classifier evaluates copy availability first, then a ready hold for a DIFFERENT patron -- and a competing hold refuses the loan. A renewal is refused the same way the moment another patron is waiting on the title. The queue has priority by construction.
Who can see a student’s reading history?
By default, no one. A per-patron reading history is surfaced only when the requester is the authorized record-holder for that student AND a consent gate clears. An ambiguous or missing posture returns nothing -- a child’s reading list is confidential by default, following the intellectual-freedom posture libraries already hold.
How does this handle FERPA?
The architecture is FERPA-aware: reading records are treated as confidential education records, access is role-walled and consent-gated, and audit entries carry no patron name. We do not claim a certification or a compliance stamp, and this is not legal advice -- your district’s counsel makes that determination.
Can students check books out themselves?
Yes, for a self-linked student, and it is deny-first: the patron is always derived on the server from the signed-in student’s own record -- a patron id in the request body is discarded. A student with no self-link is cleanly refused rather than defaulted to someone.
Can we borrow from another library through this?
The interlibrary-loan request already moves through a two-sided state machine, so a request has a real, auditable lifecycle. A live inter-district borrowing network -- actual fulfilment across schools -- is planned, not live. We will say so plainly the day it turns on.
Early access
Tell us how your library runs today.
There is no self-serve sign-up and no checkout here — on purpose. If a school library desk, a hold queue on a sticky note, and a fine envelope in a drawer sound familiar, write to us and describe how it works for you now. We are building the record underneath that, and the shape of it is decided by libraries that talk to us early.