docs: rewrite README — skinny packages, supply chain, cloud providers, accurate counts
All checks were successful
CI / skinny-install (aco) (push) Successful in 49s
CI / skinny-install (api) (push) Successful in 29s
CI / skinny-install (bcda) (push) Successful in 23s
CI / skinny-install (bib) (push) Successful in 35s
CI / skinny-install (bls) (push) Successful in 20s
CI / skinny-install (ccw) (push) Successful in 24s
CI / skinny-install (cli) (push) Successful in 27s
CI / skinny-install (cms) (push) Successful in 24s
CI / skinny-install (conf) (push) Successful in 26s
CI / skinny-install (pfs) (push) Successful in 26s
CI / skinny-install (rex) (push) Successful in 24s
CI / lint-test (push) Successful in 5m49s
Deploy / build-scan-report (push) Successful in 3m5s
All checks were successful
CI / skinny-install (aco) (push) Successful in 49s
CI / skinny-install (api) (push) Successful in 29s
CI / skinny-install (bcda) (push) Successful in 23s
CI / skinny-install (bib) (push) Successful in 35s
CI / skinny-install (bls) (push) Successful in 20s
CI / skinny-install (ccw) (push) Successful in 24s
CI / skinny-install (cli) (push) Successful in 27s
CI / skinny-install (cms) (push) Successful in 24s
CI / skinny-install (conf) (push) Successful in 26s
CI / skinny-install (pfs) (push) Successful in 26s
CI / skinny-install (rex) (push) Successful in 24s
CI / lint-test (push) Successful in 5m49s
Deploy / build-scan-report (push) Successful in 3m5s
This commit is contained in:
116
README.md
116
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
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.
|
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,955 tests at 99% coverage.
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
@@ -12,7 +12,6 @@ ROOT_KEY=$(openssl rand -hex 32)
|
|||||||
cat > .env <<EOF
|
cat > .env <<EOF
|
||||||
DOMAIN=homelab.fhirworx.io
|
DOMAIN=homelab.fhirworx.io
|
||||||
HOST_IP=192.168.1.192
|
HOST_IP=192.168.1.192
|
||||||
WOODPECKER_ADMIN=kert
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# 2. Start core services and bootstrap credentials
|
# 2. Start core services and bootstrap credentials
|
||||||
@@ -26,6 +25,20 @@ docker compose up -d
|
|||||||
curl -s http://localhost:8000/health | python3 -m json.tool
|
curl -s http://localhost:8000/health | python3 -m json.tool
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The package supports skinny installs — install only the modules you need:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install stack[aco] # ACO analytics only
|
||||||
|
pip install stack[bib] # bibliography only
|
||||||
|
pip install stack[cli] # CLI (pulls aco + api + bib)
|
||||||
|
pip install stack[all] # everything
|
||||||
|
pip install stack[aco,aws] # ACO analytics with AWS storage
|
||||||
|
```
|
||||||
|
|
||||||
|
11 modules available as optional extras: `conf`, `aco`, `api`, `bcda`, `bib`, `bls`, `ccw`, `cli`, `cms`, `pfs`, `rex`. Cloud providers: `aws`, `gcp`, `azure`. Aggregates: `all`, `lake`.
|
||||||
|
|
||||||
## Services
|
## Services
|
||||||
|
|
||||||
All services route through Traefik at `*.homelab.fhirworx.io`.
|
All services route through Traefik at `*.homelab.fhirworx.io`.
|
||||||
@@ -36,7 +49,6 @@ All services route through Traefik at `*.homelab.fhirworx.io`.
|
|||||||
| Notebooks | `notebooks.homelab.fhirworx.io` | Marimo notebooks (GPU-accelerated) |
|
| Notebooks | `notebooks.homelab.fhirworx.io` | Marimo notebooks (GPU-accelerated) |
|
||||||
| Docs | `docs.homelab.fhirworx.io` | Docusaurus — API reference + CMS bibliography |
|
| Docs | `docs.homelab.fhirworx.io` | Docusaurus — API reference + CMS bibliography |
|
||||||
| Gitea | `gitea.homelab.fhirworx.io` | Git server, container registry, LFS |
|
| Gitea | `gitea.homelab.fhirworx.io` | Git server, container registry, LFS |
|
||||||
| Woodpecker CI | `ci.homelab.fhirworx.io` | CI/CD pipelines |
|
|
||||||
| Zotero | `zotero.homelab.fhirworx.io` | Reference manager (KasmVNC desktop) |
|
| Zotero | `zotero.homelab.fhirworx.io` | Reference manager (KasmVNC desktop) |
|
||||||
| Dashboard | `homelab.fhirworx.io` | Service launcher |
|
| Dashboard | `homelab.fhirworx.io` | Service launcher |
|
||||||
| RustFS | `s3.homelab.fhirworx.io` | S3-compatible object storage |
|
| RustFS | `s3.homelab.fhirworx.io` | S3-compatible object storage |
|
||||||
@@ -54,20 +66,20 @@ All services route through Traefik at `*.homelab.fhirworx.io`.
|
|||||||
|
|
||||||
```
|
```
|
||||||
CCLF/BCDA/CMS data
|
CCLF/BCDA/CMS data
|
||||||
│
|
|
|
||||||
▼
|
v
|
||||||
src/aco/express/ narwhals expressions (DataFrame-agnostic)
|
src/aco/express/ narwhals expressions (DataFrame-agnostic)
|
||||||
│
|
|
|
||||||
▼
|
v
|
||||||
src/aco/pipe/ pipeline runner with schema validation
|
src/aco/pipe/ pipeline runner with schema validation
|
||||||
│
|
|
|
||||||
▼
|
v
|
||||||
DuckDB (local) ──or── Iceberg + Trino (lakehouse)
|
DuckDB (local) --or-- Iceberg + Trino (lakehouse) --or-- Databricks
|
||||||
│
|
|
|
||||||
▼
|
v
|
||||||
src/api/ FastAPI (health, runs, bib)
|
src/api/ FastAPI (health, runs, bib)
|
||||||
│
|
|
|
||||||
▼
|
v
|
||||||
notebooks/ Marimo interactive analysis
|
notebooks/ Marimo interactive analysis
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -101,10 +113,11 @@ All services route through Traefik at `*.homelab.fhirworx.io`.
|
|||||||
|
|
||||||
- `cfg` — attribute access (`cfg.db.aco`)
|
- `cfg` — attribute access (`cfg.db.aco`)
|
||||||
- `path("db.aco")` — absolute path resolution relative to repo root
|
- `path("db.aco")` — absolute path resolution relative to repo root
|
||||||
- `context()` — environment switching (local/lake/databricks/trino)
|
- `context()` — environment switching (local/lake/databricks/trino/aws/gcp/azure)
|
||||||
- `secret("api.secret", "STACK_API_SECRET")` — env var with config fallback
|
- `secret("api.secret", "STACK_API_SECRET")` — env var with config fallback
|
||||||
|
- `storage.get_filesystem()` — multi-cloud storage abstraction (S3/GCS/ABFS/local)
|
||||||
|
|
||||||
Override context at runtime: `STACK_CONTEXT=lake`.
|
Override context at runtime: `STACK_CONTEXT=lake` (or `aws`, `gcp`, `azure`).
|
||||||
|
|
||||||
### Bibliography
|
### Bibliography
|
||||||
|
|
||||||
@@ -137,19 +150,13 @@ 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
|
uv run python dev/scripts/gen_config.py --backend github # one-off override
|
||||||
```
|
```
|
||||||
|
|
||||||
The generator:
|
The generator reads image definitions from `stack.toml [images]`, dispatches to the active backend emitter (`dev/scripts/backends/{backend}.py`), writes workflow YAML, and auto-removes stale workflows from inactive backend directories.
|
||||||
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.
|
### Workflows
|
||||||
|
|
||||||
### Pipelines
|
Seven workflows generated per backend:
|
||||||
|
|
||||||
Six workflows generated per backend:
|
| Workflow | Trigger | What it does |
|
||||||
|
|
||||||
| Pipeline | Trigger | What it does |
|
|
||||||
|----------|---------|--------------|
|
|----------|---------|--------------|
|
||||||
| `ci.yml` | Push/PR | Ruff lint + format, pytest (99% coverage), gen_config --check, skinny-install matrix |
|
| `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 |
|
| `deploy.yml` | Push to main | Build 5 images, Trivy scan, vuln reporting |
|
||||||
@@ -157,6 +164,7 @@ Six workflows generated per backend:
|
|||||||
| `rebuild-all.yml` | Manual | Full rebuild + scan + deploy |
|
| `rebuild-all.yml` | Manual | Full rebuild + scan + deploy |
|
||||||
| `infra-ci.yml` | Path-filtered push | Hadolint Dockerfiles, dry-run builds |
|
| `infra-ci.yml` | Path-filtered push | Hadolint Dockerfiles, dry-run builds |
|
||||||
| `release.yml` | Tag (`v*`) | `uv build` + release |
|
| `release.yml` | Tag (`v*`) | `uv build` + release |
|
||||||
|
| `pkg-supply-chain.yml` | Daily / push | Package inventory, mirror sync, drift detection, vuln scan |
|
||||||
|
|
||||||
### Secret mapping
|
### Secret mapping
|
||||||
|
|
||||||
@@ -168,26 +176,27 @@ Six workflows generated per backend:
|
|||||||
|
|
||||||
### Image tagging
|
### Image tagging
|
||||||
|
|
||||||
Images are tagged with the short commit SHA (8 chars):
|
Images are tagged with the short commit SHA (8 chars). Compose resolves via `${COMMIT_SHA:-latest}` from `.env`. Custom images: `api`, `notebooks`, `zotero`, `docs`, `mc`.
|
||||||
|
|
||||||
```
|
|
||||||
fhirworx/api:22ebc46e
|
|
||||||
fhirworx/notebooks:a4a2dcb0
|
|
||||||
fhirworx/docs:a4a2dcb0
|
|
||||||
```
|
|
||||||
|
|
||||||
Compose resolves via `${COMMIT_SHA:-latest}` from `.env`. Custom images: `api`, `notebooks`, `zotero`, `docs`, `mc`.
|
|
||||||
|
|
||||||
### Vulnerability management
|
### Vulnerability management
|
||||||
|
|
||||||
The `harden.yml` pipeline rebuilds all images with `--no-cache` to pick up OS patches, runs Trivy scans, and auto-files or auto-closes Gitea issues via `api.diag.vuln`.
|
The `harden.yml` pipeline rebuilds all images with `--no-cache` to pick up OS patches, runs Trivy scans, and auto-files or auto-closes Gitea issues via `api.diag.vuln`.
|
||||||
|
|
||||||
|
### Package supply chain
|
||||||
|
|
||||||
|
Daily automated pipeline (`pkg-supply-chain.yml`) that:
|
||||||
|
1. Scans Dockerfiles, pyproject.toml, and CI workflows for all package dependencies
|
||||||
|
2. Syncs packages to the Gitea package registry (local mirror)
|
||||||
|
3. Detects drift between manifest and mirror
|
||||||
|
4. Runs Trivy vulnerability scans on dependencies
|
||||||
|
5. Auto-creates Gitea issues for missing packages and CVEs
|
||||||
|
|
||||||
## Credential management
|
## Credential management
|
||||||
|
|
||||||
All 19 service credentials derive from a single 256-bit root key via HKDF-SHA256. No passwords stored in `.env` — they regenerate deterministically from root key + commit SHA on each deploy.
|
All 19 service credentials derive from a single 256-bit root key via HKDF-SHA256. No passwords stored in `.env` — they regenerate deterministically from root key + commit SHA on each deploy.
|
||||||
|
|
||||||
```
|
```
|
||||||
ROOT_KEY + commit_sha → HKDF-SHA256 → all credentials → .env + backends
|
ROOT_KEY + commit_sha -> HKDF-SHA256 -> all credentials -> .env + backends
|
||||||
```
|
```
|
||||||
|
|
||||||
| Tier | Salt | Rotates |
|
| Tier | Salt | Rotates |
|
||||||
@@ -214,7 +223,7 @@ ROOT_KEY=$KEY uv run python -m api.auth derive $COMMIT_SHA --redact
|
|||||||
| `storage` | internal | PostgreSQL, RustFS |
|
| `storage` | internal | PostgreSQL, RustFS |
|
||||||
| `data` | internal | Nessie, Trino, Polaris |
|
| `data` | internal | Nessie, Trino, Polaris |
|
||||||
| `observability` | internal | Grafana, Prometheus, Jaeger, Loki |
|
| `observability` | internal | Grafana, Prometheus, Jaeger, Loki |
|
||||||
| `ci` | external | Woodpecker, Gitea, RustFS |
|
| `ci` | external | Gitea, Act Runner, RustFS |
|
||||||
|
|
||||||
## Design system
|
## Design system
|
||||||
|
|
||||||
@@ -231,6 +240,29 @@ Theme: **loch** (deep-navy). Traefik's `inject-loch` middleware rewrites HTML to
|
|||||||
|
|
||||||
Query via Trino CLI, JDBC, Web UI, PyIceberg, or DuckDB+PyArrow.
|
Query via Trino CLI, JDBC, Web UI, PyIceberg, or DuckDB+PyArrow.
|
||||||
|
|
||||||
|
### Cloud deployment
|
||||||
|
|
||||||
|
The platform runs on self-hosted Docker Compose by default. Cloud provider guides in `cloud/`:
|
||||||
|
|
||||||
|
| Provider | Storage | Catalog | Compute | Context |
|
||||||
|
|----------|---------|---------|---------|---------|
|
||||||
|
| Self-hosted | RustFS (S3) | Nessie / Polaris | Docker Compose | `local` / `lake` |
|
||||||
|
| AWS | S3 | Glue | ECS / EKS | `aws` |
|
||||||
|
| GCP | GCS | BigQuery | Cloud Run / GKE | `gcp` |
|
||||||
|
| Azure | ABFS | Unity Catalog | Container Apps / AKS | `azure` |
|
||||||
|
| Databricks | DBFS | Unity Catalog | Databricks Jobs | `databricks` |
|
||||||
|
|
||||||
|
Switch with `STACK_CONTEXT=aws` or edit `stack.toml [context] active`.
|
||||||
|
|
||||||
|
## Databricks
|
||||||
|
|
||||||
|
Databricks Asset Bundle (`databricks.yml`) is auto-generated from the pipeline registry. 13 pipeline tasks with dependency ordering, 3 targets (dev/staging/prod), daily schedule.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run python dev/scripts/gen_config.py --dab-sql # generate SQL + DDL (~4 min)
|
||||||
|
databricks bundle deploy -t dev # deploy to Databricks
|
||||||
|
```
|
||||||
|
|
||||||
## Project layout
|
## Project layout
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -257,16 +289,20 @@ stack/
|
|||||||
│ ├── prometheus/ Metrics collection
|
│ ├── prometheus/ Metrics collection
|
||||||
│ ├── loki/ Log aggregation
|
│ ├── loki/ Log aggregation
|
||||||
│ └── ... coredns, nginx, trino, polaris, rustfs, act-runner, gitea
|
│ └── ... coredns, nginx, trino, polaris, rustfs, act-runner, gitea
|
||||||
├── assets/ Branding and styles
|
├── assets/ Branding, styles, generated artifacts
|
||||||
│ ├── css/ Per-service CSS (dashboard, gitea, marimo, woodpecker)
|
│ ├── css/ Per-service CSS (dashboard, gitea, marimo, woodpecker)
|
||||||
│ ├── icons/ Favicons, logos, SVGs
|
│ ├── icons/ Favicons, logos, coverage badge
|
||||||
│ └── nature.py Nature-conformant chart palette
|
│ └── nature.py Nature-conformant chart palette
|
||||||
├── cloud/ Cloud provider deployment guides
|
├── cloud/ Cloud provider deployment guides
|
||||||
│ ├── self-hosted/ Docker Compose (default)
|
│ ├── self-hosted/ Docker Compose (default)
|
||||||
│ ├── aws/ S3, RDS, Glue, ECS
|
│ ├── aws/ S3, RDS, Glue, ECS
|
||||||
│ ├── gcp/ GCS, Cloud SQL, BigQuery, Cloud Run
|
│ ├── gcp/ GCS, Cloud SQL, BigQuery, Cloud Run
|
||||||
│ └── azure/ ABFS, Azure SQL, Unity Catalog, Container Apps
|
│ └── azure/ ABFS, Azure SQL, Unity Catalog, Container Apps
|
||||||
├── dev/ Dev tooling (scripts, hooks, seeds)
|
├── dev/ Dev tooling
|
||||||
|
│ ├── scripts/ Code generators, bootstrap, supply chain tools
|
||||||
|
│ ├── hooks/ Git pre-commit hook
|
||||||
|
│ ├── seeds/ Reference data (BCDA samples, CMS docs)
|
||||||
|
│ └── pipelines/ CI-agnostic pipeline specs
|
||||||
├── tests/ 11,955 tests at 99% coverage
|
├── tests/ 11,955 tests at 99% coverage
|
||||||
├── notebooks/ Marimo notebooks
|
├── notebooks/ Marimo notebooks
|
||||||
├── docs/ Docusaurus site
|
├── docs/ Docusaurus site
|
||||||
|
|||||||
Reference in New Issue
Block a user