# Installation, backup, upgrade and recovery ## Supported local baseline The qualified baseline is Linux, Python 3.13, local durable SQLite and loopback Gunicorn. Install the exact wheel hashes in requirements.lock. The development lock adds pytest; it is not required at runtime. Dependencies need downloading during installation unless you maintain a separately verified wheelhouse. The app serves Plotly locally and requires no CDN. Keep PT_DATA_DIR owned by the application user with mode0700. The generated session.key must remain an owned regular0600 file. SQLite and backups contain business data and require buyer disk encryption, access controls and retention policy. PT_DATABASE_URL optionally selects a buyer SQLAlchemy PostgreSQL URL using psycopg; PostgreSQL deployment, TLS, database permissions and native backup/restore require buyer qualification. This package's manage.py backup/restore supports SQLite only. Do not point the app at an unrelated existing database or an original customer database. ## Linux Docker ```sh docker compose build docker compose up -d ``` The pinned Python image and hashed Python wheels are used. This Compose file intentionally uses Linux host networking while Gunicorn binds 127.0.0.1:5057; requests must arrive through loopback. Docker Desktop and bridged published ports are not a qualified equivalent. The named supply-data volume retains the database and session key. The process runs UID10001, with a read-only root filesystem, limited tmpfs, dropped capabilities and bounded resources. Preserve that volume during upgrades; do not run `down -v` on real data. ## Buyer HTTPS identity gateway For remote users the buyer must deploy and qualify an authenticating gateway on this same host. Set PT_ENV=production, PT_AUTH_MODE=proxy, exact PT_PUBLIC_ORIGIN=https://your-owned-host, PT_TRUSTED_HOSTS=your-owned-host and a private PT_GATEWAY_TOKEN of at least32 characters. Configure the gateway to reject unauthenticated requests, remove incoming X-PT-* headers and inject verified X-PT-Subject (stable 1–160 ASCII identity), X-PT-Role (user or admin), optional X-PT-Name, and X-PT-Gateway-Token. Forward the original Host, browser Origin and cookies. The app ignores arbitrary forwarded peer claims. Use the same subject for a user's future logins: owner IDs derive from that stable subject, not email-domain guesses. Changing it intentionally creates a different data owner. Do not place the shared token in browser JavaScript. TLS and real gateway login are deployment responsibilities, not bundled SaaS authentication. ## Backup and restore drill Stop application writes first. Keep the source edition and dependency lock alongside an encrypted off-host backup. Run from the source directory with the same PT_DATA_DIR: ```sh .venv/bin/python manage.py backup --output /private/backups/supply-unique.sqlite3 .venv/bin/python manage.py restore --input /private/backups/supply-unique.sqlite3 --target /private/restore-drill/supply.sqlite3 ``` Create the destination parent privately before restoring. Restore refuses an existing target and verifies the expected two-table schema and integrity; it does not overwrite live data. Separately protect session.key (or your externally supplied PT_SECRET_KEY). Losing a session key logs users out; keep stable configured identity subjects to retain ownership. Do not print keys in incident reports. Point a stopped app at the fresh restored directory, restore its protected key separately if appropriate, start it, then verify selected datasets, profiles, scores and history before switching users back. Do not import untrusted backup databases. ## Upgrade and rollback 1. Record the current source archive checksum and settings; stop writes and take a backup plus a protected key copy. 2. Install the new edition in a separate directory/venv or build a new image. This1.1.0 edition uses the original two-table record schema; no destructive migration is supplied. Future editions require their own migration instructions. 3. Restore a copy into an isolated data directory and test the new edition there first. Verify totals, saved workspace history and identity mappings. Only then start it against your production volume. 4. To roll back, stop the new app and restore the pre-upgrade backup to a fresh directory; run the recorded prior edition and its lock. Never assume an older binary can read future data safely. Preserve post-upgrade writes separately for reconciliation instead of silently deleting them. No cloud service, ERP account, customer connector or model download is performed by these procedures. A technical Python team is recommended for gateway deployment, schema changes and recovery. ## Bounded model requests and proxy deadlines Use `gunicorn --config gunicorn.conf.py main:app` for both native and container launches. `PT_MODEL_DEADLINE_SECONDS` defaults to90seconds and accepts integers1–180. It bounds the complete private model child, including startup, input transfer, connection and response reading. At timeout the app kills/reaps only that owned request process/group; it does not stop your model server, retry, switch providers or claim remote cancellation. A remote inference/service may continue after disconnect; do not assume charges/work were rolled back. Core workflows remain available after a failed consultation. `PT_MODEL_MAX_OUTPUT_TOKENS` defaults512, allowed32–2048. Native Ollama uses `options.num_predict`; compatible chat completion uses standard `max_tokens` without native Ollama fields. `PT_OLLAMA_THINK=false` is the native default for concise advisory answers; use `default` to omit that field or `true` only when your selected model supports it. Discover supported thinking controls from your own Ollama `/api/show`; unsupported settings must be corrected explicitly, never automatically retried. These budgets do not guarantee model quality or provider compatibility. The supplied Gunicorn worker timeout is the configured hard deadline plus15seconds (105seconds by default). Configure the authenticating HTTPS gateway/client timeout to at least deadline+30seconds (120seconds by default); retain a bounded upper limit and enough capacity for two workers. A lower upstream timeout can disconnect before the app's error response; a longer proxy timeout does not extend the application's hard budget. Do not put credentials or bodies in process arguments, proxy/access logs or support screenshots. Model input/credentials travel to the isolated child only through private pipes; its stderr is discarded and response is bounded. Protocol references: [Ollama chat](https://docs.ollama.com/api/chat), [thinking controls](https://docs.ollama.com/capabilities/thinking), [generation token budget](https://docs.ollama.com/modelfile), [OpenAI-compatible supported fields](https://docs.ollama.com/api/openai-compatibility). Read2026-10-25; actual buyer server/model support must still be verified. ## Docker SQLite backup and genuinely fresh restore Use your actual existing Compose project name and exact accepted source/image. Quiesce application writes; a backup is private business data. These commands keep maintenance processes nonroot and avoid making a host-user600file readable to containerUID10001 through a bind mount: ```sh docker compose -p supply exec -T supply python manage.py backup --output /data/reviewed-backup.sqlite3 docker compose -p supply cp supply:/data/reviewed-backup.sqlite3 /private/supply-backup.sqlite3 chmod 600 /private/supply-backup.sqlite3 sha256sum /private/supply-backup.sqlite3 docker compose -p supply down # A DIFFERENT, unused project supplies a fresh private named volume. docker compose -p supply-recovery run --rm -T supply sh -c 'umask 077; cat > /data/restore-input.sqlite3' < /private/supply-backup.sqlite3 docker compose -p supply-recovery run --rm -T supply python manage.py restore --input /data/restore-input.sqlite3 --target /data/supply.sqlite3 docker compose -p supply-recovery up -d ``` The recovery project must use the same verified edition and reviewed environment; build that exact source or select the retained accepted image deliberately. It must not attach your live volume. Original installation data remains preserved by `down` without `-v`. Verify the copied backup checksum through your trusted private record before use. The restore target must not exist. Separately protect the session key under your policy; restoring only the SQLite database starts a fresh session key, while stable configured identity subjects preserve ownership. Reauthenticate/refresh CSRF after restart, verify selected dataset totals, shared settings, supplier profile/history and custom KPI records, then accept recovery. Do not treat loopback local mode as a remote authentication service. Remove only explicitly owned disposable test projects/volumes after qualification; never delete business volumes as a routine retry.