Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

XMAS-App

pipeline status Latest Release

The XLeistelle model-driven application schema app is a Python web application to edit and create data according to geo-spatial standards of the XLeistelle, e.g. XPlanung, XTrasse. It is based on NiceGUI for a graphical user interface and xmas-core for the data model and respective functionality.

While it could be advanced to a standalone application, its current focus is integration in QGIS to provide attribute forms etc.

Features

  • Render attribute forms for features.
  • Create and edit features in combination with the corresponding QGIS Plugin.
  • Import GML documents into a database.
  • Export GML, JSON-FG or GPKG files from a database.
  • Delete plans from a database.
  • Display and edit relations of plan objects in a tree view.

Installation

Container Image

Container images are available in the registry. They are built and signed by the pipeline and tagged per release version and per integration branch (dev-amd64); there is no latest tag. See Running via Docker Compose for configuration.

Python >= v3.11

GDAL and its Python bindings are required, so you need to make sure the GDAL system library and Python package versions match. QGIS installations come with GDAL and a Python environment that can readily be used to install the app.

Install with pip, e.g. via OSGeo4W Shell:

pip install xmas-app

Pixi

This project uses Pixi for package management. To install this repo with a self-contained environment, run

git clone https://gitlab.opencode.de/xleitstelle/xmas-app.git
cd xmas-app
pixi install

Running

Preconditions

A Postgres DB with PostGIS extension and adequate permissions for the used role. Missing tables are generated on initialization.

A database created by xplan-tools (xmas-core < 2.0) cannot be upgraded: startup reports SchemaOutdatedError. Start with a fresh database instead, with the PostGIS extension enabled and PGDATABASE pointing at it, and re-import the plans through the plan manager. The app's own tables live in the same schema as the plans, so nothing of them carries over: organisations are created again on first login, plans are attributed to whoever imports them, and attachments have to be uploaded again.

Plans written straight into the database, for example with xmas convert, get no authorization row: the app does not list them, and opening one gives a 404.

Environment Configuration

Copy the example configuration and adjust values as needed:

cp .env.example .env

.env.example documents all available variables with inline comments, including database connection, app settings, and authentication. See that file as the canonical reference.

Important:

  • Do not use the example values in production
  • All credentials and tokens must be set explicitly
  • The application will fail to start if required variables are missing

Running via Docker Compose

docker compose -f compose.yaml up -d

This spins up a complete XMAS-App stack including a PostGIS backend and Keycloak. Configure the stack via .env — see .env.example for the container-mode database settings (PGHOST=postgres, PGPORT=5432).

Optional: testing ROOT_PATH sub-path deployments

To verify running the app under a URL sub-path (so several instances can share one domain), start the reverse-proxy service via its compose profile:

ROOT_PATH=/test docker compose -f compose.yaml --profile proxy up -d

This adds an nginx container (see nginx/default.conf.template) that strips ROOT_PATH before forwarding to xmas-app, mirroring a real front-facing proxy. Browse http://localhost:${PROXY_PORT:-8000}/test/ — not xmas-app's own port directly, since ROOT_PATH only affects URL generation, not routing; see xmas_app/core/settings.py for the setting itself.

Running Locally (host machine)

First activate the Pixi shell:

pixi shell

Then run:

xmas-app

Use the local-mode database settings in .env (PGHOST=127.0.0.1, PGPORT=15432).

Optional: batch import test data

Test data can be downloaded from https://gitlab.opencode.de/xleitstelle/xplanung/testdaten, e.g. a ZIP archive with BP_Plans in v 6.0.

To import all .gml files at once, extract the archive and use the commands below. They write the plans straight into the database, without the authorization rows the app needs, so the app does not show them; for plans to work with in the app, import them through the plan manager.

Linux / macOS / WSL:

for f in <path>/bp/*.gml; do
  xmas convert "$f" postgresql://postgres:postgres@localhost:55432/postgres
done

Windows:

for %f in (*.gml) do xmas convert "%f" postgresql://postgres:postgres@127.0.0.1:55432/postgres

Authentication

The application supports two authentication modes, controlled by the AUTH_MODE environment variable.

Static token

Maps a bearer token to an identity record the operator writes. It is used for local development, for the xmas-plugin CI pipeline (which cannot run the interactive OAuth2/PKCE flow headlessly), and for self-hosted installations that do not run an identity provider. See Static tokens in production before using it for the last of these.

Set AUTH_MODE=static in .env.

Requests must include:

Authorization: Bearer <token>

Each token maps to a full identity record via the AUTH_TOKENS environment variable (JSON). The record stands in for the claims a Keycloak access token would otherwise carry:

AUTH_TOKENS={"<generated-token>": {"user_name": "test-user", "user_id": "41a74c7b-dea8-42c2-bd15-9b88012b10d7", "org_id": "25459bec-0f6e-4f3a-a3f4-3b3e6a2a5d11", "email": "test@example.org", "role": "editor"}}
Field Required Meaning
user_name no Human-readable label for whoever edits the env var (not consumed by the app)
user_id yes Subject identifier; stands in for the Keycloak sub. Any string, see below. Persisted as created_by
org_id no* UUID of the user's org. A null or absent value marks a grant-scoped external invitee
email no Display / audit only. Not used as an identity key
role no One of admin, editor, viewer. Absent or null is treated as no role, which denies writes

*org_id is optional syntactically, but null means the user is treated as an external invitee (grant-scoped only) and will be denied org-scoped plan access. For a normal dev user, set a real org UUID. When AUTH_MODE=static, AUTH_TOKENS is required, startup fails loudly if it is missing or malformed.

user_id is not required to be a UUID. A Keycloak sub happens to be one, but OIDC defines the subject as an opaque string, so the columns storing it are text and any stable, unique-per-deployment string works here (ADR-0005, Appendix A). Records are parsed into AuthenticatedUser, the same type the Keycloak path produces.

See .env.example for the full format.

Static tokens in production

Static mode is supported for self-hosted installations. It is not suitable for an installation reachable from the public internet, because it lacks what that needs:

  • No expiry. A token is valid until it is removed from the configuration.
  • No revocation short of editing the configuration and restarting.
  • No signature. The token is the secret itself, so anyone who sees it in transit, in a log, in a shell history or in a backup of .env holds the credential.
  • No rate limiting. Guessing tokens is slowed by nothing but the network.
  • Identity and role are whatever the operator wrote into AUTH_TOKENS.

How an installation is exposed is the operating organisation's decision, and so is acting on the recommendations below (ADR-0007). An installation that has to be reachable from the internet should use Keycloak.

  • Length. With APP_MODE=prod, the default, startup fails if any token is shorter than 32 characters. Generate one with python -c "import secrets; print(secrets.token_urlsafe(32))".
  • One token per person. Recommended, not enforced. With one shared token, every action is attributed to that token's identity, so the record shows the organisation rather than the person.
  • Storage. Treat AUTH_TOKENS like a password: restrict who can read .env, and keep tokens out of version control, tickets and chat.
  • One place. Set AUTH_TOKENS either in the environment or in .env, not both. The two are merged, so a token removed from one stays valid while it is still in the other.
  • Rotation. Edit AUTH_TOKENS and restart. That is the whole mechanism; there is no expiry and no revocation list.

At startup in prod, the application logs that static mode is active and how many tokens are configured, never the tokens themselves.

Validates RS256-signed access tokens issued by a Keycloak realm via the JWKS endpoint. This is the mode for any installation reachable from the public internet.

Set AUTH_MODE=keycloak in .env. See .env.example for the required Keycloak variables (KEYCLOAK_ISSUER, KEYCLOAK_AUDIENCE, KEYCLOAK_JWKS_URL).

Note: KEYCLOAK_ISSUER must match the iss claim in the token exactly — including scheme, host, and port. KEYCLOAK_JWKS_URL is the address of the Keycloak service as seen from inside the Docker network, which may differ from the issuer URL seen by clients.

The local Keycloak instance is started automatically via compose.yaml. A development realm (realm-xmas-dev.json) is imported on first start, including a test user, the xmas-plugin client for the OAuth2/PKCE flow, and the required audience mapper.

For Keycloak setup, Identity Provider federation (e.g. Microsoft Entra ID / SSO), and onboarding documentation, see keycloak/README.md.

Running Tests Locally

Integration tests use testcontainers to start a temporary PostGIS database. Make sure Docker is running before executing the tests.

The test database is seeded from a git submodule ./testdaten/ (https://gitlab.opencode.de/xleitstelle/xplanung/testdaten).

Initialize the submodule first:

git submodule update --init --recursive

Then run:

pixi run pytest tests

Agentic Coding

See .devcontainer/README.md for Claude Code setup.

Troubleshooting

PyJWKClientConnectionError

The plugin can't reach the app, and the app's container log shows PyJWKClientConnectionError. Verify that the env variables are being passed correctly and that KEYCLOAK_JWKS_URL has the right value. Make sure proxy settings aren't blocking Keycloak: add keycloak to the NO_PROXY list if not yet set in the compose file.

License

The code in this repository is licensed under the EUPL-1.2-or-later

© XLeitstelle, 2025

Metadata

Release files for xmas-app 1.0.0.dev3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xmas-app 1.0.0.dev3
File Size Uploaded Provenance Actions
xmas_app-1.0.0.dev3.tar.gz 139.1 kB Without provenance Details for xmas_app-1.0.0.dev3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for xmas-app 1.0.0.dev3
File Interpreter ABI Platform Actions
xmas_app-1.0.0.dev3-py3-none-any.whl Python 3 none any Details for xmas_app-1.0.0.dev3-py3-none-any.whl

Total release size: 313.8 kB

Release files / xmas_app-1.0.0.dev3.tar.gz

Download URL xmas_app-1.0.0.dev3.tar.gz
Size 139.1 kB
Tags Source
SHA-256 checksum
How to use checksums
00720cfc1b4f903702ee73728ef915705f82d6b98668c8e458b8c5b4a43839eb
BLAKE2b-256 checksum
How to use checksums
98be5e4e071d6a1b81e539c76e53f90c361b557dbe048d7dff44133e931970ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / xmas_app-1.0.0.dev3-py3-none-any.whl

Download URL xmas_app-1.0.0.dev3-py3-none-any.whl
Size 174.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
44ba1c900c6e50a522d845d5079f35f380b7390a9f77ee8d68111fafc402acb1
BLAKE2b-256 checksum
How to use checksums
a733e1420d3e06118811482f035b0aa9cd3d1c694e34074f9b27fc0b4b256d0a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for xmas-app 1.0.0.dev3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page