1
0

admin-manual.md 11 KB

Sprint Planner — Administrator Manual

This guide walks an administrator through the initial configuration and the day-to-day operation of the Sprint Planner web application. It assumes a Linux host with Docker and Docker Compose v2 installed.

For architecture, schema, and feature history, see SPEC.md in the repository root.


1. Prerequisites

  • A Linux (or macOS / Windows with WSL2) host with:
    • Docker Engine 24+ and the docker compose plugin.
    • Outbound HTTPS access during build (Composer + npm pull dependencies).
  • One of the following for sign-in:
    • Microsoft Entra ID tenant with permission to register an application (preferred for production), or
    • Local admin credentials (email + password) — useful for initial setup, on-prem-only deployments, or development.
  • A directory on the host to hold the persistent SQLite database and PHP session files. The compose file mounts ./data next to docker-compose.yml; nothing else is needed for storage.

2. Getting the code

git clone <repository-url> sprint_planer_web
cd sprint_planer_web

All commands in the rest of this manual are run from the repository root.


3. Initial configuration of .env

The application reads its configuration from a single .env file in the repository root. Start by copying the template:

cp .env.example .env

Open .env in an editor and fill in the values described below. The file must be readable by the user that runs docker compose; on a shared host restrict it with chmod 600 .env.

3.1 Entra ID (Microsoft 365) — production sign-in

If your team will sign in with their Microsoft 365 / Entra accounts:

Variable What to put there
ENTRA_TENANT_ID Directory (tenant) ID — a GUID, visible on the Entra "Overview" page.
ENTRA_CLIENT_ID Application (client) ID of the app registration.
ENTRA_CLIENT_SECRET A client secret you generated for the app registration. Treat as sensitive.

In the Entra app registration, configure:

  • Redirect URI (Web platform): {APP_BASE_URL}/auth/callback, e.g. https://sprint.example.com/auth/callback.
  • ID tokens issued from the auth endpoint: enabled.
  • Supported account types: usually "Accounts in this organisational directory only".
  • API permissions: openid, profile, email (delegated, default for OIDC). Grant admin consent if your tenant requires it.

Leave Entra fields blank if you only intend to use the local-admin fallback. The OIDC sign-in button still appears, but clicking it will fail until the variables are populated — keep LOCAL_ADMIN_EMAIL / LOCAL_ADMIN_PASSWORD filled if you skip Entra entirely.

3.2 Application URL

APP_BASE_URL=https://sprint.example.com

The base URL the application is reachable at, without trailing slash. This is used to construct the OIDC redirect URI and must match exactly what is registered in Entra. For local testing the default http://localhost:8080 is fine — but the compose file ships the app on port 8088, so use http://localhost:8088 if you have not edited docker-compose.yml.

3.3 Session secret

SESSION_SECRET=<random string, at least 32 bytes>

Used to derive the session cookie name and CSRF tokens. Generate one with either of:

openssl rand -hex 32
# or
head -c 48 /dev/urandom | base64

Rotating this value invalidates all active sessions — users will need to sign in again, but no data is lost.

3.4 Database and session storage paths

DB_PATH=/var/www/data/app.sqlite
SESSION_PATH=/var/www/data/sessions

Leave the defaults unless you are also remapping the volume. The parent directory /var/www/data is the volume mount point inside the container and corresponds to ./data/ on the host.

3.5 Environment mode

APP_ENV=production

production silences verbose PHP errors. Any other value (e.g. dev) turns them on — useful when troubleshooting in a non-public install.

3.6 Local admin fallback (optional)

LOCAL_ADMIN_EMAIL=admin@example.com
LOCAL_ADMIN_PASSWORD=<a long passphrase>
LOCAL_ADMIN_NAME=Local Admin

If both LOCAL_ADMIN_EMAIL and LOCAL_ADMIN_PASSWORD are set, a second sign-in form appears at /auth/local. The password is compared in plain text against the env value — so:

  • Pick a long, unique passphrase you do not reuse anywhere else.
  • Make sure .env is chmod 600 and owned by a single trusted user.
  • Rotate the passphrase by editing .env and restarting the container.

The local-admin user is recorded in the database as entra_oid = "local:<email>" with is_admin = 1. If the users table is empty at the moment of the first successful login (OIDC or local), that user is auto-promoted to admin and an audit entry of type BOOTSTRAP_ADMIN is recorded. This is how the very first administrator is created — there is no separate "create admin" step.

To disable the fallback later, blank both variables and restart.


4. Running the containers

The project ships a multi-stage Dockerfile (Node-based Tailwind build → PHP 8.3 + Apache runtime) and a docker-compose.yml that wires the container to the host.

4.1 First start

docker compose up --build

What happens:

  1. Compose builds the image. The Node stage compiles assets/css/input.css into public/assets/css/app.css. The PHP stage installs Composer dependencies and copies the application source into /var/www/html.
  2. The container starts Apache on port 80, exposed on the host as port 8088 (see docker-compose.yml).
  3. On the first request, the migration runner creates app.sqlite inside the volume and applies all migrations under migrations/.

Open http://<host>:8088. If you used the local-admin fallback, sign in at /auth/local. Otherwise click the Entra sign-in CTA on /.

4.2 Running detached (recommended for production)

docker compose up -d --build

Logs are then read with:

docker compose logs -f

Stopping the stack:

docker compose down

down keeps the ./data volume intact — your database and sessions survive. To wipe and start over, stop the stack and remove ./data manually (see §5.4).

4.3 Rebuilding after changes

You only need to rebuild the image when the build inputs change:

  • Dockerfile, composer.json / composer.lock, or package.json / package-lock.json
  • Tailwind sources under assets/css/ or any view file that introduces new utility classes (the css-builder stage scans the views).

    docker compose build --no-cache && docker compose up -d
    

For pure PHP / view edits without a rebuild, restart the container:

docker compose restart

4.4 Health check

The application exposes an unauthenticated GET /healthz route that returns 200 OK with a small JSON body. Use it from a load balancer or uptime monitor:

curl -fsS http://<host>:8088/healthz

4.5 Changing the host port

Edit the ports line in docker-compose.yml:

ports:
  - "8088:80"

Change 8088 to any free port on the host. After editing, docker compose up -d is enough — no rebuild required. Update APP_BASE_URL in .env and the redirect URI in Entra to match.


5. Day-to-day administration

5.1 Promoting and demoting administrators

Once the bootstrap admin is signed in, additional administrators are managed through the web UI:

  1. Open the hamburger menu → Users.
  2. Toggle the admin checkbox next to a user and confirm.

Guardrails enforced server-side:

  • An administrator cannot demote themselves while signed in (avoids locking yourself out).
  • The last remaining administrator cannot be demoted (avoids locking the org out).

If both guardrails fire at once — i.e. you are the only admin — the only safe escape is to sign in via the local-admin fallback (if configured) and promote a second account first.

5.2 Backups

Everything that needs a backup lives under ./data/:

  • app.sqlite — the database, including audit log.
  • sessions/ — active PHP session files. Losing these only forces re-login, no data loss.

A safe nightly backup is a straight file copy while the container is running. SQLite handles concurrent reads fine; for a guaranteed consistent snapshot, prefer sqlite3 .backup:

docker compose exec app sqlite3 /var/www/data/app.sqlite \
  ".backup /var/www/data/app.sqlite.bak"
cp ./data/app.sqlite.bak /your/offsite/backup/location/

5.3 Inspecting the audit log

Every create / update / delete on a domain entity is recorded, plus sign-in events. Admins can browse and filter the log via the hamburger menu → Audit log, with filters by entity type, action, user, and date range, and collapsible JSON diffs per row.

5.4 Resetting the application

To wipe all data and start from a blank slate:

docker compose down
rm -rf ./data
docker compose up -d

Migrations run again on the next request and you are back to "no users, first sign-in becomes admin".

5.5 Updating to a new release

git pull
docker compose build --no-cache
docker compose up -d

Schema migrations under migrations/ run automatically on the next request after restart. Always take a backup of ./data/app.sqlite before pulling.


6. Troubleshooting

Symptom Likely cause Fix
/auth/login returns "redirect URI mismatch" The redirect URI registered in Entra does not equal {APP_BASE_URL}/auth/callback. Update either APP_BASE_URL in .env or the Entra app registration.
/auth/local returns 404 LOCAL_ADMIN_EMAIL or LOCAL_ADMIN_PASSWORD is blank. Set both, then docker compose restart.
Sign-in succeeds but every page shows "not authorised" The user has no is_admin flag and is trying to access an admin-only page. Promote them via Users (logged in as another admin).
Container restarts in a loop Most often a malformed .env line or a permission problem on ./data. docker compose logs will show the PHP fatal. Check that ./data is writable by the www-data user inside the container (uid 33 on the Apache image).
CSS looks broken / unstyled The Node build stage was skipped or used a stale layer. docker compose build --no-cache and restart.
OIDC works but PHP 8.4 deprecation warnings appear in logs Known upstream issue in jumbojett/openid-connect-php. Harmless on PHP 8.3 (the shipped runtime); ignore until upstream releases a fix.

For anything else, check the audit log first — most user-facing errors also leave a trail there with the exact request that failed.


7. Where to go next

  • SPEC.md — full specification, schema, route list, and build phase history. Read this if you intend to modify the code.
  • ACCEPTANCE.md — manual acceptance checklist used to validate releases.