From fc9f061a3dbee6ac78035b4e0bd54839bf5d810a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 11:56:07 +0100 Subject: [PATCH 01/11] Make `stackpanel init` fetch from STACKPANEL_ROOT if available --- apps/stackpanel-go/cmd/cli/init.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/stackpanel-go/cmd/cli/init.go b/apps/stackpanel-go/cmd/cli/init.go index f818ae72..9da301c6 100644 --- a/apps/stackpanel-go/cmd/cli/init.go +++ b/apps/stackpanel-go/cmd/cli/init.go @@ -77,6 +77,11 @@ func runInit(cmd *cobra.Command, args []string) error { if flakeRef == "" { flakeRef = os.Getenv("STACKPANEL_FLAKE") } + if flakeRef == "" { + if root := os.Getenv("STACKPANEL_ROOT"); root != "" { + flakeRef = "path:" + root + } + } if flakeRef == "" { flakeRef = defaultStackpanelFlake } From afefc7e59e4ceee5f9afeb427cf89801eac005fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 11:56:24 +0100 Subject: [PATCH 02/11] Make `stackpanel init` fetch from private repository --- apps/stackpanel-go/cmd/cli/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stackpanel-go/cmd/cli/init.go b/apps/stackpanel-go/cmd/cli/init.go index 9da301c6..2db2d81e 100644 --- a/apps/stackpanel-go/cmd/cli/init.go +++ b/apps/stackpanel-go/cmd/cli/init.go @@ -19,7 +19,7 @@ import ( // defaultStackpanelFlake is the remote flake used to fetch init templates. // Override with --flake or STACKPANEL_FLAKE for local development. -const defaultStackpanelFlake = "github:darkmatter/stackpanel" +const defaultStackpanelFlake = "git+ssh://git@github.com/darkmatter/stackpanel" var initCmd = &cobra.Command{ Use: "init", From a80d0ed076d5ce436edc1c7c2e3d3a6e0e672ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 12:28:17 +0100 Subject: [PATCH 03/11] Replace boilerplate by newer template The template file contains a newer version of some of the scaffold files. `stackpanel init` now uses thoses. --- apps/stackpanel-go/pkg/nixeval/presets.go | 27 --- nix/flake/exports.nix | 11 +- nix/stackpanel/db/default.nix | 36 ---- nix/stackpanel/db/schemas/config.proto.nix | 193 +-------------------- 4 files changed, 11 insertions(+), 256 deletions(-) diff --git a/apps/stackpanel-go/pkg/nixeval/presets.go b/apps/stackpanel-go/pkg/nixeval/presets.go index 72bdaea2..21ae4b4f 100644 --- a/apps/stackpanel-go/pkg/nixeval/presets.go +++ b/apps/stackpanel-go/pkg/nixeval/presets.go @@ -80,16 +80,6 @@ in // This is the canonical path for user projects that consume stackpanel as a flake input. ActiveConfigPreset = `.#devShells.${builtins.currentSystem}.default.passthru.moduleConfig.stackpanel` - // InitFilesPreset evaluates the db module to get boilerplate files for project scaffolding - // Returns a map of relative paths to file contents - InitFilesPreset = ` -let - root = builtins.getEnv "STACKPANEL_ROOT"; - dbModule = import (root + "/nix/stackpanel/db") { }; -in - dbModule.initFiles -` - // DbSchemasPreset evaluates all schemas from the db module for codegen // Returns a map of entity names to JSON Schema DbSchemasPreset = ` @@ -275,23 +265,6 @@ func GetStackpanelConfig(ctx context.Context) (map[string]interface{}, error) { return config, nil } -// GetInitFiles evaluates the db module and returns the map of paths to content -// for scaffolding a new project. -// DEPRECATED: Use GetInitFilesFromFlake instead for portable scaffolding. -func GetInitFiles(ctx context.Context) (map[string]string, error) { - result, err := EvalExpr(ctx, InitFilesPreset) - if err != nil { - return nil, fmt.Errorf("failed to evaluate initFiles: %w", err) - } - - var files map[string]string - if err := result.Unmarshal(&files); err != nil { - return nil, fmt.Errorf("failed to parse initFiles: %w", err) - } - - return files, nil -} - // GetInitFilesFromFlake evaluates initFiles from a stackpanel flake reference. // This is the portable way to get scaffold templates - works from any directory. // diff --git a/nix/flake/exports.nix b/nix/flake/exports.nix index be80dc28..742a16d8 100644 --- a/nix/flake/exports.nix +++ b/nix/flake/exports.nix @@ -249,7 +249,16 @@ in # # Usage from Nix: # inputs.stackpanel.lib.initFiles - initFiles = (import ../stackpanel/db { }).initFiles; + initFiles = + let + templateDir = ../flake/templates/default/.stackpanel; + in + { + ".stackpanel/config.nix" = builtins.readFile (templateDir + "/config.nix"); + ".stackpanel/_internal.nix" = builtins.readFile (templateDir + "/_internal.nix"); + ".stackpanel/.gitignore" = builtins.readFile (templateDir + "/.gitignore"); + ".stackpanel/data.nix" = builtins.readFile (templateDir + "/data.nix"); + }; # All schemas for codegen/introspection # Usage: inputs.stackpanel.lib.schemas diff --git a/nix/stackpanel/db/default.nix b/nix/stackpanel/db/default.nix index d0a64dd4..121433cc 100644 --- a/nix/stackpanel/db/default.nix +++ b/nix/stackpanel/db/default.nix @@ -329,39 +329,6 @@ let } ''; - # ============================================================================ - # - # INTERNAL: Scaffolding / init file generation - # - # Generates the .stack/ directory structure for new projects. - # config.nix is the single source of truth (both user and agent editable). - # - # ============================================================================ - initFiles = - let - configBoilerplate = schemas.config.boilerplate or null; - configFile = { - ".stack/config.nix" = - if configBoilerplate != null then - configBoilerplate - else - '' - # Stackpanel project configuration - # See: https://stackpanel.dev/docs/config - { - enable = true; - name = "my-project"; - github = "owner/repo"; - } - ''; - }; - - gitignoreBoilerplate = schemas.config.gitignoreBoilerplate or null; - gitignoreFile = - if gitignoreBoilerplate != null then { ".stack/.gitignore" = gitignoreBoilerplate; } else { }; - - in - configFile // gitignoreFile; in { # ============================================================================ @@ -380,9 +347,6 @@ in # Entity name lists inherit entityNames dataEntityNames externalEntityNames; - # Scaffolding - inherit initFiles; - # Proto rendering inherit render protoFiles protoFileMap; diff --git a/nix/stackpanel/db/schemas/config.proto.nix b/nix/stackpanel/db/schemas/config.proto.nix index 39639337..37d8f42d 100644 --- a/nix/stackpanel/db/schemas/config.proto.nix +++ b/nix/stackpanel/db/schemas/config.proto.nix @@ -2,10 +2,7 @@ # config.proto.nix # # Protobuf schema for Stackpanel project configuration. -# This is the root configuration file at .stack/config.nix -# -# This file also contains boilerplate templates for scaffolding new projects. -# The `stackpanel scaffold` command and `nix flake init` templates use these. +# This is the root configuration file at .stackpanel/config.nix # ============================================================================== { lib }: let @@ -15,194 +12,6 @@ proto.mkProtoFile { name = "config.proto"; package = "stackpanel.db"; - # ========================================================================== - # User-editable config.nix boilerplate - # This is the main configuration file users edit - # ========================================================================== - boilerplate = '' - # ============================================================================== - # config.nix - # - # Stackpanel project configuration. - # Edit this file to configure your project. - # ============================================================================== - { - enable = true; - name = "my-project"; - github = "owner/repo"; - # debug = false; - - # --------------------------------------------------------------------------- - # CLI - Stackpanel command-line tools - # --------------------------------------------------------------------------- - cli.enable = true; - - # --------------------------------------------------------------------------- - # Theme - Starship prompt with stackpanel styling - # See: https://stackpanel.dev/docs/theme - # --------------------------------------------------------------------------- - theme.enable = true; - # theme = { - # name = "default"; - # nerd-font = true; - # minimal = false; - # - # colors = { - # primary = "#7aa2f7"; - # secondary = "#bb9af7"; - # success = "#9ece6a"; - # warning = "#e0af68"; - # error = "#f7768e"; - # muted = "#565f89"; - # }; - # - # starship = { - # add-newline = true; - # scan-timeout = 30; - # command-timeout = 500; - # }; - # }; - - # --------------------------------------------------------------------------- - # IDE Integration - Auto-generate editor config files - # --------------------------------------------------------------------------- - ide.enable = true; - ide.vscode.enable = true; - - # --------------------------------------------------------------------------- - # MOTD - Message of the day shown on shell entry - # --------------------------------------------------------------------------- - motd.enable = true; - motd.commands = [ - { - name = "dev"; - description = "Start development server"; - } - { - name = "build"; - description = "Build the project"; - } - ]; - - # --------------------------------------------------------------------------- - # Users - Team members with project access - # GitHub team members are auto-imported from data/github-collaborators.nix. - # Add overrides or additional users here. - # See: https://stackpanel.dev/docs/users - # --------------------------------------------------------------------------- - # users = { - # johndoe = { - # name = "John Doe"; - # github = "johndoe"; - # email = "john@example.com"; - # }; - # }; - - # --------------------------------------------------------------------------- - # AWS - AWS Roles Anywhere for certificate-based authentication - # See: https://stackpanel.dev/docs/aws - # --------------------------------------------------------------------------- - # aws = { - # roles-anywhere = { - # enable = true; - # region = "us-east-1"; - # account-id = "123456789012"; - # role-name = "DeveloperRole"; - # trust-anchor-arn = "arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/..."; - # profile-arn = "arn:aws:rolesanywhere:us-east-1:123456789012:profile/..."; - # cache-buffer-seconds = "300"; - # prompt-on-shell = true; - # }; - # }; - - # --------------------------------------------------------------------------- - # Step CA - Internal certificate management for local HTTPS - # See: https://stackpanel.dev/docs/step-ca - # --------------------------------------------------------------------------- - # step-ca = { - # enable = true; - # ca-url = "https://ca.internal:443"; - # ca-fingerprint = "abc123..."; # Root CA fingerprint for verification - # provisioner = "admin"; - # cert-name = "dev-workstation"; - # prompt-on-shell = true; - # }; - - # --------------------------------------------------------------------------- - # Secrets - Secrets management configuration - # See: https://stackpanel.dev/docs/secrets - # --------------------------------------------------------------------------- - # secrets = { - # enable = true; - # input-directory = ".stack/secrets"; - # - # # Code generation for type-safe env access - # codegen = { - # typescript = { - # name = "env"; - # directory = "packages/gen/env/src"; - # language = "typescript"; - # }; - # }; - # }; - - # --------------------------------------------------------------------------- - # SST - Infrastructure as code configuration - # See: https://stackpanel.dev/docs/sst - # --------------------------------------------------------------------------- - # sst = { - # enable = true; - # project-name = "my-project"; - # region = "us-west-2"; - # account-id = "123456789012"; - # config-path = "packages/infra/sst.config.ts"; - # - # kms = { - # enable = true; - # alias = "my-project-secrets"; - # }; - # - # oidc = { - # provider = "github-actions"; - # github-actions = { - # org = "my-org"; - # repo = "*"; - # }; - # }; - # }; - - # --------------------------------------------------------------------------- - # Global Services - Shared development services - # --------------------------------------------------------------------------- - # globalServices = { - # enable = true; - # project-name = "myproject"; - # postgres.enable = true; - # redis.enable = true; - # minio.enable = true; - # }; - - # --------------------------------------------------------------------------- - # Caddy - Local HTTPS reverse proxy - # --------------------------------------------------------------------------- - # caddy = { - # enable = true; - # project-name = "myproject"; - # }; - } - ''; - - # ========================================================================== - # .gitignore boilerplate - # ========================================================================== - gitignoreBoilerplate = '' - # Runtime state (gitignored) - state/ - - # Local config overrides (per-user, gitignored) - config.local.nix - ''; - options = { go_package = "github.com/darkmatter/stackpanel/packages/proto/gen/gopb"; }; From 0635d4453fdcc4fedc4409e12bd4dacbaff70fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 19:43:45 +0100 Subject: [PATCH 04/11] Replace url to the repo by private repo url We can change it back with global replace once released. --- README.md | 58 +++---- README.tmpl.md | 52 +++---- apps/docs/content/docs/contributing.mdx | 6 +- apps/docs/content/docs/install.mdx | 2 +- apps/docs/content/docs/quick-start.mdx | 8 +- apps/docs/src/app/(home)/page.tsx | 2 +- apps/stackpanel-go/cmd/cli/init.go | 50 +++--- apps/stackpanel-go/pkg/nixeval/presets.go | 146 +++++++----------- apps/web/src/components/agent-connect.tsx | 2 +- docs/ARCHITECTURE.md | 102 ++++++------ docs/MODULE_BOILERPLATE_INJECTION.md | 98 ++++++------ nix/flake/exports.nix | 2 +- nix/flake/templates/README.md | 56 +++---- nix/flake/templates/_test-fixtures/README.md | 83 ++-------- nix/flake/templates/default/flake.nix | 12 +- nix/flake/templates/devenv/devenv.yaml | 2 +- nix/flake/templates/minimal/flake.nix | 12 +- .../docs-content/content/guides/setup.mdx | 2 +- 18 files changed, 300 insertions(+), 395 deletions(-) diff --git a/README.md b/README.md index ae8b7991..fcaa1289 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@

- - - stack + + + stackpanel

Ship products, not plumbing.

- CI - Documentation - License + CI + Documentation + License

@@ -27,9 +27,9 @@ Configuration files, build tooling, environment setup - this is the bureaucracy **The value of your application lives in your source code, not configuration.** -## What Stack Does +## What Stackpanel Does -Stack provides a complete development infrastructure toolkit: +Stackpanel provides a complete development infrastructure toolkit: - **Zero-config dev environments** - Automatic setup for popular stacks with deterministic ports - **Secrets management** - Team-based encrypted secrets with AGE/SOPS @@ -40,7 +40,7 @@ Stack provides a complete development infrastructure toolkit: **No lock-in.** Generated files are standard formats in standard locations. Eject anytime with a normal codebase. -**No Nix knowledge required.** If you can write JSON, you can configure Stack. Or just use the web UI. +**No Nix knowledge required.** If you can write JSON, you can configure Stackpanel. Or just use the web UI. ## Quick Start @@ -53,7 +53,7 @@ Stack provides a complete development infrastructure toolkit: ```bash # Create a new project with the default template -nix flake init -t github:darkmatter/stack +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel # Set up direnv echo 'use flake . --impure' > .envrc @@ -72,24 +72,24 @@ nix develop --impure nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; devenv.url = "github:cachix/devenv"; - stack.url = "github:darkmatter/stack"; + stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel"; }; outputs = inputs @ {flake-parts, ...}: flake-parts.lib.mkFlake {inherit inputs;} { imports = [ inputs.devenv.flakeModule - inputs.stack.flakeModules.default + inputs.stackpanel.flakeModules.default ]; systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; perSystem = {pkgs, ...}: { devenv.shells.default = { - imports = [inputs.stack.devenvModules.default]; - + imports = [inputs.stackpanel.devenvModules.default]; + # Your config - stack.enable = true; + stackpanel.enable = true; packages = [pkgs.nodejs pkgs.bun]; }; }; @@ -99,24 +99,24 @@ nix develop --impure ## Configuration -Edit `.stack/config.nix` to configure your environment: +Edit `.stackpanel/config.nix` to configure your environment: ```nix { enable = true; - + # Themed shell prompt theme.enable = true; - + # VS Code integration ide.vscode.enable = true; - + # Global services globalServices = { postgres.enable = true; redis.enable = true; }; - + # AWS certificate auth # aws.roles-anywhere.enable = true; } @@ -141,9 +141,9 @@ my-project → base port 4200 Team-based secrets with AGE encryption: ```nix -stack.secrets = { +stackpanel.secrets = { master-key.enable = true; - + apps.api = { dev = { DATABASE_URL = "postgres://..."; @@ -157,7 +157,7 @@ stack.secrets = { Auto-generated VS Code workspace with: - Correct terminal environment -- Extension recommendations +- Extension recommendations - Debugger configurations - Task runners @@ -171,13 +171,13 @@ Local web UI at `localhost:9876` for: ## Documentation -Full documentation is available at [stack.dev](https://stack.dev/docs): +Full documentation is available at [stackpanel.dev](https://stackpanel.dev/docs): -- [Quick Start Guide](https://stack.dev/docs/quick-start) -- [Concepts](https://stack.dev/docs/concepts) -- [Configuration Reference](https://stack.dev/docs/reference) -- [Secrets Management](https://stack.dev/docs/features/secrets) -- [AWS Integration](https://stack.dev/docs/features/aws) +- [Quick Start Guide](https://stackpanel.dev/docs/quick-start) +- [Concepts](https://stackpanel.dev/docs/concepts) +- [Configuration Reference](https://stackpanel.dev/docs/reference) +- [Secrets Management](https://stackpanel.dev/docs/features/secrets) +- [AWS Integration](https://stackpanel.dev/docs/features/aws) ## Architecture diff --git a/README.tmpl.md b/README.tmpl.md index 845129ec..220bb12b 100644 --- a/README.tmpl.md +++ b/README.tmpl.md @@ -1,17 +1,17 @@

- - - stack + + + stackpanel

Ship products, not plumbing.

- CI - Documentation - License + CI + Documentation + License

@@ -27,9 +27,9 @@ Configuration files, build tooling, environment setup - this is the bureaucracy **The value of your application lives in your source code, not configuration.** -## What Stack Does +## What Stackpanel Does -Stack provides a complete development infrastructure toolkit: +Stackpanel provides a complete development infrastructure toolkit: - **Zero-config dev environments** - Automatic setup for popular stacks with deterministic ports - **Secrets management** - Team-based encrypted secrets with AGE/SOPS @@ -40,7 +40,7 @@ Stack provides a complete development infrastructure toolkit: **No lock-in.** Generated files are standard formats in standard locations. Eject anytime with a normal codebase. -**No Nix knowledge required.** If you can write JSON, you can configure Stack. Or just use the web UI. +**No Nix knowledge required.** If you can write JSON, you can configure Stackpanel. Or just use the web UI. ## Quick Start @@ -53,7 +53,7 @@ Stack provides a complete development infrastructure toolkit: ```bash # Create a new project with the default template -nix flake init -t github:darkmatter/stack +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel # Set up direnv echo 'use flake . --impure' > .envrc @@ -72,24 +72,24 @@ nix develop --impure nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; devenv.url = "github:cachix/devenv"; - stack.url = "github:darkmatter/stack"; + stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel"; }; outputs = inputs @ {flake-parts, ...}: flake-parts.lib.mkFlake {inherit inputs;} { imports = [ inputs.devenv.flakeModule - inputs.stack.flakeModules.default + inputs.stackpanel.flakeModules.default ]; systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; perSystem = {pkgs, ...}: { devenv.shells.default = { - imports = [inputs.stack.devenvModules.default]; - + imports = [inputs.stackpanel.devenvModules.default]; + # Your config - stack.enable = true; + stackpanel.enable = true; packages = [pkgs.nodejs pkgs.bun]; }; }; @@ -99,24 +99,24 @@ nix develop --impure ## Configuration -Edit `.stack/config.nix` to configure your environment: +Edit `.stackpanel/config.nix` to configure your environment: ```nix { enable = true; - + # Themed shell prompt theme.enable = true; - + # VS Code integration ide.vscode.enable = true; - + # Global services globalServices = { postgres.enable = true; redis.enable = true; }; - + # AWS certificate auth # aws.roles-anywhere.enable = true; } @@ -124,13 +124,13 @@ Edit `.stack/config.nix` to configure your environment: ## Documentation -Full documentation is available at [stack.dev](https://stack.dev/docs): +Full documentation is available at [stackpanel.dev](https://stackpanel.dev/docs): -- [Quick Start Guide](https://stack.dev/docs/quick-start) -- [Concepts](https://stack.dev/docs/concepts) -- [Configuration Reference](https://stack.dev/docs/reference) -- [Secrets Management](https://stack.dev/docs/features/secrets) -- [AWS Integration](https://stack.dev/docs/features/aws) +- [Quick Start Guide](https://stackpanel.dev/docs/quick-start) +- [Concepts](https://stackpanel.dev/docs/concepts) +- [Configuration Reference](https://stackpanel.dev/docs/reference) +- [Secrets Management](https://stackpanel.dev/docs/features/secrets) +- [AWS Integration](https://stackpanel.dev/docs/features/aws) ## Architecture diff --git a/apps/docs/content/docs/contributing.mdx b/apps/docs/content/docs/contributing.mdx index 59a98eb9..379c4fe5 100644 --- a/apps/docs/content/docs/contributing.mdx +++ b/apps/docs/content/docs/contributing.mdx @@ -9,12 +9,12 @@ icon: heart ```bash tmpdir=$(mktemp -d) cd $tmpdir -nix flake init -t github:darkmatter/stackpanel#minimal +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#minimal # or locally nix flake init -t /Users/coopermaruyama/darkmatter/stackpanel#default git init && git add -A -# 4. Update the flake to use local stackpanel (since github:darkmatter/stackpanel isn't public) -sed 's|github:darkmatter/stackpanel|path:/Users/coopermaruyama/darkmatter/stackpanel|' flake.nix > flake.nix.tmp && mv flake.nix.tmp flake.nix +# 4. Update the flake to use local stackpanel (since git+ssh://git@github.com/darkmatter/stackpanel isn't public) +sed 's|git+ssh://git@github.com/darkmatter/stackpanel|path:/Users/coopermaruyama/darkmatter/stackpanel|' flake.nix > flake.nix.tmp && mv flake.nix.tmp flake.nix ``` \ No newline at end of file diff --git a/apps/docs/content/docs/install.mdx b/apps/docs/content/docs/install.mdx index 85588e1f..20f11888 100644 --- a/apps/docs/content/docs/install.mdx +++ b/apps/docs/content/docs/install.mdx @@ -11,5 +11,5 @@ Add to your `devenv.yaml`: ```yaml inputs: stackpanel: - url: github:darkmatter/stackpanel + url: git+ssh://git@github.com/darkmatter/stackpanel ``` \ No newline at end of file diff --git a/apps/docs/content/docs/quick-start.mdx b/apps/docs/content/docs/quick-start.mdx index f88dc7ce..c8ea8693 100644 --- a/apps/docs/content/docs/quick-start.mdx +++ b/apps/docs/content/docs/quick-start.mdx @@ -40,7 +40,7 @@ If you're new to Nix, we recommend using the [Determinate Nix Installer](https:/ ```bash -nix flake init -t github:darkmatter/stackpanel +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel ``` This creates: @@ -51,7 +51,7 @@ This creates: ```bash -nix flake init -t github:darkmatter/stackpanel#devenv +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#devenv ``` This creates: @@ -64,7 +64,7 @@ For users who prefer `devenv shell` over `nix develop`. ```bash -nix flake init -t github:darkmatter/stackpanel#minimal +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#minimal ``` This creates: @@ -86,7 +86,7 @@ Or add to an existing project manually: nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; devenv.url = "github:cachix/devenv"; - stackpanel.url = "github:darkmatter/stackpanel"; + stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel"; nix2container.url = "github:nlewo/nix2container"; nix2container.inputs.nixpkgs.follows = "nixpkgs"; mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin"; diff --git a/apps/docs/src/app/(home)/page.tsx b/apps/docs/src/app/(home)/page.tsx index 62a95184..18209bfe 100644 --- a/apps/docs/src/app/(home)/page.tsx +++ b/apps/docs/src/app/(home)/page.tsx @@ -122,7 +122,7 @@ export default function HomePage() {

               {`inputs:
   stackpanel:
-    url: github:darkmatter/stackpanel
+    url: git+ssh://git@github.com/darkmatter/stackpanel
 
 imports:
   - stackpanel`}
diff --git a/apps/stackpanel-go/cmd/cli/init.go b/apps/stackpanel-go/cmd/cli/init.go
index 2db2d81e..1cdfd107 100644
--- a/apps/stackpanel-go/cmd/cli/init.go
+++ b/apps/stackpanel-go/cmd/cli/init.go
@@ -1,7 +1,3 @@
-// init.go implements `stackpanel init`, which scaffolds a new project by
-// evaluating the stackpanel flake's initFiles output and writing templates
-// into a .stack/ directory.
-
 package cmd
 
 import (
@@ -17,24 +13,24 @@ import (
 	"github.com/spf13/cobra"
 )
 
-// defaultStackpanelFlake is the remote flake used to fetch init templates.
-// Override with --flake or STACKPANEL_FLAKE for local development.
+// Default flake reference for stackpanel
+// Users can override with --flake flag or STACKPANEL_FLAKE env var
 const defaultStackpanelFlake = "git+ssh://git@github.com/darkmatter/stackpanel"
 
 var initCmd = &cobra.Command{
 	Use:   "init",
 	Short: "Initialize a new stackpanel project",
-	Long: `Initialize creates the .stack directory structure with boilerplate files.
+	Long: `Initialize creates the .stackpanel directory structure with boilerplate files.
 
 This command evaluates the stackpanel flake to get the list of files to create,
 then writes them to the appropriate locations. Existing files are NOT overwritten
 unless --force is specified.
 
 The initialized structure includes:
-  - .stack/config.nix       User-editable configuration file
-  - .stack/_internal.nix    Internal merging logic (do not edit)
-  - .stack/data.nix         Agent-editable data file
-  - .stack/.gitignore       Gitignore for state and local config
+  - .stackpanel/config.nix       User-editable configuration file
+  - .stackpanel/_internal.nix    Internal merging logic (do not edit)
+  - .stackpanel/data.nix         Agent-editable data file
+  - .stackpanel/.gitignore       Gitignore for state and local config
 
 Example:
   stackpanel init                              # Create files in current directory
@@ -53,7 +49,7 @@ var (
 func init() {
 	initCmd.Flags().BoolVar(&initForce, "force", false, "Overwrite existing files")
 	initCmd.Flags().BoolVar(&initDryRun, "dry-run", false, "Show what would be created without writing files")
-	initCmd.Flags().StringVar(&initFlake, "flake", "", "Stackpanel flake reference (default: github:darkmatter/stackpanel)")
+	initCmd.Flags().StringVar(&initFlake, "flake", "", "Stackpanel flake reference (default: git+ssh://git@github.com/darkmatter/stackpanel)")
 
 	rootCmd.AddCommand(initCmd)
 }
@@ -62,7 +58,7 @@ func runInit(cmd *cobra.Command, args []string) error {
 	verbose, _ := cmd.Flags().GetBool("verbose")
 	ctx := context.Background()
 
-	// Determine target directory (where to create .stack/)
+	// Determine target directory (where to create .stackpanel/)
 	targetDir, err := os.Getwd()
 	if err != nil {
 		return fmt.Errorf("failed to get current directory: %w", err)
@@ -179,9 +175,9 @@ func runInit(cmd *cobra.Command, args []string) error {
 		if created > 0 {
 			fmt.Println()
 			output.Info("Next steps:")
-			output.Dimmed("  1. Review the generated files in .stack/")
-			output.Dimmed("  2. Edit .stack/config.nix to configure your project")
-			output.Dimmed("  3. Create a flake.nix that imports stackpanel (or use 'nix flake init -t github:darkmatter/stackpanel')")
+			output.Dimmed("  1. Review the generated files in .stackpanel/")
+			output.Dimmed("  2. Edit .stackpanel/config.nix to configure your project")
+			output.Dimmed("  3. Create a flake.nix that imports stackpanel (or use 'nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel')")
 			output.Dimmed("  4. Run 'nix develop --impure' to enter the dev shell")
 		}
 	}
@@ -189,9 +185,7 @@ func runInit(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-// registerInitProject adds the initialized project to the user-global config
-// (~/.config/stackpanel/stackpanel.yaml). This lets the agent discover and
-// serve this project without requiring the user to be in its directory.
+// registerInitProject adds the initialized project to the user config.
 func registerInitProject(projectRoot string) error {
 	ucm, err := userconfig.NewManager()
 	if err != nil {
@@ -211,14 +205,13 @@ func registerInitProject(projectRoot string) error {
 
 // getInitFilesFromFlake evaluates initFiles from a stackpanel flake reference.
 // The flakeRef can be:
-//   - "github:darkmatter/stackpanel" (default, from GitHub)
+//   - "git+ssh://git@github.com/darkmatter/stackpanel" (default, from GitHub)
 //   - "path:/local/path/to/stackpanel" (for local development)
 //   - "git+file:///local/path/to/stackpanel" (faster local, uses git filtering)
 //   - Any valid Nix flake reference
 //
-// Gotcha: "path:" references copy the entire directory tree into the Nix store,
-// which is very slow for large repos. "git+file://" uses git's index to filter
-// to tracked files only, making it dramatically faster for local development.
+// Note: "path:" references are automatically converted to "git+file://" for better
+// performance (uses git to filter files instead of copying everything).
 func getInitFilesFromFlake(ctx context.Context, flakeRef string) (map[string]string, error) {
 	// Convert path: to git+file:// for better performance
 	// path: copies the entire directory, git+file:// uses git to filter
@@ -230,9 +223,8 @@ func getInitFilesFromFlake(ctx context.Context, flakeRef string) (map[string]str
 	return nixeval.GetInitFilesFromFlake(ctx, flakeRef)
 }
 
-// findProjectRoot walks up the directory tree to find the nearest project root.
-// A directory counts as a project root if it contains either flake.nix or
-// .stack/ — this covers both flake-based and standalone configurations.
+// findProjectRoot walks up the directory tree to find a project root
+// (directory containing flake.nix or .stackpanel)
 func findProjectRoot() (string, error) {
 	dir, err := os.Getwd()
 	if err != nil {
@@ -244,15 +236,15 @@ func findProjectRoot() (string, error) {
 		if _, err := os.Stat(filepath.Join(dir, "flake.nix")); err == nil {
 			return dir, nil
 		}
-		// Check for .stack
-		if _, err := os.Stat(filepath.Join(dir, ".stack")); err == nil {
+		// Check for .stackpanel
+		if _, err := os.Stat(filepath.Join(dir, ".stackpanel")); err == nil {
 			return dir, nil
 		}
 
 		parent := filepath.Dir(dir)
 		if parent == dir {
 			// Reached root without finding project
-			return "", fmt.Errorf("no project root found (looking for flake.nix or .stack)")
+			return "", fmt.Errorf("no project root found (looking for flake.nix or .stackpanel)")
 		}
 		dir = parent
 	}
diff --git a/apps/stackpanel-go/pkg/nixeval/presets.go b/apps/stackpanel-go/pkg/nixeval/presets.go
index 21ae4b4f..e1027416 100644
--- a/apps/stackpanel-go/pkg/nixeval/presets.go
+++ b/apps/stackpanel-go/pkg/nixeval/presets.go
@@ -11,19 +11,15 @@ import (
 	"time"
 )
 
-// Preset Nix expressions for common evaluations.
-// These are self-contained snippets passed to `nix eval --impure --expr`.
-// They rely on STACKPANEL_ROOT being set (via builtins.getEnv) to locate
-// project files. The .stack/ path is tried first with .stackpanel/ as legacy fallback.
+// Preset Nix expressions for common evaluations
+// These are self-contained snippets that can be evaluated directly
 const (
-	// UsersPreset evaluates the users configuration from .stack/data or .stackpanel/data
+	// UsersPreset evaluates the users configuration from .stackpanel/data/users.nix
 	// Returns the users attrset in stackpanel.users format
 	UsersPreset = `
 let
   root = builtins.getEnv "STACKPANEL_ROOT";
-  usersStack = root + "/.stack/data/users.nix";
-  usersLegacy = root + "/.stackpanel/data/users.nix";
-  usersPath = if builtins.pathExists usersStack then usersStack else usersLegacy;
+  usersPath = root + "/.stackpanel/data/users.nix";
 in
   if builtins.pathExists usersPath
   then import usersPath
@@ -34,50 +30,29 @@ in
 	GitHubCollaboratorsPreset = `
 let
   root = builtins.getEnv "STACKPANEL_ROOT";
-  collabsStack = root + "/.stack/data/github-collaborators.nix";
-  collabsLegacy = root + "/.stackpanel/data/github-collaborators.nix";
-  collabsPath = if builtins.pathExists collabsStack then collabsStack else collabsLegacy;
+  collabsPath = root + "/.stackpanel/data/github-collaborators.nix";
 in
   if builtins.pathExists collabsPath
   then import collabsPath
   else { collaborators = {}; }
 `
 
-	// StackpanelConfigPreset evaluates the full stackpanel config from .stack or .stackpanel.
-	// Note: config.nix may be a plain attrset or a function taking { pkgs, lib, ... }.
-	// We pass nulls for pkgs/lib since this lightweight eval can't provide them --
-	// configs that depend on pkgs won't work here (use the flake-based presets instead).
+	// StackpanelConfigPreset evaluates the full stackpanel config from .stackpanel/config.nix
 	StackpanelConfigPreset = `
 let
   root = builtins.getEnv "STACKPANEL_ROOT";
-  configStack = root + "/.stack/config.nix";
-  configLegacy = root + "/.stackpanel/config.nix";
-  configPath = if builtins.pathExists configStack then configStack else configLegacy;
-  rawConfig =
-    if builtins.pathExists configPath
-    then import configPath
-    else {};
-  evaluatedConfig =
-    if builtins.isFunction rawConfig
-    then rawConfig {
-      pkgs = null;
-      lib = null;
-      inputs = {};
-      self = null;
-      config = evaluatedConfig;
-    }
-    else rawConfig;
+  configPath = root + "/.stackpanel/config.nix";
 in
-  evaluatedConfig.stackpanel or evaluatedConfig or {}
+  if builtins.pathExists configPath
+  then (import configPath { pkgs = null; lib = null; config = {}; inputs = {}; }).stackpanel or {}
+  else {}
 `
 
-	// StackpanelSerializablePreset evaluates the full config from the flake's top-level
-	// output. Unlike the preset above, this goes through the full module system and
-	// includes computed values (ports, URLs, commands). Requires a valid flake.nix.
+	// StackpanelSerializablePreset evaluates the serializable config from the flake output
+	// This includes computed values like devshell._commandsSerializable
 	StackpanelSerializablePreset = `.#stackpanelConfig`
 
-	// ActiveConfigPreset reads the config attached to the default devshell's passthru.
-	// This is the canonical path for user projects that consume stackpanel as a flake input.
+	// ActiveConfig returns the current active stackpanel configuration as JSON (evaluated)
 	ActiveConfigPreset = `.#devShells.${builtins.currentSystem}.default.passthru.moduleConfig.stackpanel`
 
 	// DbSchemasPreset evaluates all schemas from the db module for codegen
@@ -91,10 +66,11 @@ in
 `
 )
 
-// InstalledPackagesExpr builds a Nix expression that extracts the package list
-// from a flake. It bakes the absolute projectRoot into the expression as a
-// git+file:// URI -- this avoids copying the entire worktree into the Nix store
-// (which would include node_modules, .git, etc.) and makes evaluation much faster.
+// InstalledPackagesExpr builds a Nix expression to get installed packages from a flake.
+// Tries devshell passthru first (for user projects consuming stackpanel), then falls back
+// to flake outputs (for stackpanel repo itself).
+// The projectRoot is baked directly into the expression to avoid relying on environment variables.
+// Uses git+file:// protocol to avoid copying untracked files (node_modules, etc.)
 func InstalledPackagesExpr(projectRoot string) string {
 	return fmt.Sprintf(`
 let
@@ -114,25 +90,24 @@ in
 `, projectRoot)
 }
 
-// EvalExprResult holds the raw JSON bytes from a nix eval invocation.
-// Use [EvalExprResult.Unmarshal] to decode into a typed Go struct.
+// EvalExprResult holds the raw JSON result of a Nix expression evaluation
 type EvalExprResult struct {
 	Raw json.RawMessage
 }
 
-// Unmarshal decodes the raw JSON into the provided value.
+// Unmarshal decodes the result into the provided type
 func (r *EvalExprResult) Unmarshal(v interface{}) error {
 	return json.Unmarshal(r.Raw, v)
 }
 
-// findNixBin locates the nix binary. PATH is checked first; if that fails
-// (e.g. running from a non-interactive context like launchd or systemd),
-// common Nix installation paths are tried as a fallback.
+// findNixBin locates the nix binary, checking PATH first then common locations
 func findNixBin() (string, error) {
+	// Check PATH first
 	if path, err := exec.LookPath("nix"); err == nil {
 		return path, nil
 	}
 
+	// Check common locations
 	commonPaths := []string{
 		"/nix/var/nix/profiles/default/bin/nix",
 		"/run/current-system/sw/bin/nix",
@@ -248,9 +223,8 @@ func GetGitHubCollaborators(ctx context.Context) (*GitHubCollaboratorsData, erro
 	return &data, nil
 }
 
-// GetStackpanelConfig evaluates the stackpanel section from .stack/config.nix.
-// This is a lightweight eval that passes null for pkgs/lib, so configs using
-// pkgs (e.g. for package references) will fail. Use the flake-based path for full eval.
+// GetStackpanelConfig evaluates the stackpanel section from .stackpanel/config.nix
+// Note: This is a simplified evaluation that doesn't have access to pkgs/lib
 func GetStackpanelConfig(ctx context.Context) (map[string]interface{}, error) {
 	result, err := EvalExpr(ctx, StackpanelConfigPreset)
 	if err != nil {
@@ -269,14 +243,14 @@ func GetStackpanelConfig(ctx context.Context) (map[string]interface{}, error) {
 // This is the portable way to get scaffold templates - works from any directory.
 //
 // The flakeRef can be:
-//   - "github:darkmatter/stackpanel" (from GitHub)
+//   - "git+ssh://git@github.com/darkmatter/stackpanel" (from GitHub)
 //   - "path:/local/path/to/stackpanel" (for local development)
 //   - Any valid Nix flake reference
 //
 // Example:
 //
-//	files, err := GetInitFilesFromFlake(ctx, "github:darkmatter/stackpanel")
-//	// files[".stack/config.nix"] = "..."
+//	files, err := GetInitFilesFromFlake(ctx, "git+ssh://git@github.com/darkmatter/stackpanel")
+//	// files[".stackpanel/config.nix"] = "..."
 func GetInitFilesFromFlake(ctx context.Context, flakeRef string) (map[string]string, error) {
 	// Evaluate #lib.initFiles
 	flakeAttr := flakeRef + "#lib.initFiles"
@@ -338,15 +312,12 @@ func GetDbSchemas(ctx context.Context) (map[string]interface{}, error) {
 	return schemas, nil
 }
 
-// BuildExpr performs simple ${key} substitution on a Nix expression template.
-// Values are escaped for safe embedding in Nix double-quoted strings (backslashes,
-// quotes, and ${ interpolation sequences are all escaped).
-//
-// This is intentionally not a full template engine -- for complex expressions,
-// prefer --argstr or build the expression programmatically.
+// BuildExpr builds a Nix expression from a template with variables
+// Variables are substituted as string interpolations
 func BuildExpr(template string, vars map[string]string) string {
 	result := template
 	for k, v := range vars {
+		// Escape the value for Nix string embedding
 		escaped := strings.ReplaceAll(v, "\\", "\\\\")
 		escaped = strings.ReplaceAll(escaped, "\"", "\\\"")
 		escaped = strings.ReplaceAll(escaped, "${", "\\${")
@@ -371,19 +342,16 @@ type GetInstalledPackagesOptions struct {
 	ConfigJSONPath string
 }
 
-// GetInstalledPackages returns the list of packages in the devshell. It uses
-// a waterfall of increasingly expensive strategies:
-//
-//  1. Explicit configJSONPath (from options)
+// GetInstalledPackages returns the list of installed packages from the devshell configuration.
+// It tries multiple fast paths before falling back to slow nix eval:
+//  1. Explicit configJSONPath (if provided in options)
 //  2. STACKPANEL_CONFIG_JSON env var (pre-computed at shell entry)
-//  3. State file at .stack/profile/stackpanel.json
-//  4. Generated config at .stack/gen/config.json
-//  5. Raw packages.nix data file (user-installed only, no devshell packages)
-//  6. Full flake evaluation (slow -- may download from caches, 5min timeout)
+//  3. State file at .stackpanel/state/stackpanel.json
+//  4. Generated config at .stackpanel/gen/config.json
+//  5. Nix eval against the flake (slow, last resort)
 //
-// The fast paths (1-4) read pre-generated JSON from disk. The slow path (6)
-// evaluates the actual flake, which triggers a full Nix build if the evaluation
-// requires IFD (import-from-derivation).
+// If projectRoot is empty, it will attempt to find it from STACKPANEL_ROOT env var
+// or by searching up from the current directory.
 func GetInstalledPackages(ctx context.Context, opts GetInstalledPackagesOptions) ([]InstalledPackage, error) {
 	projectRoot := opts.ProjectRoot
 
@@ -408,23 +376,21 @@ func GetInstalledPackages(ctx context.Context, opts GetInstalledPackagesOptions)
 		}
 	}
 
-	// Fast path 3: try state file (.stack/profile then .stackpanel/state)
+	// Fast path 3: try state file
 	if projectRoot != "" {
-		for _, p := range []string{projectRoot + "/.stack/profile/stackpanel.json", projectRoot + "/.stackpanel/state/stackpanel.json"} {
-			packages, err := getInstalledPackagesFromJSON(p)
-			if err == nil && len(packages) > 0 {
-				return packages, nil
-			}
+		stateFile := projectRoot + "/.stackpanel/state/stackpanel.json"
+		packages, err := getInstalledPackagesFromJSON(stateFile)
+		if err == nil && len(packages) > 0 {
+			return packages, nil
 		}
 	}
 
-	// Fast path 4: try generated config (.stack/gen then .stackpanel/gen)
+	// Fast path 4: try generated config
 	if projectRoot != "" {
-		for _, p := range []string{projectRoot + "/.stack/gen/config.json", projectRoot + "/.stackpanel/gen/config.json"} {
-			packages, err := getInstalledPackagesFromJSON(p)
-			if err == nil && len(packages) > 0 {
-				return packages, nil
-			}
+		genConfig := projectRoot + "/.stackpanel/gen/config.json"
+		packages, err := getInstalledPackagesFromJSON(genConfig)
+		if err == nil && len(packages) > 0 {
+			return packages, nil
 		}
 	}
 
@@ -475,16 +441,15 @@ func getInstalledPackagesFromJSON(configPath string) ([]InstalledPackage, error)
 	return config.Packages, nil
 }
 
-// getUserPackagesFromDataFile reads user-installed packages from .stack/data or .stackpanel/data
+// getUserPackagesFromDataFile reads user-installed packages from .stackpanel/data/packages.nix
 // This is a fallback when the config JSON doesn't have packages or doesn't exist yet.
 // The file format is a simple Nix list of attribute path strings:
 //
 //	[ "ripgrep" "jq" "htop" ]
 func getUserPackagesFromDataFile(projectRoot string) ([]InstalledPackage, error) {
-	packagesFile := projectRoot + "/.stack/data/packages.nix"
-	if _, err := os.Stat(packagesFile); os.IsNotExist(err) {
-		packagesFile = projectRoot + "/.stackpanel/data/packages.nix"
-	}
+	packagesFile := projectRoot + "/.stackpanel/data/packages.nix"
+
+	// Check if file exists
 	if _, err := os.Stat(packagesFile); os.IsNotExist(err) {
 		return nil, err
 	}
@@ -526,9 +491,8 @@ func getUserPackagesFromDataFile(projectRoot string) ([]InstalledPackage, error)
 	return packages, nil
 }
 
-// GetInstalledPackageNames returns a set of lowercased package names and attr
-// paths for O(1) membership checks. Both Name and AttrPath are indexed so
-// lookups work regardless of which identifier the caller has.
+// GetInstalledPackageNames returns just the package names as a set for fast lookup.
+// If projectRoot is empty, it will attempt to find it automatically.
 func GetInstalledPackageNames(ctx context.Context, opts GetInstalledPackagesOptions) (map[string]bool, error) {
 	packages, err := GetInstalledPackages(ctx, opts)
 	if err != nil {
diff --git a/apps/web/src/components/agent-connect.tsx b/apps/web/src/components/agent-connect.tsx
index 41526ccc..9331d8fe 100644
--- a/apps/web/src/components/agent-connect.tsx
+++ b/apps/web/src/components/agent-connect.tsx
@@ -229,7 +229,7 @@ export function AgentConnect({ onConnected }: AgentConnectProps) {
 								
 							
 							
-								nix profile install github:darkmatter/stackpanel
+								nix profile install git+ssh://git@github.com/darkmatter/stackpanel
 							
 						
 						
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 882590d9..0f8a211e 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,10 +1,10 @@ -# Stack Architecture +# Stackpanel Architecture -This document describes the internal architecture of Stack for contributors and those interested in understanding how the system works. +This document describes the internal architecture of Stackpanel for contributors and those interested in understanding how the system works. ## Overview -Stack 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. +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. ## Runtime Architecture @@ -42,7 +42,7 @@ Browser (Studio UI) ### Core Design: Adapter-Agnostic Core + Thin Adapters -All stack logic lives in `nix/stack/` and has zero dependency on devenv, NixOS, or any other module system. Adapters translate the core outputs to their target: +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 adapter - `nix/flake/modules/devenv.nix` - devenv adapter @@ -53,32 +53,32 @@ All stack logic lives in `nix/stack/` and has zero dependency on devenv, NixOS, ``` User's flake.nix -> imports flakeModules.default (nix/flake/default.nix) - -> auto-loads .stack/_internal.nix - -> lib.evalModules with nix/stack/ (the core) - -> optionally imports .stack/devenv.nix into devenv.shells.default + -> auto-loads .stackpanel/_internal.nix + -> lib.evalModules with nix/stackpanel/ (the core) + -> optionally imports .stackpanel/devenv.nix into devenv.shells.default -> creates devShells.default via pkgs.mkShell ``` ### Option Namespaces -All options are under `options.stack.*`: +All options are under `options.stackpanel.*`: | Namespace | Purpose | |-----------|---------| -| `stack.{enable, name, root, dirs}` | Project identity and paths | -| `stack.apps` / `stack.appsComputed` | App definitions and computed ports/URLs | -| `stack.ports` | Deterministic port computation config | -| `stack.services` | Canonical service type system | -| `stack.globalServices` | Convenience service definitions (postgres, redis, minio) | -| `stack.devshell` | Shell environment (packages, hooks, env, files) | -| `stack.scripts` | Shell commands | -| `stack.modules` | Extension module registry | -| `stack.secrets` | Master-key secrets management | -| `stack.ide` | VS Code and Zed integration | -| `stack.theme` | Starship prompt theming | -| `stack.step-ca` | Certificate management | -| `stack.aws` | AWS Roles Anywhere | -| `stack.process-compose` | Process orchestration | +| `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 | ## Deterministic Port System @@ -99,21 +99,21 @@ Environment variables: `STACKPANEL__PORT` (e.g., `STACKPANEL_POSTGRES_PORT= - **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 `@stack/api` +- **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 -### apps/stack-go/ - CLI + Agent (Go) +### apps/stackpanel-go/ - CLI + Agent (Go) Single Go binary with two modes: **CLI** (Cobra commands): -- `stack` - Interactive TUI navigator (default) -- `stack services {start,stop,status,restart,logs}` - Service management -- `stack caddy {start,stop,status,add,remove}` - Reverse proxy -- `stack status` - Status dashboard -- `stack agent` - Start the HTTP agent server +- `stackpanel` - Interactive TUI navigator (default) +- `stackpanel services {start,stop,status,restart,logs}` - Service management +- `stackpanel caddy {start,stop,status,add,remove}` - Reverse proxy +- `stackpanel status` - Status dashboard +- `stackpanel agent` - Start the HTTP agent server **Agent** (localhost HTTP server, port 9876): - JWT auth via popup pairing flow @@ -131,11 +131,11 @@ Single Go binary with two modes: ``` apps/web - -> @stack/api (tRPC routers + types) - -> @stack/auth (Better Auth + Polar payments) - -> @stack/db (Drizzle ORM + Neon PostgreSQL) - -> @stack/agent-client -> @stack/proto (Connect-RPC types) - -> @stack/ui-web (shadcn components) -> @stack/ui-core (cn, cva, Logo) + @radix-ui/* + -> @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 ``` ## Secrets Architecture @@ -146,7 +146,7 @@ Three-tier system: 2. **SOPS-encrypted YAML**: Per-environment files (`dev.yaml`, `staging.yaml`, `prod.yaml`) 3. **Agenix**: Per-secret `.age` files encrypted to team member SSH public keys -## .stack/ Configuration +## .stackpanel/ Configuration ### Files @@ -163,7 +163,7 @@ Three-tier system: | Path | Purpose | |------|---------| -| `state/stack.json` | Runtime state | +| `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 | @@ -179,7 +179,7 @@ Three-tier system: ## Key Conventions -- Nix modules implement behavior once in `nix/stack/lib/`, called from thin adapter modules +- 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__` @@ -191,36 +191,32 @@ Plugins are just flake inputs: ```nix inputs = { - stack.url = "github:darkmatter/stack"; + stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel"; # Community plugins - stack-aws.url = "github:someone/stack-aws"; - stack-stripe.url = "github:someone/stack-stripe"; + stackpanel-aws.url = "github:someone/stackpanel-aws"; + stackpanel-stripe.url = "github:someone/stackpanel-stripe"; }; imports = [ - inputs.stack.flakeModules.default - inputs.stack-aws.flakeModules.default + inputs.stackpanel.flakeModules.default + inputs.stackpanel-aws.flakeModules.default ]; ``` ## Directory Structure ``` -nix/stack/ - core/ # Core schema, CLI invocation, state, utilities - options/ # Centralized option definitions (multi-consumer options only) - lib/ # Pure service library functions (mkGlobalServices, etc.) - cli.nix # CLI-based file generation (includes cli options) - state.nix # Legacy state file generation (includes state options) +nix/stackpanel/ + core/ # Options definitions, CLI invocation, state, utilities devshell/ # Shell subsystem (scripts, files, codegen, bin wrappers) - network/ # Step CA certificates, port env vars, DNS (includes dns options) - services/ # Service implementations (postgres, redis, caddy, aws, binary-cache w/ options) + 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 (ports, theme, IDE, paths) - apps/ # App-level features and CI (includes ci options) - tui/ # Terminal UI theme (includes theme options) + lib/ # Pure library functions + apps/ # App-level features and CI + tui/ # Terminal UI theme packages/ # Nix package definitions ``` diff --git a/docs/MODULE_BOILERPLATE_INJECTION.md b/docs/MODULE_BOILERPLATE_INJECTION.md index e78e281d..dbcd2648 100644 --- a/docs/MODULE_BOILERPLATE_INJECTION.md +++ b/docs/MODULE_BOILERPLATE_INJECTION.md @@ -2,16 +2,16 @@ ## Overview -When users install a stack module (via CLI or web UI), the module's configuration boilerplate is automatically injected into their config files. This provides a smooth onboarding experience with commented examples ready to customize. +When users install a stackpanel module (via CLI or web UI), the module's configuration boilerplate is automatically injected into their config files. This provides a smooth onboarding experience with commented examples ready to customize. ## Architecture ### 1. Automatic Detection on Shell Entry Modules can be installed via: -- **Flake inputs**: `inputs.stack-oxlint.url = "github:...";` -- **Built-in modules**: Already in `nix/stack/modules/` -- **Local modules**: In `.stack/modules/` +- **Flake inputs**: `inputs.stackpanel-oxlint.url = "github:...";` +- **Built-in modules**: Already in `nix/stackpanel/modules/` +- **Local modules**: In `.stackpanel/modules/` The injection system automatically detects changes and updates config files on every shell entry. @@ -24,7 +24,7 @@ Each module defines a `configBoilerplate` field in its `meta.nix`: id = "oxlint"; name = "OxLint"; description = "Blazing fast JavaScript/TypeScript linter"; - + # Boilerplate to inject into user's config configBoilerplate = '' # OxLint - Blazing fast JavaScript/TypeScript linter @@ -39,7 +39,7 @@ Each module defines a `configBoilerplate` field in its `meta.nix`: ### 3. Nix-Generated Manifest -During Nix evaluation, stack generates a modules manifest at `.stack/gen/modules-manifest.json`: +During Nix evaluation, stackpanel generates a modules manifest at `.stackpanel/gen/modules-manifest.json`: ```json { @@ -63,24 +63,24 @@ During Nix evaluation, stack generates a modules manifest at `.stack/gen/modules This manifest is generated from: - All available modules (built-in + flake inputs + local) -- Their current enabled/disabled state from `stack.modules.*` +- Their current enabled/disabled state from `stackpanel.modules.*` - Their boilerplate text from `meta.nix` ### 4. Injection Markers Configuration files use special marker comments to define injection zones: -**In `.stack/config.nix`:** +**In `.stackpanel/config.nix`:** ```nix { # ... user config ... # 5. Injection Algorithm (Automatic on Shell Entry) -On every shell entry, `stack init` runs: +On every shell entry, `stackpanel init` runs: -1. **Read** the generated manifest at `.stack/gen/modules-manifest.json` -2. **Compare** with previous injection state (stored in `.stack/state/modules-injected.json`) +1. **Read** the generated manifest at `.stackpanel/gen/modules-manifest.json` +2. **Compare** with previous injection state (stored in `.stackpanel/state/modules-injected.json`) 3. **If changed**: - Parse config files to find injection markers - Rebuild injection zones with current modules (alphabetically sorted) @@ -91,7 +91,7 @@ On every shell entry, `stack init` runs: **Nix side** (in shell hook or file generation): ```nix # Generate modules manifest -stack.files.entries."gen/modules-manifest.json" = { +stackpanel.files.entries."gen/modules-manifest.json" = { type = "text"; text = builtins.toJSON { version = 1; @@ -100,7 +100,7 @@ stack.files.entries."gen/modules-manifest.json" = { name = mod.name or id; enabled = mod.enable or false; configBoilerplate = mod.meta.configBoilerplate or ""; - }) stack.modulesComputed; + }) stackpanel.modulesComputed; }; }; ``` @@ -125,23 +125,23 @@ func SyncModuleBoilerplates(manifestPath, configPath, statePath string) error { if err != nil { return err } - + // 2. Read previous state prevState, _ := readState(statePath) // ignore error if first run - + // 3. Check if changed if manifestsEqual(manifest, prevState) { return nil // Fast path: no changes } - + // 4. Get enabled modules only enabled := filterEnabled(manifest.Modules) - + // 5. Inject into config files if err := injectToFile(configPath, enabled); err != nil { return err } - + // 6. Save new state return saveState(statePath, manifest) } @@ -149,7 +149,7 @@ func SyncModuleBoilerplates(manifestPath, configPath, statePath string) error { func injectToFile(path string, modules []ModuleMetadata) error { content, _ := os.ReadFile(path) before, _, after := parseInjectionZone(string(content)) - + // Build injection content 6. User Workflow @@ -159,11 +159,11 @@ func injectToFile(path string, modules []ModuleMetadata) error { { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - stack.url = "github:darkmatter/stack"; - stack-oxlint.url = "github:stack-modules/oxlint"; # ← Add module + stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel"; + stackpanel-oxlint.url = "github:stackpanel-modules/oxlint"; # ← Add module }; - - outputs = { stack, stack-oxlint, ... }: { + + outputs = { stackpanel, stackpanel-oxlint, ... }: { # Module is automatically available }; } @@ -171,7 +171,7 @@ func injectToFile(path string, modules []ModuleMetadata) error { **Or enabling a built-in module:** ```nix -# .stack/config.nix or .stack/data/modules.nix +# .stackpanel/config.nix or .stackpanel/data/modules.nix { oxlint.enable = true; # ← Enable built-in module } @@ -180,14 +180,14 @@ func injectToFile(path string, modules []ModuleMetadata) error { **On next shell entry** (`nix develop --impure`): 1. Nix re-evaluates and detects new module 2. Generates updated manifest with oxlint -3. Shell hook runs `stack init` +3. Shell hook runs `stackpanel init` 4. CLI detects manifest change 5. Injects boilerplate automatically } - + injected.WriteString(" # ---------------------------------------------------------------------------\n") injected.WriteString(" # STACKPANEL_MODULES_END\n") injected.WriteString(" # ---------------------------------------------------------------------------") - + newContent := before + injected.String() + after return os.WriteFile(p ID string @@ -200,22 +200,22 @@ func InjectModuleConfig(configPath string, module ModuleBoilerplate) error { if err != nil { return err } - + // 2. Parse sections before, injected, after := parseInjectionZone(string(content)) - + // 3. Parse existing modules in injection zone modules := parseModules(injected) - + // 4. Add/update module modules[module.ID] = module.Boilerplate - + // 5. Sort by ID sorted := sortModules(modules) - + // 6. Rebuild injection zone newInjected := buildInjectionZone(sorted) - + // 7. Write back newContent := before + newInjected + after return os.WriteFile(configPath, []byte(newContent), 0644) @@ -237,11 +237,11 @@ User edits **outside** the injection zone are preserved. **Installing a module:** ```bash -stack module install oxlint +stackpanel module install oxlint # or via web UI ``` -**Result in `.stack/data/modules.nix`:** +**Result in `.stackpanel/data/modules.nix`:** ```nix { # ... manual configs ... @@ -249,7 +249,7 @@ stack module install oxlint # --------------------------------------------------------------------------- # STACKPANEL_MODULES_BEGIN # Auto-generated module configurations (do not edit this section manually) - # Modules are sorted alphabetically and injected by the stack CLI + # Modules are sorted alphabetically and injected by the stackpanel CLI # --------------------------------------------------------------------------- # OxLint - Blazing fast JavaScript/TypeScript linter @@ -287,10 +287,10 @@ Modules can define boilerplate for multiple targets: { # Main config.nix injection configBoilerplate = ''...''; - + # Optional: data/modules.nix injection (if different) dataBoilerplate = ''...''; - + # Optional: per-app config injection appBoilerplate = ''...''; } @@ -322,12 +322,12 @@ Sort order: 3. **Provide sensible defaults**: Make the config work with minimal edits 4. **Use clear comments**: Explain what each option does 5. **Follow Nix conventions**: Proper indentation, naming -Add modules manifest generation to Nix (stack.files.entries) +Add modules manifest generation to Nix (stackpanel.files.entries) - [ ] Implement Go parser for injection zones - [ ] Implement Go manifest reader and differ - [ ] Implement Go injector (sync on shell entry) -- [ ] Add to `stack init` command -- [ ] Add state tracking (.stack/state/modules-injected.json) +- [ ] Add to `stackpanel init` command +- [ ] Add state tracking (.stackpanel/state/modules-injected.json) - [ ] Test with real modules - [ ] Add validation (ensure configs parse correctly) - [ ] Add conflict detection (duplicate IDs) @@ -336,13 +336,13 @@ Add modules manifest generation to Nix (stack.files.entries) The injection runs as part of the shell entry process: ```nix -# nix/stack/devshell/hooks.nix -stack.devshell.hooks.main = '' +# nix/stackpanel/devshell/hooks.nix +stackpanel.devshell.hooks.main = '' # ... existing hooks ... - + # Sync module boilerplates if manifest changed - if [[ -f .stack/gen/modules-manifest.json ]]; then - stack init --sync-modules + if [[ -f .stackpanel/gen/modules-manifest.json ]]; then + stackpanel init --sync-modules fi ''; ``` @@ -351,12 +351,12 @@ Or alternatively, the manifest generation itself triggers the sync: ```nix # Generate manifest and trigger sync in one step -stack.files.entries."gen/modules-manifest.json" = { +stackpanel.files.entries."gen/modules-manifest.json" = { type = "text"; text = builtins.toJSON manifestData; # Post-write hook: sync boilerplates after manifest changes onChange = '' - ${pkgs.stack-cli}/bin/stack init --sync-modules + ${pkgs.stackpanel-cli}/bin/stackpanel init --sync-modules ''; }; ``` @@ -370,7 +370,7 @@ stack.files.entries."gen/modules-manifest.json" = { 5. **Diff preview**: Show what will be injected on shell entry (verbose mode) - [ ] Implement Go parser for injection zones - [ ] Implement Go injector/remover -- [ ] Add CLI commands: `stack module install/uninstall` +- [ ] Add CLI commands: `stackpanel module install/uninstall` - [ ] Add web UI for module installation - [ ] Add module registry API - [ ] Test with real modules diff --git a/nix/flake/exports.nix b/nix/flake/exports.nix index 742a16d8..837de363 100644 --- a/nix/flake/exports.nix +++ b/nix/flake/exports.nix @@ -245,7 +245,7 @@ in # { ".stack/config.nix" = "..."; ... } # # Usage from CLI: - # nix eval github:darkmatter/stackpanel#lib.initFiles --json + # nix eval git+ssh://git@github.com/darkmatter/stackpanel#lib.initFiles --json # # Usage from Nix: # inputs.stackpanel.lib.initFiles diff --git a/nix/flake/templates/README.md b/nix/flake/templates/README.md index 4e8eb705..b2386994 100644 --- a/nix/flake/templates/README.md +++ b/nix/flake/templates/README.md @@ -1,13 +1,13 @@ -# Stack Templates +# Stackpanel Templates -Project templates for bootstrapping new Stack projects. +Project templates for bootstrapping new Stackpanel projects. ## Available Templates | Template | Description | Use Case | |----------|-------------|----------| | `default` | devenv + flake-parts | Full-featured, recommended for most projects | -| `native` | Native Nix shell + flake-parts | When you want Stack without devenv | +| `native` | Native Nix shell + flake-parts | When you want Stackpanel without devenv | | `devenv` | Standalone devenv.yaml | For devenv-first workflows (no flake.nix) | | `minimal` | devenv without flake-parts | Simple flake without flake-parts | @@ -18,10 +18,10 @@ Project templates for bootstrapping new Stack projects. mkdir myproject && cd myproject # Initialize from template (choose one) -nix flake init -t github:darkmatter/stack # default -nix flake init -t github:darkmatter/stack#native # native -nix flake init -t github:darkmatter/stack#devenv # devenv -nix flake init -t github:darkmatter/stack#minimal # minimal +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel # default +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#native # native +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#devenv # devenv +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#minimal # minimal # Enter the dev environment direnv allow # or: nix develop --impure @@ -34,7 +34,7 @@ direnv allow # or: nix develop --impure Full-featured setup with devenv and flake-parts integration. ```bash -nix flake init -t github:darkmatter/stack +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel ``` **Structure:** @@ -43,15 +43,15 @@ nix flake init -t github:darkmatter/stack ├── flake.nix # Flake entry with flake-parts ├── nix/ │ └── devenv.nix # Devenv options (packages, languages, etc.) -├── .stack/ -│ └── config.nix # Stack options +├── .stackpanel/ +│ └── config.nix # Stackpanel options └── .envrc # direnv configuration ``` **Features:** - Multi-platform support (Linux/Darwin, x86_64/aarch64) - All devenv features (processes, services, languages) -- All Stack features (CLI, IDE, theme, services) +- All Stackpanel features (CLI, IDE, theme, services) - Clean separation of concerns --- @@ -61,22 +61,22 @@ nix flake init -t github:darkmatter/stack Lightweight setup using native `mkShell` instead of devenv. ```bash -nix flake init -t github:darkmatter/stack#native +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#native ``` **Structure:** ``` . ├── flake.nix # Flake with flakeModules.native -├── .stack/ -│ └── config.nix # Stack options +├── .stackpanel/ +│ └── config.nix # Stackpanel options └── .envrc # direnv configuration ``` **Features:** - Faster evaluation (no devenv dependency) - Pure Nix implementation -- All Stack features +- All Stackpanel features - No `devenv up` (use external process managers) --- @@ -86,16 +86,16 @@ nix flake init -t github:darkmatter/stack#native Standalone devenv setup without a flake.nix. ```bash -nix flake init -t github:darkmatter/stack#devenv +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#devenv ``` **Structure:** ``` . ├── devenv.yaml # Devenv inputs and imports -├── devenv.nix # Devenv + Stack options -├── .stack/ -│ └── config.nix # Stack options +├── devenv.nix # Devenv + Stackpanel options +├── .stackpanel/ +│ └── config.nix # Stackpanel options └── .envrc # direnv configuration ``` @@ -112,15 +112,15 @@ devenv up # Start processes Traditional flake.nix without flake-parts. ```bash -nix flake init -t github:darkmatter/stack#minimal +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#minimal ``` **Structure:** ``` . ├── flake.nix # Standard Nix flake -├── .stack/ -│ └── config.nix # Stack options +├── .stackpanel/ +│ └── config.nix # Stackpanel options └── .envrc # direnv configuration ``` @@ -131,18 +131,18 @@ nix flake init -t github:darkmatter/stack#minimal ## Configuration -All templates use the same `.stack/config.nix` structure: +All templates use the same `.stackpanel/config.nix` structure: ```nix { enable = true; - + # Shell prompt theme theme.enable = true; - + # VS Code integration ide.vscode.enable = true; - + # Global services globalServices = { postgres.enable = true; @@ -153,7 +153,7 @@ All templates use the same `.stack/config.nix` structure: ## Learn More -- [Stack Documentation](https://stack.dev/docs) -- [Quick Start Guide](https://stack.dev/docs/quick-start) +- [Stackpanel Documentation](https://stackpanel.dev/docs) +- [Quick Start Guide](https://stackpanel.dev/docs/quick-start) - [devenv Documentation](https://devenv.sh) - [Flake Parts](https://flake.parts) diff --git a/nix/flake/templates/_test-fixtures/README.md b/nix/flake/templates/_test-fixtures/README.md index cee51162..7431ca1f 100644 --- a/nix/flake/templates/_test-fixtures/README.md +++ b/nix/flake/templates/_test-fixtures/README.md @@ -1,13 +1,13 @@ # Test Fixtures -These fixtures are used for testing stack modules in CI. They are **not** meant for `nix flake init`. +These fixtures are used for testing stackpanel modules in CI. They are **not** meant for `nix flake init`. ## Notes -- Fixtures use SSH URLs (`git+ssh://git@github.com/darkmatter/stack`) which require SSH access to the repo +- Fixtures use SSH URLs (`git+ssh://git@github.com/darkmatter/stackpanel`) which require SSH access to the repo - Initial runs are slow due to fetching nixpkgs, flake-parts, etc. Subsequent runs use cached inputs - Use `--no-write-lock-file` to avoid modifying the fixture's lock file -- Override `stack` input with local path for development: `--override-input stack path:.` +- Override `stackpanel` input with local path for development: `--override-input stackpanel path:.` ## Scenarios @@ -18,64 +18,9 @@ These fixtures are used for testing stack modules in CI. They are **not** meant | `full-stack` | Multiple apps (web, server, docs) | Multi-app handling, all modules enabled | | `external-module` | Test external module | Module loading from flake input | -## Snapshot Testing (Golden Files) - -Each fixture can include a `golden/` directory containing the expected generated file contents. -When present, `nix flake check` will build the full file generation pipeline and `diff -r` the -output against `golden/`, failing if anything has changed. - -This gives you end-to-end regression testing of the entire config → module evaluation → file -generation pipeline with reviewable diffs in PRs. - -### How it works - -``` -.stack/config.nix (input: scenario configuration) - ↓ full NixOS module evaluation -stackpanelFullConfig.files (evaluated file entries) - ↓ _storePathsByFile -snapshot derivation (all file contents assembled into a directory) - ↓ diff -ru -golden/ (checked-in expected output) -``` - -### Generating golden files for the first time - -```bash -# Generate golden/ for all fixtures -./nix/flake/templates/_test-fixtures/update-golden.sh - -# Generate golden/ for specific fixtures -./nix/flake/templates/_test-fixtures/update-golden.sh basic with-oxlint -``` - -### Updating golden files after a change - -When you intentionally change what files a module generates (new file, changed content, etc.), -the snapshot check will fail with a unified diff showing exactly what changed. To accept the -new output: - -```bash -# Update the fixture(s) that changed -./nix/flake/templates/_test-fixtures/update-golden.sh with-oxlint - -# Review and commit -git diff nix/flake/templates/_test-fixtures/with-oxlint/golden/ -git add nix/flake/templates/_test-fixtures/with-oxlint/golden/ -``` - -### Inspecting the snapshot without updating - -```bash -# Build the snapshot derivation and browse its contents -nix build ./nix/flake/templates/_test-fixtures/with-oxlint#snapshot \ - --override-input stack path:. --no-write-lock-file -ls -la result/ -``` - ## Usage in CI -### Testing stack itself +### Testing stackpanel itself ```bash # Using the test script (recommended) @@ -87,7 +32,7 @@ ls -la result/ # Manual: Run all fixture checks for fixture in nix/flake/templates/_test-fixtures/*/; do echo "Testing $(basename $fixture)..." - nix flake check "$fixture" --override-input stack path:. --no-write-lock-file + nix flake check "$fixture" --override-input stackpanel path:. --no-write-lock-file done ``` @@ -97,7 +42,7 @@ Fixtures are exposed as flake templates for easy access: ```bash # Initialize a test fixture -nix flake init -t github:darkmatter/stack#test-external-module +nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#test-external-module # Override the module input with your module nix flake lock --override-input test-module path:../my-module @@ -117,29 +62,25 @@ nix flake check ```bash # List all available templates -nix flake show github:darkmatter/stack --json | jq '.templates | keys' +nix flake show git+ssh://git@github.com/darkmatter/stackpanel --json | jq '.templates | keys' ``` ## Creating New Fixtures 1. Copy an existing fixture as a starting point -2. Modify `.stack/config.nix` for your scenario +2. Modify `.stackpanel/config.nix` for your scenario 3. Add any test apps in `apps/` if needed 4. Add test checks in `flake.nix` if needed -5. Run `./update-golden.sh ` to generate the `golden/` directory -6. Commit the `golden/` directory — future `nix flake check` runs will validate against it ## Fixture Structure ``` fixture-name/ -├── flake.nix # Flake with stack + test checks +├── flake.nix # Flake with stackpanel + test checks ├── flake.lock # Optional: lock file for reproducibility -├── .stack/ -│ └── config.nix # Scenario configuration -├── golden/ # Snapshot: expected generated file contents -│ ├── .gitignore -│ └── ... +├── .stackpanel/ +│ ├── config.nix # Scenario configuration +│ └── _internal.nix # Optional: internal config └── apps/ # Optional: test apps └── web/ ├── src/ diff --git a/nix/flake/templates/default/flake.nix b/nix/flake/templates/default/flake.nix index 60462a6c..2cfeadf0 100644 --- a/nix/flake/templates/default/flake.nix +++ b/nix/flake/templates/default/flake.nix @@ -4,7 +4,7 @@ # Starter flake template for projects using stackpanel. # # Getting started: -# 1. Run: nix flake init -t github:darkmatter/stackpanel +# 1. Run: nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel # 2. Run: direnv allow # 3. Configure stackpanel in ./.stack/config.nix # @@ -23,11 +23,17 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - stackpanel.url = "github:darkmatter/stackpanel"; + stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel"; }; outputs = - { self, nixpkgs, flake-utils, stackpanel, ... }@inputs: + { + self, + nixpkgs, + flake-utils, + stackpanel, + ... + }@inputs: # Use stackpanel.lib.mkFlake for full stackpanel integration stackpanel.lib.mkFlake { inherit inputs self; } # Merge with additional custom outputs diff --git a/nix/flake/templates/devenv/devenv.yaml b/nix/flake/templates/devenv/devenv.yaml index 027f740d..38d25ffa 100644 --- a/nix/flake/templates/devenv/devenv.yaml +++ b/nix/flake/templates/devenv/devenv.yaml @@ -11,7 +11,7 @@ inputs: nixpkgs: url: github:NixOS/nixpkgs/nixos-unstable stackpanel: - url: github:darkmatter/stackpanel + url: git+ssh://git@github.com/darkmatter/stackpanel imports: - stackpanel/devenvModules/default diff --git a/nix/flake/templates/minimal/flake.nix b/nix/flake/templates/minimal/flake.nix index 441d3b63..2ad92999 100644 --- a/nix/flake/templates/minimal/flake.nix +++ b/nix/flake/templates/minimal/flake.nix @@ -5,7 +5,7 @@ # Uses standard Nix flake structure with flake-utils. # # Getting started: -# 1. Run: nix flake init -t github:darkmatter/stackpanel#minimal +# 1. Run: nix flake init -t git+ssh://git@github.com/darkmatter/stackpanel#minimal # 2. Run: direnv allow # 3. Configure stackpanel in ./.stack/config.nix # ============================================================================== @@ -15,11 +15,17 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - stackpanel.url = "github:darkmatter/stackpanel"; + stackpanel.url = "git+ssh://git@github.com/darkmatter/stackpanel"; }; outputs = - { self, nixpkgs, flake-utils, stackpanel, ... }@inputs: + { + self, + nixpkgs, + flake-utils, + stackpanel, + ... + }@inputs: # Simple usage: just call mkFlake stackpanel.lib.mkFlake { inherit inputs self; } # Merge with your own outputs diff --git a/packages/docs-content/content/guides/setup.mdx b/packages/docs-content/content/guides/setup.mdx index 76a9dbde..63007fd3 100644 --- a/packages/docs-content/content/guides/setup.mdx +++ b/packages/docs-content/content/guides/setup.mdx @@ -13,7 +13,7 @@ Install the Stackpanel CLI for terminal-based management: ```bash # Using Nix (recommended) -nix profile install github:darkmatter/stackpanel +nix profile install git+ssh://git@github.com/darkmatter/stackpanel # Using Homebrew brew install darkmatter/tap/stackpanel From 86bc3edd9e506947118b68dce74ab7954a811ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 20:08:48 +0100 Subject: [PATCH 05/11] Add bun runtime when there are bun apps --- nix/stackpanel/modules/bun/module.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nix/stackpanel/modules/bun/module.nix b/nix/stackpanel/modules/bun/module.nix index e4934805..e184715d 100644 --- a/nix/stackpanel/modules/bun/module.nix +++ b/nix/stackpanel/modules/bun/module.nix @@ -402,7 +402,10 @@ in { # bun2nix CLI: run `bun2nix` after `bun install` to regenerate bun.nix # whenever bun.lock changes. The postinstall script in the generated # package.json invokes this automatically. - stackpanel.devshell.packages = [pkgs.bun2nix]; + stackpanel.devshell.packages = [ + pkgs.bun # Bun runtime + pkgs.bun2nix # Native bun2nix CLI (converts bun.lock -> bun.nix) + ]; # ----------------------------------------------------------------------- # File Generation — package.json From 38eda2dc837eec8bb04b93eb5cd3172f5425c5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 20:47:10 +0100 Subject: [PATCH 06/11] Better way to enable bun when there are bun apps --- nix/stackpanel/modules/bun/module.nix | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nix/stackpanel/modules/bun/module.nix b/nix/stackpanel/modules/bun/module.nix index e184715d..75b03273 100644 --- a/nix/stackpanel/modules/bun/module.nix +++ b/nix/stackpanel/modules/bun/module.nix @@ -403,12 +403,17 @@ in { # whenever bun.lock changes. The postinstall script in the generated # package.json invokes this automatically. stackpanel.devshell.packages = [ - pkgs.bun # Bun runtime pkgs.bun2nix # Native bun2nix CLI (converts bun.lock -> bun.nix) ]; # ----------------------------------------------------------------------- - # File Generation — package.json + # Devshell - enable the bun language + # ----------------------------------------------------------------------- + stackpanel.languages.javascript.bun.enable = true; + stackpanel.languages.javascript.bun.install.enable = true; + + # ----------------------------------------------------------------------- + # File Generation - package.json with bun2nix postinstall # ----------------------------------------------------------------------- # Only generated for apps with generateFiles = true (default). Each entry # uses json-ops so the file is patched in-place; user-added fields are From 7cb25ac33348d8c6cd5f857e1403f5964308da64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 20:47:22 +0100 Subject: [PATCH 07/11] Make sure that .stackpanel/bin is gitignored --- nix/stackpanel/devshell/bin.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nix/stackpanel/devshell/bin.nix b/nix/stackpanel/devshell/bin.nix index f1a618ef..a59d8272 100644 --- a/nix/stackpanel/devshell/bin.nix +++ b/nix/stackpanel/devshell/bin.nix @@ -92,9 +92,12 @@ in "${generateBinScript}" ]; - # Optionally add .stack/bin to PATH + # Keep .stackpanel/bin out of git (it's regenerated on shell entry) + stackpanel.files.gitignore.extraEntries = [ ".stackpanel/bin/" ]; + + # Optionally add .stackpanel/bin to PATH stackpanel.devshell.path.prepend = lib.mkIf cfg.addToPath [ - "$STACKPANEL_ROOT/.stack/bin" + "$STACKPANEL_ROOT/.stackpanel/bin" ]; }; } From e19fd7d4b2fbe566f3887c0428c609dd8703a247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Wed, 25 Mar 2026 21:03:42 +0100 Subject: [PATCH 08/11] Nix evaluation can easily take longer --- apps/stackpanel-go/pkg/nixeval/eval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stackpanel-go/pkg/nixeval/eval.go b/apps/stackpanel-go/pkg/nixeval/eval.go index 5451fde6..aea0c5a9 100644 --- a/apps/stackpanel-go/pkg/nixeval/eval.go +++ b/apps/stackpanel-go/pkg/nixeval/eval.go @@ -200,7 +200,7 @@ func EvalOnce(ctx context.Context, opts EvalOnceParams) ([]byte, error) { } timeout := opts.Timeout if timeout <= 0 { - timeout = 10 * time.Second + timeout = 10 * time.Minute } ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() From 3f841bb50df610618c1d738bb0e84003c99f86e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Sat, 28 Mar 2026 21:30:34 +0100 Subject: [PATCH 09/11] Add new gitignored file --- .gitignore | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/.gitignore b/.gitignore index cacecdb1..aacf37b4 100644 --- a/.gitignore +++ b/.gitignore @@ -98,3 +98,73 @@ tmp# ── END stackpanel ── .dolt/ *.db .beads-credential-key +# Stackpanel generated files (cache) +.stackpanel/state/ +.stackpanel/bin/ +.sst +.tmp +.envrc.local + + +# START Stackpanel Generated Files +# DO NOT EDIT - managed by stackpanel. Changes will be overwritten. +/.envrc.local +/.stackpanel/gen/direnv-wrapper.sh +/.stackpanel/gen/vscode/devshell-loader.sh +/.stackpanel/gen/zed/devshell-loader.sh +/.stackpanel/gen/zed/settings.json +/.stackpanel/state/infra-inputs.json +/.tasks/bin/build +/.tasks/bin/clean +/.tasks/bin/db:migrate +/.tasks/bin/db:push +/.tasks/bin/db:studio +/.tasks/bin/dev +/.tasks/bin/format +/.tasks/bin/generate:proto +/.tasks/bin/generate:types +/.tasks/bin/lint +/.tasks/bin/test +/.tasks/bin/test:coverage +/.tasks/bin/test:watch +/.tasks/bin/typecheck +/.vscode/settings.json +/apps/docs/.oxlintrc.json +/apps/web/.oxlintrc.json +/devshell +/packages/gen/env/README.md +/packages/gen/env/data/shared/vars.yaml +/packages/gen/env/package.json +/packages/gen/env/src/entrypoints/docs.ts +/packages/gen/env/src/entrypoints/index.ts +/packages/gen/env/src/entrypoints/stackpanel-go.ts +/packages/gen/env/src/entrypoints/web.ts +/packages/gen/env/src/generated/docs/dev.ts +/packages/gen/env/src/generated/docs/index.ts +/packages/gen/env/src/generated/index.ts +/packages/gen/env/src/generated/stackpanel-go/dev.ts +/packages/gen/env/src/generated/stackpanel-go/index.ts +/packages/gen/env/src/generated/web/dev.ts +/packages/gen/env/src/generated/web/index.ts +/packages/gen/env/src/generated/web/prod.ts +/packages/gen/env/src/index.ts +/packages/gen/env/tsconfig.json +/packages/infra/README.md +/packages/infra/alchemy.run.ts +/packages/infra/modules/aws-secrets.ts +/packages/infra/modules/deployment.ts +/packages/infra/package.json +/packages/infra/src/index.ts +/packages/infra/src/resources/kms-alias.ts +/packages/infra/src/resources/kms-key.ts +/packages/infra/src/types.ts +/packages/infra/sst.config.ts +/packages/infra/tsconfig.json +/packages/scripts/entrypoints/docs.sh +/packages/scripts/entrypoints/stackpanel-go.sh +/packages/scripts/entrypoints/web.sh +/process-compose.yaml +/turbo.json +/.stackpanel/bin/ +/.stackpanel/secrets/keys/.sops.yaml +# END Stackpanel Generated Files From 46fc6d4099e2d23fd6e9a82dbf44434bcef18dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Sat, 28 Mar 2026 22:29:43 +0100 Subject: [PATCH 10/11] Fix new path for gitignore --- nix/stackpanel/devshell/bin.nix | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/nix/stackpanel/devshell/bin.nix b/nix/stackpanel/devshell/bin.nix index a59d8272..0ad753ec 100644 --- a/nix/stackpanel/devshell/bin.nix +++ b/nix/stackpanel/devshell/bin.nix @@ -86,18 +86,23 @@ in }; }; - config = lib.mkIf (config.stackpanel.enable && cfg.enable && binDirPaths != [ ]) { - # Run the bin generator script on shell entry - stackpanel.devshell.hooks.after = [ - "${generateBinScript}" - ]; + config = lib.mkMerge [ + # Keep .stackpanel/bin out of git — unconditional to avoid a recursion cycle: + # the bin-generation mkIf depends on devshell.packages → files → gitignore → cycle. + (lib.mkIf (config.stackpanel.enable && cfg.enable) { + stackpanel.gitignore.entries = [ ".stackpanel/bin/" ]; + }) - # Keep .stackpanel/bin out of git (it's regenerated on shell entry) - stackpanel.files.gitignore.extraEntries = [ ".stackpanel/bin/" ]; + (lib.mkIf (config.stackpanel.enable && cfg.enable && binDirPaths != [ ]) { + # Run the bin generator script on shell entry + stackpanel.devshell.hooks.after = [ + "${generateBinScript}" + ]; - # Optionally add .stackpanel/bin to PATH - stackpanel.devshell.path.prepend = lib.mkIf cfg.addToPath [ - "$STACKPANEL_ROOT/.stackpanel/bin" - ]; - }; + # Optionally add .stackpanel/bin to PATH + stackpanel.devshell.path.prepend = lib.mkIf cfg.addToPath [ + "$STACKPANEL_ROOT/.stackpanel/bin" + ]; + }) + ]; } From da182b20f1912f65c055e51c23a9205a5593a03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pedro=20Bol=C3=ADvar=20Puente?= Date: Sat, 28 Mar 2026 22:50:03 +0100 Subject: [PATCH 11/11] Remove file that shouldn't be there --- .stackpanel-root | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .stackpanel-root diff --git a/.stackpanel-root b/.stackpanel-root deleted file mode 100644 index 795c24b0..00000000 --- a/.stackpanel-root +++ /dev/null @@ -1 +0,0 @@ -/Users/cm/git/darkmatter/stackpanel