Skip to content

Architecture constraints

Technical constraints

ConstraintBackground
Python 3.13All backend packages target Python >= 3.13, < 3.14. This pins the runtime across all services and CI to a single minor version.
Docker Compose as deployment unitThe platform ships as a set of Docker Compose files. No Kubernetes support is provided. This keeps the operational bar low (a two-person IT team can deploy it) but limits horizontal scaling to what Docker Compose supports natively.
x86_64 architectureDocker images target x86_64. ARM-based systems (Apple Silicon, Graviton) are not officially supported. GPU acceleration requires NVIDIA GPUs with CUDA drivers on the host.

Organizational constraints

ConstraintBackground
Open-source distribution modelThe platform is published as open-source software (Apache 2.0 for the runtime + SDK, AGPL-3.0-or-later for the web UI, the multi-tenant administration plane, and backup orchestration — see LICENSES.md for the per-package breakdown) to build trust in regulated Swiss markets. The open-source layers are publicly auditable, which constrains what can be embedded (no proprietary SDKs, no hardcoded credentials, no customer-specific logic in the platform layer).
Swiss data protection law (nDSG) and professional secrecy (Art. 321 StGB)The platform's primary market consists of Swiss public administrations and professionals bound by secrecy obligations (lawyers, doctors, fiduciaries). The architecture must support deployments where no data leaves Switzerland and where the operator can prove this to a regulator. This drives the self-hosted deployment model and PII detection via Presidio.
Data sovereignty as a non-negotiable requirementOrganizations must be able to run the entire platform on their own infrastructure without any external dependency. Air-gapped operation with locally hosted models must be possible. This rules out mandatory SaaS dependencies, phone-home telemetry, or cloud-only features in the platform layer.
Monorepo with scope boundariesAll packages live in a single Git repository (swiss-ai-hub). Direct imports between scopes are prohibited; inter-scope communication goes through swiss_ai_hub.core. This is enforced by PreToolUse hooks during development. The monorepo enables coordinated releases but requires discipline around dependency direction.

Conventions

ConventionEnforcement
Conventional CommitsCommit messages follow the format <type>(<scope>): <subject>. Types: fix, feat, doc, test, chore. Scopes: swiss-ai-hub, iac, ci-cd, bots, dagster, deploy, ui, guards, rag, tracing, workflows. Enforced by CI (semantic-pr workflow).
Branch namingFeature branches: ^([a-z]{2,})\/([a-z-0-9]{2,})$ (e.g., feat/add-auth). No uppercase, no underscores. Enforced by CI (branchlint).
Squash merge to mainAll PRs are squash-merged. Main requires one approval, linear history, and passing checks. Every PR must carry exactly one version label (major, minor, or patch) to control automatic semver bumps.
Type hints on everythingReturn types are mandatory. Parameters use Annotated. Modern syntax: str | None not Optional[str], list[str] not List[str], PEP 695 generics. Enforced by code review and ruff rules.
Pydantic models, never dataclassesAll data structures use Pydantic. Factory classmethods (from_entity, from_request) are preferred over complex constructors.
Async I/O consistentlyAll network, database, and cache operations use async/await. Mixing sync and async I/O in the same code path is not acceptable.
One class per fileFile name must match class name (MyClass in MyClass.py). No multi-class files. No standalone function files; use service classes with @staticmethod or @classmethod.
Controller-Service-Entity separationAPI endpoints follow a three-layer pattern: Controllers handle HTTP routing, Services contain business logic, Entities combine schema definition with repository classmethods (MongoEngine).
No backward compatibility shimsBreaking changes are accepted. No re-exports, renamed aliases, or compatibility wrappers. If something is unused, delete it.
No environment variable defaults in Docker Compose templatesThe Jinja2 compose template must not use ${VAR:-default} syntax. All defaults are defined in .env.dev and .env.prod. This prevents silent misconfiguration where a missing variable falls through to a default that works in development but fails in production.
Ruff for formatting and lintingPython code is formatted with ruff format and linted with ruff check (rules: E, F, UP, I). Runs automatically via PostToolUse hooks on every file edit.
Four-language internationalizationAll user-facing strings support German, English, French, and Italian. Translation files are YAML-based. The LocaleHandler/LocaleString class hierarchy in swiss_ai_hub.core handles locale resolution.
uv as package managerThe monorepo uses uv with workspaces. A single uv.lock at the root and a single .venv at the root. Dependencies are declared in [tool.uv.sources] with { workspace = true } for inter-package references. Edits to uv.lock are blocked by a PreToolUse hook; use uv add/remove/update instead.
License compliance checkingEvery PR triggers license checks across Python packages, Node.js packages, and Docker images. Unapproved licenses block the PR. AGPL components (MinerU) are explicitly excluded via licenses.config.json because they run in isolated containers.

Built with ❤️ in Switzerland 🇨🇭