docs: apply the closed Gitea decisions 3, 19, 20 and 38
ci / frontend (push) Successful in 44s
ci / server (push) Successful in 10m45s
ci / image (push) Successful in 33s
ci / e2e (push) Successful in 3m58s

- Issue 3 (Q4b): the registered-device cookie stays; no device certificate.
- Issue 19 (Q22): upload limits stay; ClamAV stays off in both stacks,
  the workstations run Microsoft Defender.
- Issue 20 (Q23): the storage alert stays at 85 percent; phase 9 mails
  it to PS-Admins.
- Issue 38 (OPS2): the test stack stays under ~/packetstation/test on
  docker-1. test.env.example, the compose header, README, CLAUDE.md,
  DEPLOY.md and SECURITY.md give that path and the actual modes. The
  production layout is asked on issue 38.

All four confirm the built defaults, so no code changed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 08:11:06 -06:00
co-authored by Claude Opus 5.5
parent ba0720eb2e
commit 77d40f6c05
10 changed files with 42 additions and 31 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Packet Station does not read a .env file at the repository root. # Packet Station does not read a .env file at the repository root.
# Stack settings live in one env file per stack, outside the repo on docker-1: # Stack settings live in one env file per stack, outside the repo on docker-1:
# deploy/env/prod.env.example -> /etc/packetstation/prod/stack.env # deploy/env/prod.env.example -> /etc/packetstation/prod/stack.env
# deploy/env/test.env.example -> /etc/packetstation/test/stack.env # deploy/env/test.env.example -> ~/packetstation/test/stack.env (docker-1, Gitea issue 38)
# deploy/ci/ci.env -> CI and local end-to-end runs (committed, no secrets) # deploy/ci/ci.env -> CI and local end-to-end runs (committed, no secrets)
# Passwords and keys are never put in an env file. They are files in SECRETS_DIR, # Passwords and keys are never put in an env file. They are files in SECRETS_DIR,
# mounted as compose secrets. See docs/DEPLOY.md. # mounted as compose secrets. See docs/DEPLOY.md.
+7
View File
@@ -4,6 +4,13 @@ All notable changes to this project are recorded here. The format follows [Keep
## [Unreleased] ## [Unreleased]
### Decided
- Gitea issue 3: shop devices keep the registered-device cookie. No device certificate is planned.
- Gitea issue 19: the upload limits stay (MTR 25 MB, hand BOM 5 MB). ClamAV stays off in both stacks. The workstations run Microsoft Defender.
- Gitea issue 20: the storage alert stays at 85 percent. Phase 9 mails it to PS-Admins.
- Gitea issue 38: the test stack's files stay under `~/packetstation/test` on docker-1. The test env example, the compose header and the docs now give that path.
### Fixed ### Fixed
- Board columns are frames about three cards tall (700 px, never taller than the screen) that scroll on their own, with the column heading staying put, so a long column no longer pushes the next one far down the page. Narrow screens keep the plain one-column flow. - Board columns are frames about three cards tall (700 px, never taller than the screen) that scroll on their own, with the column heading staying put, so a long column no longer pushes the next one far down the page. Narrow screens keep the plain one-column flow.
+1 -1
View File
@@ -68,7 +68,7 @@ packetstation worker # scheduled jobs
Test stack on docker-1: Test stack on docker-1:
```bash ```bash
docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
-f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
``` ```
+1 -1
View File
@@ -76,7 +76,7 @@ packetstation doctor
Test stack on `docker-1`: Test stack on `docker-1`:
```bash ```bash
docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
-f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
``` ```
+1 -1
View File
@@ -5,7 +5,7 @@
# docker compose -p packetstation --env-file /etc/packetstation/prod/stack.env \ # docker compose -p packetstation --env-file /etc/packetstation/prod/stack.env \
# -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d # -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
# Test on docker-1: # Test on docker-1:
# docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ # docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
# -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d # -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
# #
# No service publishes a port. NPM reaches the app over npm_default (compose.proxy.yaml). # No service publishes a port. NPM reaches the app over npm_default (compose.proxy.yaml).
+1 -1
View File
@@ -79,7 +79,7 @@ ASSETS_QUOTA_WARN_PCT=85
# 0 measures the assets volume. A byte count measures the stored files against that many bytes. # 0 measures the assets volume. A byte count measures the stored files against that many bytes.
ASSETS_QUOTA_BYTES=0 ASSETS_QUOTA_BYTES=0
# ---- ClamAV (optional virus scan of uploads) ---- # ---- ClamAV (optional virus scan of uploads; left off by IT, Gitea issue 19) ----
# Empty means no scan. When set, start the stack with --profile clamav on every docker compose command. # Empty means no scan. When set, start the stack with --profile clamav on every docker compose command.
# An unreachable scanner then refuses uploads until it answers. # An unreachable scanner then refuses uploads until it answers.
#CLAMAV_HOST=packetstation-clamav #CLAMAV_HOST=packetstation-clamav
+9 -9
View File
@@ -1,6 +1,6 @@
# Packet Station test stack settings. Copy to /etc/packetstation/test/stack.env # Packet Station test stack settings. Copy to ~/packetstation/test/stack.env on docker-1
# (root:root 0644). It holds no secret: passwords live in SECRETS_DIR as files. # (Gitea issue 38). It holds no secret: passwords live in SECRETS_DIR as files.
# Start: docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ # Start: docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
# -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d # -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
# The test stack never points at VISUAL production. # The test stack never points at VISUAL production.
# Comments sit on their own lines. Do not put a comment after a value. # Comments sit on their own lines. Do not put a comment after a value.
@@ -9,17 +9,17 @@
# Compose project name. Keeps prod and test volumes apart. # Compose project name. Keeps prod and test volumes apart.
PROJECT_NAME=packetstation-test PROJECT_NAME=packetstation-test
# This file's own path. Compose hands it to the containers as env_file. # This file's own path. Compose hands it to the containers as env_file.
ENV_FILE=/etc/packetstation/test/stack.env ENV_FILE=/home/spencerm/packetstation/test/stack.env
# Image to run. Always a tested release tag, never latest. # Image to run. Always a tested release tag, never latest.
IMAGE=gt.pescoinc.biz/spencerm/packetstation:vX.Y.Z IMAGE=gt.pescoinc.biz/spencerm/packetstation:vX.Y.Z
# App alias on npm_default. NPM forwards to http://<alias>:8000. # App alias on npm_default. NPM forwards to http://<alias>:8000.
APP_ALIAS=packetstation-test-app APP_ALIAS=packetstation-test-app
# Host folder of secret files, root:10001 mode 0440. # Host folder of secret files. Readable by uid 10001; the folders above it are 0750.
SECRETS_DIR=/etc/packetstation/test/secrets SECRETS_DIR=/home/spencerm/packetstation/test/secrets
# Host PEM file with the PESCO CA chain. Mounted at /etc/ssl/pesco/ca.pem. # Host PEM file with the PESCO CA chain. Mounted at /etc/ssl/pesco/ca.pem.
CA_BUNDLE_FILE=/etc/packetstation/ca/pesco-ca.pem CA_BUNDLE_FILE=/home/spencerm/packetstation/test/ca/pesco-ca.pem
# Host backup folder, mounted read-only in the worker. Compose sets BACKUP_DIR=/backups inside. # Host backup folder, mounted read-only in the worker. Compose sets BACKUP_DIR=/backups inside.
BACKUP_DIR=/var/backups/packetstation/packetstation-test BACKUP_DIR=/home/spencerm/packetstation/test/backups
# ---- Stack and site ---- # ---- Stack and site ----
# prod, test, ci or dev. Guards against crossing stacks. # prod, test, ci or dev. Guards against crossing stacks.
@@ -79,7 +79,7 @@ ASSETS_QUOTA_WARN_PCT=85
# 0 measures the assets volume. A byte count measures the stored files against that many bytes. # 0 measures the assets volume. A byte count measures the stored files against that many bytes.
ASSETS_QUOTA_BYTES=0 ASSETS_QUOTA_BYTES=0
# ---- ClamAV (optional virus scan of uploads) ---- # ---- ClamAV (optional virus scan of uploads; left off by IT, Gitea issue 19) ----
# Empty means no scan. When set, start the stack with --profile clamav on every docker compose command. # Empty means no scan. When set, start the stack with --profile clamav on every docker compose command.
# An unreachable scanner then refuses uploads until it answers. # An unreachable scanner then refuses uploads until it answers.
#CLAMAV_HOST=packetstation-clamav #CLAMAV_HOST=packetstation-clamav
+10 -10
View File
@@ -38,7 +38,7 @@ Both stacks run from the same image as separate compose projects.
## Secrets ## Secrets
Docker secrets, one file each, under `/etc/packetstation/<stack>/secrets/`. Owner `root:10001` (10001 is the image's non-root user), mode `0440`. Docker secrets, one file each, in the stack's `secrets/` folder ("Env files" says where each stack keeps it). Production: owner `root:10001` (10001 is the image's non-root user), mode `0440`. Test: owner `spencerm`, mode `0444`, reachable only through `/home/spencerm` and `packetstation/test`, both mode `0750`, so no other host user can read them.
| Secret | Used by | | Secret | Used by |
|---|---| |---|---|
@@ -59,7 +59,7 @@ The PESCO CA chain is mounted into the containers once and referenced by `LDAP_C
## Env files ## Env files
One folder per stack under `/etc/packetstation/`: `/etc/packetstation/test/` and `/etc/packetstation/prod/`. Each holds `stack.env` (settings only, no secrets) and `secrets/`. The env file sets `PROJECT_NAME`, `SECRETS_DIR` and `APP_ALIAS`, which the compose files require. `.env.example` in the repo lists every setting. One folder per stack. The test stack: `/home/spencerm/packetstation/test/`, like the other compose stacks on docker-1 (decided 2026-10-18, Gitea issue 38). It holds `stack.env`, `secrets/`, `ca/pesco-ca.pem` and `backups/`, and its `stack.env` sets `ENV_FILE`, `SECRETS_DIR`, `CA_BUNDLE_FILE` and `BACKUP_DIR` to those paths. The compose files run from the checkout in `/home/spencerm/packetstation/repo/deploy`. The production stack: `/etc/packetstation/prod/` until issue 38 says otherwise. Each folder holds `stack.env` (settings only, no secrets) and `secrets/`. The env file sets `PROJECT_NAME`, `SECRETS_DIR` and `APP_ALIAS`, which the compose files require. `.env.example` in the repo lists every setting.
Key settings: Key settings:
@@ -84,7 +84,7 @@ The feed's schedule and rules are not env settings. Intervals, shop hours, the w
Test stack: Test stack:
```bash ```bash
docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
-f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
``` ```
@@ -94,13 +94,13 @@ Images come from `gt.pescoinc.biz` by tag. CI tags each tested image `:sha-<sha>
## Virus scan (ClamAV, phase 4) ## Virus scan (ClamAV, phase 4)
ClamAV is optional and off by default. It closes requirement D5. Turn it on in the test stack first, check it, then the production stack. ClamAV is optional and off by default. It closes requirement D5. IT decided on 2026-10-18 (Gitea issue 19) to leave it off in both stacks: every workstation that uploads runs Microsoft Defender. The steps below stay for the day that changes. Turn it on in the test stack first, check it, then the production stack.
1. Set `CLAMAV_HOST=packetstation-clamav` in the stack's env file (`/etc/packetstation/test/stack.env`). Leave `CLAMAV_PORT` and `CLAMAV_TIMEOUT_SECONDS` at their defaults. 1. Set `CLAMAV_HOST=packetstation-clamav` in the stack's env file (`~/packetstation/test/stack.env`). Leave `CLAMAV_PORT` and `CLAMAV_TIMEOUT_SECONDS` at their defaults.
2. Start the stack with the profile: 2. Start the stack with the profile:
```bash ```bash
docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
-f deploy/compose.yaml -f deploy/compose.proxy.yaml --profile clamav up -d -f deploy/compose.yaml -f deploy/compose.proxy.yaml --profile clamav up -d
``` ```
@@ -119,7 +119,7 @@ clamd holds the signatures in memory, about 1.2 GB with the full set and more wh
Every stored file and the housekeeping job check the assets volume. With `ASSETS_QUOTA_BYTES=0` (the default) the check reads the volume's own used and total bytes; with a byte budget it adds up the stored files' sizes against it. At `ASSETS_QUOTA_WARN_PCT` percent (85 by default) the app opens one `notification_events` alert of kind `assets_quota` and audits `storage.quota_warning`. When usage drops back under, the alert is resolved. A failed check is logged and never fails an upload. Every stored file and the housekeeping job check the assets volume. With `ASSETS_QUOTA_BYTES=0` (the default) the check reads the volume's own used and total bytes; with a byte budget it adds up the stored files' sizes against it. At `ASSETS_QUOTA_WARN_PCT` percent (85 by default) the app opens one `notification_events` alert of kind `assets_quota` and audits `storage.quota_warning`. When usage drops back under, the alert is resolved. A failed check is logged and never fails an upload.
The Admin page, Storage tab, shows the usage bar (used of total, percent, the threshold), the open quota alert with an Acknowledge button, the ClamAV line ("Not configured", or the host with reachable or not reachable) and every open alert. Phase 9 sends the alert by email. Until then, check the Storage tab. The Admin page, Storage tab, shows the usage bar (used of total, percent, the threshold), the open quota alert with an Acknowledge button, the ClamAV line ("Not configured", or the host with reachable or not reachable) and every open alert. Phase 9 sends the alert by email to PS-Admins (decided 2026-10-18, Gitea issue 20, which also kept the 85 percent threshold). Until then, check the Storage tab.
## Python packages and the Gitea index ## Python packages and the Gitea index
@@ -170,7 +170,7 @@ mfiles.pescoinc.biz uses a public Let's Encrypt certificate, so MFILES_CA_BUNDLE
Resolved 2026-10-17: the CA was switched to SHA-256 (`certutil -setreg ca\csp\CNGHashAlgorithm SHA256`, restart certsvc; the CA already used the Key Storage Provider) and the Kerberos Authentication, Domain Controller Authentication and Directory Email Replication certificates on DC-02 and PDC-1 and the Machine certificate on APP-03 were renewed with `certreq -enroll -machine -q -cert <thumbprint> renew reusekeys`. From docker-1 all three endpoints now verify against the PESCO bundle with sha256WithRSAEncryption. The CA certificate itself is still SHA-1 self-signed, which OpenSSL accepts for a trust anchor. If the problem comes back, the fix was: set the CA to issue with SHA-256 and reissue the domain controller and relay certificates. The CA certificate itself may stay SHA-1, since OpenSSL does not check the trust anchor's own signature. Until then the test stack cannot sign in or verify the relay without a test-only OpenSSL security-level override, which is Spencer's decision to apply, never the production stack's. The doctor should report a SHA-1 chain with this hint (follow-up). Resolved 2026-10-17: the CA was switched to SHA-256 (`certutil -setreg ca\csp\CNGHashAlgorithm SHA256`, restart certsvc; the CA already used the Key Storage Provider) and the Kerberos Authentication, Domain Controller Authentication and Directory Email Replication certificates on DC-02 and PDC-1 and the Machine certificate on APP-03 were renewed with `certreq -enroll -machine -q -cert <thumbprint> renew reusekeys`. From docker-1 all three endpoints now verify against the PESCO bundle with sha256WithRSAEncryption. The CA certificate itself is still SHA-1 self-signed, which OpenSSL accepts for a trust anchor. If the problem comes back, the fix was: set the CA to issue with SHA-256 and reissue the domain controller and relay certificates. The CA certificate itself may stay SHA-1, since OpenSSL does not check the trust anchor's own signature. Until then the test stack cannot sign in or verify the relay without a test-only OpenSSL security-level override, which is Spencer's decision to apply, never the production stack's. The doctor should report a SHA-1 chain with this hint (follow-up).
The test stack on docker-1 lives under /home/spencerm/packetstation/test (stack.env, secrets/, ca/pesco-ca.pem, backups/) because the session that set it up had no sudo; move it to /etc/packetstation/test when convenient. The test stack on docker-1 lives under /home/spencerm/packetstation/test (stack.env, secrets/, ca/pesco-ca.pem, backups/). It stays there: that matches the other compose stacks on the host (Gitea issue 38, 2026-10-18).
## Known issue: Docker subnet pool on docker-1 (found 2026-10-17) ## Known issue: Docker subnet pool on docker-1 (found 2026-10-17)
@@ -212,7 +212,7 @@ Docker's default address pool on docker-1 holds fifteen /16 bridge networks (172
Then prove the live connection with the doctor and `VISUAL_ENCRYPT=yes`. The image does not yet add the PESCO CA to OpenSSL's trust store for the ODBC driver (the issue 5 follow-up), so this one-off check builds a bundle in `/tmp`. It skips the entrypoint, so the M-Files password line fails. Read the VISUAL line. Then prove the live connection with the doctor and `VISUAL_ENCRYPT=yes`. The image does not yet add the PESCO CA to OpenSSL's trust store for the ODBC driver (the issue 5 follow-up), so this one-off check builds a bundle in `/tmp`. It skips the entrypoint, so the M-Files password line fails. Read the VISUAL line.
```bash ```bash
docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
-f deploy/compose.yaml -f deploy/compose.proxy.yaml run --rm --no-deps \ -f deploy/compose.yaml -f deploy/compose.proxy.yaml run --rm --no-deps \
-e VISUAL_ENCRYPT=yes --entrypoint sh app -c \ -e VISUAL_ENCRYPT=yes --entrypoint sh app -c \
'cat /etc/ssl/certs/ca-certificates.crt /etc/ssl/pesco/ca.pem > /tmp/ca.pem && SSL_CERT_FILE=/tmp/ca.pem exec packetstation doctor' 'cat /etc/ssl/certs/ca-certificates.crt /etc/ssl/pesco/ca.pem > /tmp/ca.pem && SSL_CERT_FILE=/tmp/ca.pem exec packetstation doctor'
@@ -243,7 +243,7 @@ Docker's default address pool on docker-1 holds fifteen /16 bridge networks (172
`packetstation doctor` proves a host and a stack are ready. Run it with `compose run`, not `compose exec`: the entrypoint exports MFILES_PASSWORD from its secret file, and `exec` skips the entrypoint, so the M-Files password check fails under `exec` even though the running worker has it. `packetstation doctor` proves a host and a stack are ready. Run it with `compose run`, not `compose exec`: the entrypoint exports MFILES_PASSWORD from its secret file, and `exec` skips the entrypoint, so the M-Files password check fails under `exec` even though the running worker has it.
```bash ```bash
docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \ docker compose -p packetstation-test --env-file ~/packetstation/test/stack.env \
-f deploy/compose.yaml -f deploy/compose.proxy.yaml run --rm app doctor -f deploy/compose.yaml -f deploy/compose.proxy.yaml run --rm app doctor
``` ```
+7 -3
View File
@@ -218,7 +218,7 @@ Need an answer before the named phase. Defaults apply if none arrives in time.
1. **Station timer** (Seth, before phase 5): keep, cross-check against labor tickets, or drop. Default: keep the timer, show ticket hours beside it. Phase 5 (2026-10-16) applied the default: the station timer is the clock of record and the VISUAL ticket hours are shown beside it. 1. **Station timer** (Seth, before phase 5): keep, cross-check against labor tickets, or drop. Default: keep the timer, show ticket hours beside it. Phase 5 (2026-10-16) applied the default: the station timer is the clock of record and the VISUAL ticket hours are shown beside it.
2. **PDFs on the server** (confirm before phase 7): reportlab + pypdf + pypdfium2. Default: yes. Phase 7 (2026-10-17) applied the default: reportlab 5.0.1, pypdf and pypdfium2, with Pillow 13.0.0 for the rasters. 2. **PDFs on the server** (confirm before phase 7): reportlab + pypdf + pypdfium2. Default: yes. Phase 7 (2026-10-17) applied the default: reportlab 5.0.1, pypdf and pypdfium2, with Pillow 13.0.0 for the rasters.
3. **Frontend stack** (confirm before phase 1): TypeScript + React + Vite. Default: yes. Plain JavaScript would not be faster for parity: the old 7,000 lines are screen code that must be rewritten either way, and typed API models cut rework. 3. **Frontend stack** (confirm before phase 1): TypeScript + React + Vite. Default: yes. Plain JavaScript would not be faster for parity: the old 7,000 lines are screen code that must be rewritten either way, and typed API models cut rework.
4. **PIN defaults** (confirm): 6 to 12 digits, five attempts, 10-hour shop session, device cookie. Genuinely open (Seth and Q.C., before phase 6): whether Q.C. accept and filing require the domain password even with a PIN set. Default: PIN is enough. IT: device certificate later. 4. **PIN defaults** (confirm): 6 to 12 digits, five attempts, 10-hour shop session, device cookie. Genuinely open (Seth and Q.C., before phase 6): whether Q.C. accept and filing require the domain password even with a PIN set. Default: PIN is enough. IT: device certificate later. The device cookie stays and no device certificate is planned: confirmed by Spencer (IT) on 2026-10-18, Gitea issue 3.
5. **Several open orders on a project** (before phase 3): which order a packet shows. Proposed rule: the open order whose line's top job is the packet's work order base, else the newest open order, else the newest closed one; the packet shows "via base" or "via project" and Clerical can override. Needs Seth's view. Phase 3 (2026-10-16) built the default in one function (`feed/orders.py`): a Clerical pin, else the base's own linked order, else the project order whose top job is the unit, else the project's first open order, else its newest; the source (`via`) is stored on the project and unit and shown on the project page, which lists every open order. Seth may change the rule later in that one place. 5. **Several open orders on a project** (before phase 3): which order a packet shows. Proposed rule: the open order whose line's top job is the packet's work order base, else the newest open order, else the newest closed one; the packet shows "via base" or "via project" and Clerical can override. Needs Seth's view. Phase 3 (2026-10-16) built the default in one function (`feed/orders.py`): a Clerical pin, else the base's own linked order, else the project order whose top job is the unit, else the project's first open order, else its newest; the source (`via`) is stored on the project and unit and shown on the project page, which lists every open order. Seth may change the rule later in that one place.
6. **VISUAL_ENCRYPT** (IT, before production): yes once vsql has a trusted certificate; `no` until then. 6. **VISUAL_ENCRYPT** (IT, before production): yes once vsql has a trusted certificate; `no` until then.
7. **Policy tightenings** listed under "Challenges" 1 to 3 (Seth, before phase 2). 7. **Policy tightenings** listed under "Challenges" 1 to 3 (Seth, before phase 2).
@@ -242,8 +242,8 @@ From phase 4 (2026-10-16), for Seth to veto:
19. The release gate has two server rules: every line not N/A is pulled or on an open short, and every pulled line that issued pieces has a heat unless it is N/A or a sub-assembly. The old build enforced both on the screen only. Default: both on the server. 19. The release gate has two server rules: every line not N/A is pulled or on an open short, and every pulled line that issued pieces has a heat unless it is N/A or a sub-assembly. The old build enforced both on the screen only. Default: both on the server.
20. Hold and short reasons: the old build accepted any text. Default: closed sets on the server; "Other" on a hold needs a note. 20. Hold and short reasons: the old build accepted any text. Default: closed sets on the server; "Other" on a hold needs a note.
21. Receiving a short on a packet the warehouse no longer holds: default it stays a warehouse right (WH_RECEIVE_SHORT, any status but filed), and the holder of the packet (department lead or supervisor, Q.C., Clerical) can receive too, through edit_bom. 21. Receiving a short on a packet the warehouse no longer holds: default it stays a warehouse right (WH_RECEIVE_SHORT, any status but filed), and the holder of the packet (department lead or supervisor, Q.C., Clerical) can receive too, through edit_bom.
22. Upload kinds and limits: default an MTR is PDF, PNG or JPG up to 25 MB, a hand BOM is XLSX or CSV up to 5 MB. ClamAV is optional and off unless CLAMAV_HOST is set; when set, an unreachable scanner refuses uploads (fail closed). 22. Upload kinds and limits: default an MTR is PDF, PNG or JPG up to 25 MB, a hand BOM is XLSX or CSV up to 5 MB. ClamAV is optional and off unless CLAMAV_HOST is set; when set, an unreachable scanner refuses uploads (fail closed). Confirmed by Spencer (IT) on 2026-10-18, Gitea issue 19: the limits stay, and ClamAV stays off in both stacks because every workstation that uses Packet Station runs Microsoft Defender. The `clamav` profile stays in the build, unused.
23. The quota alert: default a `notification_events` row (phase 9 builds the delivery on this table) at 85 percent of the assets volume. 23. The quota alert: default a `notification_events` row (phase 9 builds the delivery on this table) at 85 percent of the assets volume. Confirmed by Spencer (IT) on 2026-10-18, Gitea issue 20: 85 percent stays, and the alert goes to PS-Admins. Phase 9 seeds the `assets_quota` notification rule with PS-Admins as its recipient.
From phase 5 (2026-10-16), for Seth to veto: From phase 5 (2026-10-16), for Seth to veto:
@@ -284,3 +284,7 @@ From phase 8 (2026-10-18), for Seth to veto. The phase 8 design numbered these 3
- Phase 8: the data book of a synthetic filed packet compared page by page with a data book exported from the old build on the same fixture (moved from phase 7). Done in phase 8 (2026-10-18); the page table, the fixes and what is left are in the phase 8 comparison note. - Phase 8: the data book of a synthetic filed packet compared page by page with a data book exported from the old build on the same fixture (moved from phase 7). Done in phase 8 (2026-10-18); the page table, the fixes and what is left are in the phase 8 comparison note.
- Phase 9: restore rehearsal on the test stack from a pg_dump and an assets copy. - Phase 9: restore rehearsal on the test stack from a pg_dump and an assets copy.
- Phase 10: the full 38-scenario bench green in Gitea Actions. - Phase 10: the full 38-scenario bench green in Gitea Actions.
## Host layout on docker-1
OPS2 (Gitea issue 38): the test stack's files stay under `/home/spencerm/packetstation/test`, like the other compose stacks on docker-1 (gitea, npm, pesco-ncr and the rest live under `/home/spencerm`). Decided by Spencer (IT) on 2026-10-18. The move to `/etc/packetstation/test` is dropped. Whether the production stack follows the same layout is asked on issue 38. Until it is answered, DEPLOY.md keeps `/etc/packetstation/prod`.
+4 -4
View File
@@ -89,8 +89,8 @@ The M: drive build had 20 layers of protection, most of them enforced in the bro
- An XLSX must be a zip that lists `[Content_Types].xml` and `xl/workbook.xml`; a plain zip is refused. A CSV has no magic, so it is accepted only as a BOM, and only when its first 64 KB decode as UTF-8 with no NUL and no control character other than tab, CR and LF. - An XLSX must be a zip that lists `[Content_Types].xml` and `xl/workbook.xml`; a plain zip is refused. A CSV has no magic, so it is accepted only as a BOM, and only when its first 64 KB decode as UTF-8 with no NUL and no control character other than tab, CR and LF.
- Limits per kind: 25 MB (`upload_max_bytes`) for drawing, mtr and document, 5 MB (`upload_max_bytes_bom`) for a BOM. The Content-Length is checked first, then the bytes as they are read. Over the limit is 413 `too_large`. - Limits per kind: 25 MB (`upload_max_bytes`) for drawing, mtr and document, 5 MB (`upload_max_bytes_bom`) for a BOM. The Content-Length is checked first, then the bytes as they are read. Over the limit is 413 `too_large`.
- XLSX and CSV are always served as an attachment, never inline. Every blob keeps `Content-Disposition` and `nosniff`. - XLSX and CSV are always served as an attachment, never inline. Every blob keeps `Content-Disposition` and `nosniff`.
- Optional ClamAV (requirement D5). When `CLAMAV_HOST` is set, every upload and every file the worker stores (drawings from M-Files) is streamed to clamd with `zINSTREAM` after it is read in full and before it is deduped or placed. A hit is 422 `infected`, nothing is stored, and an `asset.rejected` audit row keeps the signature, size and type. A scanner that cannot be reached refuses the upload with 503 `scan_unavailable` (fail closed). With `CLAMAV_HOST` empty there is no scan. - Optional ClamAV (requirement D5). When `CLAMAV_HOST` is set, every upload and every file the worker stores (drawings from M-Files) is streamed to clamd with `zINSTREAM` after it is read in full and before it is deduped or placed. A hit is 422 `infected`, nothing is stored, and an `asset.rejected` audit row keeps the signature, size and type. A scanner that cannot be reached refuses the upload with 503 `scan_unavailable` (fail closed). With `CLAMAV_HOST` empty there is no scan. IT decided on 2026-10-18 (Gitea issue 19) to leave the scan off in both stacks: every workstation that uploads runs Microsoft Defender. Drawings the worker fetches from M-Files are not scanned on the way in. They come from PESCO's own vault.
- Quota alert. After every stored file and in the housekeeping job, the assets volume usage is checked against `ASSETS_QUOTA_WARN_PCT` (85 by default). Over it, one open `notification_events` row of kind `assets_quota` is written and audited as `storage.quota_warning`. Back under, the row is resolved. A failed check never fails an upload. Admin sees usage, the alert and the scanner on the Storage tab and can acknowledge an alert (`alert.acknowledged`). Phase 9 sends the email. - Quota alert. After every stored file and in the housekeeping job, the assets volume usage is checked against `ASSETS_QUOTA_WARN_PCT` (85 by default). Over it, one open `notification_events` row of kind `assets_quota` is written and audited as `storage.quota_warning`. Back under, the row is resolved. A failed check never fails an upload. Admin sees usage, the alert and the scanner on the Storage tab and can acknowledge an alert (`alert.acknowledged`). Phase 9 sends the email to PS-Admins (Gitea issue 20).
### Warehouse rules (phase 4) ### Warehouse rules (phase 4)
@@ -184,7 +184,7 @@ The M: drive build had 20 layers of protection, most of them enforced in the bro
### Secrets (phase 0) ### Secrets (phase 0)
- Docker secrets only, files under `/etc/packetstation/<stack>/secrets`, owner `root:10001`, mode `0440`. - Docker secrets only. Production: files under `/etc/packetstation/prod/secrets`, owner `root:10001`, mode `0440`. Test: files under `/home/spencerm/packetstation/test/secrets`, mode `0444`, behind two `0750` folders owned by spencerm (Gitea issue 38).
- Read through `*_PASSWORD_FILE` settings. MFilesKit's password is exported by the entrypoint from its secret file. - Read through `*_PASSWORD_FILE` settings. MFilesKit's password is exported by the entrypoint from its secret file.
- Never in the repo, `.env`, logs or chat. See [DEPLOY.md](DEPLOY.md). - Never in the repo, `.env`, logs or chat. See [DEPLOY.md](DEPLOY.md).
@@ -210,7 +210,7 @@ The IDs come from Part 3 of the old SECURITY.md. The status follows the approved
| A4 | Closed, phase 0 | | A4 | Closed, phase 0 |
| D1, D2, D4 | Deferred (envelope encryption, Key Vault) | | D1, D2, D4 | Deferred (envelope encryption, Key Vault) |
| D3 | IT's (host disk encryption on docker-1) | | D3 | IT's (host disk encryption on docker-1) |
| D5 | Closed, phase 4 (optional ClamAV: a `clamav` compose profile; when `CLAMAV_HOST` is set every upload and every stored drawing is scanned before it is kept, a hit is refused and audited, and an unreachable scanner refuses uploads; off when the host is not set) | | D5 | Closed, phase 4 (optional ClamAV: a `clamav` compose profile; when `CLAMAV_HOST` is set every upload and every stored drawing is scanned before it is kept, a hit is refused and audited, and an unreachable scanner refuses uploads; off when the host is not set; left off in both stacks by IT on 2026-10-18, Gitea issue 19, with Microsoft Defender on the workstations) |
| D6, D7 | Deferred (retention period and customer copy need a decision from Seth and Q.C.) | | D6, D7 | Deferred (retention period and customer copy need a decision from Seth and Q.C.) |
| M1 | Closed, phase 2 (append-only audit and packet log written only by the server; every packet write carries both) | | M1 | Closed, phase 2 (append-only audit and packet log written only by the server; every packet write carries both) |
| M2 | Closed, phase 7 (filing seal is HMAC-SHA256 with the server seal key from a Docker secret; a reopen keeps the broken seal and its data book) | | M2 | Closed, phase 7 (filing seal is HMAC-SHA256 with the server seal key from a Docker secret; a reopen keeps the broken seal and its data book) |