Porting · porting/2026-05-05-local-rebuild-foundation.md Docs Home

Local Rebuild Foundation

Date: 2026-05-05

Objective

Start the local rebuild of the legacy Picasso, Zweistein, and Studio API codebases inside LegacyBlinkin-2-Hetzner without modifying blinkin-2-platform.

Current Local Source Layout

The imported source snapshot lives in legacy-src/:

  • legacy-src/picasso-fe: Picasso FE monorepo with Houston, Picasso Editor, Widget, and shared packages.
  • legacy-src/zweistein: Zweistein admin app, NestJS server, embedding widget, Python query engine, and ingestion worker.
  • legacy-src/studio-api: Studio API NestJS backend.

The copy intentionally excludes:

  • .git
  • node_modules
  • build outputs such as dist, build, .next, coverage, and caches
  • real .env files
  • virtual environments and Python caches

Only .env.example templates are preserved.

Local Infrastructure

docker-compose.local.yml now defines the first shared local services:

  • Postgres on host port 54320
  • Redis on host port 63790
  • MinIO on host ports 9000 and 9001
  • optional Weaviate on host port 8088 behind the ai profile

The Postgres init script creates:

  • studio_api
  • zweistein

Verification

Run:

npm run legacy:verify

This verifies that the required imported packages exist and that forbidden generated folders or real env files were not copied.

Latest verification evidence:

  • npm run legacy:verify passed.
  • Imported code contains 13 package.json files.
  • Imported Python services contain 4 pyproject.toml files.
  • Imported source keeps 6 .env.example templates.
  • Forbidden generated folders: 0.
  • Forbidden real env files: 0.
  • Strict secret scan for common real key formats returned no hits after redacting Stripe values in the imported Zweistein Helm dev values copy.
  • Local Postgres, Redis, and MinIO containers started through npm run legacy:infra:up.
  • Postgres accepts connections and contains the studio_api and zweistein databases.
  • Redis responds with PONG.
  • MinIO health endpoint responds successfully.
  • Studio API now has a verified local runtime slice: dependencies installed, build passed, 84 migrations applied, and /health/ returned HTTP 200 on Node 20.
  • Zweistein server now has a verified local runtime slice: dependencies installed with Yarn 4, build passed, server booted on Node 20, /ai/healthz returned HTTP 200, and the local zweistein database contains 39 synchronized tables.
  • Zweistein admin now has a verified local frontend runtime slice: dependencies installed with Yarn 1, Dockerfile-near Vite build passed, dev server served /ai/ on 127.0.0.1:5176, and /ai/api/authz/config proxied to the local Zweistein server.
  • Zweistein embedding widget now has a verified local runtime slice: dependencies installed with npm, strict package build passed, dist/assets/emb-chat.js was emitted, and the Vite dev route responded on 127.0.0.1:5177.
  • Picasso FE now has a verified local build/runtime slice: monorepo install passed, Houston build passed, Picasso Editor build passed, Picasso widget build passed, and all three Vite dev routes responded locally.
  • Zweistein Python services now have a first local probe: Poetry metadata checks pass after restoring missing READMEs, local placeholder env templates exist, Query Engine uses Python 3.12, the FastAPI app import reaches the legacy router graph before failing on missing torch, the macOS local install is blocked by Torch wheel compatibility, and a Linux/amd64 Docker probe confirms that torch (2.10.0+cpu) is available for a Hetzner-like target.
  • The Python helper now has disk guards and dry-run install plans. On this Mac, Query Engine has one remaining local install operation (torch), while Ingestion Worker would need 307 installs and is intentionally blocked by the low-disk guard.
  • The local runtime assembly helper now maps the full service start order and current live status across infrastructure, Studio API, Zweistein Server/Admin/Widget, Picasso Houston/Editor/Widget, and Python services.
  • The repeatable non-Python smoke helper now runs npm run legacy:runtime:smoke:non-python, starts the seven non-Python app services together, verifies every health helper, checks the Zweistein DB table count, prints runtime status, and shuts down its own app processes.
  • Studio API and Zweistein Admin generated artifacts were refreshed after the assembly audit, and their local health checks passed again.
  • The database readiness gate now verifies Studio API source/applied migration counts, Studio public table count, Zweistein public table count, and the current Zweistein synchronize:true migration boundary.
  • The foundation verifier now checks for common hardcoded secret patterns and reports potential hardcoded secrets: 0.
  • The first Hetzner staging package now exists: docker-compose.hetzner.yml, placeholder-only configs/hetzner/*.env.example files, and a read-only npm run hetzner:check probe.
  • The Hetzner staging probe now has a non-printing env readiness gate: npm run hetzner:env for local status and npm run hetzner:env:check as the strict server-side pre-build check.
  • The Hetzner staging probe now includes a post-start health gate: npm run hetzner:health for status and npm run hetzner:health:check for strict staging readiness.
  • The Hetzner staging probe now includes a host preflight gate: npm run hetzner:host for status and npm run hetzner:host:check to require native Linux/amd64, Docker Compose, free ports, and enough disk before building.
  • The Hetzner Compose topology now includes static Nginx services for Picasso Widget and Zweistein Embedding Widget, plus .dockerignore files to keep local generated folders and env files out of Docker build contexts.
  • Existing backend and Python Docker contexts now have stricter .dockerignore coverage for generated folders, caches, virtual environments, and real env files before server-side builds.
  • A Hetzner rsync filter now documents how to move the source tree to the server without generated folders or real env files: configs/hetzner/rsync-filter.rules.
  • The Hetzner source sync helper now wraps that filter in npm run hetzner:sync:check, npm run hetzner:sync:dry-run, and guarded npm run hetzner:sync:push.
  • The first public edge is now mapped as an optional Caddy reverse-proxy profile with configs/hetzner/Caddyfile.example and configs/hetzner/reverse-proxy.env.example.
  • The first backup/restore runbook now exists with configs/hetzner/backup.env.example and scripts/hetzner-backup.mjs.
  • The first server-side evidence workflow now exists with scripts/hetzner-evidence.mjs.
  • The first Hetzner bootstrap gate now exists with scripts/hetzner-bootstrap.mjs.
  • The first Hetzner runtime config initializer now exists with scripts/hetzner-config.mjs; it creates ignored config files from templates without overwriting existing files.
  • The first Hetzner stack runner now exists with scripts/hetzner-stack.mjs; it guards heavy Compose build/start commands for native Linux x64/amd64.
  • A coordinated non-Python local runtime smoke now proves Studio API, Zweistein Server/Admin/Widget, and Picasso Houston/Editor/Widget can run together with HTTP 200 health checks.

Local Commands

Start local base infrastructure:

npm run legacy:infra:up

Check local base infrastructure:

npm run legacy:infra:status

Stop local base infrastructure:

npm run legacy:infra:down

Set LEGACY_BLINKIN_AI=1 before legacy:infra:up to include the optional Weaviate container.

Show the local runtime assembly plan:

npm run legacy:runtime:plan

Check what is currently generated or running:

npm run legacy:runtime:status
npm run legacy:runtime:missing

Check local database readiness:

npm run db:local:check

Run the coordinated non-Python local smoke:

npm run legacy:runtime:smoke:non-python

Show the Hetzner staging plan:

npm run hetzner:plan

Validate the Hetzner staging files without building images:

npm run hetzner:sync:plan
npm run hetzner:sync:check
npm run hetzner:bootstrap:plan
npm run hetzner:config:plan
npm run hetzner:config:status
npm run hetzner:stack:plan
npm run hetzner:stack:commands
npm run hetzner:host
npm run hetzner:check
npm run hetzner:env
npm run hetzner:health
npm run hetzner:backup:plan
npm run hetzner:evidence:plan

Not Yet Done

  • Dependency installation and build paths have not been verified for every imported legacy package yet. Studio API, Zweistein server, Zweistein admin, Zweistein embedding widget, and Picasso FE are the first verified slices.
  • Runtime env files are not generated locally by default. Use npm run hetzner:config:init on Hetzner to create ignored placeholder copies, then replace every placeholder with real staging values.
  • NestJS migrations are not adapted for every service yet. Studio API migrations have been applied against local Postgres. Zweistein server currently uses TypeORM synchronize: true because no migration source files were found in the imported snapshot.
  • Strict TypeScript checking is not clean for Zweistein admin yet, even though the Dockerfile-near Vite build passes.
  • Picasso FE builds and serves locally, but its authenticated flows are not yet wired end to end to the local Studio API, Zweistein server, and Houston runtime.
  • The Zweistein embedding widget and Picasso widget are locally buildable and now mapped into first static Hetzner services, but their server-side image builds and production public paths are not verified yet.
  • Python query and ingestion services are not fully containerized for local Hetzner-style operation yet. Query Engine local Poetry install is currently blocked by a Torch wheel mismatch on macOS/Python 3.12, while Linux/amd64 resolves the required CPU Torch wheel. A full Query Engine Docker build still needs to pass on native Linux/amd64 or a Hetzner staging box.
  • Ingestion Worker local Poetry install is intentionally blocked on this Mac by the Python low-disk guard; its dry-run plan needs 307 installs, including heavy ML/media dependencies.
  • The Hetzner Compose package has only been validated locally for file presence and YAML shape. Full Docker Compose config validation and image builds still need to run on Hetzner/Linux after real staging env files are created.
  • A coordinated non-Python local runtime smoke is proven, but no complete end-to-end local product flow is running yet.
  • Runtime assembly currently reports no missing required local runtime artifacts, but the services are not all running together yet.

This foundation makes the next slice concrete: keep adding one verified service at a time, then connect the verified services into a local end-to-end flow.