# HELIX > **EVOLV and every R&D strand, one helix.** > > The R&D showcase platform of Waterschap Brabantse Delta. EVOLV at its core, > every innovation along the strands. HELIX collects projects, innovations, and updates from across the R&D team in one place — with deep links to repos, dashboards, and demos. Sign-in is gated to the `RnD` Gitea organisation; viewing is open. ## Stack - **SvelteKit 2** + **Svelte 5** + TypeScript - **Tailwind v4** (CSS-first design tokens) - **SQLite** (single file) + **Drizzle ORM** - **Gitea OAuth2** for authoring (no passwords stored) - Pure **SVG + CSS** helix animation — no WebGL ## Run it locally — two ways ### 1. Docker (production-like, recommended for testing) Mirrors how the EVOLV stack is exercised locally. One command, named volume for SQLite, healthcheck wired: ```bash cd /path/to/helix docker compose up -d --build # build image + start container docker compose logs -f helix # tail logs # → http://localhost:3000 ``` If port 3000 is in use (Node-RED, another dev server, …) pick another: ```bash HELIX_PORT=3030 \ ORIGIN=http://localhost:3030 \ GITEA_REDIRECT_URI=http://localhost:3030/auth/gitea/callback \ docker compose up -d --build # → http://localhost:3030 ``` Useful commands: ```bash docker compose ps # status + health docker inspect helix --format '{{.State.Health.Status}}' docker compose exec helix sh # shell into the container docker compose exec helix sqlite3 /data/helix.db # inspect the DB docker compose down # stop (keeps the volume) docker compose down -v # stop + DELETE all data ``` The SQLite database lives in the **`helix_helix-data`** named volume at `/data/helix.db`. Migrations + the idempotent seed both run on every boot. Once you have real content, set `SEED_ON_BOOT=false` in your env to stop re-seeding the demo entries. ### 2. Native Node (faster dev loop, hot reload) ```bash nvm use # .nvmrc → Node 20 (Tailwind v3 also works on 18) cp .env.example .env # fill in Gitea OAuth — see below npm install npm run db:generate # generate drizzle/0000_*.sql from the schema npm run db:migrate # apply migrations to ./helix.db npm run db:seed # idempotent demo content npm run dev # http://localhost:5173 ``` ## Gitea OAuth setup HELIX uses your existing Gitea identity. Create an OAuth2 application once: 1. Open 2. Click **Create new OAuth2 application** 3. Application name: `HELIX (local)` (or `HELIX (production)`) 4. Redirect URI: - local: `http://localhost:3000/auth/gitea/callback` - prod: `https:///auth/gitea/callback` 5. Click **Create application** 6. Copy the **Client ID** and **Client Secret** into your `.env`: ``` GITEA_CLIENT_ID=... GITEA_CLIENT_SECRET=... ``` `GITEA_ALLOWED_ORG=RnD` restricts authoring (sign-in) to members of the `RnD` organisation. Leave it empty to allow any authenticated Gitea user. ## Adding a dashboard embed allowlist host `src/lib/config.ts` → `DASHBOARD_ALLOWED_HOSTS` controls which Grafana / dashboard origins can be embedded inline on a project page. Add a hostname (no scheme, no path) and redeploy. Hosts not on the list render as a "Open in new tab" card instead — never blindly iframed. ## Production deploy Same compose file as local. On the production host: ```bash cp .env.example .env # Set: ORIGIN=https://your-host, GITEA_REDIRECT_URI=https://your-host/auth/gitea/callback, # GITEA_CLIENT_ID, GITEA_CLIENT_SECRET (from a separate prod OAuth app) docker compose up -d --build ``` Put a TLS-terminating reverse proxy (nginx/caddy/traefik) in front of port 3000. Back up the volume periodically: ```bash docker compose exec helix sqlite3 /data/helix.db .dump | gzip > helix-backup-$(date +%F).sql.gz ``` ## Project layout ``` helix/ ├── src/ │ ├── lib/ │ │ ├── components/ # Helix.svelte, ProjectCard, PostCard, Nav, Footer, DashboardEmbed │ │ ├── server/ │ │ │ ├── db/ # Drizzle schema + client (better-sqlite3) │ │ │ ├── auth.ts # session token + cookie helpers │ │ │ └── gitea.ts # OAuth2 dance + user fetch │ │ ├── config.ts # site name, tagline, dashboard allowlist │ │ └── markdown.ts # marked wrapper + slug/id helpers │ ├── routes/ │ │ ├── +page.svelte # landing (helix hero + recent projects/posts) │ │ ├── projects/ # /projects, /[slug], /new │ │ ├── posts/ # /posts, /[slug], /new │ │ ├── login/ # sign-in page (kicks off Gitea OAuth) │ │ ├── logout/+server.ts # POST → invalidates session │ │ └── auth/gitea/ # OAuth start + callback │ ├── app.html │ ├── app.css # Tailwind v4 entry + design tokens │ ├── app.d.ts # Locals types │ └── hooks.server.ts # session hydration ├── drizzle/ # generated migrations ├── scripts/ │ ├── migrate.js # run pending migrations │ └── seed.js # idempotent example data ├── static/favicon.svg ├── Dockerfile ├── docker-compose.yml ├── drizzle.config.ts ├── svelte.config.js ├── vite.config.ts ├── tailwind.config.ts (none — Tailwind v4 uses @theme in app.css) ├── tsconfig.json └── package.json ``` ## How to post After signing in (top-right → Sign in → Gitea), use: - **+ New** in the nav (or `/projects/new`) — a project: title, summary, markdown body, links - **`/posts/new`** — a shorter update / write-up Both are auth-gated. Anyone in `RnD` on Gitea can post. ## Contributing This is internal R&D — open a PR against `main` on [`gitea.wbd-rd.nl/RnD/helix`](https://gitea.wbd-rd.nl/RnD/helix). ## License Internal — Waterschap Brabantse Delta R&D.