kasl-server 0.8.0

Team server for kasl: collects work-time data from employees' kasl agents and turns it into dashboards, reports, and personal pages
Documentation
# The stand: server plus its database, built and run on the target machine.
#
# Deliberately small. A deployment worth handing to someone else - backups,
# restore, an install guide, a published image - is its own milestone; this is
# what it takes to have a running instance that real kasl agents can reach.
#
# Secrets come from a .env file next to this one, which is never committed:
#
#   POSTGRES_PASSWORD=...
#   KASL_AGENTS=employee@example.com:...
services:
  db:
    image: postgres:18-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: kasl
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: kasl
    volumes:
      # postgres:18+ keeps data in a version-specific subdirectory; the mount
      # sits one level up or the entrypoint refuses to start.
      - db-data:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U kasl -d kasl"]
      interval: 5s
      timeout: 3s
      retries: 10
    # No published port: only the server talks to it, over the private network.

  server:
    build: .
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://kasl:${POSTGRES_PASSWORD}@db:5432/kasl
      KASL_SERVER_ADDR: 0.0.0.0:8080
      # Agent tokens, until the admin UI issues them. Empty is valid: the
      # agents already in the database keep working.
      KASL_AGENTS: ${KASL_AGENTS:-}
      # First administrator, as `email:password`. Empty once the account exists:
      # there is no reason to keep a password in the environment forever.
      KASL_ADMIN: ${KASL_ADMIN:-}
      # This stand is reached over plain http, where a Secure cookie would be
      # dropped by the browser and login would appear to do nothing.
      KASL_SECURE_COOKIES: ${KASL_SECURE_COOKIES:-false}
      # Upload bounds. The defaults suit a team of agents backfilling a month;
      # raise them only for a deliberately larger import.
      KASL_MAX_BATCH_DAYS: ${KASL_MAX_BATCH_DAYS:-31}
      KASL_MAX_BODY_BYTES: ${KASL_MAX_BODY_BYTES:-4194304}
      RUST_LOG: ${RUST_LOG:-kasl_server=info,tower_http=info}
    ports:
      # 8082 on the host: 8080 and 8081 are taken by the other services on this
      # machine. The port is the stand's address, so it is not a default to
      # change lightly.
      - "${KASL_SERVER_PORT:-8082}:8080"

volumes:
  db-data: