In August 2023, my team developed Shelfree for Kloud Hackathon #3, an event for teams of up to three people. Shelfree is an unattended library lending system that uses face recognition and RFID. It was designed for clubs, societies, and laboratories that cannot assign a dedicated person to manage books but still want members to share their recommendations.
We planned and built Shelfree as a team and received the Design Award. The complete system is a collective result. My responsibilities were the web application and the integration that connected information from face recognition and RFID devices to the lending process.
Borrowing a Book with Just Your Face and the Book
Small collections are often managed by writing in a ledger or asking the person responsible for the books. That person, however, may not always be present. When the procedure becomes inconvenient, people have fewer opportunities to share books owned by individuals or laboratories.
We designed Shelfree around the following lending flow:
- Stand in front of the camera so that the borrower can be identified from a previously registered face image.
- Hold a book near the RFID reader so that its attached tag identifies it.
- Confirm the borrower and selected books on screen, then process the loans together.
- Check the lending status and due dates from the web application.
- Place a book in the return box, which connects the return to a LINE notification for the administrator.

Rather than building only an administration screen, we tried to shorten the interaction from picking up a book to completing the loan. The web application also exposes whether a book is available, who is borrowing it, and when it is due, making the state visible to both users and administrators.
The Team's Overall System
The system combines a browser-based web application, an HTTP server running on a Raspberry Pi, face recognition, an RFID reader, and a return box.

- Next.js 13 / TypeScript: book lists and details, profiles, lending history, the lending confirmation screen, and Route Handlers
- Clerk: connects web accounts to the user IDs included in face-recognition results
- Prisma / Vercel Postgres: stores books, owners, checkout dates, due dates, and return dates
- Zod: validates user, book, and timestamp payloads from Python and responses from the web API at runtime
- TanStack Query: manages device-state polling and refetching book and user information
- Python 3 / Flask: exposes face-recognition and RFID results from the Raspberry Pi to the web application over HTTP
- face_recognition: compares webcam frames with registered images to obtain a user ID
- MFRC522 / MIFARE Classic 1K: reads the identifier of the RFID tag attached to each book
For the return box, the team explored a combination of sensors, a motor, a camera, and an RFID reader so that it would accept a book after checking its lending state. The core of the system was to have the web application and the devices refer to the same books and transactions instead of treating either side as a self-contained prototype.
My Work on the Web Application and Device Integration
I implemented the Next.js screens and API, authentication, and data model. I also connected the device-side information describing who was borrowing which books to confirmation in the web interface and the recorded transaction. The public repository contains the commit history for the web application.
The lending screen polls the Flask server on the Raspberry Pi every two seconds. A recognised user and books placed near the RFID reader appear on screen. When the user confirms the selection, the web application obtains a confirmed payload and sends it to a Next.js Route Handler, which creates the lending records.
const { data } = useQuery({
queryKey: ["payload"],
queryFn: async () => {
const response = await fetch("http://localhost:5000/")
return PayloadSchema.parse(JSON.parse(await response.text()))
},
refetchInterval: 2000,
})The application does not trust the incoming JSON shape without validation. Zod validates its structure and timestamps. A book uses its RFID tag identifier as its ID, while a face-recognition result contains a Clerk user ID and the time at which the person was seen.
const PayloadSchema = z.object({
book: z.record(z.string().datetime({ offset: true })),
person: z
.object({
id: z.string().min(1),
seenAt: z.string().datetime({ offset: true }),
})
.nullable(),
timestamp: z.string().datetime({ offset: true }),
signature: z.string().min(1),
confirm: z.boolean().optional(),
})Rejecting Stale Recognition and Duplicate Loans
In addition to validating the payload, the lending API checks the freshness of the recognition result and the current lending state.
if (new Date(payload.person.seenAt).getTime() < Date.now() - 5 * 60 * 1000) {
throw new Error("More than five minutes have passed since face recognition")
}
if (book.transactions.length > 0) {
throw new Error(`${book.title} is already on loan`)
}
await prismaClient.transaction.create({
data: {
book: { connect: { id: book.id } },
userId: user.id,
dueAt: new Date(Date.now() + book.availableDays * 24 * 60 * 60 * 1000),
},
})The API verifies that the user exists in Clerk, that the face was recognised within the previous five minutes, and that each requested book exists and is not already on loan. After checking all requested books, it calculates each due date from the book's availableDays value.
The repository implementation checks that a signature value is present, but cryptographic signature verification remains a TODO. It should therefore not be described as a completed mechanism for detecting tampering. Beyond making a prototype communicate across devices and the web, the project exposed the need to decide where the trust boundary belongs and which validation must be completed.
A Design Award for an Experience that Included Hardware
The theme of Kloud Hackathon #3 was “A Summer Story of Friendship Connected by Code.” Instead of presenting Shelfree only as an administrative system, we proposed it as an experience through which people could connect by recommending books to one another.
We kept the screen focused and associated each physical action—showing a face, presenting a book, and confirming the loan—with a visible state. Book covers, availability, due dates, and messages from owners remained easy to follow on mobile. We kept the presentation consistent across the pitch materials, web interface, and physical flow. The team received the Design Award for Shelfree.
What I Learned
This project taught me that connecting the web to hardware requires more than exposing a single API. Camera and RFID results change over time, and another user may already have borrowed the same book. The screen, authentication, database, and devices must agree on which state and timestamp should be trusted.
It also showed the distance between a working hackathon prototype and a lending system that can be operated continuously. Cryptographic signature verification, recovery from failures, database transactions when recording multiple books, and reliable return processing all remained areas for further work. Understanding those limitations, as well as the completed parts, was an important outcome of building across the boundary between a web application and physical devices.

