Use when setting up or configuring the Stacks development environment - dev server, hot reload, development utilities, or IDE configuration. Covers the @stacksjs/development package, the dev server, CLI commands, reverse proxy, SSL, and dev workflow.
Install
npx skillscat add stacksjs/stacks/stacks-development Install via the SkillsCat registry.
Stacks Development
Key Paths
- Development package:
storage/framework/core/development/(stub - exports{}) - Dev server entry:
storage/framework/server/src/index.ts - Dev server utils:
storage/framework/server/src/utils.ts - Server build script:
storage/framework/server/build.ts - Dev action handlers:
storage/framework/core/actions/src/dev/ - Buddy CLI dev commands:
storage/framework/core/buddy/src/commands/dev.ts - Preloader:
storage/framework/defaults/resources/plugins/preloader.ts - Port config:
config/ports.ts - STX config:
config/ui.ts - IDE defaults:
storage/framework/defaults/ide/ - SSL setup:
storage/framework/core/actions/src/setup/ssl.ts - Bun config:
bunfig.toml - Dockerfile:
storage/framework/server/Dockerfile
Source Files
actions/src/dev/
├── index.ts # Re-exports all run*DevServer() wrappers
├── api.ts # API dev server (bun-router with CORS, port from config.ports.api)
├── views.ts # Frontend dev server (bun-plugin-stx serve, .stx templates)
├── dashboard.ts # Dashboard dev server (STX + native Craft window + config API)
├── dashboard-utils.ts # Model discovery, sidebar config, icon map, waitForServer()
├── docs.ts # Docs dev server (@stacksjs/bunpress, watch mode)
├── components.ts # Components dev server (native stx component playground)
├── desktop.ts # Desktop dev server (runs `bun run dev` in framework/views/desktop)
└── system-tray.ts # System tray dev server (runs `bun run dev` in framework/system-tray)
server/
├── src/
│ ├── index.ts # Production HTTP server entry (Bun.serve, one-shot queue worker mode)
│ └── utils.ts # cleanCopy(), useCustomOrDefaultServerConfig(), buildDockerImage()
├── build.ts # Server build script (Bun.build for ESM, Docker image)
├── dev # Shell script for running server via Docker with volume mounts
├── Dockerfile # Multi-stage build: oven/bun:1.3.10, exposes port 3000
├── package.json # stacks-server package
├── tsconfig.docker.json
└── tsconfig.json
buddy/src/commands/
├── dev.ts # All `buddy dev` subcommands
├── build.ts # All `buddy build` subcommands
├── setup.ts # `buddy setup` and `buddy setup:ssl`
├── doctor.ts # `buddy doctor` health checks
└── maintenance.ts # `buddy down`, `buddy up`, `buddy status`Port Configuration (config/ports.ts)
{
frontend: env.PORT ?? 3000,
backend: env.PORT_BACKEND ?? 3001,
admin: env.PORT_ADMIN ?? 3002,
library: env.PORT_LIBRARY ?? 3003,
desktop: env.PORT_DESKTOP ?? 3004,
email: env.PORT_EMAIL ?? 3005,
docs: env.PORT_DOCS ?? 3006,
inspect: env.PORT_INSPECT ?? 3007,
api: env.PORT_API ?? 3008,
systemTray: env.PORT_SYSTEM_TRAY ?? 3009,
database: 3010,
}CLI Commands
Dev Server Commands
buddy dev-- Start ALL dev servers in parallel (frontend, API, docs, dashboard + reverse proxy if custom domain)buddy dev [server]-- Start a specific server:frontend,api,components,dashboard,desktop,system-tray,docsbuddy dev -i/--interactive-- Interactive prompt to choose which server to startbuddy dev --browser-- Override a configured native launch and open the browser appbuddy dev --site-- Open the marketing site instead of the app or native windowbuddy dev --verbose-- Show detailed output including proxy info and dependency logsbuddy dev:frontend(aliases:dev:pages,dev:views) -- Frontend onlybuddy dev:api-- API server onlybuddy dev:dashboard(alias:dev:admin) -- Dashboard with native Craft windowbuddy dev:docs-- Documentation server onlybuddy dev:components-- stx component playgroundbuddy dev:desktop-- Desktop app dev serverbuddy dev:native-- Equivalent native app development flowbuddy dev:system-tray(alias:dev:tray) -- System tray dev server
Build Commands
buddy build-- Build stacks framework (default when no option specified)buddy build components-- Build component librariesbuddy build docs-- Build documentation sitebuddy build server(alias:build:docker) -- Build Docker server imagebuddy build:core-- Build Stacks core packagesbuddy build:stacks-- Build the full Stacks frameworkbuddy build:cli-- Build Buddy CLI binary
Setup & Maintenance
buddy setup(alias:ensure) -- Full project setup (pantry, deps, config)buddy setup:ssl(alias:ssl:setup) -- Generate and trust SSL certs for custom domainbuddy doctor-- Health checks (Bun version, Node.js, package.json, .env, APP_KEY)buddy fresh-- Clean project + reinstall dependenciesbuddy clean-- Remove build artifacts viacleanProject()buddy down-- Maintenance mode (supports--message,--secret,--retry,--allow)buddy up-- Exit maintenance modebuddy status-- Check maintenance mode status
Dev Server Architecture
Unified Dev Server (buddy dev with no arguments)
When buddy dev runs without a specific server flag, startDevelopmentServer() in dev.ts:
- Reads
config/app.tsdevLaunch;nativeopensappPathin Craft, whilebrowserkeeps the browser default - Reads port configuration from env vars (
PORT,PORT_API,PORT_DOCS,PORT_ADMIN) - Detects custom domain from
APP_URL(e.g.,stacks.localhost) - Prints Vite-style banner with URLs for Frontend, API, Docs, Dashboard
- Sets
STACKS_PROXY_MANAGED=1to prevent subprocesses from starting their own proxies - Starts all servers in parallel via
Promise.all():runFrontendDevServer()-- STX template server on port 3000runApiDevServer()-- bun-router API on port 3008runDocsDevServer()-- bunpress docs on port 3006runDashboardDevServer()-- STX + Craft native window on port 3002startReverseProxy()-- rpx HTTPS proxy on port 443 (only if custom domain set)
- Registers SIGINT/SIGTERM handlers that
SIGKILLthe entire process group
Action Execution (runAction in helpers/utils.ts)
- Dev actions (paths starting with
dev/) automatically get--watchflag viabun --watch - The
dev/viewsaction is special-cased for maximum performance: importsbun-plugin-stx/servedirectly instead of spawning a subprocess NODE_PATHis set to includepantry/so compiled pantry packages can resolve@stacksjs/*at runtime- Dev action subprocess output is suppressed by default (quiet mode), visible with
--verbose
Frontend Dev Server (views.ts)
Uses bun-plugin-stx serve function with pattern-based view resolution:
- User views:
resources/views(checked first) - Default views:
storage/framework/defaults/resources/views(fallback) - User layouts:
resources/layouts - Default layouts:
storage/framework/defaults/resources/layouts - Components:
storage/framework/defaults/resources/components/Dashboard
API Dev Server (api.ts)
- Uses
@stacksjs/routerwith CORS middleware - Calls
route.importRoutes()to load route definitions - Serves on
127.0.0.1atconfig.ports.api(default 3008)
Dashboard Dev Server (dashboard.ts)
- Uses installed, published STX and ts-css packages by default.
STX_SRCis an explicit framework-development opt-in; neighbouring source checkouts never override an application automatically. Validate released dependencies without source overrides. - Starts the STX server on
config.ports.adminand optionally opens a native Craft window - Mounts the config API on the same dashboard origin under
/api/config/* - Config routes list, read, expose source for, and atomically update
config/*.tsfiles - Config updates support top-level scalar literals only and preserve comments and environment-backed expressions
- Discovers ORM models from
app/Models/andstorage/framework/defaults/app/Models/ - Writes
.discovered-models.jsonmanifest for sidebar population - Preloads ORM model globals for server scripts, while framework server helpers use explicit imports
- Leaves
resources/functionsto the STX client auto-import pipeline because browser helpers may depend on signals and other browser-only primitives - Opens a Craft
createApp()window when a supported SDK and native binary are available (1400x900, titlebar hidden) - Builds the web sidebar from configured sections and discovered model dashboard metadata
Docs Dev Server (docs.ts)
- Uses
@stacksjs/bunpresswith{ watch: true, quiet: true } - Serves from
docs/directory on port 3006
Reverse Proxy & HTTPS
When APP_URL is set to a custom domain (not localhost):
startReverseProxy()uses@stacksjs/rpxto proxy all subdomains through HTTPS on port 443- Proxy mapping:
localhost:3000 -> domain,localhost:3008 -> api.domain,localhost:3006 -> docs.domain,localhost:3002 -> dashboard.domain - SSL certs stored in
~/.stacks/ssl/with 825-day validity - Individual dev servers check
STACKS_PROXY_MANAGEDto avoid starting duplicate proxies
SSL Setup (setup/ssl.ts)
setupSSL(options)-- Complete SSL setup: hosts file + cert generation + trustgenerateCertificates(domain)-- Uses@stacksjs/tlsxto create Root CA + host certaddDomainToHosts(domain)-- Adds127.0.0.1 <domain>to/etc/hosts(requires sudo)trustCertificate(domain)-- Adds cert to macOS Keychain or Linux ca-certificates- Supports non-interactive mode via
SUDO_PASSWORDenv var - Cert files:
~/.stacks/ssl/<domain>.crt,<domain>.key,<domain>.ca.crt
Hot Reload / Watch Mode
- Dev actions use
bun --watch: All actions with paths starting withdev/are automatically run with Bun's--watchflag, which restarts the process on file changes - Frontend (STX):
bun-plugin-stx/servehas its own built-in watch/HMR for.stxtemplates - API server: Runs via
bun --watch, restarts on route or handler changes - Docs:
@stacksjs/bunpressruns withwatch: truefor live documentation updates - Components: Uses the native component development workflow; inspect core/actions/src/dev/components.ts for the selected entry
Preloader (preloader.ts)
Configured in bunfig.toml as preload = ["./storage/framework/defaults/resources/plugins/preloader.ts"].
Execution Phases
- Fast command skip: Commands like
dev,build,test,lint,--version,--helpskip the preloader entirely for maximum startup speed - Environment detection: Deploy/production commands (
deploy,cloud:remove, etc.) setAPP_ENVandNODE_ENVbefore loading env files - Env loading: Calls
autoLoadEnv({ quiet: true })from@stacksjs/envfor encrypted.envfile support - Auto-imports (non-fast commands only):
- Loads 21
@stacksjs/*packages intoglobalThis(actions, router, orm, validation, etc.) - Loads all functions from
resources/functions/**/*.ts - Loads all ORM model instances into
globalThis(priority: user > framework > defaults) - Loads all Job instances from
app/Jobs/intoglobalThis - Loads all Controller classes into
globalThis - Runs
discoverPackages()for package auto-discovery
- Loads 21
- Protected globals: 80+ built-in names (process, console, Buffer, fetch, Bun, etc.) are never overwritten
bunfig.toml Configuration
preload = ["./storage/framework/defaults/resources/plugins/preloader.ts"]
[test]
preload = ["./tests/setup.ts"]
coverage = true
[run]
bun = true # equivalent to `bun --bun` for all `bun run` commands
[serve.static]
plugins = ["bun-plugin-stx"] # STX template engine plugin for Bun.serve
[install]
registry = { url = "https://registry.npmjs.org/", token = "$BUN_AUTH_TOKEN" }
linker = "hoisted"Production Server (server/src/index.ts)
- Queue worker mode: When
QUEUE_WORKERenv var is set, imports and runs the job fromapp/Jobs/<JOB>.tswith retry support (configurable viaJOB_RETRIES,JOB_BACKOFF_FACTOR,JOB_INITIAL_DELAY,JOB_JITTER) - HTTP server:
Bun.serve()onPORT(default 3000), delegates toserverResponse()from@stacksjs/router - Realtime: Separate authenticated broadcast server; the HTTP entry does not upgrade WebSocket requests
- Development mode: Auto-enabled when
APP_ENVis notproductionorprod - Docker: Multi-stage build with
oven/bun:1.3.10, runs as non-rootbunuser, exposes port 3000, includes curl for healthchecks
Server Build Process (server/build.ts)
- Stops any running
stacks-serverDocker container - Cleans previous build artifacts (app/, config/, dist/, docs/, storage/)
- Builds server entry (
src/index.ts) todist/usingBun.build(ESM, target: bun) - Builds all
app/**/*.{ts,js}files toserver/app/(ESM, no splitting, external:@swc/wasm) - Post-processes built files: rewrites
storage/framework/serverimports todist/ - Strips
export { ENV_KEY, ENV_SECRET, fromEnv }from dist (bundler workaround) - Copies custom server config if present (
server/dir at project root) - Optionally builds Docker image if
cloud.api.deployis enabled
IDE Support
Default configurations provided at storage/framework/defaults/ide/:
Cursor
cursor/rules/: 11 rule files (auto-imports, code-style, documentation, error-handling, key-conventions, project-structure, syntax-formatting, testing, typescript, ui-styling, readme)
VS Code
vscode/.vscode/extensions.json-- Recommended extensionsvscode/.vscode/settings.json-- Editor settingsvscode/.vscode/stacks.code-snippets-- Code snippetsvscode/package.json-- the Stacks extension (Stacks.vscode-stacks). Self-contained: it installs no extension pack and depends on no extension. It builds in.stxsupport from@stacksjs/stx-vscode(the stx extension's language support as a library; the standalone stx extension,Stacks.vscode-stx, stands down next to it), pickier lint/format/fix-all through the project's own pickier, and env file hover decryption andbuddy env:*commands. Runbun run sync:stxthere after updating@stacksjs/stx-vscode; never map*.stxtohtmlinfiles.associations, which takes.stxfiles away from the stx language.
JetBrains
.idea/config (ESLint, modules, VCS, web resources).fleet/settings.json-- Fleet IDE settingsoptions/(GitHub Copilot, Node.js config)templates/(JavaScript and Stacks live templates)
Zed
zed/.zed/settings.json-- Zed editor settings
Dictionary
dictionary.txt-- Custom spell-check dictionary
Doctor Health Checks
buddy doctor verifies:
- Bun runtime version against the installed framework minimum (Bun >= 1.3.0)
- Node.js version (pass if v18+, warn otherwise)
- package.json existence and project name
.envfile presence (warn if missing, not a failure)APP_KEYis set (warn if not, suggestsbuddy key:generate)
STX Configuration (config/stx.ts)
{
componentsDir: 'components',
layoutsDir: 'layouts',
partialsDir: 'partials',
}Note: Dashboard mode overrides these settings via its own serve() options.
Gotchas
- The
@stacksjs/developmentpackage is currently a stub (export {}) -- all actual dev tooling lives in the actions and server packages - The preloader skips entirely for
dev,build,test,lint, and version/help commands -- auto-imports are NOT available in those contexts buddy dev(no arguments) starts ALL servers simultaneously -- usebuddy dev apiorbuddy dev frontendto start only one- The
STACKS_PROXY_MANAGED=1env var prevents individual dev servers from starting their own reverse proxy -- only the unifiedbuddy devcommand manages the proxy - Dev actions are executed with
bun --watchautomatically -- do NOT add--watchmanually or you get double-restart behavior - The
dev/viewsaction is special-cased inrunAction()to run in-process (no subprocess) for performance -- other dev actions spawn subprocesses NODE_PATHis injected to includepantry/directory so compiled pantry packages can resolve@stacksjs/*dependencies at runtime- The dashboard config API shares the dashboard origin and atomically modifies top-level scalar literals in
config/*.ts. Nested values, arrays, and environment-backed expressions remain read-only. - SSL certificates are stored in
~/.stacks/ssl/, NOT in the project directory -- they persist across projects using the same domain - Custom server configuration in
server/at the project root overrides the default server config via file copy (checked byuseCustomOrDefaultServerConfig()) - The production server doubles as a queue worker when
QUEUE_WORKERenv var is set -- it imports the job by name fromapp/Jobs/and exits after completion - Docker build strips
src/directories, test files, sourcemaps, andnode_modulesfrom the image to minimize size - The
bun-plugin-stxserve plugin is resolved with a fallback: first tries normalimport('bun-plugin-stx/serve'), then falls back topantry/bun-plugin-stx/dist/serve.js buddy freshis NOT the same asbuddy clean--freshcleans AND reinstalls dependencies, whilecleanonly removes artifacts- The Craft native window for the dashboard uses
http://localhost:<port>directly (not HTTPS) even when a custom domain proxy is running
Verify the selected entrypoint
Use core/actions/src/dev.ts and dev/views.ts for the actual launch and STX
request pipeline. Page middleware, API forwarding, request scopes and SEO are
installed there and in core/buddy/src/production-server.ts, not by copying an
example Bun.serve into a page. Native launch, marketing-site launch and browser
launch are distinct options; read config/app.ts and buddy dev --help.
A preloader skip does not mean the selected server never initializes model
globals: server entrypoints invoke their own runtime boot.