Strona i system zapisów na unconference D-Day w Hakierspejsie w Łodzi.
- Kiedy: sobota 8 sierpnia 2026, 14:00–22:00
- Gdzie: Hakierspejs, Zielona 30 LU3, Łódź
- Wstęp: darmowy, zapisy obowiązkowe (20 miejsc + 20 na liście rezerwowej)
- Zapisy startują: niedziela 26 lipca 2026, 15:00 czasu polskiego
Powyższe daty to wartości domyślne — wszystkie są konfigurowalne przez zmienne środowiskowe, patrz Zmiana terminu wydarzenia.
Dwie binarki Go (bez CGO), wspólny pakiet z datami i konfiguracją terminu:
| Komponent | Ścieżka | Opis |
|---|---|---|
| Serwer WWW | . (main.go, server.go, templates.go) |
landing + rejestracja; index.html, privacy.html i style.css są go:embed-owane w binarce |
| Bot Matrix | cmd/bot, internal/matrixbot |
nasłuchuje !start, wysyła DM z prywatnym linkiem (rejestracja albo panel) |
| Bramka czasowa i daty | internal/regwindow |
jedyne źródło prawdy dla terminów; generuje polskie opisy dat |
| Baza | internal/store |
SQLite przez modernc.org/sqlite (czysty Go) |
| Style | style.css |
jedyny arkusz stylów; sekcje body.landing / body.page / body.privacy |
Trasy serwera WWW:
| Trasa | Opis |
|---|---|
GET / |
landing (index.html); tylko dokładnie /, żadnego przeglądania katalogu |
GET /register?t=… |
formularz zapisu dla ważnego tokenu |
POST /register |
zapis (miejscowość + e-mail); tożsamość wyłącznie z tokenu |
GET /api/count |
JSON: obłożenie miejsc + wszystkie daty i ich opisy (patrz niżej) |
GET /api/registered?h=@user:hs |
wewnętrzne, dla bota; wymaga Authorization: Bearer $INTERNAL_TOKEN, bez tokenu 404 |
GET /api/registrations?since=<id> |
wewnętrzne, dla bota: zgłoszenia o id > since do ogłoszeń na kanale; wymaga Authorization: Bearer $INTERNAL_TOKEN, bez tokenu 404, bez e-maila i miejscowości |
GET /panel?t=… |
panel uczestnika dla ważnego magic linku: status + przycisk „Wycofaj udział" |
POST /panel |
wycofanie udziału (tożsamość wyłącznie z tokenu) |
GET /admin?t=… |
panel admina: podgląd wszystkich zgłoszeń; wymaga ADMIN_TOKEN, bez tokenu 404 |
GET /privacy |
polityka prywatności (privacy.html) |
GET /style.css |
wspólny arkusz stylów wszystkich stron (style.css) |
GET /healthz |
health check (używany też przez dday -healthcheck) |
- Użytkownik pisze
!startdo bota (DM albo pokój z allowlisty).!registerdziała nadal jako alias — komendą oficjalną (tą z landing page) jest!start. - Bot sprawdza bramkę czasową i — jeśli ma
INTERNAL_TOKEN— pyta/api/registered, czy ten handle już się zapisał. - Bot generuje token:
{handle, issued}zaszyfrowane kluczem age (odbiorcą jest klucz publiczny serwera), podpisane HMAC-em (TOKEN_SECRET), całość base64. - Bot wysyła w DM link
REGISTER_URL?t=<token>. GET /register?t=…odszyfrowuje i weryfikuje token: zły podpis → „Nieprawidłowy link", starszy niż 48 h (lub wystawiony w przyszłości ponad 5 min zapasu na zegar) → „Link wygasł".- Formularz zbiera miejscowość i e-mail;
POSTzapisuje rekord w SQLite i przydziela numer uczestnika (AUTOINCREMENT). - Numery 1–20 to miejsca potwierdzone, 21–40 lista rezerwowa, powyżej — brak miejsc.
- Link jest jednorazowy: po zapisie ten sam link pokazuje potwierdzenie, a próba
ponownego
POSTkończy się stroną „Już zapisany", bez drugiego rekordu.
Kolejne !start od kogoś, kto jest już zapisany (/api/registered), nie jest odmową —
bot wysyła w DM magic link do panelu (PANEL_URL?t=<token>), a na kanale zostawia
tylko publiczny nudge „sprawdź prywatne wiadomości" (numeru uczestnika nie ujawnia publicznie).
- Token panelu ma ten sam mechanizm co rejestracyjny (age + HMAC, TTL 48 h), ale niesie
inny kind (
panelvsreg), objęty HMAC-em. Dzięki temu link rejestracyjny nie otworzy panelu, a magic link do panelu nie pozwoli się zapisać. - W panelu widać nick, numer uczestnika i status (uczestnik / lista rezerwowa, pozycja). Jedyną akcją jest „Wycofaj udział" — usuwa rekord i zwalnia miejsce (osoba z rezerwy awansuje).
- Po wycofaniu można zapisać się ponownie:
!start→ bot widzi, że nie jesteś zapisany → link rejestracyjny (o ile są miejsca). - Gdy
PANEL_URLnie da się ustalić, bot degraduje się do dawnej publicznej odpowiedzi „jesteś już zapisany", zamiast wysyłać zepsuty link.
GET /admin to widok tylko do odczytu dla organizatora: podsumowanie obłożenia
(uczestnicy X/20, lista rezerwowa Y/20, łącznie) oraz tabela wszystkich zgłoszeń —
numer uczestnika, status (uczestnik / rezerwa #poz), nick, handle Matrix,
miejscowość, e-mail i data zapisu (strefa Europe/Warsaw).
Dostęp chroni ADMIN_TOKEN. Token można podać na dwa sposoby:
https://dday.hs-ldz.pl/admin?t=<ADMIN_TOKEN>
curl -H "Authorization: Bearer <ADMIN_TOKEN>" https://dday.hs-ldz.pl/admin
- Token bierze się z pliku
.env—make upgeneruje go raz i potem reużywa (grep ADMIN_TOKEN .env). - Pusty
ADMIN_TOKENwyłącza endpoint całkowicie (404), tak samo jakINTERNAL_TOKENwyłącza/api/registered. Zły lub brakujący token → 401. Porównanie jest constant-time. - URL zawiera sekret — nie wklejaj go na czat, do issue ani w screenshot.
Odpowiedź ma
Cache-Control: no-store, a globalneReferrer-Policy: no-referrerpilnuje, żeby token nie wyciekł nagłówkiem Referer. Kto ma token, ten widzi dane osobowe wszystkich zapisanych. - Panel nic nie modyfikuje: wycofanie udziału robi sam uczestnik w
/panel, a awaryjne usunięcie danych to CLIdday -delete <handle>(patrz sekcja RODO). - Status liczony jest z rangi (pozycji wśród aktualnych wierszy), więc po wycofaniu się uczestnika awans z listy rezerwowej widać od razu.
Rejestracja kończy się po stronie WWW, więc bot nie dowiaduje się o niej sam — cyklicznie
odpytuje wewnętrzny endpoint GET /api/registrations?since=<ostatnie ogłoszone id>
(bearer INTERNAL_TOKEN) i ogłasza to, czego jeszcze nie ogłosił, na kanale macierzystym:
🎉 alice dołącza do D-Day — uczestnik #5
🎉 bob zapisał(a) się na D-Day — lista rezerwowa, pozycja #2
- Bez danych osobowych. Ogłoszenie zawiera wyłącznie handle Matrix (jako mention
przez
matrix.to) i numer uczestnika albo pozycję na liście rezerwowej. E-mail i miejscowość nie wychodzą poza serwer — endpoint/api/registrationsw ogóle ich nie zwraca, a typ, którym posługuje się bot, nie ma na nie pól. - Bez powtórek po restarcie. Ostatnie ogłoszone
idjest zapisywane w tym samym pliku co cache DM (DM_CACHE_PATH, format{"dms":{…},"lastAnnounced":N}; stary, płaski format wczytuje się nadal, zlastAnnounced=0). - Pierwszy start bez stanu nie odtwarza historii: bot ustawia
lastAnnouncedna aktualne maksymalneidi ogłasza dopiero kolejne zapisy. - Wyłączenie: puste
ANNOUNCE_ROOM(iMATRIX_ROOM) albo brakINTERNAL_TOKEN.
Landing pobiera stan z /api/count (licznik zapisanych, pasek, lista rezerwowa,
flaga open i daty). Bez API (np. GitHub Pages) strona degraduje się do wartości
wpisanych na sztywno w index.html.
Serwer WWW:
| Zmienna | Domyślnie | Opis |
|---|---|---|
PORT |
3329 |
port nasłuchu |
STATIC_DIR |
(puste) | katalog z index.html/privacy.html/style.css zamiast wersji wbudowanej (dev) |
DB_PATH |
./dday.db |
plik bazy SQLite |
AGE_KEY |
config/dday_ed25519 |
ścieżka klucza prywatnego age |
AGE_KEY_DATA |
(puste) | ten sam klucz przekazany base64 (ma pierwszeństwo; używane w kontenerze) |
TOKEN_SECRET |
(puste) | wspólny sekret HMAC dla tokenów; musi być identyczny u bota |
INTERNAL_TOKEN |
(puste) | bearer chroniący /api/registered i /api/registrations; puste = endpointy wyłączone (404) |
ADMIN_TOKEN |
(puste) | token chroniący /admin; puste = panel admina wyłączony (404) |
REGISTRATION_OPEN |
(puste) | opcjonalny override do testów: 1/true/yes wymusza otwarte zapisy, 0 wymusza zamknięte, brak/puste = automat wg REGISTRATION_OPEN_AT |
REGISTRATION_OPEN_AT |
2026-07-26 15:00 |
moment otwarcia zapisów |
EVENT_START_AT |
2026-08-08 14:00 |
początek wydarzenia |
EVENT_END_AT |
2026-08-08 22:00 |
koniec wydarzenia |
Bot Matrix (cmd/bot, konfiguracja zwykle w matrix.env):
| Zmienna | Domyślnie | Opis |
|---|---|---|
MATRIX_HOMESERVER |
wymagane | np. https://matrix.org |
MATRIX_USER |
wymagane | np. @ddaybot:matrix.org |
MATRIX_PASSWORD |
wymagane | hasło bota |
MATRIX_ROOM |
(puste) | pokój używany przez skrypty make matrix-hello / matrix-send |
REGISTER_URL |
https://dday.hs-ldz.pl/ |
baza linku rejestracyjnego (jej origin służy też do /api/registered) |
PANEL_URL |
origin(REGISTER_URL) + /panel |
baza magic linku do panelu uczestnika |
AGE_PUB |
config/dday_ed25519.pub |
klucz publiczny age, którym bot szyfruje tokeny |
AGE_PUB_DATA |
(puste) | ten sam klucz base64 (ma pierwszeństwo) |
TOKEN_SECRET |
(puste) | jak wyżej — musi zgadzać się z serwerem |
INTERNAL_TOKEN |
(puste) | włącza pytanie /api/registered przed wydaniem linku oraz ogłaszanie nowych zapisów (/api/registrations) |
ANNOUNCE_ROOM |
MATRIX_ROOM |
pokój, w którym bot ogłasza nowe zapisy; puste = ogłaszanie wyłączone |
ANNOUNCE_INTERVAL |
30s |
jak często bot odpytuje /api/registrations (format time.ParseDuration) |
ALLOWED_ROOMS |
(puste) | lista room id po przecinku; puste = bot reaguje wszędzie |
DM_CACHE_PATH |
(puste) | plik stanu bota (cache handle → room id + lastAnnounced), przeżywa restart |
CRYPTO_DB_PATH |
/data/crypto.db |
pure-Go SQLite crypto store (tożsamość urządzenia bota + sesje megolm); na wolumenie, żeby przeżyć redeploy — patrz Szyfrowane DM-y (E2EE) |
CRYPTO_PICKLE_KEY |
(puste) | klucz szyfrujący konto olm w spoczynku; musi być stały — z tym samym CRYPTO_DB_PATH utrzymuje urządzenie i sesje między restartami. Puste = generowany efemerycznie z ostrzeżeniem (restart gubi wszystkie sesje) |
REGISTRATION_OPEN, REGISTRATION_OPEN_AT |
jak wyżej | bot używa tej samej bramki i tego samego opisu daty |
Formaty dat (*_AT): RFC3339 (2026-07-26T15:00:00+02:00) albo
2006-01-02 15:04 / 2006-01-02 15:04:05 / 2006-01-02 interpretowane w strefie
Europe/Warsaw. Wartość niepoprawna → wpis w logu i użycie domyślnej (bez crasha).
make keys # jednorazowo: para kluczy age do config/ (gitignored)
make run # serwer WWW na :3329 (index.html z binarki)
make dev # to samo, ale index.html czytany z dysku (live edit)
make bot # bot Matrix, konfiguracja z ./matrix.env
make check # walidacja matrix.env
make test # go test -race ./...Sekrety trzymamy poza repo: matrix.env (wzór w matrix.env.example), .env
generowany przez make up i katalog config/ — wszystkie są w .gitignore.
Podgląd z niestandardowymi datami:
REGISTRATION_OPEN_AT="2026-09-01 12:00" \
EVENT_START_AT="2026-09-05 10:00" EVENT_END_AT="2026-09-05 18:00" make runmake up # generuje .env i odpala docker compose up -d --build
make logs # docker compose logs -f
make down # zatrzymanie stackumake up sprawdza obecność kluczy age i matrix.env, a następnie zapisuje .env
z: AGE_KEY_DATA, AGE_PUB_DATA (klucze base64 — plik 0600 byłby nieczytelny dla
użytkownika nonroot w obrazie distroless), INTERNAL_TOKEN, TOKEN_SECRET i
ADMIN_TOKEN (losowane raz i potem reużywane) oraz REGISTRATION_OPEN
(domyślnie pusty — czyli rejestracja otwiera się automatycznie, patrz niżej;
jawnie ustawiona wartość z istniejącego .env jest zachowywana). Po zapisie
make up wypisuje tryb rejestracji: AUTO, FORCED OPEN albo FORCED CLOSED.
docker-compose.yml wystawia serwis dday przez Traefika na
dday.hs-ldz.pl (entrypoint websecure, certresolver myresolver) i uruchamia
serwis bot. Dane trwałe: wolumen dday-data (/data/dday.db) i bot-data
(cache DM-ów bota oraz crypto store E2EE /data/crypto.db — patrz niżej).
Zmienne REGISTRATION_OPEN_AT / EVENT_START_AT / EVENT_END_AT są
przepuszczane do obu serwisów — ustaw je w .env, jeśli zmieniasz termin.
Jeden .env, oba serwisy. make up zapisuje pojedynczy .env, z którego
docker compose podstawia zmienne do obu usług: sekrety wspólne
(TOKEN_SECRET, INTERNAL_TOKEN) trafiają i do dday, i do bot, a każda
usługa dostaje dodatkowo swoje (ADMIN_TOKEN, AGE_KEY_DATA → tylko web;
CRYPTO_PICKLE_KEY, AGE_PUB_DATA → tylko bot). Sam bot bierze jeszcze
MATRIX_* i REGISTER_URL z matrix.env (też gitignored). Dzięki temu nie ma
„rozjechanego" wdrożenia — token, którym bot podpisuje link, i sekret, którym web
go weryfikuje, pochodzą z tej samej linii w tym samym pliku.
Bot czyta i odpowiada w end-to-end szyfrowanych pokojach. Ma to znaczenie w
praktyce: Element (i większość klientów Matrix) domyślnie szyfruje prywatne
konwersacje, więc !start napisane botowi na priv przychodzi jako
m.room.encrypted. Transport to maunium.net/go/mautrix + cryptohelper z
czysto-Go implementacją olm/megolm (build tag goolm), dzięki czemu binarka
linkuje się statycznie przy CGO_ENABLED=0 (bez taga mautrix ciągnie libolm
przez cgo i nie zlinkuje). Ta sama logika komend obsługuje szyfrowane i
nieszyfrowane wiadomości — po odszyfrowaniu event jest zwykłym m.room.message.
Crypto store na wolumenie — kluczowe dla persystencji. Tożsamość urządzenia
bota i sesje megolm żyją w pure-Go SQLite pod CRYPTO_DB_PATH (domyślnie
/data/crypto.db, na wolumenie bot-data). Przy pierwszym starcie bot loguje
się hasłem, tworzy urządzenie i uploaduje na homeserver klucze urządzenia
(device keys). Przy kolejnych startach cryptohelper odnajduje zapisane
device_id w tym store i loguje się na to samo urządzenie — sesje megolm
zostają, więc bot dalej czyta i odpowiada bez ponownej akceptacji kluczy.
CRYPTO_PICKLE_KEYszyfruje konto olm w spoczynku i musi być stały.make upgeneruje go raz i reużywa (grep CRYPTO_PICKLE_KEY .env). Zmiana lub utrata klucza (albo skasowanie wolumenu zcrypto.db) = nowe urządzenie, nowe klucze i utrata dotychczasowych sesji. PusteCRYPTO_PICKLE_KEYw devie daje klucz efemeryczny i log-ostrzeżenie (restart gubi wszystkie sesje).- Weryfikacja / „wysyłaj tylko do zweryfikowanych sesji". Bot nie robi
cross-signingu ani interaktywnej weryfikacji. Ustawia jawnie
SendKeysMinTrust = ShareKeysMinTrust = Unset(najniższy próg — to zresztą domyślne zachowanie biblioteki), więc wysyła klucze megolm także do niezweryfikowanych urządzeń i w typowej konfiguracji Elementa działa bez żadnej weryfikacji. Wyjątek: jeśli klient uczestnika ma włączone twarde „Never send encrypted messages to unverified sessions", jego wiadomości do bota mogą nie dojść, dopóki tej opcji nie wyłączy albo (docelowo) nie doda się botowi weryfikacji. W praktyce ta opcja jest domyślnie wyłączona, więc zwykle nic nie trzeba robić.
Build bota (w Dockerfilu): CGO_ENABLED=0 go build -tags goolm → statyczny ELF
na obrazie distroless/static:nonroot (potwierdzone: „not a dynamic
executable"). Round-trip E2EE jest udowodniony testem integracyjnym make e2e
przeciw prawdziwemu Synapse — patrz Test E2EE.
Po przejściu na E2EE bot utrzymuje tożsamość urządzenia i wymaga dwóch rzeczy, których nie wolno zgubić:
CRYPTO_PICKLE_KEY— stały, w.env(generuje gomake up, tak samo jakINTERNAL_TOKEN/TOKEN_SECRET/ADMIN_TOKEN; istniejąca wartość jest reużywana).- Wolumen
bot-data— trzymacrypto.db(device_id + sesje megolm) orazdm_cache.json. Nie kasuj tego wolumenu (docker compose down -v,docker volume rm) — bot straciłby tożsamość urządzenia i historię sesji i musiałby uploadować nowe device keys jako świeże, niezweryfikowane urządzenie.
Pierwsze uruchomienie po wdrożeniu E2EE. Bot loguje się hasłem, tworzy
urządzenie i uploaduje device keys na homeserver. Od tej chwili każdy, kto
napisze !start w szyfrowanym DM do bota, jest obsłużony (odpowiedź wraca
zaszyfrowana). Element domyślnie dzieli klucze megolm także do niezweryfikowanych
sesji, więc działa bez żadnej weryfikacji; tylko uczestnik z twardą polityką
„never send to unverified sessions" musiałby najpierw zweryfikować bota.
Czysty redeploy obu serwisów z jednego .env (na hoście produkcyjnym):
cd /ścieżka/do/dday
git pull --ff-only
# upewnij się, że w .env NIE ma wymuszonego REGISTRATION_OPEN (pusta wartość albo
# brak linii = automatyczny time gate; 1 = na stałe otwarte — patrz sekcja niżej):
grep '^REGISTRATION_OPEN=' .env # spodziewane: puste albo brak
make up # dopisuje BRAKUJĄCE sekrety (w tym
# CRYPTO_PICKLE_KEY), reużywa istniejące,
# i robi docker compose up -d --build
docker compose ps # MUSI pokazać OBA: dday (web) i botdocker compose ps musi wylistować oba serwisy — dday (web) i bot — w tym
samym projekcie compose. Jeśli web chodzi osobnym mechanizmem (np. inny
compose-project albo ręczny docker run), przenieś go do tego samego
docker-compose.yml, żeby dzielił .env — inaczej TOKEN_SECRET/INTERNAL_TOKEN
się rozjadą: token, którym bot podpisuje link, nie zweryfikuje się po stronie web
(„Nieprawidłowy link"), a /api/registered odrzuci bota.
Weryfikacja po wdrożeniu:
docker compose logs bot | tail -30- ma być wpis
logged in, crypto initialised(zuser_id/device_id), - brak crash-loopu (kontener nie restartuje się w kółko),
- brak ostrzeżenia
WARNING: CRYPTO_PICKLE_KEY not set— jeśli je widzisz, klucz jest efemeryczny i restart zgubi sesje: uzupełnij.envimake up.
Test właściwy: napisz !start w szyfrowanym DM do @ddaybot → w ciągu paru
sekund powinien wrócić (zaszyfrowany) link …/register?t=….
Nie trzeba nic robić ręcznie w momencie startu zapisów. Rejestracja otwiera
się sama o REGISTRATION_OPEN_AT (domyślnie 2026-07-26 15:00 czasu polskiego):
- Serwer WWW liczy bramkę
regwindow.Open()przy każdym żądaniu, więc/registeri/api/countprzełączają się w tej samej sekundzie — bez restartu i bez redeploya. - Bot Matrix liczy ją przy każdej komendzie, więc od tego momentu zaczyna wydawać linki zamiast odpowiadać „jeszcze nie wystartowały".
- Landing odpytuje
/api/countco 60 s i dodatkowo natychmiast w chwili, gdy odliczanie do startu przekroczy zero. Otwarta w przeglądarce strona sama pokaże licznik zapisanych, sekcję „Jak się zapisać" i aktywne CTA — bez F5. Jeśli API kiedykolwiek odpowiedziało, to ono rozstrzyga (uwzględnia też wymuszoneREGISTRATION_OPEN=0); bez API (np. GitHub Pages) decyduje zegar przeglądarki i wbudowana data fallbackowa.
REGISTRATION_OPEN to opcjonalny override wyłącznie do testów: 1 wymusza
otwarte zapisy, 0 wymusza zamknięte, brak/puste = automat.
Starsze wersje make up wpisywały do .env REGISTRATION_OPEN=1. Taka linia
wymusza otwarte zapisy na stałe i całkowicie ignoruje moment otwarcia. Na
hoście produkcyjnym trzeba ją usunąć (albo wyzerować) przed startem zapisów:
cd /ścieżka/do/dday
grep '^REGISTRATION_OPEN=' .env # sprawdź, co tam jest
sed -i 's/^REGISTRATION_OPEN=.*/REGISTRATION_OPEN=/' .env # pusta wartość = automat
docker compose up -d # przeładuj env kontenerów
curl -s https://dday.hs-ldz.pl/api/count | grep -o '"open":[a-z]*'Ostatnia komenda przed terminem musi pokazać "open":false. Alternatywnie
make up — zachowa jawną wartość, jeśli linia dalej ma 1, więc i tak trzeba ją
najpierw wyczyścić. Restart nie jest potrzebny do samego otwarcia — jedynie do
wczytania zmienionego .env.
Terminy pochodzą wyłącznie z internal/regwindow: serwer używa ich na stronach
„zapisy nieotwarte", bot w odpowiedzi „jeszcze nie wystartowały", a landing
pobiera je z /api/count (openAt, eventStartAt, eventEndAt + gotowe polskie
teksty openText, openHowto, openShort, openShortTime, eventText,
eventShort, eventShortTime, eventBadge). Polskie odmiany („niedziela”,
„lipca”, „w niedzielę”) generuje sam pakiet — nic nie synchronizuje się ręcznie.
Żeby przesunąć termin, ustaw w .env (deployment) albo w środowisku procesu:
REGISTRATION_OPEN_AT="2026-09-01 12:00"
EVENT_START_AT="2026-09-05 10:00"
EVENT_END_AT="2026-09-05 18:00"Restart kontenerów i tyle — landing, formularz i bot mówią to samo.
Ręcznie trzeba poprawić tylko rzeczy, których nie da się wyliczyć w runtime:
index.html:<meta name="description">i<meta property="og:description">— statyczne, bo crawlery nie wykonują naszego JS.index.html: literały fallbackowe (REG_OPEN,EVENT, teksty kafelków) — używane wyłącznie, gdy strona jest serwowana bez API (np. GitHub Pages). Warto je odświeżyć przy zmianie terminu, choć produkcja bierze daty z API.- Nagłówek tego README.
privacy.html— pole „Ostatnia aktualizacja”, jeśli zmieniacie samą politykę.
Na żądanie uczestnika (kontakt: kontakt@hakierspejs.pl) usuwamy jego rekord
z bazy zapisów. Służy do tego wbudowany tryb CLI binarki, wskazujący uczestnika
po identyfikatorze Matrix (matrix_handle):
# w kontenerze (usługa dday używa /data/dday.db z wolumenu):
docker compose run --rm dday -delete @user:homeserver
# lub bezpośrednio przy uruchomionej binarce (DB_PATH wskazuje bazę):
DB_PATH=./dday.db dday -delete @user:homeserverKomenda wypisuje wynik i kończy się kodem 0, gdy rekord został usunięty, albo
1, gdy takiego zapisu nie było (lub wystąpił błąd). Uwaga: numer uczestnika to
AUTOINCREMENT — po usunięciu nie jest odtwarzany i nie zostaje przydzielony
ponownie. To celowe i akceptowalne.
make test # go test -race -tags goolm ./...
make fmt vet # gofmt -w . && go vet -tags goolm ./...Pokrycie: pełny przepływ rejestracji (token → formularz → zapis → duplikat →
lista rezerwowa → komplet), TTL i manipulacja tokenem, bramka czasowa i parsowanie
*_AT, generowanie polskich opisów dat, pola /api/count, nagłówki
bezpieczeństwa, tryb -delete, ładowanie kluczy oraz logika bota (allowlista
pokojów, cache DM-ów, odpowiedzi przed otwarciem zapisów).
Zwykłe make test używa wewnętrznego, in-memory fake'a transportu Matrix i
nie potrzebuje serwera — jest hermetyczne i szybkie.
make e2e uruchamia integracyjny test szyfrowanego przepływu przeciw
prawdziwemu Synapse w Dockerze (potrzebny Docker):
make e2e # stawia Synapse, rejestruje userów bot+alice, odpala test, sprzątaTest (test/e2e/e2e_test.go, za build-tagami e2e && goolm) prowadzi
prawdziwego bota (matrixbot.NewMxClient + Client.Serve, ten sam
konstruktor co cmd/bot) i drugiego klienta „alice”: alice tworzy szyfrowany
pokój, zaprasza bota, wysyła zaszyfrowane !start; bot je deszyfruje, buduje link
rejestracyjny i odpowiada zaszyfrowaną wiadomością, którą alice deszyfruje —
a token z linku jest dekodowany i weryfikowany (Handle == @alice, Kind == reg).
Drugi przypadek powtarza to w pokoju nieszyfrowanym (brak regresji plaintext).
Ręcznie, przeciw działającemu serwerowi:
bash scripts/e2e-synapse.sh up
go test -tags 'e2e goolm' -run E2E -v ./test/e2e/...
bash scripts/e2e-synapse.sh downBez serwera test SKIP-uje (nie failuje), więc nie blokuje deweloperów bez Dockera.
CI (GitHub Actions, .github/workflows) uruchamia gofmt -l, go vet,
go build, go test -race oraz build obrazu Dockera jako wymagane joby.
Test E2EE z Synapse jest osobnym, opcjonalnym jobem e2e
(continue-on-error: true, także workflow_dispatch) — bywa cięższy/flaky, więc
nigdy nie blokuje merge'a; źródłem prawdy jest lokalne make e2e.