Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

anki-bridge

A self-hosted, review-only HTTP bridge between an Anki collection and a Light Phone client. It embeds the official Anki pylib (anki==25.9.5) and exposes a small API for fetching cards and submitting ratings.

License: AGPL-3.0-or-later (embedding pylib requires it).

Safety model: all collection writes go through Anki's own scheduler and sync code — the bridge never mutates the collection or revlog itself. The phone only fetches due cards and submits ratings; those ratings are applied by Anki's scheduler and propagated to your sync server unchanged.

Configuration

Settings come from the environment (Settings.from_env()):

Env var Required Purpose
BRIDGE_COLLECTION yes Path to the .anki2 collection file.
BRIDGE_TOKEN yes Bearer token clients must present.
BRIDGE_DATA_DIR no Bridge-local state (answer-dedup db); NOT the collection dir. Default /data/bridge.
SYNC_ENDPOINT no Sync server URL (e.g. http://sync-server:8080/).
SYNC_USERNAME no Sync account username.
SYNC_PASSWORD no Sync account password.

If the three SYNC_* vars are unset (or partially set), sync is disabled and studying still works entirely against the local collection — study/start and study/finish simply report synced: false, "sync not configured". Sync failures are also non-fatal: a failed sync never aborts a study session.

First run (IMPORTANT)

A brand-new bridge collection cannot normal-sync against a sync server until one full sync has established a shared baseline. Until you resolve this, the bridge detects a full-sync-required state and /v1/study/start returns 503 needs_attention.

Resolve it once, with the anki-bridge-init CLI, picking the direction:

  • anki-bridge-init download — the server already holds your collection (the usual case: you have been using Anki elsewhere). The server wins.
  • anki-bridge-init upload — a fresh/empty sync server, and the bridge holds the collection to seed. The bridge wins.

Both directions are destructive to one side (they discard the other side's collection). The CLI prints exactly which side it will discard and waits for you to press Enter to confirm (Ctrl-C, EOF, or piped-closed stdin aborts cleanly).

needs_attention resolution

needs_attention means the bridge and the sync server have diverged and a full sync is required to reconcile them. The bridge never auto-resolves this: auto-picking a direction could silently discard review history on one side. It sets the flag, blocks /v1/study/start with 503, and waits for the operator to run anki-bridge-init download or upload. Once the full sync completes, normal syncing resumes.

Running

Uvicorn factory (see the Dockerfile CMD):

uvicorn anki_bridge.app:app_from_env --factory --host 0.0.0.0 --port 8000

Or via docker compose:

BRIDGE_TOKEN=<your-token> docker compose up -d

Note: the docker-compose.yml pins a ghcr.io/ankitects/anki-sync-server image tag that could not be validated in the build environment — validate it on first deploy. If the tag is missing, list available tags (see the comment in docker-compose.yml) and pin the nearest same-minor version. Fallback if no official image works: run the server straight from pylib with python -m anki.syncserver, setting SYNC_USER1 / SYNC_BASE / SYNC_HOST / SYNC_PORT. That is the exact path the round-trip integration test validates.

Client protocol notes

  • Answers are idempotent by client UUID. Re-submitting the same UUID returns duplicate and applies nothing a second time — safe to retry.
  • Batch truncation on unexpected error. If applying an item raises an unexpected error, the batch response is truncated: that item gets error and every item after it gets no result entry (no ack). Clients retry un-acked items by UUID.
  • stale answers must be dropped, not retried. A stale result means the card was already reviewed elsewhere (the states the phone echoed were superseded). Re-submitting will only return stale again — drop it.

Tests

pytest              # unit suite (no sync server needed)
pytest -m sync      # round-trip tests; needs a running sync server (port 18080)

For -m sync, run a sync server first: docker compose up -d sync-server, or the validated fallback python -m anki.syncserver with SYNC_USER1=test:test123 on port 18080.

tests/fixtures/local/*.apkg is a gitignored slot for dropping in a personal-deck corpus to run sweeps against real cards.

About

Self-hosted companion service for Recall — serves an Anki collection to a Light Phone III over a review-only JSON API. Built on Anki's official backend (pylib). AGPL-3.0.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages