Перейти к содержанию

Iris — Release & build runbook

Command-by-command for cutting one release: compile → build → sign → publish → installers, for the commercial (detection-only) edition and both world / RU channels. Run on a build host with internet (PyPI/GitHub/PyTorch/HF reachable) + Docker + a GPU is not needed to build. See docs/CODE_PROTECTION.md, COMPLIANCE.md, DISTRIBUTION_AND_UPDATES.md.

0. One-time setup (do once, keep secret)

# Vendor signing key — signs licenses, trials, AND release manifests. NEVER ship the private part.
python3 scripts/gen_vendor_key.py --bits 2048 --out vendor_private.json
#   -> prints the PUBLIC env (IRIS_LICENSE_PUBKEY_N/E) to bake into the image/.env.
Store vendor_private.json offline (HSM/secrets manager). The PUBLIC key ships in the image so the device verifies licenses + manifests offline. (Optional: a separate online issuance key for the license server vs the offline release key — see docs/CODE_PROTECTION.md.)

1. Bump version (3 files + changelog)

# VERSION, pyproject.toml, app/__init__.py -> X.Y.Z ; add a CHANGELOG entry. Then tag.
git tag -a vX.Y.Z -m "Release vX.Y.Z" && git push origin vX.Y.Z

2. Compile the crown-jewel modules (free Nuitka → native .so)

scripts/compile_core.sh          # reid/faces/audit/workers/licensing -> .so ; source .py removed
.venv/bin/pytest -q              # full suite must stay green against the compiled tree

3. Build the sealed, license-clean image

# Apache detector + drop AGPL YOLO; do NOT bundle non-commercial weights (COMPLIANCE.md §5).
python3 scripts/fetch_rtdetr.py --out models/rtdetr.onnx
rm -f models/yolov8n.onnx models/buffalo_l* models/osnet*        # commercial bundle excludes these
# LGPL ffmpeg (multi-stage; see COMPLIANCE.md §5c) instead of Debian GPL ffmpeg.
YT_DLP_VERSION=2025.06.30 MIVOLO_ARCHIVE=<sha> \
  scripts/build_sealed_image.sh ghcr.io/yakden/iris:X.Y.Z dist
#   -> dist/iris-image-*.tar.gz (+ .sha256) for air-gap, and the local image tag for push.
gen_integrity_manifest.py runs inside the Dockerfile build stage and bakes the signed manifest in.

4. Sign the image (Cosign — free, no CA, no sanctions)

cosign sign --yes --bundle dist/iris-image.cosign.bundle ghcr.io/yakden/iris:X.Y.Z
docker push ghcr.io/yakden/iris:X.Y.Z            # online channel; air-gap uses the tar from step 3

5. Publish signed update manifests — BOTH channels

DIGEST=$(docker inspect --format '{{index .RepoDigests 0}}' ghcr.io/yakden/iris:X.Y.Z)
for ch in stable stable-ru; do
  python3 scripts/publish_release.py --key vendor_private.json --version X.Y.Z \
    --channel "$ch" --image "$DIGEST" --min-version 1.40.0 --out manifest-$ch.json
  curl -fsS -X POST "$UPDATE_SERVER/channels/$ch/publish" \
    -H "X-Admin-Token: $UPDATE_ADMIN" -H 'content-type: application/json' --data @manifest-$ch.json
done
Devices then see the update via GET /api/system/update (signature + channel + subscription gated) and the host agent applies it with health-gated rollback (iris_update.sh).

6. Installers (thin host bootstrap; carry no secrets)

IRIS_VERSION=X.Y.Z nfpm package -f packaging/iris.nfpm.yaml -p deb -t dist/   # Linux .deb
IRIS_VERSION=X.Y.Z nfpm package -f packaging/iris.nfpm.yaml -p rpm -t dist/   # Linux .rpm
# Windows (.exe) — on a Windows runner: ISCC.exe /DMyAppVersion=X.Y.Z packaging\windows\iris-installer.iss
(All of steps 2-6 are wired in .github/workflows/release.yml to run on git tag vX.Y.Z.)

7. Cloud services (deploy once; keep running)

# License/activation/trial/billing/portal — your VPS, NOT shipped to clients.
IRIS_LS_ADMIN_TOKEN=<secret> IRIS_LS_KEY=vendor_private.json \
IRIS_STRIPE_WEBHOOK_SECRET=whsec_… IRIS_YOOKASSA_SECRET=<secret> \
IRIS_TRIAL_DAYS=7 IRIS_TRIAL_MODULES= IRIS_TRIAL_CAMERAS= \
IRIS_HEARTBEAT_URL=https://lic.example.com/heartbeat \
  uvicorn cloud.license_server:app --port 8300
# Trial term = 7 days (the standard week-long evaluation; matches legal/EULA.md §1a and the code
# default — do NOT raise it without updating the EULA). IRIS_TRIAL_MODULES empty = full-feature
# trial; set e.g. "faces,reid" to limit. _CAMERAS unset = unlimited.

8. Provision an appliance (model B) — commercial defaults

EDITION=commercial scripts/provision_appliance.sh         # biometrics off, RT-DETR, integrity on,
#   derived key, local auth + offline + license enforce. Prints the hardware fingerprint to license.

Customer activation (two automatic types)

  • Trial (online-only): first boot, no license, trial_auto_enabled=true + trial_server_url → device auto-requests a signed, hardware-bound 7-day trial (full features). One per machine; heartbeat required (can't run offline / clock-rollback). After 7 days / expiry → enforcement restricts. The 7-day term is stated in the EULA (§1a) accepted at install.
  • Paid: purchase → cloud issues an activation token → device POST /activate {token, fingerprint} → signed full license; works offline (anti-rollback clock keeps the timer honest).

Release hygiene checklist

  • [ ] VERSION + pyproject + app/__init__.py bumped, CHANGELOG entry, tag pushed.
  • [ ] Suite green against the compiled tree.
  • [ ] AGPL/non-commercial weights NOT in the commercial bundle; ffmpeg LGPL.
  • [ ] Image cosign-signed; manifests published to both channels.
  • [ ] Installers built; cloud services up with trial/heartbeat env set.
  • [ ] gh release create vX.Y.Z with notes.