docs + hardening: CI backend switching, README update, zotero CVEs (refs #150, #158)
Some checks failed
CI / skinny-install (aco) (push) Failing after 8s
CI / skinny-install (api) (push) Failing after 7s
CI / skinny-install (bcda) (push) Failing after 7s
CI / skinny-install (bib) (push) Failing after 7s
CI / skinny-install (bls) (push) Failing after 8s
CI / skinny-install (ccw) (push) Failing after 8s
CI / skinny-install (cli) (push) Failing after 7s
CI / skinny-install (cms) (push) Failing after 7s
CI / skinny-install (conf) (push) Failing after 8s
CI / skinny-install (pfs) (push) Failing after 8s
CI / skinny-install (rex) (push) Failing after 7s
CI / lint-test (push) Has been cancelled
Infra CI / notebooks (push) Failing after 6s
Infra CI / zotero (push) Failing after 7s
Infra CI / docs (push) Successful in 39s
Infra CI / api (push) Successful in 9s
Infra CI / mc (push) Successful in 6s
Deploy / build-scan-report (push) Has been cancelled

- Add CI backend switching section to README with backend table, switch
  instructions, secret mapping, and workflow descriptions
- Update project layout section to reflect P24 tree reorg (infra/,
  assets/, cloud/)
- Fix stale references: styles/ → assets/, .woodpecker/ → .gitea/
- Fix gen_config.py --help to list all 3 backends (gitea|github|woodpecker)
- Add dist-upgrade + CVE commentary to zotero Dockerfile; Go stdlib
  CVEs (CVE-2024-24790, CVE-2025-68121) are in base image static
  binaries — tracked by weekly harden.yml --no-cache rebuilds
This commit is contained in:
kert
2026-03-24 16:38:36 -04:00
parent 5c288ecc57
commit 2054143219
3 changed files with 96 additions and 36 deletions

117
README.md
View File

@@ -1,6 +1,6 @@
# Stack
![coverage](styles/coverage.svg)
![coverage](assets/icons/coverage.svg)
Healthcare analytics platform on self-hosted infrastructure. Replaces dbt SQL models with narwhals DataFrame-agnostic expression functions, backed by DuckDB locally and Iceberg/Trino in the lakehouse. 22 services, 13 data pipelines, 11,931 tests at 99% coverage.
@@ -112,16 +112,59 @@ Override context at runtime: `STACK_CONTEXT=lake`.
## CI/CD
Six Woodpecker pipelines in `.woodpecker/`:
### Backend switching
CI/CD workflows are auto-generated from `stack.toml` by `gen_config.py`. Three backends are supported:
| Backend | Workflows directory | When to use |
|---------|---------------------|-------------|
| `gitea` | `.gitea/workflows/` | Self-hosted Gitea instance (default) |
| `github` | `.github/workflows/` | GitHub repos / GitHub Actions |
| `woodpecker` | `.woodpecker/` | Woodpecker CI server |
Switch backends by editing `stack.toml`:
```toml
[ci]
backend = "gitea" # change to "github" or "woodpecker"
```
Then regenerate:
```bash
uv run python dev/scripts/gen_config.py # generate new workflows
uv run python dev/scripts/gen_config.py --check # verify (used in CI)
uv run python dev/scripts/gen_config.py --backend github # one-off override
```
The generator:
1. Reads image definitions from `stack.toml [images]`
2. Dispatches to the active backend emitter (`dev/scripts/backends/{backend}.py`)
3. Writes workflow YAML files to the correct directory
4. Auto-removes stale workflows from inactive backend directories
Each backend has its own `[ci.{backend}]` config section in `stack.toml` with runner labels, registry endpoints, and authentication settings.
### Pipelines
Six workflows generated per backend:
| Pipeline | Trigger | What it does |
|----------|---------|--------------|
| `ci.yml` | Push | Ruff lint, pytest, marimo check |
| `deploy.yml` | Push to main | Build images, Trivy scan, deploy, rotate credentials |
| `harden.yml` | Manual/cron | Rebuild all images `--no-cache`, scan, auto-close vuln issues |
| `ci.yml` | Push/PR | Ruff lint + format, pytest (99% coverage), gen_config --check, skinny-install matrix |
| `deploy.yml` | Push to main | Build 5 images, Trivy scan, vuln reporting |
| `harden.yml` | Weekly cron / manual | Rebuild `--no-cache`, scan, auto-close/file vuln issues |
| `rebuild-all.yml` | Manual | Full rebuild + scan + deploy |
| `infra-ci.yml` | Push | Hadolint Dockerfiles, build validation |
| `release.yml` | Tag | Release pipeline |
| `infra-ci.yml` | Path-filtered push | Hadolint Dockerfiles, dry-run builds |
| `release.yml` | Tag (`v*`) | `uv build` + release |
### Secret mapping
| Secret | Gitea | GitHub | Woodpecker |
|--------|-------|--------|------------|
| Registry auth | `REGISTRY_USER` + `REGISTRY_TOKEN` | `GITHUB_TOKEN` (built-in) | `registry_username` + `registry_password` |
| Gitea API | `GITEA_TOKEN` | N/A | `gitea_token` |
| Vuln reporting | `GITEA_TOKEN` | `GITHUB_TOKEN` | `gitea_token` |
### Image tagging
@@ -175,7 +218,7 @@ ROOT_KEY=$KEY uv run python -m api.auth derive $COMMIT_SHA --redact
## Design system
Theme: **loch** (deep-navy). Traefik's `inject-loch` middleware rewrites HTML to inject `loch.css` and the favicon. Per-service CSS in `styles/`. Altair chart theme in `styles/nature.py` (Nature-conformant, accessible palette, IBM Plex Mono headings).
Theme: **loch** (deep-navy). Traefik's `inject-loch` middleware rewrites HTML to inject `loch.css` and the favicon. Per-service CSS in `assets/css/`. Altair chart theme in `assets/nature.py` (Nature-conformant, accessible palette, IBM Plex Mono headings).
## Data lakehouse
@@ -194,34 +237,40 @@ Query via Trino CLI, JDBC, Web UI, PyIceberg, or DuckDB+PyArrow.
stack/
├── compose.yml Docker Compose (22 services)
├── stack.toml Centralised configuration
├── pyproject.toml Python project (uv)
├── pyproject.toml Python project (uv, optional deps per module)
├── src/
│ ├── aco/
│ │ ├── express/ narwhals transformation functions (15 modules)
│ │ ├── pipe/ pipeline runner + 13 pipeline modules
│ │ ├── table/ table schemas with column metadata
│ │ └── dag.py pipeline DAG
│ ├── api/
│ │ ├── routes/ health, pipelines, bib endpoints
│ │ ├── auth/ HKDF credential derivation + provisioning
│ │ └── diag/ Trivy vuln reporter
│ ├── bib/ Zotero store, tags, metadata
│ ├── conf/ Config loader (cfg, path, context, secret)
│ ├── bcda/ BCDA API client
│ ├── cms/ CMS data utilities
│ ├── aco/ ACO analytics (express, pipe, table, lake, load)
│ ├── api/ FastAPI server + auth + diag
│ ├── bcda/ BCDA FHIR R4 client
│ ├── bib/ Zotero bibliography store, tags, metadata
│ ├── bls/ BLS data
│ ├── ccw/ CCW data dictionary
│ ├── cli/ CLI entry point (typer)
│ ├── cms/ CMS public data tables
│ ├── conf/ Config loader, storage abstraction, table base
│ ├── pfs/ Physician Fee Schedule
│ └── rex/ REX data processing
├── notebooks/ Marimo notebooks (12 notebooks)
├── tests/ 11,931 tests at 99% coverage
├── styles/ Loch design system (CSS, favicons, nature.py)
├── docs/ Docusaurus site (griffe → Node → nginx)
├── .woodpecker/ CI/CD pipelines (6 pipelines)
├── api/Dockerfile FastAPI service
├── notebooks/Dockerfile Marimo + CUDA 12.6 + cuDF
├── zotero/Dockerfile Zotero desktop (KasmVNC + GPU)
├── docs/Dockerfile 3-stage: griffe → pnpm build → nginx:alpine
├── traefik/ Reverse proxy config + loch injection
├── data/ DuckDB, bib.sqlite, BCDA/CMS data
│ └── rex/ REX fixed-width file processing
├── infra/ Service configs and Dockerfiles
│ ├── images/ All Dockerfiles (api, notebooks, zotero, docs, mc)
│ ├── traefik/ Reverse proxy + loch CSS injection
│ ├── grafana/ Dashboards and datasource provisioning
│ ├── prometheus/ Metrics collection
│ ├── loki/ Log aggregation
│ └── ... coredns, nginx, trino, polaris, rustfs, act-runner, gitea
├── assets/ Branding and styles
│ ├── css/ Per-service CSS (dashboard, gitea, marimo, woodpecker)
│ ├── icons/ Favicons, logos, SVGs
│ └── nature.py Nature-conformant chart palette
├── cloud/ Cloud provider deployment guides
│ ├── self-hosted/ Docker Compose (default)
│ ├── aws/ S3, RDS, Glue, ECS
│ ├── gcp/ GCS, Cloud SQL, BigQuery, Cloud Run
│ └── azure/ ABFS, Azure SQL, Unity Catalog, Container Apps
├── dev/ Dev tooling (scripts, hooks, seeds)
├── tests/ 11,955 tests at 99% coverage
├── notebooks/ Marimo notebooks
├── docs/ Docusaurus site
├── data/ DuckDB, bib.sqlite, BCDA/CMS data (gitignored)
└── .env Derived credentials (not in git)
```

View File

@@ -218,7 +218,9 @@ def main() -> int:
help="Verify files are up-to-date (exit 1 if not)",
)
parser.add_argument(
"--backend", default=None, help="Override CI backend (woodpecker|github)"
"--backend",
default=None,
help="Override CI backend (gitea|github|woodpecker)",
)
parser.add_argument(
"--dab-sql",

View File

@@ -3,8 +3,17 @@ FROM ghcr.io/selkies-project/nvidia-egl-desktop:latest
USER root
# System-level security patches — pulls latest fixes for all OS packages.
# Go stdlib CVEs (CVE-2024-24790, CVE-2025-68121) are in static binaries
# from the base image; they resolve when upstream rebuilds with patched Go.
# The harden.yml weekly --no-cache rebuild ensures we track upstream fixes.
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get dist-upgrade -y \
&& rm -rf /var/lib/apt/lists/*
# Install Zotero
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
&& curl -sL https://raw.githubusercontent.com/retorquere/zotero-deb/master/install.sh | bash \