Files
shopdb-flask/.env.example
cproudlock 86f5f1be68 ADR-013 Phase 1: signed plugin artifacts (pack/validate/keygen)
Packaging + provenance for the plugin marketplace. No runtime behavior change
yet - verification is available on demand; enforcing it at plugin load/migrate
and pulling from a shelf are Phase 2.

- signing.py: ed25519 key pairs + provenance. Provenance is a sorted per-file
  SHA-256 map plus metadata; the detached signature covers the exact
  serialized provenance bytes, so verifying is re-hash files, re-serialize,
  check signature. verify() accepts any of several trusted keys (rotation).
  Uses cryptography (already a dependency).
- packaging.py: pack() builds a signed <name>-<version>.shopdbplugin (zip +
  PROVENANCE.json + PROVENANCE.sig). verify_artifact()/verify_dir() re-hash
  and check the signature, and flag a tampered file, an unexpected file, a
  wrong/absent key - all fail closed.
- CLI: `flask plugin keygen` (publisher key pair), `flask plugin pack <name>
  --key` (validates then signs), and `flask plugin validate` extended to a
  signed artifact by path (--pubkey, else PLUGIN_TRUSTED_KEYS).
- config PLUGIN_TRUSTED_KEYS: os.pathsep-separated public-key PEM paths,
  delivered with the site config, never read from the shelf. .env.example
  documents it.
- docs/PLUGIN-SIGNING.md: curator flow (keygen offline, review, pack, publish,
  pin keys, rotate).

The signature proves an artifact is exactly what a curator signed, not that the
code is safe - human review before signing is the control. 11 tests: sign/verify
round trip, wrong key, provenance excludes noise, serialize determinism, pack +
verify, tamper -> hash mismatch, extra file, no-key fail-closed, verify_dir.
1028 pass, naming green.
2026-07-18 20:21:57 -04:00

95 lines
3.7 KiB
Plaintext

# shopdb-flask environment template.
#
# Copy to .env and fill in the values. .env is gitignored. ProductionConfig
# refuses to boot when SECRET_KEY, JWT_SECRET_KEY, DATABASE_URL, or
# CORS_ORIGINS are missing or use the dev defaults.
#
# See docs/DEPLOY.md for the full per-site deployment runbook.
# ---- Flask ----
FLASK_APP=wsgi.py
# Set to 'production' for live sites. Other valid values: 'development',
# 'testing'. Production triggers ProductionConfig.validate() at boot.
FLASK_ENV=production
# ---- Required secrets (production refuses to boot without these) ----
# Generate strong random values, e.g.:
# python -c "import secrets; print(secrets.token_urlsafe(64))"
SECRET_KEY=change-this-to-a-secure-random-string
JWT_SECRET_KEY=change-this-to-another-secure-random-string
# ---- Database (required) ----
# Format: mysql+pymysql://<user>:<password>@<host>:<port>/<database>?charset=utf8mb4
# In docker-compose, host is `db` (the service name).
# The ?charset=utf8mb4 keeps the connection on utf8mb4; create the database as
# utf8mb4 too (see docs/DEPLOY.md). Both must be utf8mb4 to match the schema.
DATABASE_URL=mysql+pymysql://shopdb:CHANGE_ME@db:3306/shopdb_flask?charset=utf8mb4
# ---- CORS (required, no wildcards in production) ----
# Comma-separated list of explicit origins permitted to call the API.
# Example for a single-host facility deploy:
# CORS_ORIGINS=https://shopdb.facility-a.example.com
# Wildcard '*' is rejected by ProductionConfig.validate().
CORS_ORIGINS=http://localhost:5173
# ---- JWT lifecycle (optional, defaults shown) ----
JWT_ACCESS_TOKEN_EXPIRES=3600
JWT_REFRESH_TOKEN_EXPIRES=2592000
# ---- Logging (optional) ----
LOG_LEVEL=INFO
# ---- docker-compose only ----
# These are read by docker-compose.yml; not used by the Flask app directly.
MYSQL_ROOT_PASSWORD=CHANGE_ME_ROOT_PASSWORD
MYSQL_PASSWORD=CHANGE_ME_APP_PASSWORD
MYSQL_PORT=3306
API_PORT=5001
# ---- Zabbix integration (optional, for printer supply monitoring) ----
ZABBIX_URL=
ZABBIX_TOKEN=
# ---- Per-plugin collector API keys (optional) ----
# Per ADR-006, each plugin can accept external collector input at
# /api/collector/<pluginname>. The framework checks
# COLLECTOR_API_KEY_<PLUGINNAME> first, then COLLECTOR_API_KEY as fallback.
# COLLECTOR_API_KEY=
# COLLECTOR_API_KEY_COMPUTERS=
# ---- Trusted plugin publisher keys (ADR-013, optional) ----
# Public-key PEM paths (OS path separator: ':' on Linux, ';' on Windows) used
# to verify signed plugin artifacts. Delivered with this config, NEVER from the
# plugin shelf. Empty on a site that does not adopt marketplace plugins.
# PLUGIN_TRUSTED_KEYS=/etc/shopdb/keys/curator.pub
# ---- Employee directory database (optional, read-only) ----
# Separate HR/employee lookup DB consumed by the notifications plugin and the
# public shopfloor kiosks. Leave unset if the feature is not used; there is no
# safe default for the password, so an unset password fails loud.
# EMPLOYEE_DB_HOST=
# EMPLOYEE_DB_USER=
# EMPLOYEE_DB_PASSWORD=
# EMPLOYEE_DB_NAME=wjf_employees
# ---- cmmc_usb database (USB check-in/out) ----
# Separate read-write MySQL DB used by the USB plugin to track device
# check-in/out, lockers, and the check-in/out log. Leave unset if the feature
# is not used; there is no safe default for the password, so an unset password
# fails loud.
# CMMC_USB_DB_HOST=
# CMMC_USB_DB_USER=
# CMMC_USB_DB_PASSWORD=
# CMMC_USB_DB_NAME=cmmc_usb
# ---- Subpath deployment (optional) ----
# Serve the app under a URL prefix instead of the server root, e.g. as an IIS
# Application at /ops under an existing site. The frontend must be rebuilt with
# the matching base: VITE_BASE_PATH=/ops/ npm run build. Leave unset when the
# app owns its own site/port (the default). See docs/INSTALL-WINDOWS-IIS.md.
# MOUNT_PATH=/ops