The open source Library-in-a-Box to preserve and lend digital books.
Learn more Β»
Issues
Β·
Pull Requests
Β·
License
- About the Project
- Features
- OPDS 2.0 Feed
- Technologies
- Endpoints
- Getting Started
- Development Setup
- Importing Test Books
- Testing Readium Server
- Rebuilding
- FAQs
- Tests
- Project Structure
- Contributing
- Pilot
- Open Topics
- Community & Support
- License
Lenny is a free, open source, Library-in-a-Box for libraries to preserve and lend digital books.
- π Designed for libraries that want control over their digital collections.
- π Built with modern authentication, DRM, and flexible storage options.
- π Easy to self-host, customize, and scale for any library size.
- π Active development and open to contributions!
- Full Lending Workflow: Borrow, return, and manage digital books.
- API-first: RESTful endpoints for integration and automation.
- Containerized: Simple Docker deployment and robust Makefile for scripts.
- Book Importer: Quickly load hundreds of test books for demos or pilots.
- Readium Integration: Secure, browser-based reading experience.
- Flexible Storage: S3, Internet Archive, or local file support.
- Database-backed: Uses PostgreSQL and SQLAlchemy.
- Lenny is powered by OPDS 2.0 Specs.Lenny has its own OPDS 2.0 Package
pyopds2_lennymore on pyopds2_lenny repo.
- Docker for deployment and containerization
- nginx as a reverse proxy
- FastAPI (Python) as the web & API framework
- Minio API for storing digital assets
- YAML for configuring library-level rules
- PostgreSQL for the database
- SQLAlchemy as the Python ORM
- Readium LCP for DRM
- Readium Web SDK for a secure web reading experience
- OPDS for syndicating holdings
/v{1}/api/v{1}/manage/v{1}/read/v{1}/opds/v{1}/stats
To install and run Lenny as a production application:
curl -fsSL https://raw.githubusercontent.com/ArchiveLabs/lenny/refs/heads/main/install.sh | sudo bashgit clone https://github.com/ArchiveLabs/lenny.git
cd lenny
make all- This will generate a
.envfile with reasonable defaults (if not present). - Navigate to
localhost:8080(or your$LENNY_PORT). - Enter the API container with:
docker exec -it lenny_api bash
docker exec -it lenny_api python scripts/load_open_books.py If you'd like to change the encrypted state of an existing book (e.g. with id=1) to test borrowing, run the following:
First, exec into the api docker container and start a python session:
docker compose exec -it api pythonSecond, fetch the db item and set its encrypted state to:
from lenny.core.models import Item, db
i = db.query(Item).filter(Item.id == 1).first()
i.encrypted = True
db.add(i)
db.commit()To add a book to Lenny, you must provide an OpenLibrary Edition ID (OLID). Books without an OLID cannot be uploaded.
Sign in to your Openlibrary.org account.
https://openlibrary.org/books/add
navigate to the above link and add all the details.
make addbook olid=OL123456M filepath=/path/to/book.epub [encrypted=true]# Add an unencrypted book
make addbook olid=OL60638966M filepath=./books/mybook.epub
# Add an encrypted book
make addbook olid=OL60638966M filepath=./books/mybook.epub encrypted=true
# Using numeric OLID format (without OL prefix and M suffix)
make addbook olid=60638966 filepath=./books/mybook.epub- File Location: The EPUB file must be within the project directory (e.g., in
./books/or project root) - OLID Formats: Accepts both
OL123456Mand123456formats - Duplicates: If a book with the same OLID already exists, the upload will fail with a conflict.
If you get a "File not found" or permission error, make sure:
- The file is copied into your lenny project directory.
- You're using a relative path from the project root (e.g.,
./books/mybook.epub)
BOOK=$(echo -n "s3://bookshelf/32941311.epub" | base64 | tr '/+' '_-' | tr -d '=')
echo "http://localhost:15080/$BOOK/manifest.json"
curl "http://localhost:15080/$BOOK/manifest.json"docker compose -p lenny down
docker compose -p lenny up -d --buildEverything is broken and I need to start from scratch
make tunnel rebuild start preload items=10 logIf I disconnect from the internet and tunnel stops working, what do I do?
make untunnel tunnel startI am getting database connection problems
make resetdb restart preload items=5I need to stop services (also kills the tunnel)
make stop All automated tests are in the tests/ directory.
To run tests:
pytest- Install dependencies:
pip install -r requirements.txt - Test configs via
.env.testif needed.
/
βββ lenny/ # Core application code
β βββ routes/ # API route definitions and docs
βββ scripts/ # Utility scripts (e.g. load_open_books.py)
βββ tests/ # Automated tests
βββ docker/ # Docker configuration
βββ Makefile # Make commands for setup/maintenance
βββ install.sh # Production install script
βββ .env # Environment variables (generated)
βββ README.md # Project documentation
Contributions are welcome! Please open an issue or pull request if you have suggestions or improvements.
We're seeking partnerships with libraries who would like to try lending digital resources to their patrons.
- Authentication - How does your library perform authentication currently?
- GitHub Issues β File bugs, request features, ask questions
- Email: [email protected]
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
Empowering libraries to share digital knowledge.