Sign inSign up

skymanrm/galkaapp-api

By skymanrm

Updated 1 day ago

Offline-first sync backend for Galka, a to-do app for iOS and macOS. FastAPI, Postgres, Redis, SSE.

Image
Web servers
0

194

skymanrm/galkaapp-api repository overview

Galka API

Sync backend for Galka, an offline-first to-do app for iOS and macOS.

One image, one process: uvicorn app.main:app on port 8000. FastAPI · SQLAlchemy 2 (async) · PostgreSQL (asyncpg) · Redis pub/sub · Server-Sent Events.

Tags

TagWhat it is
latestThe newest build that passed the test suite.
sha-<12>An immutable tag per commit. Pin this one; roll back by pointing at an older one.

Every published tag is the exact manifest CI ran pytest against — the tests ship inside the image, so what you pull is what passed.

What it does

Offline-first sync with last-write-wins conflict resolution and delta-by-cursor pulls. Each record carries a client-generated uuid and a client-set updated_at (the conflict clock). The server keeps a per-user monotonically increasing seq; a client stores the last seq it applied and asks for everything newer.

EndpointWhat it does
POST /auth/register · /auth/login · /auth/logoutOne bearer token per device.
GET /sync?since=NDelta pull: everything with seq > N, tombstones included.
POST /syncBatch push; LWW on updated_at. Returns the newer server rows as conflicts.
GET /eventsSSE stream emitting {"seq": N, "origin": "<device-id>"} after each push, so a device can ignore its own writes.
GET /trash · POST /trash/emptyTwo-stage deletes: trashed first, tombstoned on purge.
GET /logsAppend-only activity history.
GET /healthLiveness probe.
GET /docs · /openapi.jsonInteractive API docs and the schema.

Plus server-rendered public pages at /, /privacy, /terms, /support, a /signup form and an HTTP Basic /admin. Those are excluded from the OpenAPI schema.

Quick start

name: galka

services:
  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB: galka
      POSTGRES_USER: galka
      POSTGRES_PASSWORD: change-me
    volumes: [pgdata:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U galka -d galka"]
      interval: 10s
      retries: 10

  redis:
    image: redis:8-alpine
    command: ["redis-server", "--save", "", "--appendonly", "no"]

  # Runs to completion before the API starts. There is no Alembic — the schema
  # comes from create_all(), and two workers racing to emit the same CREATE TABLE
  # would kill the process. This does it once, then exits.
  migrate:
    image: skymanrm/galkaapp-api:latest
    command: ["python", "scripts/init_db.py"]
    restart: "no"
    environment:
      TODOAPI_DATABASE_URL: postgresql+asyncpg://galka:change-me@db:5432/galka
      TODOAPI_REDIS_URL: redis://redis:6379/0
    depends_on:
      db: {condition: service_healthy}

  api:
    image: skymanrm/galkaapp-api:latest
    restart: unless-stopped
    ports: ["8000:8000"]
    environment:
      TODOAPI_DATABASE_URL: postgresql+asyncpg://galka:change-me@db:5432/galka
      TODOAPI_REDIS_URL: redis://redis:6379/0
      TODOAPI_ADMIN_PASSWORD: change-me-too
    depends_on:
      migrate: {condition: service_completed_successfully}

volumes:
  pgdata:
docker compose up -d
curl localhost:8000/health

Behind a reverse proxy, run the API with --proxy-headers and keep the read and idle timeouts unlimited — /events is held open for the life of a client.

Configuration

All settings take the TODOAPI_ prefix.

VariableDefaultWhat it is
TODOAPI_DATABASE_URLpostgresql+asyncpg://postgres:postgres@localhost:5432/todoapiAsync SQLAlchemy URL. Must be the asyncpg driver.
TODOAPI_REDIS_URLredis://localhost:6379/0Pub/sub fan-out for /events. Nothing is persisted there.
TODOAPI_SSE_PING_SECONDS15Keep-alive interval on the SSE stream.
TODOAPI_ADMIN_USERNAMEadminHTTP Basic user for /admin.
TODOAPI_ADMIN_PASSWORDadminChange this/admin is reachable wherever the app is.
TODOAPI_OWNER_NAMEAndrey FanyaginShown on the public pages.
TODOAPI_CONTACT_EMAIL[email protected]Shown on the public pages.
TODOAPI_PUBLIC_URLhttps://api.getgalka.ruCanonical URL used in the public pages.
TODOAPI_APP_STORE_URLthe Galka listingSet to an empty string to hide the App Store button.

Image details

  • Base python:3.12-slim, single architecture linux/amd64.
  • Runs as the non-root user galka (uid 10001); nothing in the container writes to disk.
  • Exposes 8000. Default command: uvicorn app.main:app --host 0.0.0.0 --port 8000.
  • The test suite is included — docker run --rm skymanrm/galkaapp-api pytest -q runs it against in-memory SQLite and an in-memory event bus, no services required.

Source

https://github.com/GalkaApp/galkaapp-api — the repository is public and carries the full sync protocol documentation. Licensed MIT.

Tag summary

Content type

Image

Digest

sha256:2db631a20

Size

74 MB

Last updated

1 day ago

docker pull skymanrm/galkaapp-api