gameskill: id: verdande name: "verdande" category: "Apps" description: > Self-hosted tasks and projects, shared with other people. Natural-language quick add in Danish and English, sub-tasks, labels, saved filters, board and calendar views, recurring tasks, reminders, comments and attachments. One binary with SQLite inside it — no database to set up. Set BASE_URL to the address you reach it on, point the SMTP settings at your mail server for invites and reminders, then start it and open the URL to create the first admin account. author: "kristianwind" # Bumped whenever the variables below change. The panel keys its catalogue on # this: a manifest with new fields and an unchanged version is a manifest the # panel has no reason to re-read, so the fields never appear and the failure is # silent — the server's settings page simply does not offer them. # Bumpet til 6: v5 flyttede imaget til vores egen Gitea, og det virkede ikke. # En rune opdaterer ikke sig selv — panelet skriver rune-rækken fra en # UI-handling, så ændringen her kræver **Runes → verdande → Update**, og # katalog-listen caches ti minutter, så to forsøg lige efter hinanden kan begge # give den gamle version. version: 6 icon: "app" # The image is distroless — no shell — so keep_entrypoint runs the binary the # image already declares as its ENTRYPOINT, and startup.exec passes it argv # directly rather than through /bin/sh, which does not exist in there. # GHCR, ikke vores egen Gitea. v5 prøvede det sidste og tog instansen ned. # # Panelets `PullImage` sætter aldrig `RegistryAuth`, så registret SKAL kunne # læses uden legitimation. GHCR kan: dens token-endepunkt svarer 200 og udsteder # en pull-token til hvem som helst. Gitea på gitea.nolimit.dk kan ikke — # `/v2/token?scope=repository:kw/verdande:pull` svarer 401, også efter at repoet # blev gjort offentligt. Instansen kræver login på alt, registret inklusive, og # det er en indstilling for hele instansen frem for for denne pakke. # # Sådan ser det ud, når det går galt, for det tog en time at finde: panelet # river containeren ned for at anvende runen, pullet fejler, `pullImageRetry` # kasserer fejlen, og `ContainerCreate` slår derefter op LOKALT og siger # `No such image`. Serveren står som "running" uden container_id, og udadtil er # det en 502. Fejlen nævner aldrig, at det var et login, der manglede. # # Sådan afgør man det, uden at blive narret: spørg registrets token-endepunkt # med `curl` og se efter en 200 med et token i. Test det ALDRIG med # `docker --config pull` — en tom config stopper ikke maskinens # credential-helper, så pullet bliver autentificeret uden at se sådan ud, og # svaret er præcis det modsatte af sandheden. docker: image: "ghcr.io/kristianwind/verdande:latest" data_path: /data keep_entrypoint: true variables: # verdande cannot work out its own public address: the host port is not chosen # until the server is created. {{PUBLIC_URL}} is what the panel knows and this # is what invite links, password resets and ICS feeds are built from — get it # wrong and the links in your email point somewhere that does not answer. - key: VERDANDE_BASE_URL name: "Public address (used in invite and reset links)" type: string default: "{{PUBLIC_URL}}" required: true - key: VERDANDE_SMTP_HOST name: "SMTP host (blank = show invite links in the app instead of mailing them)" type: string default: "" - key: VERDANDE_SMTP_PORT name: "SMTP port" type: int default: 587 min: 1 max: 65535 - key: VERDANDE_SMTP_USER name: "SMTP username" type: string default: "" - key: VERDANDE_SMTP_PASS name: "SMTP password" type: string default: "" secret: true - key: VERDANDE_SMTP_FROM name: "Sender address" type: string default: "" pattern: '^$|^[^@\s]+@[^@\s]+\.[^@\s]+$' hint: "must be an email address like verdande@example.dk, or blank to leave it unset" - key: VERDANDE_SMTP_STARTTLS name: "Use STARTTLS (turn off only for port 465, which is TLS from the start)" type: bool default: true - key: VERDANDE_SESSION_TTL name: "How long a login lasts (Go duration, e.g. 720h for 30 days)" type: string default: "720h" pattern: '^[0-9]+(ns|us|ms|s|m|h)$' hint: "must be a duration like 720h, 30m or 45s" # Off by default because it is an outbound request nobody asked for. It sends # no identifier and reads one public URL — GitHub's releases API — but that is # a choice to make rather than a default to inherit. - key: VERDANDE_UPDATE_CHECK name: "Look for new releases (asks GitHub once a day; sends nothing about you)" type: bool default: false # Restarting from verdande's own settings, which is also how it picks up a new # version: a container cannot replace its own image, so the panel recreates it # and pulls :latest on the way. All three are needed together; the page names # whichever are missing. - key: VERDANDE_PANEL_URL name: "Panel address, so verdande can ask to be restarted from its own settings" type: string default: "" - key: VERDANDE_PANEL_TOKEN name: "Panel API token — from an account with control of this server" type: string secret: true default: "" - key: VERDANDE_PANEL_SERVER_ID name: "This server's id in the panel (the one in its URL)" type: string default: "" - key: VERDANDE_SECRET_KEY name: "Key for the tokens and mailbox passwords in the database — 32 bytes of base64, from: openssl rand -base64 32. Leave empty to keep one in a file beside the data. Change it and every mailbox must be connected again." type: string secret: true default: "" install: # The image has no shell, so the install step borrows one. It only has to make # the directory layout exist; verdande creates and migrates its own database on # first start. image: "busybox:latest" script: | set -eu mkdir -p /data/files /data/backups echo "verdande data directory ready at /data" startup: # No command: with keep_entrypoint the image's own ENTRYPOINT (/verdande) runs, # and everything is configured through the environment above. done_regex: '"msg":"listening"' ports: - { name: web, default: 8080, protocol: tcp } # Everything worth keeping is under /data. The -wal and -shm files are included # deliberately: a WAL-mode SQLite database copied without them can be missing the # most recent writes. backup: include: ["."] # The container logs JSON — VERDANDE_DEV is off in production — so these patterns # match the JSON form, not the human-readable one seen when developing locally. watchers: - name: "Database errors" pattern: 'database is locked|disk I/O error|database disk image is malformed' threshold: 1 window_secs: 300 action: kvasir - name: "Failed logins" pattern: '"path":"/api/v1/auth/login".*"status":401' threshold: 10 window_secs: 300 action: notify - name: "Server errors" pattern: '"status":5[0-9][0-9]' threshold: 5 window_secs: 300 action: kvasir events: # Capture group 1 is the client address, which the panel rolls up per source — # so a brute-force run shows up as one row per address per hour. - key: login_failed label: "Failed login" match: '"path":"/api/v1/auth/login".*"status":401.*"remote":"([^"]+)"' - key: http_5xx label: "HTTP 5xx" match: '"status":5[0-9][0-9].*"remote":"([^"]+)"'