Documentation

Stacksmith docs

Describe a local development stack in a .stacksmith.yml file, then run, inspect, diagnose, and control it from the macOS app or over MCP.

Quick start

Create .stacksmith.yml in your project root, open the project in Stacksmith, then start the stack. Stacksmith validates the config, resolves references, starts components in dependency order, and keeps runtime state tied to each component. It never modifies your project folder unless you explicitly create an example project.

CompatibilityConfigs are versioned so future updates stay backward compatible. New configs use version: 1.

Configuration

One file describes the whole stack: apps, workers, jobs, tunnels, links, interpolation, and dependency-aware startup. References resolve after YAML decoding and before validation, so ports, URLs, commands, paths, and conditions are still checked.

.stacksmith.yml
example
1version: 12name: my_awesome_app3 4apps:5  api:6    name: API Server7    command: cargo run --bin api8    cwd: .9    env:10      DATABASE_URL: ${secret.DATABASE_URL}11      STRIPE_KEY: ${dotenv.STRIPE_KEY}12    port: 808013    url: http://localhost:${apps.api.port}14    health: ${apps.api.url}/health15 16workers:17  worker:18    name: Background Worker19    command: cargo run --bin worker20    cwd: .21    depends_on:22      api: healthy23 24jobs:25  migrate:26    name: Migrate Databases27    command: cargo run --bin migrate28    cwd: .29    env:30      DATABASE_URL: ${secret.DATABASE_URL}31    depends_on:32      api: port_listening33 34tunnels:35  stripe_webhook:36    command: ngrok http --url=${tunnels.stripe_webhook.public_url} ${apps.api.port}37    cwd: .38    public_url: https://guiding-lucky-tick.ngrok-free.app39    forwards_to: api40    depends_on:41      api: healthy42 43links:44  api: ${apps.api.url}

Component roles

Every runnable entry has a role and a run policy. Component ids must be globally unique across apps, workers, jobs, and tunnels. Display order is apps, workers, tunnels, then jobs.

apps

Long-running foreground processes: the services you run.

commandShell command that starts the process. Required.required
cwdWorking directory the command runs in.required
nameDisplay name shown in the UI.optional
portOwned port. Enables preflight, conflict, and release checks.optional
urlProject URL, often built from the port via interpolation.optional
healthHTTP URL or command probe that gates readiness.optional
envStructured environment values resolved at launch.optional
depends_onMap of component id to the condition to wait for.optional

workers

Long-running background processes and queue runners.

commandShell command that starts the worker. Required.required
cwdWorking directory.required
nameDisplay name shown in the UI.optional
healthOptional HTTP or command health check.optional
envStructured environment values resolved at launch.optional
depends_onUsually waits on an app being healthy.optional

jobs

One-shot commands that complete, fail, and can be rerun.

commandShell command to run once. Required.required
cwdWorking directory.required
nameDisplay name shown in the UI.optional
auto_startSet false to skip Start All; run explicitly instead. Default true.optional
envStructured environment values resolved at launch.optional
depends_onConditions that must hold before the job runs.optional

tunnels

Auxiliary processes that expose a public URL for a local app.

commandTunnel command, e.g. an ngrok invocation. Required.required
cwdWorking directory.required
public_urlThe public URL the tunnel exposes.required
forwards_toId of the app this tunnel forwards to. Must be an app.required
nameDisplay name shown in the UI.optional
envStructured environment values resolved at launch.optional
depends_onUsually the forwarded app being healthy.optional

links

Named URLs shown with the project. Not runnable components.

<id>: <url>Maps a name to a URL, e.g. api: ${apps.api.url}.required

Health checks

A health check gates a component's readiness and is what satisfies the healthy dependency condition. It can be an HTTP probe or a command probe.

HTTP health

Provide a URL string. The check passes on any 2xx response.

.stacksmith.yml
http health
1apps:2  api:3    command: cargo run --bin api4    port: 80805    health: http://localhost:8080/health

Command health

Provide a mapping with a command. The check passes when the command exits 0; a nonzero exit, launch failure, or timeout is unhealthy. Probes run serially and never overlap. Timing fields are in seconds.

.stacksmith.yml
command health
1apps:2  db:3    command: postgres -D ./data4    health:5      command: pg_isready -h localhost -p 54326      interval: 57      timeout: 28      start_period: 5
commandShell command to run as the probe. Exit 0 is healthy.required
intervalSeconds between probes.optional
timeoutSeconds before a probe is killed and marked unhealthy.optional
start_periodGrace seconds before failures start counting.optional
cwdWorking directory for the probe. Defaults to the component cwd, then the project root.optional
Health vs jobsUse command health for a quick, repeatable probe. Use a job with depends_on: completed for one-shot setup or migrations that should run once and unblock dependents.

Interpolation

String values may reference other parts of the config with ${...}. A string can contain more than one reference. Missing or unsupported references are validation errors. Stacksmith does not evaluate shell syntax, expand environment variables, or support conditionals or expressions.

${apps.<id>.port}The app's configured port.
${apps.<id>.url}The app's configured URL.
${apps.<id>.health}The app's health-check URL.
${tunnels.<id>.public_url}A tunnel's public URL.
${links.<id>}A declared link URL.
.stacksmith.yml
interpolation
1apps:2  api:3    port: 80804    url: http://localhost:${apps.api.port}5    health: ${apps.api.url}/health6 7tunnels:8  stripe_webhook:9    command: ngrok http ${apps.api.port}

Secrets & environment

Components can define structured env values. Use ${secret.NAME} for Keychain secrets and ${dotenv.NAME} for local dotenv values. Stacksmith resolves values immediately before process launch, injects them into the child process environment, and does not store resolved values in runtime snapshots.

.stacksmith.yml
env
1jobs:2  migrate:3    command: cargo run --bin migrate4    env:5      DATABASE_URL: ${secret.DATABASE_URL}6      STRIPE_KEY: ${dotenv.STRIPE_KEY}
${secret.NAME}Reads from project-scoped macOS Keychain secrets.
${dotenv.NAME}Reads from configured dotenv files, with later files overriding earlier ones.
${env.NAME}Advanced: reads from Stacksmith's parent process environment.

Dotenv files

Stacksmith reads .env from the project root by default when it exists. Add environment.dotenv.files only when you want custom or multiple dotenv files such as .env.local. Files are read in order, and later files override earlier values.

.stacksmith.yml
dotenv
1environment:2  dotenv:3    files:4      - .env5      - .env.local

Dotenv parsing supports KEY=value, KEY="value", KEY='value', comments, and blank lines. It does not evaluate shell syntax, command substitution, or scripts.

Missing valuesA missing Keychain secret, dotenv key, or parent environment value blocks startup with a non-sensitive error. The error names the missing reference but never includes a resolved value.
Not a password managerStacksmith reads project-scoped Keychain secrets and local environment providers, then injects resolved values into launched processes. It does not store plaintext secrets in .stacksmith.yml, logs, snapshots, or project data.

Dependencies

depends_on controls startup order across all roles. Each entry maps a component id to the condition it must satisfy before the dependent component starts. Cycles across any roles are invalid.

startedThe dependency process has launched.Any component
healthyThe dependency passed its health check.Components with health
port_listeningThe dependency owns and listens on its configured port.Components with a port
completedThe dependency job exited successfully.Jobs only

List more than one dependency to require all of them. The entries combine with AND, so the component only starts once every listed dependency is satisfied. Each healthy dependency must point at a component that defines a health: check.

.stacksmith.yml
multiple deps
1tunnels:2  stripe_webhook:3    command: ngrok http ${apps.api.port}4    public_url: https://example.ngrok-free.app5    forwards_to: api6    depends_on:7      api: healthy8      worker: healthy
Manual jobsIf a component depends on a manual job with completed, Start All will not run it automatically. The dependant stays blocked until you run the job and it succeeds.

Lifecycle

Apps, workers, and tunnels are long-running. Readiness is determined by health check first, then owned port, then startup settling. Any unexpected exit is a failure, including exit code 0.

Jobs are one-shot. A successful job enters completed, is no longer active, is skipped by Stop All, and can be run again explicitly. Jobs default to auto_start: true; set auto_start: false for migrations and other commands that should only run on an explicit Run action or stacksmith_run_job call.

Ports & URLs

Port ownership applies to apps. Port preflight, conflicts, and release checks only apply to components with an owned port. A tunnel does not own the forwarded app port: the public URL belongs to the tunnel, while the forwarded local port stays owned by the app.

Diagnosis

Diagnosis is role-aware. Stacksmith reports the failure, the affected component, the evidence, and a recommended action instead of leaving you to infer state from scattered logs.

  • Port conflicts are reported only against apps that own ports.
  • Health failures apply only to components with health checks.
  • Failed jobs include exit status and rerun guidance; completed jobs are healthy context.
  • Blocked workers and tunnels show the dependency they are waiting on.
  • Long-running exit 0 is treated as unexpected.
  • Tunnel issues focus on public URL and forwarding state, not the forwarded app port.

On-device AI

The app always builds a deterministic diagnosis from observed runtime state and bounded log evidence. When Apple's on-device language model is available, the debug area can generate a grounded incident brief from the same confirmed evidence and allowed recovery actions. Generated explanations are validated before display and fall back to the deterministic brief when generation is unavailable or invalid. Nothing leaves your Mac.

MCP server

Stacksmith bundles the stacksmith-mcp helper. It uses MCP's stdio transport and forwards requests over a local Unix socket to the running app, which remains the owner of all project state and processes. The helper lives at a stable path in an installed app:

helper path
1/Applications/Stacksmith.app/Contents/Helpers/stacksmith-mcp

Configure a generic MCP client to launch it:

mcp.json
example
1{2  "mcpServers": {3    "stacksmith": {4      "command": "/Applications/Stacksmith.app/Contents/Helpers/stacksmith-mcp"5    }6  }7}

Or register the same executable with Codex:

terminal
codex
1codex mcp add stacksmith -- \2  /Applications/Stacksmith.app/Contents/Helpers/stacksmith-mcp3 4codex mcp get stacksmith
Agent skillAn installable skill teaches Claude Code and Codex to author .stacksmith.yml files and drive these MCP tools. Get it from the agent-skill folder on GitHub.
Enable firstMCP is disabled by default. Open Stacksmith Settings → MCP, enable read-only inspection, then enable component control separately if you want mutating tools. Select a project before calling project tools.

Tools

stacksmith_get_project_statusreadOverall project and health summary.
stacksmith_list_componentsreadAll components with role and current state.
stacksmith_get_component_statusreadOne component's current snapshot.
stacksmith_get_recent_logsreadBounded recent log tail for a component.
stacksmith_get_diagnosisreadDeterministic diagnosis brief from runtime state.
stacksmith_validate_loaded_projectreadValidate the loaded .stacksmith.yml.
stacksmith_start_componentcontrolStart an app, worker, or tunnel.
stacksmith_stop_componentcontrolStop an app, worker, or tunnel.
stacksmith_restart_componentcontrolRestart an app, worker, or tunnel.
stacksmith_run_jobcontrolRun (or rerun) a one-shot job.

Read-only tools never change runtime state. Mutating tools (start, stop, restart, run_job) require the local component-control permission, affect only the named component, and never touch dependencies or dependants. Jobs are exclusive to stacksmith_run_job.

Troubleshooting

The app must be open so the local IPC server is available. To verify the running app is accepting bridge requests, check its Unix socket, or use the non-mutating Test Connection action on the MCP settings page:

terminal
1ls -l "$HOME/Library/Application Support/Stacksmith/runtime.sock"2lsof -U | rg 'Stacksmith/runtime.sock'

Licensing

Stacksmith starts with a one-time 7-day free trial. Download it, open the app, and start the trial with no licence key required. The trial unlocks the full app for seven days; when it ends, enter a licence key to keep using Stacksmith. The trial is recorded locally so it cannot be reset by reinstalling.

The direct-distribution build uses Lemon Squeezy for activation and update eligibility, with licence credentials stored in your macOS Keychain. An already-activated installation keeps working when licensing is temporarily unavailable, and licensing never stops managed processes or edits project data.

Beta pricingStacksmith is in beta. A licence bought during beta is a one-time purchase that rises to $19.99 once Stacksmith leaves beta. Buy now to lock in the lower price.
Download the trialThe latest notarized build is published on GitHub releases. Buy a licence any time to convert your trial into a full installation.