Self-hosted manga collection manager and reader. Auto-monitors sources for new chapters, downloads them, and packages into CBZ+ComicInfo.xml. Can be read via built-in reader, Kavita integration, or OPDS.
C#
14
824 commits
updated Oct 4, 2026
Maki is a manga collection manager in the spirit of Sonarr and Radarr: add a series once and Maki keeps it complete. It watches sites for new chapters, downloads the pages, and writes CBZ files with ComicInfo.xml metadata, which Kavita and most comic readers understand. You can also read them in Maki's built-in reader or over OPDS.
Maki is almost entirely AI-slop-built, developed with Anthropic's latest Claude models.
.zip is
placed unchanged (a CBZ is a zip), .cbr/.rar/.7z/.tar are repacked, and a folder of
loose images becomes one CBZ per folder. A PDF is the one exception: it is kept as it is and
read in place. Manual library import converts files the same way and leaves your originals in
place.{Series}/{Series} Vol.X Ch.Y.cbz, with configurable folder
and file naming. ComicInfo.xml carries series, chapter number, volume, authors, genres,
language, and reading direction. Files are written atomically, so a reader never sees a
half-written CBZ. Maki can also write a cover.jpg into each series folder for readers that
look for one./api/v1 with X-Api-Key auth. Swagger is served at /swagger
when running in the Development environment.Sources are built into Maki, so there are no extensions to install.
| Source | Notes |
|---|---|
| MangaDex | Official API, language filter |
| MANGA Plus | Official (Shueisha), language-specific mappings |
| WEBTOON | Official (webtoons.com), ORIGINALS and CANVAS in en, id, th, es, fr, zh-Hant, de |
| Naver Webtoon | Official (comic.naver.com), Korean, WEBTOON tier only |
| GigaViewer sites | Official Japanese: Shonen Jump+, Comic Days, Sunday Webry, MAGCOMI, Tonari no Young Jump, Comic Zenon, Kurage Bunch (free episodes only) |
| KadoComi (Comic Walker) | Official, Japanese |
| MangaPill | |
| Weeb Central | |
| MangaFire | Language filter, requires FlareSolverr |
| TCB Scans | |
| Asura Scans | Manhwa/manhua |
| Flame Comics | Manhwa/manhua |
| TopManhua | Manhwa/manhua, requires FlareSolverr |
| MangaKatana | |
| MangaKakalot | Requires FlareSolverr |
| Atsumaru | atsu.moe |
| Dynasty Scans | English, yuri |
| Toonily | Manhwa, adult, requires FlareSolverr |
| Manhwa18.net | Adult, requires FlareSolverr |
| Baozi Manhua | Simplified Chinese manhua |
| Manhuagui | Simplified Chinese, mirror override via MAKI_SOURCE_MANHUAGUI_BASEURL |
| Manga Livre | Brazilian Portuguese |
| Taiyō | Brazilian Portuguese |
| ManhwaWeb | Spanish |
| Olympus Scanlation | Spanish, requires FlareSolverr |
| Anime-Sama | French, requires FlareSolverr |
| Manga-Tube | German |
| MangaWorld | Italian |
| MangaLib | Russian |
| MangaDenizi | Turkish |
| Shinigami | Indonesian, manhwa/manhua/manga |
| Cứu Truyện | Vietnamese, FlareSolverr when challenged, mirror override via MAKI_SOURCE_CUUTRUYEN_BASEURL |
| Team-X | Arabic, requires FlareSolverr; the domain rotates, override it with MAKI_SOURCE_TEAMX_BASEURL |
| Sen Manga | Japanese raws |
| Rawkuma | Japanese raws, FlareSolverr when challenged, mirror override via MAKI_SOURCE_RAWKUMA_BASEURL |
services:
maki:
image: ghcr.io/orbitmpgh/maki:latest
container_name: maki
environment:
- PUID=1000
- PGID=1000
volumes:
- ./maki-config:/config
- /path/to/manga-library:/library
ports:
- "8990:8990"
restart: unless-stopped
# Optional. Point a Kavita library at the same folder.
kavita:
image: jvmilazz0/kavita:latest
container_name: kavita
volumes:
- ./kavita-config:/kavita/config
- /path/to/manga-library:/library
ports:
- "5000:5000"
restart: unless-stopped
# Optional. Needed for Cloudflare-protected sources (see the Sources table).
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
ports:
- "8191:8191"
restart: unless-stopped
http://localhost:8990 and create the administrator account./library as a root folder.http://flaresolverr:8191 and hit Test.Upgrading from a single-user Maki? The first page asks you to set a username and password. Your library, history, and tracker connections are already attached to that account.
The interface ships in English, German, French, Spanish, Brazilian Portuguese, Polish, Russian, Turkish, Japanese, Simplified Chinese, and Korean. Set yours under Settings → My account → Language, or leave it on Automatic to follow the browser. The choice is stored per account and follows you across devices. It is separate from Title language, which controls how series titles are displayed.
English is the source language. Everything else is machine-translated and marked as needing review, so expect awkward phrasing. Untranslated strings fall back to English.
Corrections are the easiest way to contribute. The simplest route is Maki on Hosted Weblate: sign in, pick your language, and fix or approve strings in the browser. Weblate sends the changes back here as a pull request. The translated percentage only counts strings a person has approved, so it starts low even though every string already has a machine translation.
If you would rather work in the repo, the catalogs are gettext PO files under locales/, one
directory per language. Or open an issue quoting the string and what it should say.
scripts/i18n/glossary.md lists the terms that stay in English.
Each account has its own reading history, preferences, and tracker connections. The library itself is shared: one copy of the files, one set of series and chapters, so a second reader costs no disk.
Maki authenticates with an HttpOnly session cookie and per-user API keys. Before putting it on a public address:
Secure and turns on HSTS. Do not enable it before TLS works: a Secure
cookie over plain HTTP is never returned, so sign-in silently fails.172.18.0.0/16).
Until you do, Maki ignores X-Forwarded-For and attributes every failed sign-in to the proxy.
Trusting the header from any address would let a client forge the audit log and get around
rate limiting and lockout.Security settings are read at startup, so restart Maki after changing them.
Two directories under /config are credential material and deserve the same permissions as the
database: dataprotection-keys (whoever holds it can mint a session cookie for any user) and
backups. Backups exclude the key ring, which is why restoring onto a different machine signs
everyone out once.
API keys and OPDS feed URLs are shown exactly once. Only a SHA-256 fingerprint is stored, so a lost key cannot be recovered; generate a new one.
Single sign-on is optional and works alongside local passwords. Tested against Authelia, Keycloak, Authentik, and Entra ID; anything with OpenID Connect discovery and the authorization code flow should work.
Register Maki with your provider as a confidential or public client using the authorization code
flow with PKCE. The redirect URI is https://<your-host>/api/v1/auth/oidc/callback. Then fill in
Settings → Single sign-on and restart Maki; the provider configuration is read once at
startup.
| Field | Notes |
|---|---|
| Issuer URL | For example https://auth.example.com. Maki appends /.well-known/openid-configuration. |
| Client ID / secret | Leave the secret empty for a public client; PKCE protects the exchange either way. |
| Scopes | openid is always requested. Add groups (or your provider's equivalent) for claim mapping. |
| Create accounts on first sign-in | Off by default. When on, anyone the provider authenticates gets an account. Fine for a household realm; leave it off for a shared company realm. |
| Admin claim / Permission claim | Optional, see below. |
sub claim, so upstream renames do not
strand anyone. The first time an unknown subject signs in, Maki links it to a local account with
the same email if the provider marks the address as verified and exactly one account has it.
Otherwise it creates an account (if allowed) or refuses.claim=value (groups=maki-admins); the permission claim is
a claim name whose values are matched against permission names (DownloadChapters, UseOpds,
and so on). Unmatched values are ignored, and Admin only comes from the admin claim.MAKI_ALLOW_LOCAL_LOGIN=1 and restart: password sign-in returns for every account, and Maki logs
a warning and shows a banner until you remove it.http:// issuers are allowed for a provider on the same LAN or Docker network, with a
startup warning. Tokens are signed either way, but the discovery document and signing keys travel
in the clear. Prefer https:// where you can.Settings → Backup & Restore takes zip snapshots of the database plus config.json. Together
these are everything that cannot be cheaply regenerated. The MangaBaka dump, embeddings, covers,
and cache are excluded so backups stay small. Backups are stored under {ConfigDir}/backups, and
a setting controls how many are kept.
Maki also backs up automatically before any upgrade applies a database migration. Migrations are forward-only, so this is your recovery path if an upgrade goes wrong.
Backups contain the secrets from your settings (API keys, passwords) in plain text. Treat a downloaded backup like a password.
Restoring replaces the database and settings, then restarts Maki. Under Docker
(restart: unless-stopped) or systemd it comes back on its own; a process started with
dotnet run or the Windows executable exits and must be started again by hand. You can restore a
backup from another machine, but not one taken by a newer Maki version.
The multi-stage Dockerfile builds the frontend (Node 24) and backend (.NET 10)
and packages them into an aspnet:10.0 runtime image serving the SPA from wwwroot/.
docker build -t maki:local .
docker run -d --name maki \
-p 8990:8990 \
-v "$PWD/maki-config:/config" \
-v "/path/to/manga-library:/library" \
maki:local
# Multi-arch push with Buildx
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/<you>/maki:latest --push .
locales/ in (the API embeds
server.po, Vite bundles client.po), and the build fails if any language is missing from
either the frontend or the backend stage.entrypoint.sh fixes ownership of /config and drops to PUID/PGID via gosu./config volume; the library is a separate mount you can share with Kavita.distribution/docker/Dockerfile is an identical copy used by CI and the distribution/build-*.ps1
scripts. Keep the two in step.# Backend (http://localhost:8990, Swagger at /swagger in the Development environment only)
dotnet run --project src/Maki.Api
# Frontend dev server (http://localhost:5173, proxies /api and /signalr)
npm --prefix frontend run dev
# Tests
dotnet test
# Release publish (what the container ships)
dotnet publish src/Maki.Api -c Release
State lives in MAKI_CONFIG_DIR. If it is unset, Maki uses /config when that directory exists
(the Docker convention) and otherwise a per-user application-data folder, %APPDATA%\Maki on
Windows. It holds the SQLite database, logs, covers, page cache, MangaBaka dump, embedding index,
and precomputed recommendation data. For local development point it at a throwaway directory:
MAKI_CONFIG_DIR="$PWD/.devconfig" dotnet run --project src/Maki.Api
EF Core migrations apply automatically on startup.
src/
├── Maki.Api/ ASP.NET Core host, REST /api/v1, SignalR, Quartz jobs, download workers
├── Maki.Core/ Domain: entities, ISource/IMetadataProvider, parser, naming, CBZ pipeline
├── Maki.Data/ EF Core + SQLite
├── Maki.Sources/ One ISource implementation per site
└── Maki.Metadata/ MangaBaka provider + local dump + ONNX embeddings
frontend/ Vite + React + TypeScript + Mantine SPA
Sources implement a single ISource interface (search / series / chapters / pages) and are
registered in DI. Adding a site is one class plus one registration. Page requests carry their own
headers (Referer, cookies) through to the image fetch, so hotlink-protected CDNs work.
Maki is pre-1.0 and under active development. Schema and API can still shift between releases. Issues and pull requests are welcome.
Maki is a tool for organizing your library. Scraper sources access third-party websites. You are responsible for complying with those sites' terms of service and your local laws. Support the industry: buy official releases.
C#
59.1%
HTML
21.8%
TypeScript
15.9%
CSS
1.6%
Self-hosted manga collection manager and reader. Auto-monitors sources for new chapters, downloads them, and packages into CBZ+ComicInfo.xml. Can be read via built-in reader, Kavita integration, or OPDS.
C#
14
824 commits
updated Oct 4, 2026
Maki is a manga collection manager in the spirit of Sonarr and Radarr: add a series once and Maki keeps it complete. It watches sites for new chapters, downloads the pages, and writes CBZ files with ComicInfo.xml metadata, which Kavita and most comic readers understand. You can also read them in Maki's built-in reader or over OPDS.
Maki is almost entirely AI-slop-built, developed with Anthropic's latest Claude models.
.zip is
placed unchanged (a CBZ is a zip), .cbr/.rar/.7z/.tar are repacked, and a folder of
loose images becomes one CBZ per folder. A PDF is the one exception: it is kept as it is and
read in place. Manual library import converts files the same way and leaves your originals in
place.{Series}/{Series} Vol.X Ch.Y.cbz, with configurable folder
and file naming. ComicInfo.xml carries series, chapter number, volume, authors, genres,
language, and reading direction. Files are written atomically, so a reader never sees a
half-written CBZ. Maki can also write a cover.jpg into each series folder for readers that
look for one./api/v1 with X-Api-Key auth. Swagger is served at /swagger
when running in the Development environment.Sources are built into Maki, so there are no extensions to install.
| Source | Notes |
|---|---|
| MangaDex | Official API, language filter |
| MANGA Plus | Official (Shueisha), language-specific mappings |
| WEBTOON | Official (webtoons.com), ORIGINALS and CANVAS in en, id, th, es, fr, zh-Hant, de |
| Naver Webtoon | Official (comic.naver.com), Korean, WEBTOON tier only |
| GigaViewer sites | Official Japanese: Shonen Jump+, Comic Days, Sunday Webry, MAGCOMI, Tonari no Young Jump, Comic Zenon, Kurage Bunch (free episodes only) |
| KadoComi (Comic Walker) | Official, Japanese |
| MangaPill | |
| Weeb Central | |
| MangaFire | Language filter, requires FlareSolverr |
| TCB Scans | |
| Asura Scans | Manhwa/manhua |
| Flame Comics | Manhwa/manhua |
| TopManhua | Manhwa/manhua, requires FlareSolverr |
| MangaKatana | |
| MangaKakalot | Requires FlareSolverr |
| Atsumaru | atsu.moe |
| Dynasty Scans | English, yuri |
| Toonily | Manhwa, adult, requires FlareSolverr |
| Manhwa18.net | Adult, requires FlareSolverr |
| Baozi Manhua | Simplified Chinese manhua |
| Manhuagui | Simplified Chinese, mirror override via MAKI_SOURCE_MANHUAGUI_BASEURL |
| Manga Livre | Brazilian Portuguese |
| Taiyō | Brazilian Portuguese |
| ManhwaWeb | Spanish |
| Olympus Scanlation | Spanish, requires FlareSolverr |
| Anime-Sama | French, requires FlareSolverr |
| Manga-Tube | German |
| MangaWorld | Italian |
| MangaLib | Russian |
| MangaDenizi | Turkish |
| Shinigami | Indonesian, manhwa/manhua/manga |
| Cứu Truyện | Vietnamese, FlareSolverr when challenged, mirror override via MAKI_SOURCE_CUUTRUYEN_BASEURL |
| Team-X | Arabic, requires FlareSolverr; the domain rotates, override it with MAKI_SOURCE_TEAMX_BASEURL |
| Sen Manga | Japanese raws |
| Rawkuma | Japanese raws, FlareSolverr when challenged, mirror override via MAKI_SOURCE_RAWKUMA_BASEURL |
services:
maki:
image: ghcr.io/orbitmpgh/maki:latest
container_name: maki
environment:
- PUID=1000
- PGID=1000
volumes:
- ./maki-config:/config
- /path/to/manga-library:/library
ports:
- "8990:8990"
restart: unless-stopped
# Optional. Point a Kavita library at the same folder.
kavita:
image: jvmilazz0/kavita:latest
container_name: kavita
volumes:
- ./kavita-config:/kavita/config
- /path/to/manga-library:/library
ports:
- "5000:5000"
restart: unless-stopped
# Optional. Needed for Cloudflare-protected sources (see the Sources table).
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
ports:
- "8191:8191"
restart: unless-stopped
http://localhost:8990 and create the administrator account./library as a root folder.http://flaresolverr:8191 and hit Test.Upgrading from a single-user Maki? The first page asks you to set a username and password. Your library, history, and tracker connections are already attached to that account.
The interface ships in English, German, French, Spanish, Brazilian Portuguese, Polish, Russian, Turkish, Japanese, Simplified Chinese, and Korean. Set yours under Settings → My account → Language, or leave it on Automatic to follow the browser. The choice is stored per account and follows you across devices. It is separate from Title language, which controls how series titles are displayed.
English is the source language. Everything else is machine-translated and marked as needing review, so expect awkward phrasing. Untranslated strings fall back to English.
Corrections are the easiest way to contribute. The simplest route is Maki on Hosted Weblate: sign in, pick your language, and fix or approve strings in the browser. Weblate sends the changes back here as a pull request. The translated percentage only counts strings a person has approved, so it starts low even though every string already has a machine translation.
If you would rather work in the repo, the catalogs are gettext PO files under locales/, one
directory per language. Or open an issue quoting the string and what it should say.
scripts/i18n/glossary.md lists the terms that stay in English.
Each account has its own reading history, preferences, and tracker connections. The library itself is shared: one copy of the files, one set of series and chapters, so a second reader costs no disk.
Maki authenticates with an HttpOnly session cookie and per-user API keys. Before putting it on a public address:
Secure and turns on HSTS. Do not enable it before TLS works: a Secure
cookie over plain HTTP is never returned, so sign-in silently fails.172.18.0.0/16).
Until you do, Maki ignores X-Forwarded-For and attributes every failed sign-in to the proxy.
Trusting the header from any address would let a client forge the audit log and get around
rate limiting and lockout.Security settings are read at startup, so restart Maki after changing them.
Two directories under /config are credential material and deserve the same permissions as the
database: dataprotection-keys (whoever holds it can mint a session cookie for any user) and
backups. Backups exclude the key ring, which is why restoring onto a different machine signs
everyone out once.
API keys and OPDS feed URLs are shown exactly once. Only a SHA-256 fingerprint is stored, so a lost key cannot be recovered; generate a new one.
Single sign-on is optional and works alongside local passwords. Tested against Authelia, Keycloak, Authentik, and Entra ID; anything with OpenID Connect discovery and the authorization code flow should work.
Register Maki with your provider as a confidential or public client using the authorization code
flow with PKCE. The redirect URI is https://<your-host>/api/v1/auth/oidc/callback. Then fill in
Settings → Single sign-on and restart Maki; the provider configuration is read once at
startup.
| Field | Notes |
|---|---|
| Issuer URL | For example https://auth.example.com. Maki appends /.well-known/openid-configuration. |
| Client ID / secret | Leave the secret empty for a public client; PKCE protects the exchange either way. |
| Scopes | openid is always requested. Add groups (or your provider's equivalent) for claim mapping. |
| Create accounts on first sign-in | Off by default. When on, anyone the provider authenticates gets an account. Fine for a household realm; leave it off for a shared company realm. |
| Admin claim / Permission claim | Optional, see below. |
sub claim, so upstream renames do not
strand anyone. The first time an unknown subject signs in, Maki links it to a local account with
the same email if the provider marks the address as verified and exactly one account has it.
Otherwise it creates an account (if allowed) or refuses.claim=value (groups=maki-admins); the permission claim is
a claim name whose values are matched against permission names (DownloadChapters, UseOpds,
and so on). Unmatched values are ignored, and Admin only comes from the admin claim.MAKI_ALLOW_LOCAL_LOGIN=1 and restart: password sign-in returns for every account, and Maki logs
a warning and shows a banner until you remove it.http:// issuers are allowed for a provider on the same LAN or Docker network, with a
startup warning. Tokens are signed either way, but the discovery document and signing keys travel
in the clear. Prefer https:// where you can.Settings → Backup & Restore takes zip snapshots of the database plus config.json. Together
these are everything that cannot be cheaply regenerated. The MangaBaka dump, embeddings, covers,
and cache are excluded so backups stay small. Backups are stored under {ConfigDir}/backups, and
a setting controls how many are kept.
Maki also backs up automatically before any upgrade applies a database migration. Migrations are forward-only, so this is your recovery path if an upgrade goes wrong.
Backups contain the secrets from your settings (API keys, passwords) in plain text. Treat a downloaded backup like a password.
Restoring replaces the database and settings, then restarts Maki. Under Docker
(restart: unless-stopped) or systemd it comes back on its own; a process started with
dotnet run or the Windows executable exits and must be started again by hand. You can restore a
backup from another machine, but not one taken by a newer Maki version.
The multi-stage Dockerfile builds the frontend (Node 24) and backend (.NET 10)
and packages them into an aspnet:10.0 runtime image serving the SPA from wwwroot/.
docker build -t maki:local .
docker run -d --name maki \
-p 8990:8990 \
-v "$PWD/maki-config:/config" \
-v "/path/to/manga-library:/library" \
maki:local
# Multi-arch push with Buildx
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/<you>/maki:latest --push .
locales/ in (the API embeds
server.po, Vite bundles client.po), and the build fails if any language is missing from
either the frontend or the backend stage.entrypoint.sh fixes ownership of /config and drops to PUID/PGID via gosu./config volume; the library is a separate mount you can share with Kavita.distribution/docker/Dockerfile is an identical copy used by CI and the distribution/build-*.ps1
scripts. Keep the two in step.# Backend (http://localhost:8990, Swagger at /swagger in the Development environment only)
dotnet run --project src/Maki.Api
# Frontend dev server (http://localhost:5173, proxies /api and /signalr)
npm --prefix frontend run dev
# Tests
dotnet test
# Release publish (what the container ships)
dotnet publish src/Maki.Api -c Release
State lives in MAKI_CONFIG_DIR. If it is unset, Maki uses /config when that directory exists
(the Docker convention) and otherwise a per-user application-data folder, %APPDATA%\Maki on
Windows. It holds the SQLite database, logs, covers, page cache, MangaBaka dump, embedding index,
and precomputed recommendation data. For local development point it at a throwaway directory:
MAKI_CONFIG_DIR="$PWD/.devconfig" dotnet run --project src/Maki.Api
EF Core migrations apply automatically on startup.
src/
├── Maki.Api/ ASP.NET Core host, REST /api/v1, SignalR, Quartz jobs, download workers
├── Maki.Core/ Domain: entities, ISource/IMetadataProvider, parser, naming, CBZ pipeline
├── Maki.Data/ EF Core + SQLite
├── Maki.Sources/ One ISource implementation per site
└── Maki.Metadata/ MangaBaka provider + local dump + ONNX embeddings
frontend/ Vite + React + TypeScript + Mantine SPA
Sources implement a single ISource interface (search / series / chapters / pages) and are
registered in DI. Adding a site is one class plus one registration. Page requests carry their own
headers (Referer, cookies) through to the image fetch, so hotlink-protected CDNs work.
Maki is pre-1.0 and under active development. Schema and API can still shift between releases. Issues and pull requests are welcome.
Maki is a tool for organizing your library. Scraper sources access third-party websites. You are responsible for complying with those sites' terms of service and your local laws. Support the industry: buy official releases.
C#
59.1%
HTML
21.8%
TypeScript
15.9%
CSS
1.6%