Kiosk is a simple web application to manage, read and download your ebooks.
Self-hosted ebook library with a gorgeous web interface, book collections, and metadata editing.
Kiosk allows you to manage your books, sort them into collections, and to read and download them. It is built on SvelteKit and can run in serverless environments. Kiosk also employs password-less authentication via Passkeys.
For now, Kiosk has only been tested properly in a node.js environment. If you intend to host it in a serverless environment like Cloudflare Workers, Netlify or AWS Lambda, I'll do my best to support you!
To install Kiosk in a node environment, clone the repository:
npx create @project-kiosk/create
Modify the .env file to match your environment, then start the preview server:
npm run preview
Congrats – Kiosk should be running on localhost:4173!
Kiosk can be configured via environment variables. The reference is grouped in the following sections:
Used for storing metadata, library content, and user data. Supported providers: Anything Prisma supports – right now, that is:
For all providers, you'll have to set the following variables:
DATABASE_PROVIDER
: Database type, as per the
Prisma documentation
DATABASE_URL
: Connection URL, including authentication info, for the database to use.JSON Web Tokens are used to encode user authorization information along with their access token and will be stored in a user cookie.
JWT_SECRET
: Secret to encode JSON web tokens with. Generate a unique value for this.JWT_COOKIE_NAME
: Name of the JWT cookie. Defaults to jwt
.SESSION_ID_COOKIE_NAME
: Name of the authentication session cookie. Defaults to ksid
Used for sending out authentication pass codes and other account-related communication. Supported providers: Mailjet, Mailgun, Cloudflare (via MailChannels) or anything NodeMailer supports (using a custom adapter).
MAIL_ADAPTER
: One of mailjet
, mailgun
, cloudflare
.MAIL_SENDER_NAME
: Name of the sender in outgoing emails. Defaults to Kiosk
.MAIL_SENDER_ADDRESS
: Email address of the sender in outgoing emails.Depending on the chosen adapter, you'll need to provide the following values, too:
MAILJET_API_KEY
: API key for the Mailjet account.MAILJET_API_SECRET
: API key secret for the Mailjet account.MAILJET_API_KEY
: API key for the Mailgun account.Note: Using the Cloudflare MailChannels integration is limited to Kiosk running in a Cloudflare Workers or Pages environment. While it works remarkably flawlessly, I'm a bit hesitant to rely on this service being free forever.
On the bright side of things, however, Kiosk makes switching the mail adapter exceptionally simple, so if any problems arise, you can just swap it out for one of the others.
Used for storing temporary KV data, mostly for caching. Supported providers: In-memory, Redis, or Cloudflare KV.
KEY_VALUE_ADAPTER
: One of memory
, redis
, or cloudflare
.Depending on the chosen adapter, you'll need to provide the following values, too:
Note: Using KV is limited to Kiosk running in a Cloudflare Workers or Pages environment.
KV_NAMESPACE
: The
KV namespace to use.REDIS_URL
: Connection URL for the redis instance to use, provided in the format
redis[s]://[[username][:password]@][host][:port][/db-number]
. Defaults to redis://localhost
.REDIS_SOCKET
: Path to the UNIX socket redis is listening on. Will be preferred over REDIS_DSN
, if given.Used for storing books and images. Supported providers: Node (local filesystem), AWS S3 (https://aws.amazon.com/s3/), or Cloudflare R2.
STORAGE_ADAPTER
: One of node
, aws
, or cloudflare
.Depending on the chosen adapter, you'll need to provide the following values, too:
STORAGE_PATH
: Absolute path to the storage root directory. Root traversal is prevented by Kiosk automatically.TBD
Note: Using R2 is currently limited to Kiosk running in a Cloudflare Workers or Pages environment. This restriction could be removed in the future, however. PRs welcome!
STORAGE_BUCKET_NAME
: Name of the
storage bucket
to write to and read from.Used for fetching information about authors, publishers and books automatically. Supported providers: Google Graph Search, Google Enterprise KG Search, Wikidata (in progress)
KNOWLEDGE_GRAPH_ADAPTER
: One of googleKgSearch
, googleEnterpriseKg
, or wikidata
.Depending on the chosen adapter, you'll need to provide the following values, too:
Please note that Google is really ambiguous with the wording on the knowledge graph search API page; I cannot tell whether they are going to deprecate the API, or whether you're just not supposed to use it in your own search engine. Nevertheless, it's here, it's working, and has a generous quota, so there's that.
PUBLIC_GOOGLE_KNOWLEDGE_GRAPH_API_KEY
: Your Google API key.
Follow the directions here to get one.PUBLIC_GOOGLE_KNOWLEDGE_GRAPH_API_URL
: URL to the query endpoint. Usually not required. Defaults to
https://content-kgsearch.googleapis.com/v1/entities:search
.This is Google's newer offering, purportedly for "Enterprise" customers, whatever that means. The product seems to be a strange chimera of data enrichment and public knowledge graph search somehow tacked on as a neat side effect. Using the enterprise graph search is free – for now, apparently – and works really reliably, so that is what I'd recommend.
GOOGLE_ENTERPRISE_KNOWLEDGE_GRAPH_API_CREDENTIALS
: Your Google cloud credentials for the enterprise knowledge
graph search API. These must be provided as a base64-encoded string, which you'll want for storing in an environment
variable anyway. Follow the directions here to
get credentials, then run cat $CREDENTIALS_FILE | base64
to encode them into a string.GOOGLE_ENTERPRISE_KNOWLEDGE_GRAPH_API_URL
: URL to the query endpoint. Note that it contains a placeholder for
the Google Cloud project ID. Usually not required. Defaults to
https://enterpriseknowledgegraph.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/publicKnowledgeGraphEntities:Search
.GOOGLE_ENTERPRISE_KNOWLEDGE_GRAPH_API_SCOPE
: Scope for the OAuth token requested by the client. Usually not
required. Defaults to https://www.googleapis.com/auth/cloud-platform
.Unfortunately, I wasn't able to implement the Wikidata adapter yet, because it's just so darn hard to ask them for a
person or thing reliably without studying some obscure query language for a few decades.
If you have experience in working with the Wikipedia API, or the Wikidata SPARQL engine, please let me now!
Used for indexing and searching for books and recommending content. Supported providers: Algolia.
Note: Right now, there's only support for Algolia, although I plan to include an adapter for Elasticsearch (and OpenSearch, by extension). Let me know if you're interested in helping out.
SEARCH_ADAPTER
: One of algolia
.SEARCH_INDEX_BOOKS
: Name of the search index for books. Defaults to dev_books
.SEARCH_INDEX_AUTHORS
: Name of the search index for authors. Defaults to dev_authors
.Depending on the chosen adapter, you'll need to provide the following values, too:
PUBLIC_ALGOLIA_SEARCH_API_KEY
: Your Algolia search API key.PUBLIC_ALGOLIA_ADMIN_API_KEY
: Your Algolia admin API key.ALGOLIA_APP_ID
: Your Algolia app ID.Originally, Kiosk was born from a Dribbble shot of a prototype library. Unfortunately, I can't find that shot anymore,
so I can't give credit to the designer. Back in 2017, I cobbled together an express server with redis and epub.js to get
a proof of concept. It worked, but was quite cumbersome to extend. Back then, I was working with PHP in my day job, so
after a while, I decided to build Kiosk from scratch, using PHP and Laravel. I never got quite around implementing all
functionality of the original app. This was a nice lesson on why you shouldn't rebuild things from scratch!
Fast-forward a few years, I finally got back to working on Kiosk. PHP is annoying me more and more, and the frontend
world has progressed substantially. So this time, I've settled on a technology stack which feels just perfect for this
project: SvelteKit in conjunction with SQLite.
If you're interested in improving or extending Kiosk, please reach out. I'm quite preoccupied with my day job, family duties and doing things away from a computer, so I cannot spend as much time on Kiosk as I'd like to.