# workshop configuration template.
#
# Copy to /opt/smart-home/workshop/workshop.env on the container host, fill in, and
# chmod 600 it. Same never-commit handling as every other *.env here.

# ---------------------------------------------------------------------------
# Auth — required. Fails closed (rejects every request) while empty.
#   openssl rand -hex 32
# ---------------------------------------------------------------------------
WORKSHOP_TOKEN=

# ---------------------------------------------------------------------------
# Storage. THE SPLIT THAT MATTERS: the database is the index, the workspace is the
# filing cabinet. Queryable state (projects, parts, decisions) goes in SQLite;
# artefacts (datasheets, generated diagrams, photos, notes you want to open from a
# laptop) are ordinary files under the workspace, which is exported read-write over
# SMB. See docs/workshop-assistant.md.
#
# The workspace is the ONE writable share in this stack — the photo gallery share is
# deliberately read-only — so it gets its own volume and its own SMB account. Nothing
# on the host ever executes anything found in it.
# ---------------------------------------------------------------------------
WORKSHOP_DB_PATH=/data/workshop.db
WORKSHOP_WORKSPACE_DIR=/workspace

# ---------------------------------------------------------------------------
# Run behaviour
# ---------------------------------------------------------------------------
# 8098 is identity_web — see CoreSystemConfig.json's ports block, which the validator
# checks for duplicates.
WORKSHOP_PORT=8102
LOG_LEVEL=INFO

# ---------------------------------------------------------------------------
# Knowledge store — the second database, and the one that is NEVER pruned.
#
# workshop.db is a record of WORK (projects, decisions, the log). This is a record of
# WHAT IS TRUE: standing instructions about how you work, facts and specs by keyword,
# what was learned about a project, and the hardware inventory. Every other store in
# this stack has a retention window because stale observations are worse than none;
# this one is the opposite — the whole point is that you tell it once. There is no
# cutoff and no cleanup pass, by design.
# ---------------------------------------------------------------------------
WORKSHOP_KNOWLEDGE_DB_PATH=/data/workshop-knowledge.db

# ---------------------------------------------------------------------------
# Gitea (optional; repo creation is refused while unset).
#
# Lets a project be given a git repository. The service CREATES repos and reports
# them — it never pushes, commits or clones, and it has no delete path at all, even
# though the token technically permits one.
#
# The token: Gitea → Settings → Applications → Generate New Token, scope
# `write:repository` (plus `write:user` if GITEA_OWNER is left empty so repos are made
# under the token's own account).
#
# STRONGLY PREFERRED: a dedicated Gitea user ("workshop-bot") with access to ONE
# organisation, named in GITEA_OWNER — rather than a token on your own account, which
# can reach everything you own. The blast radius of a leaked env file is then one org
# of generated repos.
# ---------------------------------------------------------------------------
GITEA_URL=
GITEA_TOKEN=
GITEA_OWNER=
# Generated repos are private unless this is explicitly "false".
GITEA_PRIVATE_REPOS=true
GITEA_TIMEOUT=15

# ---------------------------------------------------------------------------
# Git. The assistant commits and pushes unattended; it NEVER rewrites history.
#
# Allowed: commit, push, branch, tag, merge — everything that ADDS to history, because
# the worst case is a bad commit you revert.
# Never, by any path: force-push, rebase, amend, reset --hard, filter-branch,
# filter-repo, branch/tag deletion, reflog expiry, gc --prune. Rewriting has no undo.
#
# That refusal lives in git_ops.py, but the real enforcement is Gitea BRANCH
# PROTECTION, applied automatically when a repo is created (enable_force_push: false,
# enable_delete: false). Client-side refusal is a policy; branch protection is a
# control. Check the create-repo response's `repo.protection.applied` — do not assume
# it is on because the repo exists.
#
# When history genuinely has to be scrubbed (a committed API token), POST
# /projects/<slug>/scrub-request returns the commands to run BY HAND from the repo on
# the SMB share. The manual step is the safety mechanism; automating it would remove
# the only thing making it safe.
#
# The working tree is code/ inside the project workspace — never the workspace root —
# so no git operation can reach the datasheets and photos beside it.
# ---------------------------------------------------------------------------
WORKSHOP_GIT_AUTHOR_NAME=workshop assistant
WORKSHOP_GIT_AUTHOR_EMAIL=workshop@localhost
WORKSHOP_GIT_BRANCH=main
WORKSHOP_GIT_TIMEOUT=120

# ---------------------------------------------------------------------------
# Infrastructure health — CheckMK + every OPNsense firewall, polled here.
#
# WHY HERE AND NOT IN digest-engine: the digest runs four times a day, and "is the NAS
# disk failing right now" is not a question with a six-hour answer. This service is
# always on, so it polls and keeps ~30 days of samples; digest-engine reads THIS over
# HTTP (its ENABLE_INFRA_HEALTH_INGEST). One poller, two consumers — and the digest
# gets "critical since Tuesday" instead of a snapshot it can't compare to anything.
#
# READ-ONLY, and not by promise: the CheckMK user should have the Guest role, which
# cannot acknowledge, downtime or reschedule. From OPNsense this reads exactly one
# endpoint (GET /api/ids/service/status — is Suricata running); the alert query stays
# in digest-engine, which already does the paging properly.
#
# OPNSENSE_JSON is written by tools/config-export.py from the `opnsense` LIST in
# CoreSystemConfig.json — every firewall carries its own name, and that name reaches
# the digest and the display, so "the IDS is running" can never stand in for two
# firewalls of which one is down.
# ---------------------------------------------------------------------------
CHECKMK_BASE_URL=
CHECKMK_SITE=cmk
CHECKMK_USERNAME=
CHECKMK_SECRET=
CHECKMK_ONLY_PROBLEMS=true
CHECKMK_MAX_ROWS=200
OPNSENSE_JSON=
WORKSHOP_HEALTH_INTERVAL_SECONDS=300
WORKSHOP_HEALTH_TIMEOUT=20
WORKSHOP_HEALTH_RETENTION_DAYS=30

# ---------------------------------------------------------------------------
# Network cameras the workshop display may show.
#
# The USB webcams in this project are fixed-purpose (a kitchen item held to a lens, a
# doorway) and their angles are useless for anything else — so what the workshop shows
# is the NETWORK cameras, via the go2rtc/Frigate this stack already runs.
#
# Format: name:stream_id[,name:stream_id...] where stream_id is the camera's name in
# go2rtc. The frontend builds a WebRTC/MSE URL from it; nothing here proxies video,
# because putting a Python HTTP server in the path of an H.264 stream is how you turn
# a working camera into a stuttering one.
# ---------------------------------------------------------------------------
WORKSHOP_CAMERAS=
# Base URL of go2rtc's own web API, reachable from the BROWSER (not from this
# container) — the kiosk connects to it directly.
GO2RTC_URL=
