feat(deploy): instances keep their dictionaries current, weekly, by themselves
EDRDG's licence asks of a server showing its dictionaries that it update
them at least monthly. The instance now does it alone: a `dictionaries`
service in compose, built from the curator (halkyon_learn_curator/
Dockerfile, its own unprivileged user 10001, 1 GB at most), runs `curate
update --every 7`, downloading EDRDG's files of the day once its files are
a week old and replacing them in a volume the server mounts read-only.
The curator never enters the server's image or process (decided
2026-10-09: a container of its own, weekly). The server rereads the
folder daily at 04:07 (DictionaryReloadFutureCall), loading what changed,
and loads now run one at a time. redeploy.sh builds both images.
An operator who'd rather supply the files removes the service and mounts
deploy/dictionaries/ into the server, as compose.yaml says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 16:52:37 +02:00
|
|
|
# The dictionaries' image: the curator, keeping a server's dictionary files current
|
|
|
|
|
# from EDRDG's files of the day (`curate update`, the README). Built from the
|
|
|
|
|
# repository root (the build context), where the workspace's root pubspec and lockfile
|
|
|
|
|
# live:
|
|
|
|
|
#
|
|
|
|
|
# docker build -f halkyon_learn_curator/Dockerfile .
|
|
|
|
|
#
|
|
|
|
|
# A container of its own beside the server (deploy/compose.yaml, decided 2026-10-09):
|
|
|
|
|
# the curator (AGPL) never enters the server's image or process, and a conversion
|
|
|
|
|
# that fails can't touch the server. They share only the folder of files.
|
|
|
|
|
|
|
|
|
|
# ── Build stage: compile the curator to a native executable ───────────────────────
|
|
|
|
|
FROM dart:3.13.4 AS build
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
COPY pubspec.yaml pubspec.lock ./
|
|
|
|
|
COPY halkyon_learn_curator halkyon_learn_curator
|
|
|
|
|
|
|
|
|
|
# The workspace root with only the curator package, as the server's Dockerfile does it
|
|
|
|
|
# with the server's. The check fails the build loudly if the member list changes shape.
|
|
|
|
|
RUN sed -i -e '/^ - halkyon_learn_client$/d' -e '/^ - halkyon_learn_flutter$/d' \
|
|
|
|
|
-e '/^ - halkyon_learn_server$/d' pubspec.yaml \
|
|
|
|
|
&& grep -q '^ - halkyon_learn_curator$' pubspec.yaml \
|
|
|
|
|
&& ! grep -q -e 'halkyon_learn_client' -e 'halkyon_learn_flutter' \
|
|
|
|
|
-e 'halkyon_learn_server' pubspec.yaml
|
|
|
|
|
RUN dart pub get
|
|
|
|
|
WORKDIR /app/halkyon_learn_curator
|
|
|
|
|
RUN dart build cli --target bin/curate.dart --output build
|
|
|
|
|
|
|
|
|
|
# ── Runtime stage ─────────────────────────────────────────────────────────────────
|
feat(dictionary): Japanese cut into words and looked up on the device
Once the person agrees, the device holds MeCab's IPADIC and the
instance's JMdict and cuts lines as Jisho.org does, so their text never
leaves it (ADR 0012). MeCab comes through mecab_for_dart, whose build
hook compiles it; its tokens are grouped by Ve's rules, written anew in
Dart, with に kept a word of its own. Each word is found as it stands or
as a form of a word, else by its dictionary form, else as the longest
words it holds from token to token; longer words over token boundaries
are offered as before.
The curator packs Ubuntu's compiled IPADIC beside the dictionary files
(curate ipadic, and every update run). The server exports its JMdict
from its tables, so the device finds the same keys and keeps the same
versions, and serves both at /device-dictionaries/, named in
dictionary.deviceFiles('ja') with their hashes; Caddy forwards the path.
The app checks each file against its hash, unpacks IPADIC, builds a
SQLite database from the export on an isolate, and asks the instance
for newer files every 30 days, downloading only what changed. The Watch
screen offers the download once per device with its size (34 MB, some
150 MB once set up); Settings downloads and removes them. Without them,
or should cutting on the device fail, lines go to the instance as
before. MeCab's, IPADIC's and Ve's licences join the licence list.
Every build now needs a C++ compiler; mecab_for_dart holds code_assets
at 1.x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 21:55:00 +02:00
|
|
|
# Ubuntu 24.04 as the server's, with CA certificates for EDRDG's HTTPS, and MeCab's
|
|
|
|
|
# IPADIC, which the package compiles as it is installed: the curator packs it for the
|
|
|
|
|
# app's devices (ADR 0012). Its sources go once it is compiled.
|
feat(deploy): instances keep their dictionaries current, weekly, by themselves
EDRDG's licence asks of a server showing its dictionaries that it update
them at least monthly. The instance now does it alone: a `dictionaries`
service in compose, built from the curator (halkyon_learn_curator/
Dockerfile, its own unprivileged user 10001, 1 GB at most), runs `curate
update --every 7`, downloading EDRDG's files of the day once its files are
a week old and replacing them in a volume the server mounts read-only.
The curator never enters the server's image or process (decided
2026-10-09: a container of its own, weekly). The server rereads the
folder daily at 04:07 (DictionaryReloadFutureCall), loading what changed,
and loads now run one at a time. redeploy.sh builds both images.
An operator who'd rather supply the files removes the service and mounts
deploy/dictionaries/ into the server, as compose.yaml says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 16:52:37 +02:00
|
|
|
FROM ubuntu:24.04
|
|
|
|
|
RUN apt-get update \
|
feat(dictionary): Japanese cut into words and looked up on the device
Once the person agrees, the device holds MeCab's IPADIC and the
instance's JMdict and cuts lines as Jisho.org does, so their text never
leaves it (ADR 0012). MeCab comes through mecab_for_dart, whose build
hook compiles it; its tokens are grouped by Ve's rules, written anew in
Dart, with に kept a word of its own. Each word is found as it stands or
as a form of a word, else by its dictionary form, else as the longest
words it holds from token to token; longer words over token boundaries
are offered as before.
The curator packs Ubuntu's compiled IPADIC beside the dictionary files
(curate ipadic, and every update run). The server exports its JMdict
from its tables, so the device finds the same keys and keeps the same
versions, and serves both at /device-dictionaries/, named in
dictionary.deviceFiles('ja') with their hashes; Caddy forwards the path.
The app checks each file against its hash, unpacks IPADIC, builds a
SQLite database from the export on an isolate, and asks the instance
for newer files every 30 days, downloading only what changed. The Watch
screen offers the download once per device with its size (34 MB, some
150 MB once set up); Settings downloads and removes them. Without them,
or should cutting on the device fail, lines go to the instance as
before. MeCab's, IPADIC's and Ve's licences join the licence list.
Every build now needs a C++ compiler; mecab_for_dart holds code_assets
at 1.x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-15 21:55:00 +02:00
|
|
|
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
|
|
|
ca-certificates mecab-ipadic-utf8 \
|
|
|
|
|
&& test -s /var/lib/mecab/dic/ipadic-utf8/sys.dic \
|
|
|
|
|
&& rm -rf /usr/share/mecab/dic/ipadic /var/lib/apt/lists/*
|
feat(deploy): instances keep their dictionaries current, weekly, by themselves
EDRDG's licence asks of a server showing its dictionaries that it update
them at least monthly. The instance now does it alone: a `dictionaries`
service in compose, built from the curator (halkyon_learn_curator/
Dockerfile, its own unprivileged user 10001, 1 GB at most), runs `curate
update --every 7`, downloading EDRDG's files of the day once its files are
a week old and replacing them in a volume the server mounts read-only.
The curator never enters the server's image or process (decided
2026-10-09: a container of its own, weekly). The server rereads the
folder daily at 04:07 (DictionaryReloadFutureCall), loading what changed,
and loads now run one at a time. redeploy.sh builds both images.
An operator who'd rather supply the files removes the service and mounts
deploy/dictionaries/ into the server, as compose.yaml says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 16:52:37 +02:00
|
|
|
WORKDIR /app
|
|
|
|
|
|
|
|
|
|
COPY --from=build /app/halkyon_learn_curator/build/bundle/ ./
|
|
|
|
|
COPY --from=build /app/halkyon_learn_curator/data/ data/
|
|
|
|
|
|
|
|
|
|
# An unprivileged user of its own: user ID 10001, shared with neither the containers
|
|
|
|
|
# of PostgreSQL and the server (999) nor the CI runner on the host (61000). The folder
|
|
|
|
|
# it writes into belongs to it, which a new volume mounted there takes over.
|
|
|
|
|
RUN useradd --system --uid 10001 --no-create-home --shell /usr/sbin/nologin curator \
|
|
|
|
|
&& mkdir /dictionaries \
|
|
|
|
|
&& chown curator /dictionaries
|
|
|
|
|
USER curator
|
|
|
|
|
|
|
|
|
|
# Updates the files whenever they are a week old (EDRDG's licence asks at least
|
|
|
|
|
# monthly), and sleeps in between.
|
|
|
|
|
ENTRYPOINT ["./bin/curate", "update", "--into", "/dictionaries", "--data", "data"]
|
|
|
|
|
CMD ["--every", "7"]
|