One app for all your media trackers. Log a movie, show, anime, or manga once, and Shinobu writes it to every tracker you've connected.

A detail page and NISIOISIN's works, with no tracker connected. Person pages remember your poster or list preference locally.
Shinobu works with Simkl, Trakt, AniList, Letterboxd, and Serializd. Connect any of them, each through its own sign-in. There is no Shinobu account.
- Log once. Mark something watched and Shinobu writes it to every connected tracker that handles that kind of media, in parallel. If one write fails, Shinobu tells you which tracker failed.
- One feed. Your history from every connected tracker merges into a single timeline. If one tracker is down, you lose its row, not the whole screen.
- One diary. Every watch and read log from every tracker sits in one list, newest first. When two trackers log the same title on the same day, the diary shows one row.
- One watchlist. The watchlist page gathers every tracker's watchlist into one poster wall that you can filter by tracker. When you add a title, the picker shows which trackers already have it.
- No backend, no database. Each tracker's token is its session. Tokens stay on your device, encrypted on iOS and Android. Nothing about you is stored anywhere else.
- Up Next in your timezone. An episode counts as watchable once it has aired where you are, not where the show airs.
- Local release notifications. iOS and Android schedule reminders for upcoming episodes on the device. There is no push server.
- A link when a write can't happen. If a tracker rejects a write, or can't take one on your platform, you get a link to that title's page on the tracker so you can log it there.
- Detail pages. Metadata comes from TMDB first, with Trakt and AniList as fallbacks. The header sets the title beside the poster, and wide screens fit the overview there too. Pages list cast, crew, seasons, and related titles. AniList titles also show characters, staff, recommendations, and tags. A Variants row links to the same title on TMDB and the trackers, and pressing a title copies it.
- Episode pages. Swipe between episodes. Marking one watched opens the next.
- Person and studio pages. A person's or studio's work sits on one timeline, newest first, filtered by format and role.
- Anime seasons. Browse each season's anime from AniList, split into series and films.
- Four platforms. Web, iOS, iPadOS, and Android all build from one Expo project.
| Expo + Expo Router | One codebase for all four platforms, with file-based routing. CNG generates the native projects, so they are never committed. |
| Uniwind | Tailwind CSS for React Native, from the Unistyles team. Theme tokens with light and dark variants. |
| TanStack Query | Every read and write, including caching, invalidation, and writes to several trackers at once. |
| Effect | Typed errors, retries, and rate-limit backoff. Effect stays inside the provider service layer and never reaches a component. |
| Nitro Modules | react-native-mmkv stores tokens encrypted. react-native-nitro-fetch sends requests through Cronet or URLSession on native. nitro-webview runs the tracker sign-in and write WebViews. |
| Legend List | Virtualized lists everywhere. It is pure TypeScript, runs on web, and sits behind one components/List wrapper. |
| expo-image + galeria | Posters cached in memory and on disk, so long grids don't exhaust memory. Tap a poster or headshot to zoom through Galeria on native and a separate lightbox on web. |
| Ionicons | Bundled, font-rendered UI icons on all platforms. Nano Icons generates fonts from custom SVGs; the existing icon pack needs no extra generation pipeline. |
| pressto | Every pressable, with debounced, animated presses built on gesture-handler and Reanimated. |
| pulsar | Haptics on press and when a log finishes. Native only, since iOS Safari has no haptics API. |
| @expo/ui + bottom sheet | Native SwiftUI and Compose controls such as date pickers and switches, plus the sheets for card actions and log confirmation. |
| react-hook-form + zod | Every connect form. Client ids, tokens, and credentials are checked before any request goes out. |
| torph + Reanimated | Text that morphs in place when your state changes it, such as progress counts and the log button's episode number. torph runs on web, and native animates one Reanimated glyph per character. |
| react-native-keyboard-controller | Keyboard avoidance that behaves the same on every platform. |
| expo-notifications + expo-background-task | Release reminders scheduled on the device and refreshed in the background. |
| TMDB | The metadata source, never a tracker. It feeds detail pages, cast and crew, and the person and studio pages. |
| Cloudflare Workers | Hosts the web build, plus three relays with path allowlists for Serializd, Letterboxd reads, and the 4chan catalogs. |
| React Compiler | Memoizes automatically. useMemo and useCallback are banned by lint. |
| bun | Package manager, script runner, and test runner through bun:test. There is no Jest. |
| oxlint | Enforces the repo's conventions as lint rules. |
Note
Shinobu links native Nitro modules, so it can't run in Expo Go. iOS and Android need a custom dev client.
bun install
bun web # run in the browser
bun ios # build and run the iOS dev client
bun android # build and run the Android dev client
bun ios.clean # regenerate the native project first, needed after
bun android.clean # app.json, config plugin, or native dependency changesWeb development also needs the Worker running, because Metro doesn't serve /api/*:
bun run dev:worker # wrangler dev on :8787, the Serializd, Letterboxd, and 4chan proxiesbun lint # conventions, import rules, filename casing
bun check:classnames # every composed className goes through cn()
bun check:router-push # navigation uses usePushRoute, not useRouter().push
bun typecheck # tsc --noEmit
bun test # bun:test
bun check:links # probe the provider URLs the app depends onci.yml runs everything except check:links on every PR, and a PR can't merge until they pass. Provider URLs break over time rather than because of a diff, so link-health.yml runs check:links daily and on PRs that touch the provider layer.
bun run build:web # static export
bun run deploy:web # export, then wrangler deployThe web build is live at shinobu.glpecile.xyz.
Android releases build from a tag on free GitHub-hosted runners, without EAS. bun release:bump patch|minor|major|X.Y.Z updates expo.version and expo.android.versionCode together. Pushing the matching vX.Y.Z tag starts release.yml. It builds signed universal and arm64-v8a APKs, checksums both, and publishes a GitHub Release with categorized notes.
android-warm.yml runs the same release build on main whenever a native input changes, and once a week. GitHub scopes caches per ref, so this build fills the Gradle cache that tag builds restore. It also catches a broken Android build before you tag. The full runbook is in docs/releasing.md.
AGENTS.mdlists the conventions the code follows.plan.mdcovers the product vision and architecture.docs/plans/has one blueprint per feature, written before the build.docs/solutions/has one file per solved bug or non-obvious platform behavior.docs/brainstorms/holds raw exploration notes.

