Kaeri doesn’t host any shows. It has no servers of its own. The one exception is this website. There is no Kaeri account, and nothing the apps do is reported to the people who make Kaeri. Every request an app makes goes straight to one of the services on this page, and most of them only happen once you set something up.
This page covers the Mac, iPhone, iPad, Android, Android TV, Windows and Linux apps and the terminal app. They all run the same engine, so unless a section names one of them, it applies to every one.
- There is no telemetry, analytics or crash reporting, in any app or on this website.
- Your watch history is kept on your device. A tracker gets it only if you sign in to one.
- Sources come only from plugins and services you add yourself.
AniList, for browsing
Section titled “AniList, for browsing”Search, title pages, seasonal lists and the airing calendar come from AniList’s public API at
graphql.anilist.co. AniList sees your IP address and what you search for and open. Browsing is
asked without your account, even when you have signed in to AniList as a tracker: only the
requests for your list carry your token.
Covers and banners come from AniList’s image server. Episode stills come from the plugin that found the episode, or from the image servers of the services AniList lists for it. Images are kept on the device for 30 days, so each one is fetched about once a month.
When AniList can’t answer, most of the same requests go to Tenrai (api.tenrai.org), a public
MyAnimeList-compatible metadata API, and its images come from MyAnimeList’s image server. To turn this
off, set fallback = false under [meta] in config.toml, the file the terminal app and the Mac
app share.
In the terminal app, a trailer opens only when you ask for it, in your browser or the video site’s app, or in mpv when yt-dlp is installed. The terminal app never plays one by itself.
The other apps play a title’s YouTube trailer inside the app. On the title page it plays by
itself, muted, in place of the banner, and on Windows and Linux a hover preview does the same.
Play trailer opens it with sound in a sheet or window, and on Android TV in a full-screen
player, and only when you ask. The player is a page from www.youtube-nocookie.com, with its script
from www.youtube.com, loaded in the system’s web view. YouTube sees your IP address, which
video, and an address that names the app, which YouTube needs to play it. No cookies or other site
data are kept between trailers. A trailer is not sent anything about your account or history.
Play trailers in Settings is Always, Wi-Fi only or Off, and Always is the default. Off stops trailers from starting by themselves, so YouTube is asked only when you choose Play trailer. A trailer also doesn’t start by itself in Low Data Mode, on a metered connection, in a power saving mode, with reduced or removed animations, or with a screen reader running. While spoiler protection is on, the trailer of a title that follows one you haven’t finished doesn’t start by itself.
Episode synopses
Section titled “Episode synopses”When a source lists an episode without a synopsis, Kaeri can fill it in from a public episode
service at api.ani.zip. It is asked once for a title when you open the title’s episodes, with
that title’s AniList id and nothing else. The service sees your IP address and that id. The answer
is kept on the device for six hours while the show airs and a week after, so the same title is not
asked again in that time. If the service refuses or does not answer, the episodes stay as they
were. To turn this off, set episode_text = false under [meta] in config.toml.
Episode discussions
Section titled “Episode discussions”Once an episode counts as watched, the app looks for that episode’s discussion thread on AniList’s
forum, at graphql.anilist.co. It asks quietly in the background, once an hour at most for the
same thread, and opening a thread asks for its comments a page at a time. While spoiler
protection is on, the thread of an episode you haven’t reached is neither looked up nor opened.
The requests carry your AniList token when you are signed in, so AniList can say which comments
you like. Without a sign-in, reading carries none.
Posting, replying, editing, deleting and liking a comment use your AniList sign-in, and they are public on AniList as you. The Android TV app only reads. Images in comments load only if you turn on Load images in comments, which is off by default. With it off, an image is a line that opens in your browser, and the app asks nothing of the hosts that people post images on.
Episode discussions is on by default. Turn it off with Episode discussions in Settings, under Discussions on Windows and Linux, and nothing is looked up.
Background checks
Section titled “Background checks”Once a day, each app asks GitHub’s API at api.github.com whether a newer release exists. The answer
is kept for 24 hours, and the request carries nothing about you. An update comes from GitHub
releases, and it is only downloaded when you ask for one, except on Windows and on Linux when it
runs as an AppImage. Those two download it once it is found, check it against the size and
SHA-256 the release lists, and install it only when you choose Restart to update. Every app
has a switch for the check: Check for updates in Settings, under Integrations on a Mac,
Windows or Linux, under Advanced on an iPad, and under About on an iPhone, Android or Android TV.
In the terminal app it is on the Settings screen, which writes check = false under [updates].
To match a title across the trackers, the engine keeps two public lists of title ids current. They
come from raw.githubusercontent.com: one is checked daily and the other weekly, and a list that
hasn’t changed costs one short request. The requests carry nothing about you or your history.
At launch, each app asks AniList what aired for the shows in your history since it was last
opened, and says so in one line. airing_digest = false under [notifications] in config.toml
turns it off.
While new-episode notifications are on or a widget is placed, the Apple and Android apps check AniList for new episodes of the shows you follow, about every six hours, when the system lets them. A widget on iPhone, iPad or Mac that has nothing from the app yet asks AniList for the day’s schedule itself. Turn notifications off under Settings, Notifications, and remove the widgets, and these checks stop.
The Windows app asks AniList the same question from a timer while it runs, in its window or in the notification area: soon after it starts, then about every 15 minutes. The Linux app does not check in the background. It asks only at launch, as above.
Playing an episode
Section titled “Playing an episode”When an episode starts, the engine asks aniskip at api.aniskip.com for the times of its opening
and ending, so the player can skip them, mark them on the seek bar or show a skip button. The
request names the show’s MyAnimeList id and the episode number, and, if the first answer fails,
the episode’s length. The answer is kept until the app quits, so playing the episode again asks
nothing.
The engine asks only while something uses the answer: Skip opening automatically, Mark
opening and ending or Show skip button in Settings, which are skip_opening,
mark_chapters and skip_button under [playback] in config.toml. A show set to skip its
opening asks for that show. With all three off, aniskip is never asked.
The video itself comes from the source that found it, as the next sections describe.
Trackers
Section titled “Trackers”Sync is off until you sign in to a tracker. Kitsu is listed on the Accounts screen from the start, and sends nothing until you sign in. In the terminal app, Kitsu is on by default.
| Tracker | Address | What it receives |
|---|---|---|
| AniList | graphql.anilist.co |
Progress, list status and score |
| MyAnimeList | api.myanimelist.net |
Progress, list status and score |
| Kitsu | kitsu.io |
Progress, list status and score |
| Simkl | api.simkl.com |
Watched episodes, list status and rating |
| Trakt | api.trakt.tv |
Watched episodes |
An episode counts as watched once you pass 85% of it, and the tracker is told about a minute later. A status or score goes when you set it. Changes made offline wait in a queue on the device and go when the network is back. While an app is open, it reads your list from each tracker every 15 minutes, and when you open Library. AniList also tells the app which form of titles your account prefers.
Signing in uses the tracker’s own page. On Apple devices that is the system’s sign-in sheet in a
private browser session, and on Android a browser tab. On Windows, AniList and MyAnimeList open
in your browser and send the answer back to a port on 127.0.0.1, which is your own computer.
On Linux they open in your browser and send the answer back through an kaeri://auth link
that the desktop hands to the app. Simkl and Trakt give a code that you type on their site. Kitsu has no sign-in page: your email or username and
password go once to Kitsu, which answers with a token, and the password is not kept.
The TV has no browser to sign in with. For AniList and Kitsu it shows a QR code and an address for a page the TV serves on your local network, which a phone opens. That page works for ten minutes at a one-time address, and what the phone sends is sealed with a one-time key in the address’s fragment, so nothing you type crosses your network in plain text. Simkl and Trakt use a code on the TV’s screen. MyAnimeList has no sign-in on the TV.
On Android, AniList hands the sign-in back through https://kaeri.app/auth/anilist, which
Android opens in the app. The token is in the part of the address after #, which browsers never
send to a server. If Android hasn’t linked that address to the app yet, the page loads from this
website, says how to finish, and sends nothing anywhere.
Tokens are kept here:
| App | Where |
|---|---|
| iPhone and iPad | The system Keychain |
| Mac | Where the terminal app keeps them, so one sign-in serves both: the Keychain, as one item named kaeri-secrets that holds them all, or files if token_storage = "file" |
| Android and Android TV | A private file in the app, sealed with a key that the Android Keystore holds and never lets out |
| Windows | Windows Credential Manager, as generic credentials named kaeri/ and the token’s id, kept for your user on that computer |
| Linux | Files only you can read, in the data directory the terminal app uses, ~/.local/share/kaeri |
| Terminal | The system’s credential store (the Keychain on macOS, the Secret Service on Linux, the Credential Manager on Windows), or owner-only files in the data directory with token_storage = "file" or KAERI_TOKEN_STORAGE=file |
Where the terminal app finds no credential store, it falls back to the files and says so on the Accounts screen.
Signing out on the Accounts screen deletes the token from the device. To withdraw a token everywhere, also remove Kaeri from the connected apps in the tracker’s own settings.
Plugins
Section titled “Plugins”A plugin is a source you install, from a file or a plugin store, and it runs on your device in a sandbox.
A plugin is given what it needs to find an episode: your search terms or the title you opened, the episode number, whether you want it subbed or dubbed, and its own settings. Its own requests go only to the hosts it declares, which Kaeri shows before you install it and again when an update adds one. A plugin can send what it was given to those hosts.
The video and subtitles a plugin finds are then fetched from the addresses it returns, which can be a media server on another host. Each host a plugin reaches, and each media server, sees your IP address and what was asked of it.
A link to a licensed service opens in that service’s own app or website.
To stop a plugin, remove it under Sources.
Plugin stores
Section titled “Plugin stores”A plugin store is run by its owner, not by Kaeri. You join one with a link its owner gives you.
Joining with an invite sends the invite code and a name for this device. The name starts as the kind of device it is, such as “Mac”, “iPhone”, “iPad”, “Android phone”, “Android tablet”, “Windows PC” or “Linux computer”, or “Computer” in the terminal app, and you can change it before you join. Kaeri never fills in the device’s own name. The store’s owner sees the name they gave you, each of your devices by its name, when each joined, and when each was last seen, to within ten minutes. Joining with an open link sends no name, and the owner sees only when each device joined and was last seen. To limit joins, the store counts them per address for an hour, by a digest of the address.
After you join, the app asks each store for its plugin list at launch, and downloads a plugin when you install or update it. Each request carries this device’s token, which is kept with the tracker tokens. Like any web server, the store sees the address each request comes from, and its host may log it. The store doesn’t see what you watch: plugins run on your device, and their requests go to their own hosts, not through the store.
Choose Leave this store on the store’s page to give up your access. See Plugin stores and Running a store for what an owner can do.
Torrents
Section titled “Torrents”Torrents are off until you turn them on, and Kaeri finds no torrents itself. A source plugin that names torrents receives your searches like any other source. The torrent’s trackers and every peer in the swarm see the IP address you connect from and the torrent you are downloading or sharing.
To keep your IP address private, you can route torrent traffic through a proxy. It is optional and off by default. Its check connects to your proxy and nowhere else. DHT is off through a proxy, port forwarding is never requested, nothing listens for incoming connections, and there is no local peer discovery.
Seeding after an episode is off by default.
Integrations
Section titled “Integrations”Discord presence is off by default, and only the apps on a computer have it: Mac, Windows, Linux and the terminal app. When it is on, your Discord profile shows the title and episode while one plays, or only “Watching anime” if you keep the title off. The app tells the Discord app on your computer, and Discord shows it to others.
Rich presence is a second switch, also off by default. It adds the show’s cover, a progress bar, a “View on AniList” button and whether the episode is paused or buffering. Discord fetches the cover from AniList’s image server itself, so that server learns which cover your Discord asked for, and anyone who looks at your profile can follow the button to the show’s AniList page. Every part of it names the show, so it goes out only while the title is shown: with the title off you get only “Watching anime”.
The Mac app’s Integrations settings set these for the terminal app too, which shares its config. On Windows and Linux the switches are under Discord in Settings.
Watch parties
Section titled “Watch parties”A watch party connects Kaeri to the Syncplay server you choose. Kaeri has no server of its own and suggests the Syncplay project’s public one, which other people run. Anyone can run a server, so pick one you trust.
The server, and everyone in the room, sees:
- the name you join with and the room’s name;
- the show and episode you have open, as “Title · Episode 3”, and its length;
- when you play, pause and seek, with the position, and whether you’re ready;
- how long messages take to reach you;
- what you type in the room’s chat, and your reactions, which are chat lines such as
*laughs* 12:04with the position in the episode.
They never see the stream’s address, your sources or your sign-ins. A party shares no video: everyone plays the episode from their own sources.
The connection is encrypted when the server supports it. The party panel says whether it is.
An invite link carries the server, the room, the name of the person who sent it, the show’s title and the episode number, and nothing else. Anyone with the link can join the room, so share it with the people you want there.
The invite link is an address on this website, https://kaeri.app/party, with the invite after
the #. When you open it, the page reads that part in your browser, shows who invited you and
which episode, and offers to open the app. Browsers never send the part after # to a server, so
this website never receives the invite. The page makes no other request, runs no analytics, and
passes the invite only to the Kaeri app on your device. Cloudflare sees that the page was
opened, as it does for any page here, but not whose room it was.
Messages others send in the room show up on your player, and you can answer them.
A party doesn’t save your position while you watch. When the episode ends, it’s marked watched and synced to your trackers, as any other play is.
Chat goes through the room’s server, to its operator and to everyone in the room, and is never end to end encrypted. Kaeri keeps chat in memory for as long as you are in the party and nowhere else: not on disk, not in the last ten rooms, not in a bug report and not in the log. Leaving drops it, and someone who joins later sees nothing said before. Links in chat are plain text and never open from the app. Mute hides a member for that party on your device only, and Hide chat in Settings turns chat off on the screen, though the room still has it. A server can turn chat off, and then the app says so and sends nothing.
Watch together keeps the last ten rooms you were in on your device, so you can go back to one: the server and room, the show and the last episode the room reached, the names of the people who were there, and the show’s cover address. It keeps no source or stream, and it sends this list nowhere.
Sync between your devices
Section titled “Sync between your devices”Sync is off until you turn it on. It keeps your history, resume points, lists, per-show choices and a short list of shared settings the same on your devices. Settings that belong to one device, such as paths, the VPN and key bindings, stay on it. So do tracker sign-ins, plugin store invites and downloaded files.
Two devices on the same network can sync directly. You pair them once with a six-digit code, and after that they talk only to each other, over an encrypted connection.
To sync across networks, the devices share a folder in a service you choose:
| Service | Where the files go | What Kaeri can reach |
|---|---|---|
| Google Drive | A hidden app folder in your Drive | Only that folder |
| Dropbox | Apps/Kaeri |
Only that folder |
| OneDrive | Apps/Kaeri |
Only that folder |
| Nextcloud | kaeri-sync in your files |
Your files, through an app password named Kaeri |
| WebDAV | The folder you give | What that account can reach |
| A folder on the device | The folder you pick, such as one in iCloud Drive or Syncthing | Only that folder |
Every file Kaeri writes there is encrypted on your device first, with a key the service never sees. The service sees how many devices sync, how big the files are and when they were written. It doesn’t see titles, episodes, settings or anything else inside them. While an app is open, it reads the folder when it starts, when you come back to it, and every 5 minutes.
The key lives in your device’s secret store. When you set up the first folder, Kaeri shows a recovery key, which lets a new device join from the folder alone. Keep it somewhere safe: anyone with the recovery key and access to the folder can read your sync files. You can see it again or replace it in Settings, Devices.
Signing in uses the service’s own page, as trackers do. To remove Kaeri’s access, turn sync off on each device, delete the folder, and remove Kaeri in the service’s own settings: Google’s “Third-party apps and services”, Dropbox’s “Connected apps”, Microsoft’s “Apps and services you’ve given access”, or Nextcloud’s “Devices and sessions”.
On your device
Section titled “On your device”The apps keep these on the device, and send them nowhere unless a section above says so:
- your watch history, progress, list statuses, scores and the sync queue;
- your settings, the plugins you installed and the stores you joined;
- cached title data, cover art and the title id lists;
- downloads, and the torrent cache, which is 20 GB at most by default, oldest first;
- a log, one file a day, which can name the titles you open. It keeps only the host of an address, never the rest of it, since that can carry a token. Each app deletes its logs after 14 days.
They live here:
| App | Where |
|---|---|
| Mac, and the terminal app on macOS | ~/Library/Application Support/kaeri and ~/Library/Caches/kaeri |
| Terminal on Linux | ~/.config/kaeri, ~/.local/share/kaeri and ~/.cache/kaeri |
| Terminal on Windows | kaeri in %APPDATA% and in %LOCALAPPDATA% |
| Windows | Settings in %APPDATA%\kaeri, and the history, downloads and cache in %LOCALAPPDATA%\kaeri |
| Linux | The same folders as the terminal app on Linux, so the two share a history and a config |
| iPhone and iPad | The app’s own storage, which iOS includes in your device backups as it does any app’s data |
| Android and Android TV | The app’s private storage. The apps opt out of Android’s backups. |
Notifications are scheduled on the device. There is no push server.
On Apple devices, Spotlight’s index of titles, the widgets, Shortcuts and the Now Playing controls stay on the device. Handoff offers the title page or the episode you are watching, with its position, to your own devices signed in to the same Apple Account, and Apple carries it between them. Handoff is turned off in the system’s settings, under AirPlay and Handoff.
On Android, installing or updating the app makes Android fetch
https://kaeri.app/.well-known/assetlinks.json, to confirm that the sign-in and watch party
addresses belong to the app. Android does this itself.
To remove everything, uninstall the app on iPhone, iPad, Android or Android TV. On a Mac, Windows or
Linux, or with the terminal app, delete the folders above. Also delete the items kept under the
service kaeri in the Keychain or your system’s credential store, which on Windows are the
credentials whose names start with kaeri/ in Credential Manager.
This website
Section titled “This website”kaeri.app is a static site on Cloudflare Pages. It runs no analytics, sets no cookies, and loads no scripts or fonts from other sites. The home and apps pages ask GitHub’s API, from your browser, which release is the latest, to link its files, and the downloads come from GitHub releases. Cloudflare serves each request as any host does and sees your IP address. Its network error reporting asks browsers to tell Cloudflare when a connection to the site fails.
Questions
Section titled “Questions”Ask on GitHub issues. If an app sends or keeps something this page doesn’t say, or a token or your history can be read where it shouldn’t be, report it privately instead, as SECURITY.md describes.