import 'dart:io'; import 'package:test/test.dart'; /// halkyon.li's first `.env` (ADR 0039; the developer's choices of 8 Nov 2026): /// every secret made fresh on the host, halkyon.li's settings, and never an existing /// file overwritten, as the session peppers must never change once people sign in. void main() { const script = 'deploy/host/halkyon-li-env.sh'; late Directory folder; setUp(() => folder = Directory.systemTemp.createTempSync('halkyon-li-env')); tearDown(() => folder.deleteSync(recursive: true)); ProcessResult make(String target) => Process.runSync('sh', [script, target]); Map settings(String text) => { for (final line in text.split('\n')) if (line.isNotEmpty && !line.startsWith('#')) line.substring(0, line.indexOf('=')): line.substring( line.indexOf('=') + 1, ), }; group('a new .env', () { late Map env; late File file; setUp(() { file = File('${folder.path}/.env'); final result = make(file.path); expect(result.exitCode, 0, reason: '${result.stderr}'); env = settings(file.readAsStringSync()); }); test('is readable by its owner alone', () { expect(file.statSync().modeString(), 'rw-------'); }); test("makes every secret fresh, each of 32 bytes, none another's", () { const secrets = [ 'INSTANCE_SECRET', 'LEARN_POSTGRES_PASSWORD', 'LEARN_SESSION_KEY_HASH_PEPPER', 'SIGN_IN_DECOY_KEY', 'TRAIN_POSTGRES_PASSWORD', 'TRAIN_SESSION_KEY_HASH_PEPPER', 'MAP_KEY_SECRET', 'TODO_POSTGRES_PASSWORD', 'TODO_SESSION_KEY_HASH_PEPPER', 'MEDITATE_POSTGRES_PASSWORD', 'MEDITATE_SESSION_KEY_HASH_PEPPER', ]; for (final name in secrets) { expect(env[name], matches(RegExp(r'^[0-9a-f]{64}$')), reason: name); } expect( secrets.map((name) => env[name]).toSet(), hasLength(secrets.length), ); }); test('differs from the next one made', () { final other = File('${folder.path}/other.env'); make(other.path); expect( settings(other.readAsStringSync())['INSTANCE_SECRET'], isNot(env['INSTANCE_SECRET']), ); }); test("serves halkyon.li's names, handles on halkyon.li itself (D7)", () { expect(env['LEARN_DOMAIN'], 'learn.halkyon.li'); expect(env['LEARN_PUBLIC_HOST'], 'learn.halkyon.li'); expect(env['ACCOUNT_DOMAIN'], 'halkyon.li'); expect(env['TRAIN_DOMAIN'], 'train.halkyon.li'); expect(env['TODO_DOMAIN'], 'todo.halkyon.li'); expect(env['MEDITATE_DOMAIN'], 'meditate.halkyon.li'); expect(env['JELLYFIN_DOMAIN'], 'jellyfin.halkyon.li'); }); test('lets people in through SimpleLogin alone, once an operator ' 'approves them, and mails nothing', () { expect(env['SIGN_IN_WAYS'], 'simplelogin'); expect(env['SIGN_UP'], 'approval'); expect(env['SIMPLELOGIN_CLIENT_ID'], isEmpty); expect(env['SIMPLELOGIN_CLIENT_SECRET'], isEmpty); for (final name in [ 'EMAIL_SIGN_IN_PEPPER', 'EMAIL_SMTP_HOST', 'TOTP_KEY', 'RECOVERY_CODE_KEY', ]) { expect(env[name] ?? '', isEmpty, reason: name); } }); test('runs everything chosen, behind the forward, each file there', () { final files = env['COMPOSE_FILE']!.split(':'); expect(files, [ 'compose.learn.yaml', 'compose.jellyfin.yaml', 'compose.languagetool.yaml', 'compose.train.yaml', 'compose.maps.yaml', 'compose.search.yaml', 'compose.todo.yaml', 'compose.todo-search.yaml', 'compose.meditate.yaml', 'compose.forwarded.yaml', ]); for (final name in files) { expect(File('deploy/$name').existsSync(), isTrue, reason: name); } }); test("keeps Train's map and routes to southern Bavaria, its search to " 'Germany, in memory beside the rest', () { expect(env['MAP_REGION'], '10,47.2,13.9,49'); expect(env['BROUTER_REGION'], '10,45,15,50'); expect(env['PHOTON_REGION'], 'europe/germany'); expect(env['PHOTON_HEAP'], '2g'); expect(env['PHOTON_MEMORY'], '3g'); }); test('says Germany for the data and the backups, and reads the library ' 'from the HDD', () { expect(env['DATA_LOCATION'], 'DE'); expect(env['BACKUP_LOCATION'], 'DE'); expect(env['JELLYFIN_MEDIA'], '/data/media'); expect(env.containsKey('MEDITATE_LIBRARY'), isFalse); }); }); test('never overwrites an existing .env, whose peppers must stay', () { final file = File('${folder.path}/.env')..writeAsStringSync('KEPT=1\n'); final result = make(file.path); expect(result.exitCode, isNot(0)); expect(result.stderr, contains('exists')); expect(file.readAsStringSync(), 'KEPT=1\n'); }); }