The server's half of D25: every account has a way in, and the anonymous accounts left from before go with the daily pruning. - The module signs nobody in anonymously: `anonymousIdp` and `gitHubUpgrade` are gone, and so is linking GitHub at the register. - A password is set only by someone whose address is theirs (a `firstPassword` code) or who signs in with GitHub alone (a fresh sign-in and a `signUp` code); an anonymous account is refused `wrongCredentials` before any code is used. - SimpleLogin refuses to link onto a caller without `identified` (`wrongCaller`), the member's endpoint too, so nothing turns an anonymous account into one with a way in. - `pruneAnonymousPeople` deletes every anonymous account left, however new, never one identified. Learn and Todo schedule it as `prune-accounts` and cancel the old `prune-abandoned-anonymous-people` entry once. - Learn: someone without `identified` keeps no files; `FILE_LIMIT_ANONYMOUS_MB` is gone. `minimumAppBuild` is 23, as builds up to 22 start with an anonymous account. - Learn's sync tests make their person and session by SQL in the development database, as Serverpod stores sessions. - Docs, store listings and ADRs 0010, 0014, 0016, 0017 and 0019 amended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
157 lines
9.1 KiB
Shell
157 lines
9.1 KiB
Shell
# Copy to deploy/.env, restrict it (chmod 600), and fill in. deploy/.env is git-ignored
|
|
# and never goes into an image. How it reaches the server is decided when the server is
|
|
# set up (e.g. rendered from Proton Pass on the deploying machine and copied over SSH,
|
|
# so the server itself never holds Proton credentials).
|
|
|
|
# `http://localhost` for the local rehearsal; `learn.halkyon.eu` on its server.
|
|
DOMAIN=http://localhost
|
|
|
|
# The domain people's handles are on (`raisondetredev@halkyon.eu`) and their profile
|
|
# pages (`https://halkyon.eu/@raisondetredev`, ADR 0011), when it isn't DOMAIN's host:
|
|
# point its DNS (A and AAAA) at this server, and Caddy serves the profile pages there,
|
|
# nothing else. Unset: DOMAIN's host. Decide before anyone follows a handle: it can
|
|
# never change once the fediverse knows it.
|
|
#ACCOUNT_DOMAIN=halkyon.eu
|
|
|
|
# The instance's other services (optional, ADR 0016), such as Halkyon Todo: Halkyon
|
|
# Learn holds the instance's register of accounts, so an address, a GitHub account and a
|
|
# handle are one person in every service, and deleting the account deletes it in all.
|
|
# The register learns when someone signs in to which service (the privacy page says so).
|
|
# INSTANCE_SECRET is the secret the services share, carried by every call between them:
|
|
# the same value in each service's environment, never given to a device. Empty: no
|
|
# service calls another. Generate: openssl rand -hex 32
|
|
#INSTANCE_SECRET=
|
|
# The web servers of the other services as this server reaches them on the instance's
|
|
# own network (never through the internet), separated by commas or spaces
|
|
# (`http://todo-server:8082`): told when an account goes, so each deletes that person's
|
|
# data too, and told again hourly until each answers. Each of them sets, in its own
|
|
# environment, the same INSTANCE_SECRET and ACCOUNT_REGISTER_URL, this server's web
|
|
# server as it reaches it (`http://server:8082`). Learn never takes ACCOUNT_REGISTER_URL:
|
|
# it doesn't start with one, as it holds the register.
|
|
#INSTANCE_MEMBERS=
|
|
|
|
# Sidecars switched on beside the instance (sidecars/README.md), each by its compose file
|
|
# after the instance's own: compose.jellyfin.yaml, a Jellyfin people watch and listen to
|
|
# learn from. Unset: the instance alone.
|
|
#COMPOSE_FILE=compose.yaml:compose.jellyfin.yaml
|
|
|
|
# With the Jellyfin sidecar: the domain it answers at, which the instance then suggests
|
|
# to its people (the app's Jellyfin sign-in fills it in).
|
|
#JELLYFIN_DOMAIN=jellyfin.example.org
|
|
|
|
# Postgres password, shared by the postgres and server services.
|
|
# Generate: openssl rand -base64 32
|
|
POSTGRES_PASSWORD=
|
|
|
|
# Pepper for hashing session keys (server-side sessions). NEVER change it once
|
|
# sessions exist: everyone would be signed out, and anonymous accounts can't sign
|
|
# back in. Rotation only via fallbackSessionKeyHashPeppers.
|
|
# Generate: openssl rand -base64 32
|
|
SESSION_KEY_HASH_PEPPER=
|
|
|
|
# GitHub sign-in (optional): the client ID and secret of this instance's own GitHub
|
|
# OAuth app (GitHub → Settings → Developer settings → OAuth Apps), registered with the
|
|
# callback URLs app.halkyon.learn://github-callback and http://127.0.0.1/github-callback.
|
|
# Empty: GitHub sign-in is off, and the app doesn't offer it.
|
|
GITHUB_CLIENT_ID=
|
|
GITHUB_CLIENT_SECRET=
|
|
|
|
# Email sign-in (optional, ADR 0010): people sign in with a code mailed to their address,
|
|
# of which the instance keeps only a keyed hash. Off unless the pepper and all of the SMTP
|
|
# service's settings are set; the app then doesn't offer it.
|
|
# The pepper: NEVER change it once anybody signs in with an address, as every address's
|
|
# hash would be lost, and with it those people's way in.
|
|
# Generate: openssl rand -hex 32
|
|
EMAIL_SIGN_IN_PEPPER=
|
|
# The SMTP service codes go through, over TLS only (STARTTLS, or TLS directly on 465).
|
|
# Proton (a paid plan with an address on your own domain): smtp.protonmail.ch, 587, the
|
|
# address as the user, an SMTP token made for it in Proton Mail's settings (IMAP/SMTP) as
|
|
# the password, and the same address as EMAIL_FROM. Some hosts block outgoing 25 and 465;
|
|
# 587 gets through.
|
|
EMAIL_SMTP_HOST=
|
|
EMAIL_SMTP_PORT=587
|
|
EMAIL_SMTP_USER=
|
|
EMAIL_SMTP_PASSWORD=
|
|
# The address codes come from (shown as "Halkyon Learn"); its domain needs SPF, DKIM and
|
|
# DMARC records for the service, or mail lands in spam.
|
|
EMAIL_FROM=
|
|
|
|
# Password sign-in (ADR 0017): an address, a password that never leaves the device, and
|
|
# a code from an authenticator app. Off unless all three keys and email sign-in are set
|
|
# up (resets go by mail); each key at least 32 characters, or the server doesn't start.
|
|
# They stay outside the database, so a copy of it alone gives none of this away:
|
|
# TOTP_KEY seals the authenticators' secrets: changing it makes every authenticator
|
|
# useless (people then reset theirs, 72 hours, by mail).
|
|
# RECOVERY_CODE_KEY keys the recovery codes' hashes: changing it voids every recovery
|
|
# code (people make new ones once signed in).
|
|
# SIGN_IN_DECOY_KEY makes the answers for addresses nobody has look like an account's:
|
|
# changing it lets someone who watched those answers tell which addresses are unused.
|
|
# SimpleLogin needs it alone (below), for handles nobody has.
|
|
# Generate each: openssl rand -hex 32
|
|
TOTP_KEY=
|
|
RECOVERY_CODE_KEY=
|
|
SIGN_IN_DECOY_KEY=
|
|
# SimpleLogin (ADR 0020): people sign in with their SimpleLogin or Proton account, of
|
|
# which the instance keeps only SimpleLogin's ID for them (no address, no name). Off
|
|
# unless both are set: the client ID and secret of this instance's own SimpleLogin app
|
|
# (SimpleLogin → Settings → Developer → New website), registered with the callback
|
|
# /simplelogin-callback on the server's web address (on learn.halkyon.eu's server
|
|
# https://learn.halkyon.eu/simplelogin-callback), its forwarding page back to the apps.
|
|
# Takes SIGN_IN_DECOY_KEY (above), or the server doesn't start: a broken link is mended
|
|
# with the handle and the recovery phrase, a handle nobody has answered as an account's.
|
|
SIMPLELOGIN_CLIENT_ID=
|
|
SIMPLELOGIN_CLIENT_SECRET=
|
|
# Another callback than this server's own page, as the app registered it (https only).
|
|
#SIMPLELOGIN_CALLBACK_URL=https://learn.example.org/simplelogin-callback
|
|
# The ways in the app offers, of those set up above: simplelogin, password, or both
|
|
# (separated by spaces or commas). Empty: every way set up. An unknown name stops the
|
|
# server as it starts.
|
|
#SIGN_IN_WAYS=simplelogin
|
|
|
|
# The day GitHub stops signing anyone in, three months after the release that brings
|
|
# passwords (ADR 0017, D2): from then on its people sign in with their address and
|
|
# password only, and the daily pruning forgets every GitHub ID. Empty: GitHub goes on.
|
|
#GITHUB_SIGN_IN_ENDS=2027-01-22
|
|
|
|
# Where the source of what this instance runs is, shown in the app's About dialog: both
|
|
# licences (AGPL for the app, SSPL for the server) require offering it to the people who
|
|
# use the instance. Empty: the project's repository, which is right for unmodified code.
|
|
# Running modified code, set it to where your modified source is published.
|
|
SOURCE_URL=
|
|
|
|
# The languages besides Japanese whose dictionaries this instance keeps (the dictionaries
|
|
# service): Chinese from CC-CEDICT, weekly (some 4 MB), in simplified and traditional
|
|
# characters; Korean from KRDict, about monthly (some 84 MB); their characters from
|
|
# Unihan, weekly (some 8 MB); the rest from Wiktionary (German and Greek with English
|
|
# meanings, English with German ones; for Korean the forms of its words and the
|
|
# readings of its Hanja), whose whole dump (some 3 GB) it downloads whenever kaikki.org
|
|
# makes a new one, about monthly, and converts in ten minutes. Unset: de,el,en,ko,zh.
|
|
# Empty: none. Files of a language taken out are removed, and the server unloads them.
|
|
#DICTIONARY_LANGUAGES=de,el,en,ko,zh
|
|
|
|
# The Jellyfin server the app suggests to this instance's people, who watch and listen
|
|
# to learn from it (Halkyon Learn's Watch screen). Empty: `https://jellyfin.` on the
|
|
# domain registered for this instance (learn.example.org → jellyfin.example.org), if a
|
|
# Jellyfin server answers there; `none`: no suggestion.
|
|
JELLYFIN_URL=
|
|
|
|
# Where this instance keeps people's data, and its backups, which the app shows as a flag
|
|
# and in words when someone chooses an instance: `EU`, a country's code (ISO 3166-1
|
|
# alpha-2, `DE`, `CH`) or a subdivision's (ISO 3166-2, `DE-BY`, `US-CA`); for the backups
|
|
# also `none`. As precise as you know: the app adds "(EU)" to a member country and the
|
|
# country to a subdivision ("Germany (EU)", "California, United States"). Where the disks
|
|
# are, not which law governs you as the operator: claim only that. Empty: the app says
|
|
# nothing.
|
|
DATA_LOCATION=
|
|
BACKUP_LOCATION=
|
|
|
|
# How much room people's images and sounds may take on this instance, in MiB. Empty: the
|
|
# defaults, 500 for a person and 15000 for everyone together (0: no limit for everyone
|
|
# together). Every file is in the database, so also in each of the 3 dumps
|
|
# this server keeps (and a fourth while a deploy writes its own): files take five times
|
|
# their size on the disk. One person may get more than the default; as the operator,
|
|
# in deploy/ (their id is their account's, "authUserId"):
|
|
# docker compose exec postgres psql -U halkyon_learn -d halkyon_learn -c "INSERT INTO
|
|
# file_allowance (\"authUserId\", \"maxBytes\", note) VALUES ('<id>', 2147483648, 'asked by mail')"
|
|
FILE_LIMIT_IDENTIFIED_MB=
|
|
FILE_LIMIT_INSTANCE_MB=
|