* refactor(sync): kind-agnostic replica primitives Extract dict-only sync into shared primitives (registry, pull/apply orchestrator, persist env, schema allowlist) so other kinds can plug in. Companion changes: per-replica Storage Manager grouping, useReplicaPull boot-race recovery, manifest=null reconciliation on every boot pull, copyFile takes explicit srcBase + dstBase, settled- event helpers, lenient webDownload Content-Length (R2/S3 signed URLs commonly omit it), and generic "File" transfer toast copy any replica kind can share. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(sync): cross-device custom font sync Plug the font replica adapter into the kind-agnostic primitives: font store gains replica wiring, custom font import publishes the replica row + queues a binary upload, and bootstrap registers the font adapter and download-complete handler. Includes legacy flat-path migration so pre-existing fonts sync without re-import, full @font-face activation on auto-download (load + mount the rule, mirroring manual import), and a fix to createCustomFont so contentId / bundleDir / byteSize survive the trip through addFont — otherwise import-time publish silently no-oped on missing contentId. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Self-Hosting with Docker/Podman with Compose
Stack
| service | Image | Description |
|---|---|---|
| client | from ../Dockerfile |
readest frontend |
| db | supabase/postgres |
psql db with supabase extensions |
| kong | kong:2.8.1 |
api gateway routing requests to supabase services |
| auth | supabase/gotrue:v2.185.0 |
auth service (email, JWT) |
| rest | postgrest/postgrest:v14.3 |
psql rest api |
| minio | minio/minio |
s3 storage |
| minio-setup | minio/mc |
helper container to create s3 buckets |
Exposed ports
| Port | Service |
|---|---|
3000 |
readest |
7000 |
kong API gateway |
9000 |
MinIO S3 API |
9001 |
MinIO console UI |
Running with Docker/Podman Compose
1. setup .env
cp docker/.env.example docker/.env
update docker/.env:
- update
POSTGRES_PASSWORDto a strong password (32+ chars) - update
JWT_SECRETto a random secret (32+ chars) - regenerate
ANON_KEYandSERVICE_ROLE_KEYas HS256 JWTs signed with yourJWT_SECRET(use jwt.io or a similar tool):ANON_KEYpayload:{"role": "anon"}SERVICE_ROLE_KEYpayload:{"role": "service_role"}
- set
MINIO_ROOT_PASSWORDto a strong password
2. Start the Stack
run from the docker/ directory:
cd docker
docker compose up --build -d
the client image is built locally on first run. subsequent starts reuse the cached image.
3. Access
- Readest app:
http://localhost:3000 - MinIO console:
http://localhost:9001(login withMINIO_ROOT_USER/MINIO_ROOT_PASSWORD)
Hot Reload (development)
to develop using the compose stack, set the build target on client to development-stage, which'll runs the next.js dev server. to enable hot reload, uncomment the volumes block in the client service in compose.yaml:
volumes:
- ../:/app
- /app/node_modules
- /app/apps/readest-app/node_modules
- /app/apps/readest-app/public/vendor
- /app/apps/readest-app/.next
- /app/packages/foliate-js/node_modules
the first mount overlays your local repo into the container. the remaining anonymous volumes shadow the directories that were pre-built inside the image, so the container's installed deps and vendor assets are used instead of what's on your host.
Stop the Stack
cd docker
docker compose down
to also remove volumes (database and storage data):
cd docker
docker compose down -v
Building the Dockerfile standalone
the Dockerfile requires Build args for the next.js public env vars (they are inlined at build time)
docker build \
--target production-stage \
--build-arg NEXT_PUBLIC_SUPABASE_URL=http://localhost:7000 \
--build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key> \
--build-arg NEXT_PUBLIC_APP_PLATFORM=web \
--build-arg NEXT_PUBLIC_API_BASE_URL=http://localhost:3000 \
--build-arg NEXT_PUBLIC_OBJECT_STORAGE_TYPE=s3 \
--build-arg NEXT_PUBLIC_STORAGE_FIXED_QUOTA=1073741824 \
--build-arg NEXT_PUBLIC_TRANSLATION_FIXED_QUOTA=50000 \
-t readest-client \
.
run the built image:
docker run -p 3000:3000 \
-e SUPABASE_URL=http://kong:8000 \
-e SUPABASE_ANON_KEY=<anon-key> \
-e SUPABASE_ADMIN_KEY=<service-role-key> \
-e S3_ENDPOINT=http://localhost:9000 \
-e S3_REGION=us-east-1 \
-e S3_BUCKET_NAME=readest-files \
-e S3_ACCESS_KEY_ID=<minio-user> \
-e S3_SECRET_ACCESS_KEY=<minio-password> \
readest-client