This document describes the internal architecture of Stackpanel for contributors and those interested in understanding how the system works.
Stackpanel is a Nix-based development environment framework that provides deterministic dev environments, service orchestration, secrets management, IDE integration, and a web-based studio for managing everything.
The system has three runtime planes:
- Nix plane - Evaluates configuration, computes ports, provisions the devshell, generates files. Runs once on shell entry.
- Go agent plane - A localhost HTTP server (default port 9876) that bridges the web UI to the local Nix environment. Provides REST + Connect-RPC APIs, SSE events, file watching, and process management.
- Web plane - A React (TanStack Start) application that serves as the studio UI. Communicates with the cloud API via tRPC and with the local Go agent via HTTP REST + Connect-RPC.
Browser (Studio UI)
|
|-- tRPC --> Cloud API (Hono on Cloudflare Workers)
| |-- Better Auth (sessions, Polar payments)
| |-- Drizzle ORM --> Neon PostgreSQL
|
|-- HTTP/Connect-RPC --> Go Agent (localhost:9876)
|-- nix eval (config, options, packages)
|-- process-compose (service lifecycle)
|-- file system (secrets, config, code)
|-- Caddy (reverse proxy, TLS)
|-- Step CA (certificates)
| Component | Role |
|---|---|
| Agent | Go binary serving the GUI locally. Executes Nix commands. Works offline. |
| GUI (Studio) | Browser interface to configure stack, trigger builds, view status. |
| Flake | Source of truth. flake-parts modules define the entire stack. |
| Generated Files | Standard paths (.github/, Dockerfile). Git-tracked. CI works without Nix. |
All stackpanel logic lives in nix/stackpanel/ and has zero dependency on devenv, NixOS, or any other module system. Adapters translate the core outputs to their target:
nix/flake/default.nix- flake-parts adapternix/flake/modules/devenv.nix- devenv adapternix/flake/devenv.nix- standalone devenv adapter
User's flake.nix
-> imports flakeModules.default (nix/flake/default.nix)
-> auto-loads .stack/_internal.nix
-> lib.evalModules with nix/stackpanel/ (the core)
-> optionally imports .stack/devenv.nix into devenv.shells.default
-> creates devShells.default via pkgs.mkShell
All options are under options.stackpanel.*:
| Namespace | Purpose |
|---|---|
stackpanel.{enable, name, root, dirs} |
Project identity and paths |
stackpanel.apps / stackpanel.appsComputed |
App definitions and computed ports/URLs |
stackpanel.ports |
Deterministic port computation config |
stackpanel.services |
Canonical service type system |
stackpanel.globalServices |
Convenience service definitions (postgres, redis, minio) |
stackpanel.devshell |
Shell environment (packages, hooks, env, files) |
stackpanel.scripts |
Shell commands |
stackpanel.modules |
Extension module registry |
stackpanel.secrets |
Master-key secrets management |
stackpanel.ide |
VS Code and Zed integration |
stackpanel.theme |
Starship prompt theming |
stackpanel.step-ca |
Certificate management |
stackpanel.aws |
AWS Roles Anywhere |
stackpanel.process-compose |
Process orchestration |
Ports are computed from the project name hash, ensuring identical ports across all team members without manual configuration:
- Hash project name with MD5
- Convert first 8 hex chars to decimal
- Constrain to range
[3000, 65000), round to nearest 100 - Apps get sequential ports at
base + 0-9 - Services get stable ports via
hash(projectName + serviceName)within the project's port range
Environment variables: STACKPANEL_<KEY>_PORT (e.g., STACKPANEL_POSTGRES_PORT=6410).
- Framework: TanStack Start (SSR-capable React) + Vite + Cloudflare Workers
- Router: TanStack Router with file-based routing
- State: TanStack Query with SuperJSON serialization
- Cloud API: tRPC via
@stackpanel/api - Agent API: Dual protocol - HTTP REST and Connect-RPC
- Auth: Better-Auth client with JWT session management
- UI: Radix UI primitives + shadcn components + Tailwind CSS v4
Single Go binary with two modes:
CLI (Cobra commands):
stackpanel- Interactive TUI navigator (default)stackpanel services {start,stop,status,restart,logs}- Service managementstackpanel caddy {start,stop,status,add,remove}- Reverse proxystackpanel status- Status dashboardstackpanel agent- Start the HTTP agent server
Agent (localhost HTTP server, port 9876):
- JWT auth via popup pairing flow
- SSE event broadcasting on config changes
- File watching + FlakeWatcher
- 60+ REST API endpoints
- Framework: Next.js with Fumadocs (static export for Cloudflare)
- Content: MDX in
content/docs/ - Auto-generation from Nix options and Cobra commands
apps/web
-> @stackpanel/api (tRPC routers + types)
-> @stackpanel/auth (Better Auth + Polar payments)
-> @stackpanel/db (Drizzle ORM + Neon PostgreSQL)
-> @stackpanel/agent-client -> @stackpanel/proto (Connect-RPC types)
-> @stackpanel/ui -> @stackpanel/ui-web -> @stackpanel/ui-core + @stackpanel/ui-primitives
Three-tier system:
- Master keys: AGE-encrypted master keys (local auto-generated, team-shared)
- SOPS-encrypted YAML: Per-environment files (
dev.yaml,staging.yaml,prod.yaml) - Agenix: Per-secret
.agefiles encrypted to team member SSH public keys
| Path | Purpose |
|---|---|
config.nix |
Primary user-editable config |
_internal.nix |
Merge point for all config sources |
devenv.nix |
Devenv-specific config |
config.local.nix |
Per-user gitignored overrides |
data/*.nix |
Auto-loaded data tables |
secrets/ |
SOPS config, encrypted YAML files |
| Path | Purpose |
|---|---|
state/stackpanel.json |
Runtime state |
state/shellhook.sh |
Generated shell hook |
state/starship.toml |
Generated prompt config |
gen/ide/vscode/ |
VS Code workspace + devshell loader |
- Monorepo orchestration: Turborepo (JS/TS tasks) + Nix (dev environment)
- Package manager: Bun with workspaces
- Web deployment: Cloudflare Workers via Alchemy + Vite Cloudflare plugin
- Docs deployment: Static export to Cloudflare
- Go build:
gomod2nixfor Nix,airfor hot reload in dev - CI: GitHub Actions with OIDC-based AWS access
- Nix modules implement behavior once in
nix/stackpanel/lib/, called from thin adapter modules - The Go CLI is the single writer for generated files (avoids symlink issues, ensures atomicity)
- Proto definitions in
packages/proto/are the contract between Go agent and TypeScript web app - Environment variables follow the pattern
STACKPANEL_<KEY>_<PROPERTY> - All user-editable config uses YAML/Nix with JSON Schema validation for IDE intellisense
Plugins are just flake inputs:
inputs = {
stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel";
# Community plugins
stackpanel-aws.url = "github:someone/stackpanel-aws";
stackpanel-stripe.url = "github:someone/stackpanel-stripe";
};
imports = [
inputs.stackpanel.flakeModules.default
inputs.stackpanel-aws.flakeModules.default
];nix/stackpanel/
core/ # Options definitions, CLI invocation, state, utilities
devshell/ # Shell subsystem (scripts, files, codegen, bin wrappers)
network/ # Step CA certificates, port env vars
services/ # Service implementations (postgres, redis, caddy, aws)
secrets/ # Master-key encryption (agenix, SOPS, combined)
ide/ # VS Code and Zed file generation
modules/ # Auto-discovered directory modules
lib/ # Pure library functions
apps/ # App-level features and CI
tui/ # Terminal UI theme
packages/ # Nix package definitions