A studyset in German, Greek, English, Chinese or Korean finds its dictionary as one in Japanese does (ADR 0008), and its subtitles are cut into words by its language (`WordCutting`): Chinese by its longest words as they stand, the longer words over each cut offered as for Japanese; Korean at its spaces, a word the dictionary lacks as it stands taken without the particle it ends with (집에서 is 집, 아이 stays a child); every other language at its spaces, a word written exactly so first (the noun Essen before the verb essen). A studyset finds the dictionary of its language tag, else of its language alone (zh for zh-Hans). A Wiktionary word shows its genders, principal forms, IPA, romanisation and Hanja; a German or Greek noun of one gender goes onto its card with its article (das Haus, το σπίτι); its tag is its word class, named in full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would read as JMdict's codes. Articles, pronouns, prepositions and conjunctions are left out of a video's words. Chinese words show their pinyin over each character on the Watch screen, carry their HSK level as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and by none for a language without. A reading is typed in a session only when it is kana, so pinyin isn't. Words are looked up on the web on Jisho in Japanese, on Wiktionary at the language's section otherwise, and a search's hint speaks of kana and romaji only in Japanese. `DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance keeps (unset: de,el,en,ko,zh; empty: none). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
158 lines
6.6 KiB
YAML
158 lines
6.6 KiB
YAML
# The Halkyon Learn instance: Caddy in front, the Serverpod server, PostgreSQL, and
|
|
# the dictionaries kept current beside them.
|
|
#
|
|
# cd deploy
|
|
# cp .env.example .env && chmod 600 .env # then fill in the secrets
|
|
# docker compose up -d --build
|
|
#
|
|
# Only Caddy publishes ports. Docker opens published ports directly in the kernel's
|
|
# packet filter, past ufw, and the Serverpod server listens on every interface, so the
|
|
# server and Postgres publish nothing: they're reachable only inside this project's
|
|
# networks, where Caddy finds the server by its service name.
|
|
#
|
|
# Two networks (decided 2026-10-14): `edge`, Caddy's, which the server shares with it,
|
|
# and the internal default one, where the server reaches Postgres. Caddy never reaches
|
|
# Postgres, nor does a sidecar behind Caddy.
|
|
#
|
|
# Sidecars (decided 2026-10-14) are each a compose file of their own beside this one,
|
|
# switched on in .env (COMPOSE_FILE=compose.yaml:compose.jellyfin.yaml): each adds its
|
|
# service and mounts its site into Caddy's sidecars/ folder (sidecars/README.md).
|
|
|
|
name: halkyon-learn
|
|
|
|
# Logs go to the host's journal, whose size and age limits apply (journald.conf).
|
|
x-logging: &logging
|
|
driver: journald
|
|
|
|
services:
|
|
caddy:
|
|
image: caddy:2
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
- "443:443/udp" # HTTP/3
|
|
environment:
|
|
# `learn.halkyon.eu` on its server (automatic Let's Encrypt certificate);
|
|
# `http://localhost` for the local rehearsal (plain HTTP, no certificate).
|
|
DOMAIN: ${DOMAIN:?set DOMAIN in deploy/.env}
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy_data:/data # certificates: must survive restarts (rate limits)
|
|
- caddy_config:/config
|
|
networks:
|
|
edge:
|
|
# Fixed, so containers behind Caddy can trust it as their proxy (the
|
|
# visitor's own address comes in X-Forwarded-For).
|
|
ipv4_address: 172.30.80.2
|
|
depends_on:
|
|
- server
|
|
logging: *logging
|
|
|
|
server:
|
|
build:
|
|
context: ..
|
|
dockerfile: halkyon_learn_server/Dockerfile
|
|
image: halkyon-learn-server
|
|
restart: unless-stopped
|
|
# Applies pending migrations at every start, then serves. One instance only, so
|
|
# no two servers ever migrate at once. (On learn.halkyon.eu, deploy/redeploy.sh has
|
|
# applied them in a run of their own before, so nothing is left to do here.)
|
|
command: ["--apply-migrations"]
|
|
environment:
|
|
SERVERPOD_DATABASE_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in deploy/.env}
|
|
SERVERPOD_PASSWORD_serverSideSessionKeyHashPepper: ${SESSION_KEY_HASH_PEPPER:?set SESSION_KEY_HASH_PEPPER in deploy/.env}
|
|
# GitHub sign-in, only with this instance's GitHub OAuth app; empty: off.
|
|
SERVERPOD_PASSWORD_githubClientId: ${GITHUB_CLIENT_ID:-}
|
|
SERVERPOD_PASSWORD_githubClientSecret: ${GITHUB_CLIENT_SECRET:-}
|
|
# Where the source of what this instance runs is; empty: the project's.
|
|
SOURCE_URL: ${SOURCE_URL:-}
|
|
# The Jellyfin server suggested to people; empty: jellyfin. on the
|
|
# instance's own domain, if one answers there; `none`: none.
|
|
JELLYFIN_URL: ${JELLYFIN_URL:-}
|
|
# How much room people's files may take, in MiB; empty: the defaults.
|
|
FILE_LIMIT_ANONYMOUS_MB: ${FILE_LIMIT_ANONYMOUS_MB:-}
|
|
FILE_LIMIT_IDENTIFIED_MB: ${FILE_LIMIT_IDENTIFIED_MB:-}
|
|
FILE_LIMIT_INSTANCE_MB: ${FILE_LIMIT_INSTANCE_MB:-}
|
|
# The git repositories of published studysets (ADR 0005, rule 3).
|
|
REPOSITORIES_DIR: /app/repositories
|
|
# The dictionary files it loads at start and rereads hourly, those that changed
|
|
# (see the server's README): those the dictionaries service keeps current, read
|
|
# only. To supply files yourself instead, remove that service and mount
|
|
# ./dictionaries here (`- ./dictionaries:/app/dictionaries:ro`).
|
|
volumes:
|
|
- dictionaries:/app/dictionaries:ro
|
|
# Every release of every published studyset: backed up beside the database
|
|
# (backup.sh), never deleted with `down -v`.
|
|
- repositories:/app/repositories
|
|
networks:
|
|
- default
|
|
- edge
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
logging: *logging
|
|
|
|
# Keeps the dictionary files current from EDRDG's files of the day, once they are a
|
|
# week old (EDRDG's licence asks it of servers showing its dictionaries at least
|
|
# monthly; the curator's README), and the languages of DICTIONARY_LANGUAGES: Chinese
|
|
# from MDBG's CC-CEDICT as often, the rest from Wiktionary's dump whenever kaikki.org
|
|
# makes a new one (about monthly, some 3 GB, converted as it downloads): in a
|
|
# container of its own, the curator never in the server's image or process. It only
|
|
# downloads and writes into the volume; the files are never backed up, as they can be
|
|
# had again.
|
|
dictionaries:
|
|
build:
|
|
context: ..
|
|
dockerfile: halkyon_learn_curator/Dockerfile
|
|
image: halkyon-learn-dictionaries
|
|
restart: unless-stopped
|
|
# Docker's stop signal reaches the curator through a small init.
|
|
init: true
|
|
# JMdict's conversion takes some 400 MB for a minute a week, Wiktionary's some
|
|
# 300 MB for ten minutes a month.
|
|
mem_limit: 1g
|
|
# Unset: German, Greek, English, Korean and Chinese; empty: none.
|
|
command: ["--every", "7", "--languages", "${DICTIONARY_LANGUAGES-de,el,en,ko,zh}"]
|
|
volumes:
|
|
- dictionaries:/dictionaries
|
|
logging: *logging
|
|
|
|
postgres:
|
|
image: postgres:16
|
|
restart: unless-stopped
|
|
# The server's session is UTC anyway (the driver sets it); this makes the
|
|
# database's own default UTC too, for psql, COPY loads and future sidecars.
|
|
command: ["postgres", "-c", "timezone=UTC", "-c", "log_timezone=UTC"]
|
|
environment:
|
|
POSTGRES_USER: halkyon_learn
|
|
POSTGRES_DB: halkyon_learn
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in deploy/.env}
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U halkyon_learn -d halkyon_learn"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 12
|
|
logging: *logging
|
|
|
|
networks:
|
|
edge:
|
|
name: halkyon-learn-edge
|
|
ipam:
|
|
config:
|
|
- subnet: 172.30.80.0/24
|
|
gateway: 172.30.80.1
|
|
# Containers without an address of their own (the server) get one from the
|
|
# upper half, so the fixed ones below it (Caddy .2, sidecars from .10) are
|
|
# never taken: the server started first once and took Caddy's (2026-10-14).
|
|
# (With a range alone, Docker would move the gateway to its first address.)
|
|
ip_range: 172.30.80.128/25
|
|
|
|
volumes:
|
|
caddy_data:
|
|
caddy_config:
|
|
dictionaries:
|
|
postgres_data:
|
|
repositories:
|