Files
ImpTune/.planning/research/STACK.md
T
kawaandClaude Sonnet 4.6 59c384ed13 docs: complete project research
Adds STACK, FEATURES, ARCHITECTURE, PITFALLS, and SUMMARY research files
covering the full ImpTune technology stack, feature set, architecture
patterns, and critical pitfalls for the printer deployment package generator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:34:25 +02:00

12 KiB

Stack Research

Domain: Self-hosted single-container internal tool — Windows printer deployment package generator Researched: 2026-04-10 Confidence: HIGH (core stack), MEDIUM (.intunewin reimplementation specifics)


Core Technologies

Technology Version Purpose Why Recommended
Python 3.12 Runtime LTS-stable, broad library support, zipfile/cryptography stdlib covers .intunewin needs without C extensions. 3.13 is fine too; avoid 3.11 and below (FastAPI 0.130+ requires 3.10+ but 3.12 is the sweet spot for Docker image size vs feature parity).
FastAPI 0.115.x (latest stable) HTTP framework + API De-facto standard for Python internal tools in 2025. Async-capable, Pydantic validation built-in, native file upload/download support, StreamingResponse for generated archives. Simpler than Django for this scope; more structured than Flask.
Uvicorn 0.30.x ASGI server FastAPI's recommended server. Handles subprocess management internally — Gunicorn is not needed for a single-container internal tool with no concurrency requirements.
Jinja2 3.1.x Server-side HTML templating Ships with FastAPI's template support. No build step, no Node. HTMX+Jinja2 renders the full UI from the server.
HTMX 2.0.x (CDN) Dynamic UI without JavaScript SPA Handles partial page updates (form submissions, driver list refresh, package generation progress) with zero build tooling. Ideal for CRUD-heavy internal tools. Single <script> tag.
Alpine.js 3.x (CDN) Client-side UI state Complements HTMX: dropdowns, modals, toggle states, file-input previews. Replaces React/Vue for this scope. No npm, no bundler.
SQLite (stdlib) 3.x (bundled) Persistence: driver records, printer configs No external database process. Python's built-in sqlite3 module is sufficient for sync operations. Single-writer model is not a concern for a single-user internal tool. Mount via Docker named volume.
Tailwind CSS 4.x (CDN Play) Utility-first CSS For an internal tool with no public users, the CDN Play script is acceptable. It avoids a Node.js build step inside the Docker image. If CSS size ever matters, switch to the standalone Tailwind CLI binary (no Node required).

Supporting Libraries

Library Version Purpose When to Use
python-multipart 0.0.9 Multipart file upload parsing Required by FastAPI for UploadFile. Always install alongside FastAPI when handling file uploads.
pycryptodome 3.20.x AES-256-CBC + HMAC-SHA256 for .intunewin encryption The .intunewin inner package is encrypted with AES-256-CBC; the HMAC-SHA256 digest is written to Detection.xml. Use pycryptodome (not the deprecated pycrypto).
lxml or xml.etree.ElementTree (stdlib) stdlib Generate Detection.xml metadata Detection.xml is simple enough for stdlib ElementTree. Only reach for lxml if namespaces become complex.
aiofiles 23.x Async file I/O Used with FastAPI's StreamingResponse when streaming generated ZIPs to the browser without loading the full archive into memory.
python-dotenv 1.0.x Environment-based configuration Allows Docker-level config overrides (data directory, port, base URL) without rebuilding the image.
peewee 3.17.x ORM for SQLite Lightweight sync ORM — perfectly matched to SQLite's single-writer model. Avoids the async overhead of SQLModel/aiosqlite, which provides no benefit with SQLite. Use only if raw sqlite3 becomes unwieldy across multiple tables.

Development Tools

Tool Purpose Notes
Docker (python:3.12-slim-bookworm) Container base image Slim variant keeps image under 200 MB. Bookworm (Debian 12) has modern glibc. Do NOT use Alpine Linux — pycryptodome and other C-extension wheels often lack musl-compatible builds, causing silent failures.
uvicorn --reload Dev server with hot reload Run locally during development. In Docker, use CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] without --reload.
Docker named volume Driver + SQLite persistence Mount /data as a named volume. Store driver ZIPs and the SQLite file there. Never store state in the container filesystem.

Installation

# Core runtime
pip install fastapi==0.115.* uvicorn[standard]==0.30.* jinja2==3.1.* python-multipart==0.0.9

# File generation and encryption
pip install pycryptodome==3.20.* aiofiles==23.*

# Configuration + optional ORM
pip install python-dotenv==1.0.* peewee==3.17.*

# Dev only
pip install httpx pytest pytest-asyncio

Tailwind CSS and HTMX are loaded from CDN in templates — no npm install.


Alternatives Considered

Recommended Alternative When to Use Alternative
FastAPI + Jinja2 + HTMX Django + django-htmx If the project grows to need Django admin, multi-tenant auth, or complex ORM migrations. Overkill for this scope.
FastAPI + Jinja2 + HTMX FastAPI + React/Vue SPA If the UI needs real-time collaborative editing or complex client-side state (drag-and-drop ordering, offline mode). Not needed here.
Python (FastAPI) Go (Gin/Chi) If single binary with no runtime dependency is the top priority. Go produces a smaller Docker image but adds complexity for the .intunewin crypto/zip logic that Python stdlib already handles.
pycryptodome cryptography (PyCA) cryptography is fine too and arguably better maintained. Either works for AES-CBC. Prefer cryptography if you want FIPS-like assurance; pycryptodome is simpler to use for this exact pattern (CBC + HMAC).
SQLite (builtin / peewee) PostgreSQL If multi-user write concurrency is ever needed. Not the case here.
Tailwind CDN Tailwind standalone CLI Use the standalone CLI binary (no Node) if you want purged, production-sized CSS. Drop the tailwindcss binary into the Docker image at build time. Adds ~10 MB to the image but removes CDN dependency.
python:3.12-slim-bookworm python:3.12-alpine Alpine breaks C-extension wheels. Slim Debian is the correct base for any project using pycryptodome or similar.

What NOT to Use

Avoid Why Use Instead
IntuneWinAppUtil.exe inside Docker It is a Windows PE binary. It will not run in a Linux container. Even Wine would add hundreds of MB and introduce instability. Reimplement the format in Python: zipfile (stdlib) for inner ZIP with ZIP_STORED, pycryptodome for AES-256-CBC encryption, xml.etree.ElementTree for Detection.xml. The format is fully documented by reverse engineering (svrooij.io).
Alpine Linux base image musl libc breaks pre-built wheels for pycryptodome and other C-extension packages, causing pip to fall back to source builds or fail silently. python:3.12-slim-bookworm — Debian-based, glibc, pre-built wheels always work.
Celery / Redis / task queue Massively overengineered for a single-user internal tool. .intunewin generation takes < 5 seconds per package. FastAPI BackgroundTasks is sufficient for post-response cleanup; synchronous generation on the request thread is fine.
SQLAlchemy async (aiosqlite) Async gives zero throughput benefit with SQLite (single-writer lock). Adds complexity for no gain. Synchronous sqlite3 stdlib or peewee. Run database calls synchronously inside asyncio.run_in_executor if truly needed.
React / Vue / Next.js Requires a Node.js build pipeline in the Docker image, a separate frontend build stage, and a JS bundle. Unnecessary complexity for an internal CRUD tool. HTMX + Alpine.js served from Jinja2 templates. No build step.
PyCrypto (original) Unmaintained since 2012. Known vulnerabilities. pycryptodome — a maintained drop-in replacement.

Stack Patterns by Variant

If .intunewin generation needs to stay on the request thread (simplest path):

  • Generate the .intunewin file synchronously, stream it with FileResponse or StreamingResponse, then delete the temp file with BackgroundTasks.
  • No async file I/O needed for packages under ~500 MB.

If driver ZIPs are very large (>500 MB) and upload times block Uvicorn:

  • Use aiofiles for async reads during upload processing.
  • Uvicorn is single-threaded by default in development; add --workers 2 in production Dockerfile if needed (though unlikely for an internal tool).

If Tailwind CDN becomes a problem (slow intranet, offline use):

  • Download the Tailwind standalone CLI (tailwindcss-linux-x64) into the Docker image during build.
  • Run tailwindcss -i input.css -o static/output.css --minify as a Dockerfile RUN step.
  • Serve the generated CSS as a static file. Zero Node.js required.

.intunewin Reimplementation (Critical Detail)

The official IntuneWinAppUtil.exe is Windows-only. The format is well-documented through reverse engineering:

  1. Inner ZIP — compress source folder using zipfile.ZipFile(..., compression=ZIP_DEFLATED) (note: unlike the outer ZIP, the inner package uses DEFLATE not STORED)
  2. Encrypt — AES-256-CBC with a random 32-byte key and 32-byte IV; prepend HMAC-SHA256 (32 bytes) then IV (32 bytes) to the ciphertext
  3. Detection.xml — XML file with base64-encoded EncryptionKey, InitializationVector, Mac (HMAC), FileDigest (SHA256 of plaintext), FileDigestAlgorithm ("SHA256"), and ProfileIdentifier ("ProfileVersion1")
  4. Outer ZIP — ZIP_STORED containing IntuneWinPackage/Contents/IntunePackage.intunewin (the encrypted blob) and IntuneWinPackage/Metadata/Detection.xml

Reference implementation: svrooij.io — Creating IntuneWin files with C# — the logic is language-agnostic and Python stdlib + pycryptodome covers all requirements. Confidence: MEDIUM (based on reverse-engineering documentation; validate against a real Intune upload during Phase 1).


Version Compatibility

Package Compatible With Notes
fastapi 0.115.x pydantic 2.x FastAPI 0.115+ requires Pydantic v2. Do not mix with Pydantic v1.
pycryptodome 3.20.x Python 3.12, 3.13 Use pycryptodome not pycryptodomex (different package name, same code, different import path). Import as from Crypto.Cipher import AES.
uvicorn 0.30.x fastapi 0.115.x Compatible. Use uvicorn[standard] to include uvloop and httptools for better performance.
peewee 3.17.x Python 3.12 Sync only. Does not conflict with FastAPI's async model — call peewee from sync functions or run_in_executor.
Tailwind CDN v4 Any browser Play CDN is development-only per Tailwind docs. For production isolation, use standalone CLI instead.

Sources


Stack research for: ImpTune — printer deployment package generator Researched: 2026-04-10