From e54242178fb09d6f355b1c86410949212c7893a9 Mon Sep 17 00:00:00 2001 From: m17hr1l Date: Mon, 18 May 2026 21:53:03 +0200 Subject: [PATCH] =?UTF-8?q?stage-8:=20deployable=20platform=20=E2=80=94=20?= =?UTF-8?q?Dockerfile=20+=20compose=20for=20company-network=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lean python:3.12-slim platform image (cockpit + CLI + workers, 214 MB — no GPU, no model). docker-compose.yml runs cockpit + mock-cert on a persistent psyc-data volume. DATA_DIR is now overridable via PSYC_DATA_DIR so the container's data path is explicit. docs/deploy.md covers Proxmox hosting, first-run ingestion, and the honest caveats — no built-in auth (deploy behind the perimeter), the GPU model server is separate, egress-proxy config. Co-Authored-By: Claude Opus 4.7 --- Dockerfile | 25 ++++++++++++++ docker-compose.yml | 37 +++++++++++++++++++++ docs/deploy.md | 79 ++++++++++++++++++++++++++++++++++++++++++++ src/psyc/__init__.py | 5 ++- 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docs/deploy.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7f5cfcb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +# psyc platform image — cockpit + CLI + workers. No GPU, no model. +# (The fine-tuned model runs separately; see Dockerfile.train + serve_model.py.) +# +# Build: docker build -t psyc:latest . +# Run: docker compose up -d +# +# psyc has NO built-in authentication — deploy behind the company reverse +# proxy / SSO / VPN, or firewall the ports to the SOC subnet. See docs/deploy.md. + +FROM python:3.12-slim + +ENV PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + PSYC_DATA_DIR=/data + +WORKDIR /app +COPY pyproject.toml ./ +COPY src/ ./src/ +RUN pip install . + +VOLUME /data +EXPOSE 8767 8770 + +# Default service is the cockpit; docker-compose overrides the command for mock-cert. +CMD ["psyc", "serve", "--host", "0.0.0.0", "--port", "8767"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3a1d6ae --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +# psyc — company-network deployment (cockpit + mock destination receiver). +# +# docker compose up -d --build +# +# WARNING: psyc has no built-in authentication. The cockpit exposes cases, the +# ledger, and sealed-package metadata to anyone who can reach port 8767. Deploy +# behind the company reverse proxy / SSO / VPN, or firewall the ports to the +# SOC subnet. See docs/deploy.md. + +services: + cockpit: + build: . + image: psyc:latest + command: ["psyc", "serve", "--host", "0.0.0.0", "--port", "8767"] + ports: + - "8767:8767" + volumes: + - psyc-data:/data + restart: unless-stopped + # Behind a company egress proxy, uncomment and set: + # environment: + # HTTPS_PROXY: http://proxy.corp:3128 + # HTTP_PROXY: http://proxy.corp:3128 + + mock-cert: + image: psyc:latest + command: ["psyc", "mock-cert", "--host", "0.0.0.0", "--port", "8770"] + ports: + - "8770:8770" + volumes: + - psyc-data:/data + depends_on: + - cockpit + restart: unless-stopped + +volumes: + psyc-data: diff --git a/docs/deploy.md b/docs/deploy.md new file mode 100644 index 0000000..193ec9a --- /dev/null +++ b/docs/deploy.md @@ -0,0 +1,79 @@ +# psyc — deployment + +Deploying the psyc platform (cockpit + workers) as Docker containers — e.g. on a +Proxmox-hosted VM in the company network. + +## Read this before deploying + +- **No built-in authentication.** The cockpit exposes cases, the ledger, and + sealed-package metadata to anyone who can reach port 8767. Deploy it **behind + the company reverse proxy / SSO / VPN**, or firewall the ports to the SOC + subnet. Do not expose 8767 to the open network. (If you want in-app auth + instead of relying on the perimeter, that's a feature to add — not present today.) +- **The live model is separate.** This image has no GPU and no torch. The + fine-tuned-model bot needs `serve_model.py` running in the CUDA container on a + GPU host (Proxmox GPU passthrough to a VM). Without it the Classifier bot + falls back to rules — the platform works fine, just rules-only. +- **Outbound network.** Scoutline (URLhaus / CISA KEV / Feodo) and Mapline + (ip-api.com) make outbound HTTPS. Behind a company egress proxy, set + `HTTPS_PROXY` / `HTTP_PROXY` in the container environment (see the commented + block in `docker-compose.yml`). +- **mock-cert is a stand-in.** It accepts submissions for testing — it is not a + real destination. Wire real CERT / MISP / abuse endpoints (and their + credentials, per `docs/dossier.md` §18) before relying on routing in production. + +## Proxmox + +Docker is not native to Proxmox. Run it inside a Proxmox **VM** (recommended — +clean isolation, simplest Docker support) or a privileged LXC. Install Docker + +the Compose plugin in that guest, give it outbound network for the feeds, then +deploy as below. The GPU inference server, if used, needs a separate VM with +GPU passthrough. + +## Deploy + +```bash +git clone ssh://git@gitea.neuronetz.ai:222/m17hr1l/psyc.git +cd psyc +docker compose up -d --build +``` + +Starts two containers from one `psyc:latest` image: + +| Service | Port | Role | +|---|---|---| +| `cockpit` | 8767 | operator UI | +| `mock-cert` | 8770 | stand-in destination receiver (testing) | + +The sqlite db, sealed packages, and recipient keys persist in the `psyc-data` +named volume — they survive container restarts and rebuilds. + +## First run + +The schema is created on cockpit startup, but there are no cases until you +ingest. Run inside the container: + +```bash +docker compose exec cockpit psyc fetch-all +docker compose exec cockpit psyc classify-all +docker compose exec cockpit psyc map-all +``` + +Keep it ingesting by scheduling `fetch-all` — a host cron entry calling +`docker compose exec cockpit psyc fetch-all`, e.g. hourly. + +## Updating + +```bash +git pull +docker compose up -d --build +``` + +The `psyc-data` volume is preserved across updates. + +## Health + +```bash +curl http://:8767/healthz # cockpit +curl http://:8770/healthz # mock-cert +``` diff --git a/src/psyc/__init__.py b/src/psyc/__init__.py index a5db1f8..59153f3 100644 --- a/src/psyc/__init__.py +++ b/src/psyc/__init__.py @@ -1,9 +1,12 @@ """psyc — defensive CTI routing & evidence-sealing platform.""" +import os from pathlib import Path __version__ = "0.1.0" PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent -DATA_DIR = PROJECT_ROOT / "data" +# PSYC_DATA_DIR lets a container/deployment set the data path explicitly; +# otherwise data lives next to the repo (works for an editable install). +DATA_DIR = Path(os.environ["PSYC_DATA_DIR"]) if os.environ.get("PSYC_DATA_DIR") else PROJECT_ROOT / "data"