conorganizer

command module
v0.0.0-...-cfe8a2a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2026 License: MIT Imports: 41 Imported by: 0

README

Con Organizer

Why

The main purpose of this project is to help the Regncon festival achieve its goals of having all the players get to play at least one game that they are very interested in during the festival.

Quick Start

Task, templ and Air are tool dependencies in go.mod, so you do not install them separately. Run every Taskfile command as go tool task <name>, and the other tools as go tool templ and go tool air.

Editing needs two things on the machine that runs your editor, whichever platform it is: Go, and Node.js with npm. templ formats <style> and <script> blocks through Prettier, which go tool task setup:editor installs with npm; the same task installs the templ version from go.mod for the editor. Run it once after cloning (see IDE Setup).

The app needs no .env file to start, but it does need a database and a writable image directory. go tool task start opens database/events.db and refuses to create it when it is missing. If local-event-images/ (present in the checkout) is missing or not writable, the app starts in degraded mode. A fresh checkout has no database, so download data first:

  1. Set DB_SSH_USER in .env (see .env).
  2. go tool task download:main (see Get the Latest Database Backup and Images).
  3. go tool task start (see Run Locally).

Choose your preferred method to run the project:

Mac/Linux Setup

Install Go and Node.js. Downloads also need ssh, sqlite3 and tar, and go tool task test needs sqlite3.

The container has everything the app needs, but your editor runs on the host, so install Go and Node.js on Windows as well and run go tool task setup:editor there once.

Start the application using Docker Compose

docker compose up --build

Docker serves the application through Caddy using HTTPS and HTTP/2. The Go server stays internal on port 7332. Caddy uses HTTPS_PORT from .env as its public HTTPS port, falling back to 7331 when HTTPS_PORT is not set. With HTTPS_PORT=7331, open https://localhost:7331.

Caddy starts once the Go server reports healthy, so the first start waits for templates to generate and the server to compile.

The first time Caddy starts, trust its local certificate authority for your user account. Keep Docker Compose running, open another terminal, and run the command for your operating system.

Windows PowerShell:

.\scripts\trust-docker-ca.ps1

If PowerShell refuses with running scripts is disabled on this system, run the script with a bypass that only applies to that one process:

powershell -ExecutionPolicy Bypass -File .\scripts\trust-docker-ca.ps1

Linux or macOS:

sh scripts/trust-docker-ca.sh

On Linux the script also adds the certificate to the Chrome and Firefox certificate databases, which needs certutil (libnss3-tools on Debian/Ubuntu, nss-tools on Fedora, nss on Arch).

Restart the browser after trusting the certificate, then open the HTTPS URL for the configured HTTPS_PORT. The certificate is retained in the caddy_data Docker volume across container rebuilds. Removing that volume, for example with docker compose down -v, creates a new certificate authority. Run the trust script again afterwards; the old certificate stays trusted until you remove it from your certificate store.

Testing from a phone

Set DEV_LAN_IP in .env to your computer's local network IP address, for example DEV_LAN_IP=192.168.1.20, and restart Docker Compose. Then:

  1. Run the trust script once so the certificate is saved to tmp/caddy/conorganizer-caddy-root.crt.
  2. Copy that file to the phone and install it as a trusted certificate authority.
    • iOS: open the file, install the profile under Settings → General → VPN & Device Management, then enable it under Settings → General → About → Certificate Trust Settings.
    • Android: Settings → Security → Encryption & credentials → Install a certificate → CA certificate.
  3. Open https://<DEV_LAN_IP>:7331 on the phone, using your HTTPS_PORT.

While DEV_LAN_IP is set, use https://localhost:7331 on the computer itself: connections to an IP address carry no hostname, so Caddy answers 127.0.0.1 with the LAN address certificate. If the phone cannot connect, check that the firewall allows inbound connections on the HTTPS port.

.env

.env is gitignored and holds only local, per-developer values and secrets. Task loads it (dotenv: .env in Taskfile.yml), and the app loads it on a best-effort basis with godotenv. Known keys:

Key Used by
DB_SSH_USER Required by go tool task download:*
HTTPS_PORT, DEV_LAN_IP Docker Compose and Caddy dev setup
CHECKIN_KEY, CHECKIN_SECRET Optional. Checkin API credentials, read by service/checkIn
GOOSE_DRIVER, GOOSE_MIGRATION_DIR Optional Goose CLI convenience
PORT Optional. HTTP port, defaults to 8080
HOST Optional. Address to listen on, defaults to every interface. The server units set 127.0.0.1
LOG_LEVEL Optional. DEBUG, INFO, WARN or ERROR, defaults to INFO

The Descope project ID is not configuration: it is the hardcoded constant authctx.DescopeProjectID in service/authctx/authctx.go. It is not a secret, since the login page shows it to every browser. Any DESCOPE_PROJECT_ID left in an old .env is ignored.

Get the Latest Database Backup and Images

[!NOTE] Downloads require DB_SSH_USER in your .env file or shell environment.

go tool task download:main
go tool task download:demo

Add :db or :images to download only one part, for example go tool task download:main:images. Plain go tool task download only prints usage and exits with status 1, so nothing is downloaded without naming an environment.

Each task runs bash scripts/download-environment <main|demo> <all|db|images>:

  • It opens one SSH ControlMaster connection to the production server as DB_SSH_USER, so you authenticate once per run.
  • The database is exported on the server by conorganizer-export-db <env> and streamed back as a tar. The main export is anonymized. See documentation/sqlite-and-health.md for how the export works.
  • The export is extracted into a fresh temporary directory and checked with PRAGMA quick_check. Only when the check passes is the local database (and its -wal/-shm files) replaced: database/events.db for main, database/events-demo.db for demo. A failed or interrupted download leaves your working database alone.
  • Images are streamed as a tar from the environment's event-images directory into the shared local-event-images/ directory. Existing files are never pruned. Image files are named after the random event id, so main and demo images do not collide; an event present in both gets the same file name, and the last download wins.

DB_SSH_USER must be allowed to run conorganizer-export-db and to read /mnt/HC_Volume_103911252/environments/{main,demo}/database/events.db and the matching event-images directories. The export only writes to the user's default temporary directory, not to the backup area.

  • Mac/Linux: the download runs on your machine and needs ssh, sqlite3 and tar.
  • Windows: the download runs inside the Docker container, so keep docker compose up running. Without Go installed on Windows, run the task in the container instead: docker compose exec webserver go tool task download:main. The container has no SSH key, so ssh asks for your password, once per run. After the container has been recreated, it also asks you to confirm the server's fingerprint.

Restart the application after downloading a database so it uses the new file.

Run a Restored Database Backup

restored.lekeplassen.regncon.no runs a selected production database backup. It is a public, isolated environment: restoring a backup replaces its database and refreshes its event images from main, without changing main or demo.

On the production server, choose one of the backup files in /mnt/HC_Volume_103911252/backups/sqlite and run:

sudo conorganizer-sqlite-restore events-20260920T120007Z.db.zst

The command decompresses the backup and runs PRAGMA integrity_check and a core-table check before replacing the database and event images in /mnt/HC_Volume_103911252/environments/restored/, then starts conorganizer-restored.service (port 19082, running as deploy:www-data). Verify the result at https://restored.lekeplassen.regncon.no/.

For the first rollout, add the DNS record, let the first application deployment install the restored binary, apply the configuration-as-code changes, then run the restore command. The first application deployment only installs its binary until a backup has been selected.

See documentation/deployment.md for the environments, ports and CI/CD.

Run Locally

go tool task start
go tool task start:demo

Then open your browser and navigate to: http://localhost:8080

Main and demo data use separate local SQLite files, so there is no command for switching environments:

Command Database Written by
go tool task start database/events.db go tool task download:main
go tool task start:demo database/events-demo.db go tool task download:demo

Both use the shared local-event-images/ directory. Air passes the paths to the app as -dbp <path> -image-path local-event-images.

Run tests

go tool task test

go tool task test first regenerates schema.sql from your local database/events.db (sqlite3 database/events.db ".schema --indent --nosys") and then runs go test ./.... Test databases are built from schema.sql (service/testdb.go).

schema.sql is committed, so the task overwrites the committed file with your local database's schema. If your local database is missing a migration, schema.sql loses those tables or constraints, and tests that use them fail. To fix it:

  • Never edit schema.sql by hand.
  • Apply the pending migrations to the local database, for example goose -dir migrations sqlite3 database/events.db up, or download a fresh database with go tool task download:main:db. Then run go tool task test again.
  • Check the schema.sql diff before committing.

Plain go test ./... uses the committed schema.sql as it is:

go test ./...

go tool task test:report runs the tests and prints the BDD behavior report used in CI. See documentation/automated-tests.md for how the automated tests are written.

Templ

Generated *_templ.go and *_templ.txt files are gitignored; commit only the .templ sources. They never show up in git status, which is expected. They are generated by the Taskfile (build:templ, and start:templ while go tool task start runs), during Docker startup (which runs go tool task start), and in CI before tests, build and lint. golangci-lint also excludes _templ.go files.

While go tool task start runs, natively or in Docker, it regenerates them on every .templ save; don't run go tool templ generate or go tool task build next to it. When no dev server is running, run:

go tool task build

Its build:templ dependency runs go tool templ generate for components, pages, layouts and service before go build. Compile errors in the generated code, such as unused variables or a shadowed err in {{ }} Go blocks, only show up after generation.

Formatting

CI fails when a .templ file is not formatted. To format every .templ file in place, including the <style> and <script> blocks, run:

go tool task fmt:templ

go tool task check:templ-fmt is the check CI runs; make fmt and make check wrap the two. Both need Prettier on PATH, which go tool task setup:editor installs (see IDE Setup).

A bare return line in template markup is rendered as text, not as a Go early exit, so rendering continues. For guard and early-exit logic inside a component, use a Go code block, as pages/event/event_page.templ does:

if event == nil {
	<p>Event not found</p>
	{{ return nil }}
}

IDE Setup

See Templ Guide: Developer Tools for detailed IDE support information.

On every platform, run this once after cloning and again whenever go.mod bumps templ:

go tool task setup:editor

It installs the templ version from go.mod globally, because VS Code falls back to the templ binary on PATH, and the Prettier version CI uses for formatting <style> and <script> blocks. go tool task check:editor shows what is on PATH next to what the repository expects.

VS Code setup, the templ binary version on Windows and Prettier for <style> blocks live in documentation/editor-setup.md.

Neovim-specific setup lives in documentation/neovim-setup.md.

Troubleshooting

Common issues and solutions:

  • Manual templ generation: If you encounter issues with Templ, run:
go tool templ generate
  • Docker HTTPS port in use: Check if another service is using the port set by HTTPS_PORT in .env (or port 7331 when it is unset)
  • Build errors: Run go mod tidy to fix dependencies
  • Admin route returns 404: every admin route is registered in SetupAdminRoute in pages/admin/admin.go. Feature handlers such as programPublishingRoute (pages/admin/publiser_program.templ) and puljefordelingStatusRoute (pages/admin/puljefordeling.go) live next to their feature and are only reachable when SetupAdminRoute calls them. A handler and a frontend @put call are not enough. Merges can drop these calls, so check that every *Route(adminRouter, ...) call is still there.

Migrations

documentation/migrations.md

Update Dependencies

Update all Go dependencies:

go get -u
go mod tidy

Check what changed:

git diff go.mod go.sum

Verify tool versions used by the repo:

go tool templ --version
go tool task --version
go tool air -v

templ, Task and Air are tool dependencies in go.mod, so updating go.mod updates them everywhere: CI and the Dockerfile use the go.mod versions.

When changing the Go version in go.mod, also update go-version in .github/workflows/buildAndTest.yml and .github/workflows/golangci-lint.yml, and the golang: image in Dockerfile.

If golangci-lint fails in CI with "the Go language version ... used to build golangci-lint is lower than the targeted Go version", bump the golangci-lint version: pin (and go-version if needed) in .github/workflows/golangci-lint.yml. Do not downgrade the project's Go version.

Agent Skills Path Compatibility

Some agents do not discover skills directly from .agents/skills.

For Claude Code, run go tool task setup:skills once per clone. It links .claude/skills to .agents/skills (a junction on Windows, so no admin rights are needed).

For other agents, link the skills into that agent's own skills folder (create the folder first if needed).

If you need a true symlink instead (may require admin/dev mode):

$agentSkillsFolder = ".codex\skills"  # replace with your agent's skills folder
New-Item -ItemType Directory -Force -Path $agentSkillsFolder | Out-Null
New-Item -ItemType SymbolicLink -Path "$agentSkillsFolder" -Target ".agents\skills"

Grafana MCP Server

The repository registers Grafana's MCP server for https://grafana.regncon.no, so agents can query production logs in Loki, metrics in Prometheus and dashboards. Each agent reads its own file:

Agent File
Claude Code .mcp.json
Codex .codex/config.toml
GitHub Copilot in VS Code .vscode/mcp.json

All three run the mcp-grafana program from PATH, read-only (--disable-write) with usage statistics turned off. Install it once, and again whenever MCP_GRAFANA_VERSION in Taskfile.yml changes:

go tool task setup:mcp

The task runs go install for the pinned version and prints it. It fails if Go's bin folder is not on PATH, and says which folder to add. The agents do not use go run: it takes about 15 seconds to check its build on every start, which can exceed Claude Code's 30-second MCP startup limit.

Service account token

Each developer uses their own token. Claude Code and Codex read it from the GRAFANA_SERVICE_ACCOUNT_TOKEN environment variable, not from .env. VS Code asks for it instead (see Check that it works).

A Grafana admin creates the token:

  1. In Grafana, open Administration → Users and access → Service accounts.
  2. The first time, click Add service account, name it claude-mcp and give it the Viewer role. Viewer can query Loki and Prometheus and read dashboards. Keep it at Viewer: the server has a general grafana_api_request tool even with --disable-write, so the role is what stops it from changing anything.
  3. Open claude-mcp, click Add service account token, name it after the developer, set an expiry, click Generate and copy the glsa_… value. Grafana shows it only once. One token per developer means one can be revoked without breaking the others.
Set the token

Windows PowerShell:

setx GRAFANA_SERVICE_ACCOUNT_TOKEN "glsa_paste_your_token_here"

setx only affects programs started afterwards, so restart the Claude or Codex desktop app, or open a new terminal before starting claude or codex.

Linux: add the line below to ~/.profile, then log out and in again so both terminals and desktop apps see it.

macOS: add it to ~/.zprofile, then open a new terminal and start claude or codex from it.

export GRAFANA_SERVICE_ACCOUNT_TOKEN="glsa_paste_your_token_here"

On macOS, apps opened from the Dock or Finder do not read ~/.zprofile. If the Claude or Codex desktop app reports the variable as missing, run this and restart the app. It lasts until the next reboot:

launchctl setenv GRAFANA_SERVICE_ACCOUNT_TOKEN "glsa_paste_your_token_here"
Check that it works

Claude Code asks once whether to trust the project's grafana-conorganizer server. Approve it, then run:

claude mcp list

grafana-conorganizer should show as connected. The server was called grafana until October 2026; if you set it up then, Claude Code asks again under the new name.

Codex reads .codex/config.toml only in a project you have marked as trusted; Codex asks the first time you open the folder. Then run:

codex mcp get grafana-conorganizer

Codex passes only a short list of environment variables to MCP servers, so .codex/config.toml forwards the token with env_vars.

In VS Code, open .vscode/mcp.json and click Start above grafana-conorganizer. VS Code asks for the token once, hides what you type and stores it. VS Code calls .vscode/mcp.json deprecated in favour of a root .mcp.json, but it only reads the root file with the experimental chat.mcp.workspaceRootConfig.enabled setting, so keep using .vscode/mcp.json for now.

In every agent, a 401 in the server's log means the token is missing or expired.

Production logs carry the labels app="conorganizer", job="systemd-journal" and service="conorganizer-main", and each line is slog JSON, so | json works directly. Log times are UTC. This selector finds the main service:

{service=~"conorganizer-main", app=~".+", job=~".+"}

Additional Resources

Project documentation in documentation/:

External:

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd
testreport command
qrcode
Package qrcode draws QR codes as SVG, so they stay sharp when printed.
Package qrcode draws QR codes as SVG, so they stay sharp when printed.
pages
feedback
Package feedback wires up the logged-in "Gi tilbakemelding" form at /tilbakemelding.
Package feedback wires up the logged-in "Gi tilbakemelding" form at /tilbakemelding.
feedback
Package feedback stores anonymous feedback from users.
Package feedback stores anonymous feedback from users.
puljefordeling
Package puljefordeling runs the seating-distribution solver against live conorganizer data to produce a read-only preview of how participants would be assigned to events in each pulje.
Package puljefordeling runs the seating-distribution solver against live conorganizer data to produce a read-only preview of how participants would be assigned to events in each pulje.
puljefordeling/solver
Package solver implements the min-cost max-flow based event assignment algorithm.
Package solver implements the min-cost max-flow based event assignment algorithm.
puljefordeling/solver/model
Package model defines the core data types for the puljefordeling algorithm.
Package model defines the core data types for the puljefordeling algorithm.
bdd
qrtest
Package qrtest scans QR codes drawn by components/qrcode in tests.
Package qrtest scans QR codes drawn by components/qrcode in tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL