diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0e7a416 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,78 @@ +# VolcanicCore — project context for Claude sessions + +VolcanicCore is a self-hosted N8N-style workflow engine (C# backend + React frontend). Operator draws a directed graph of nodes (triggers → actions → terminals) in a browser canvas; each workflow can be activated independently. When active, trigger nodes subscribe/listen and each event fires one execution through the graph. + +## Documents to read first + +- **Design spec** — `docs/superpowers/specs/2026-08-28-volcaniccore-workflow-engine-design.md`. The whole architecture (execution model, extension contracts, webhook mechanics, persistence, deploy shape). Read this before writing plans or code. +- **Plan 1** (backend foundation, executed) — `docs/superpowers/plans/2026-08-28-plan-1-backend-foundation.md`. Shows the working style and the file structure conventions. +- **Follow-ups** — `docs/superpowers/followups.md`. Cross-plan items surfaced during review that later plans must handle. Read before starting a plan; extend as new items surface. + +## Current status + +**Plan 1 (backend foundation) shipped.** Head commit at plan close: `86bf173`. `dotnet build` clean, `dotnet test` 20/20. + +What's done: +- ASP.NET Core 9 monolith. Four projects: `VolcanicCore.Core` (contracts), `.Persistence` (SQLite, migrator, encryption, repos), `.Api` (endpoints, auth, registries), `VolcanicCore.Tests` (xUnit + WebApplicationFactory). +- Admin cookie auth (BCrypt, `ADMIN_PASSWORD` env), sessions endpoints. +- SQLite with hand-rolled versioned migrator (3 migrations: `admin_config`, `connections`, `workflows`). +- AES-GCM encryption for connection secrets (key from `ENCRYPTION_KEY` env or `/data/.enc-key`). +- Extension contracts: `INodeType` / `IActionNode` / `ITriggerNode` / `IConnectionProvider` in `.Core`. +- DI-collected `NodeRegistry` and `ConnectionProviderRegistry`. +- REST endpoints: session, discovery (empty in Plan 1), connections CRUD, workflows CRUD + activate/deactivate. + +What's next: **Plan 2** — the workflow engine itself. Not yet written. When authored, it will cover: +- Executions + node_executions tables (Migration 004 + 005). +- `Executor` — in-degree scheduler, fan-out, failure propagation per §4 of the design spec. +- `WebhookRouter` + `POST /wh/{workflow-id}/{*path}` — see §5 of the spec. +- `TriggerHost` lifecycle — start triggers on activation, dispose on deactivation. +- NATS `IConnectionProvider` + all five nodes: NATS Hook, Webhook, PythonTransform (IronPython 3), WebResponse, NATS Publish. +- Manual test-run endpoint. +- Retention `BackgroundService`. +- Discovery endpoints will start returning real node types + providers once these are DI-registered. + +Plans 3 (React SPA) and 4 (Coolify Dockerfile + deploy) follow. + +## Invariants (do NOT relearn from scratch) + +- **Git commit identity.** The repo has no configured identity. Every commit must pass `-c user.email="claude@volcanic" -c user.name="Claude"`: + ```bash + git -c user.email="claude@volcanic" -c user.name="Claude" commit -m "..." + ``` +- **Working branch.** `main`. The user opted in to this at project start. No feature branches unless requested. +- **Test parallelism is disabled at the assembly level.** See the `[assembly: CollectionBehavior(DisableTestParallelization = true)]` attribute at the top of `tests/VolcanicCore.Tests/WebFactory.cs`. Multiple `WebFactory` instances share the process; pre-`Configuration.Bind` env-var reads made parallel classes flaky. Do not re-enable parallelism without a redesign. +- **Cookie authentication is in the shared framework.** Do NOT add `Microsoft.AspNetCore.Authentication.Cookies` as a `PackageReference`. The last standalone version on NuGet is 2.3.0 (ASP.NET Core 2.x, 2019). .NET 9's `Microsoft.AspNetCore.App` shared framework provides the cookie-auth types with no PackageReference. +- **`Program.cs` env-var loop honors pre-set config.** In tests, `WebFactory.ConfigureWebHost` uses `builder.UseSetting(...)` to inject `DataDirectory` and `AdminPassword`. The env-var loop in `Program.cs` skips keys already set by upstream sources (`if (!string.IsNullOrEmpty(builder.Configuration[prop])) continue;`). Do not remove this check. +- **`Migration001Initial`, `002Connections`, `003Workflows`** are registered in the `Migrator` factory in `Program.cs`. Add new migrations to the same array in version order. +- **`AdminBootstrap`** stores `admin.password_hash_source` in plaintext by design (see the comment in `AdminBootstrap.cs`) — it is the change-detector for the env-provided `ADMIN_PASSWORD`. Do not "fix" this by hashing the source. Encryption at rest for `admin_config` is a Plan 2 follow-up. +- **`IConnectionSecretPolicy`** in Plan 1 is `HardcodedSecretPolicy` (fixed field-name set). Plan 2 replaces it with a schema-driven implementation that reads `secretFields` off each registered provider's `ConfigSchema`. Do not extend the hardcoded set further — replace instead. + +## Working style + +- **Design** with `superpowers:brainstorming`, **plan** with `superpowers:writing-plans`, **execute** with `superpowers:subagent-driven-development`. This is the tempo Plan 1 shipped on. +- **Split large scopes into multiple plans** (Plan 1 was foundation, Plan 2 is engine, Plan 3 is frontend, Plan 4 is deploy). Each plan should end in a testable state. +- **TDD** in each task: write the failing test, watch it fail, implement, watch it pass, commit. `Migrator`, connection CRUD, workflow CRUD all follow this pattern — see their commits for examples. +- **Two-stage review** per task: spec-compliance reviewer (haiku), then code-quality reviewer (sonnet). For crypto or wire-format work, escalate quality-reviewer to opus. For pure interface/type tasks, one combined reviewer is fine. +- **Follow-ups.** When a reviewer surfaces a real concern that isn't in scope for the current task, add it to `docs/superpowers/followups.md` with which plan should handle it. Do not silently drop review feedback. +- **Commit style.** Conventional Commits (`feat(scope):`, `fix(scope):`, `chore:`, `docs:`). One meaningful change per commit; TDD tasks land one commit per task at the end (test + impl together). + +## How to run + +```bash +# Build + test +dotnet build +dotnet test + +# Live server (boot with a real SQLite DB) +DATA_DIRECTORY=$(pwd)/_smoke ADMIN_PASSWORD=hunter2 \ + dotnet run --project src/VolcanicCore.Api --urls http://127.0.0.1:5099 +# Then: curl -c jar -X POST -H 'content-type: application/json' \ +# -d '{"password":"hunter2"}' http://127.0.0.1:5099/api/session +# And follow up with -b jar for authenticated calls. +``` + +`dotnet run --project X` sets the content root to X's directory, so `DATA_DIRECTORY=./relative` lands under `src/VolcanicCore.Api/` — always pass an absolute path. + +## Deploy target + +Coolify — env vars `COOLIFY_URL`, `COOLIFY_KEY`, `GIT_URL`, `GIT_USER`, `GIT_KEY` are available in the container. Actual container packaging + Coolify wiring is Plan 4. Global CLAUDE.md rule applies: fire-and-forget deploys via the v4 API, never block on build.