{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://stacktora.com/schema/stacktora.schema.json",
  "title": "Stacktora stack recipe",
  "description": "The stack.json recipe format Stacktora reads and writes. Enable editor autocomplete and validation by adding \"$schema\": \"https://stacktora.com/schema/stacktora.schema.json\" as the first field in stacktora.json.",
  "type": "object",
  "required": ["stacktora", "stack"],
  "properties": {
    "$schema": { "type": "string" },
    "stacktora": {
      "const": 1,
      "description": "Schema/format version. Always 1 for the current format."
    },
    "generatedAt": {
      "type": "string",
      "description": "Date the file was last written by `stacktora sync` or `stacktora init` (YYYY-MM-DD). Informational only — Stacktora does not read this back."
    },
    "stack": {
      "type": "object",
      "required": ["runtime", "datastores", "services", "tools", "config"],
      "additionalProperties": false,
      "properties": {
        "runtime": {
          "type": "string",
          "enum": ["node", "python", "go", "ruby", "php", "java", "rust", "dotnet", "bun", "deno", "elixir"],
          "description": "The project's language runtime."
        },
        "version": {
          "type": "string",
          "description": "Runtime version, e.g. \"24\" for Node, \"3.12\" for Python. Valid values depend on the runtime — see the wizard at stacktora.com for the current list per runtime."
        },
        "datastores": {
          "type": "array",
          "description": "Databases and caches this project talks to.",
          "items": {
            "type": "string",
            "enum": ["postgres", "mysql", "redis", "mongo", "clickhouse", "mariadb", "valkey", "pgvector"]
          }
        },
        "services": {
          "type": "array",
          "description": "Backing services beyond datastores (message queues, search, object storage, etc.).",
          "items": {
            "type": "string",
            "enum": ["rabbitmq", "minio", "mailpit", "elastic", "kafka", "localstack", "nats", "meilisearch"]
          }
        },
        "tools": {
          "type": "object",
          "description": "Toolchain choices. Valid packageManager/linter/formatter values depend on the runtime (e.g. pip/ruff/black for Python, npm/eslint/prettier for Node) — see the wizard for the current list per runtime.",
          "properties": {
            "packageManager": { "type": "string" },
            "linter": { "type": "string" },
            "formatter": { "type": "string" },
            "codeStyle": {
              "type": "object",
              "description": "Optional. Only takes effect if formatter is \"prettier\" — generates a real .prettierrc.json. Omit entirely to skip generating this file.",
              "properties": {
                "semicolons": { "type": "boolean", "description": "Default true." },
                "singleQuote": { "type": "boolean", "description": "Default false." },
                "printWidth": { "type": "integer", "minimum": 20, "maximum": 400, "description": "Default 80." },
                "tabWidth": { "type": "integer", "minimum": 1, "maximum": 16, "description": "Default 2." },
                "trailingComma": { "type": "string", "enum": ["all", "es5", "none"], "description": "Default \"all\"." }
              },
              "additionalProperties": false
            }
          },
          "additionalProperties": false
        },
        "config": {
          "type": "object",
          "properties": {
            "appName": { "type": "string", "description": "Used as the app's container/service name and default image name." },
            "appPort": { "type": "integer", "minimum": 1, "maximum": 65535 },
            "hotReload": { "type": "boolean" },
            "seed": { "type": "boolean", "description": "Whether to generate a database seed script." },
            "healthGate": { "type": "boolean", "description": "Whether generated CI/compose wait for a health check before considering the stack up." },
            "worker": { "type": "boolean", "description": "Whether this project has a background worker process in addition to the main app." },
            "resources": {
              "type": "object",
              "description": "Optional. Sizes the Kubernetes and Fly.io outputs consistently. Omit entirely to keep each output's own defaults.",
              "properties": {
                "cpu": { "type": "number", "exclusiveMinimum": 0, "description": "Cores, e.g. 1 or 0.5. Used as the Kubernetes limit (request is set at half) and to pick the nearest Fly.io VM size preset." },
                "memory": { "type": "number", "exclusiveMinimum": 0, "description": "Megabytes, e.g. 512. Used as the Kubernetes limit (request is set at half) and the Fly.io VM memory." }
              },
              "additionalProperties": false
            },
            "env": {
              "type": "array",
              "description": "Custom environment variables beyond what the runtime/datastores need automatically.",
              "items": {
                "type": "object",
                "required": ["key", "value"],
                "properties": {
                  "key": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
                  "value": { "type": "string", "description": "Written as-is to .env and .env.example — never a real secret in .env.example." },
                  "source": { "type": "string", "description": "Optional. A plain-text pointer to where the real value lives (e.g. \"1Password > Acme > Stripe\") — never the secret itself. Printed as a comment above the variable." }
                },
                "additionalProperties": false
              }
            },
            "svc": {
              "type": "object",
              "description": "Per-datastore/service overrides (version, port), keyed by the datastore/service id.",
              "additionalProperties": {
                "type": "object",
                "properties": {
                  "version": { "type": "string" },
                  "port": { "type": "integer", "minimum": 1, "maximum": 65535 }
                }
              }
            },
            "web": {
              "type": "object",
              "description": "Optional web frontend, generated alongside the backend.",
              "properties": {
                "enabled": { "type": "boolean" },
                "framework": { "type": "string", "enum": ["vite-react", "vite-vue", "sveltekit", "angular"] },
                "port": { "type": "integer", "minimum": 1, "maximum": 65535 }
              },
              "additionalProperties": false
            },
            "mobile": {
              "type": "object",
              "description": "Optional mobile client, generated alongside the backend.",
              "properties": {
                "enabled": { "type": "boolean" },
                "framework": { "type": "string", "enum": ["expo", "rn", "flutter"] }
              },
              "additionalProperties": false
            },
            "outputs": {
              "type": "object",
              "description": "Optional generated files, each off by default. Omit this object entirely, or any key within it, to keep today's default output set unchanged.",
              "properties": {
                "dockerfile": { "type": "boolean", "description": "Multi-stage production Dockerfile." },
                "k8s": { "type": "boolean", "description": "Starter Kubernetes manifests (k8s/manifests.yaml + k8s/README.md)." },
                "fly": { "type": "boolean", "description": "Fly.io deploy config (fly.toml + fly/README.md). App container only — datastores are separate managed Fly resources." },
                "taskfile": { "type": "boolean" },
                "editorconfig": { "type": "boolean" },
                "precommit": { "type": "boolean" },
                "envrc": { "type": "boolean", "description": "direnv auto-load." },
                "gitpod": { "type": "boolean" },
                "devcontainer": { "type": "boolean" },
                "aliases": { "type": "boolean" }
              },
              "additionalProperties": false
            }
          },
          "additionalProperties": true
        }
      }
    }
  }
}
