{
  "manifest_version": "1.0.0",
  "template": {
    "id": "e69b541d-263a-491f-9516-fe7f1968a199",
    "slug": "sillytavern-llm",
    "name": "SillyTavern [Updated Sep '26]",
    "description": "SillyTavern — LLM Frontend, Auto-Secured Admin Password",
    "url": "https://railway.com/deploy/sillytavern-llm",
    "upstream": {
      "image": "ghcr.io/sillytavern/sillytavern:latest"
    }
  },
  "services": [
    {
      "name": "SillyTavern-v2",
      "source": {
        "image": "ghcr.io/sillytavern/sillytavern:latest"
      },
      "needs_volume": true,
      "volume_mount_path": "/home/node/app/persist",
      "http": true
    }
  ],
  "required_inputs": [
    {
      "key": "SILLYTAVERN_PORT",
      "service": "SillyTavern-v2",
      "description": "The port SillyTavern's server listens on inside the container. Matches the image's default — don't change unless you also update the service's public networking to match.",
      "secret": false,
      "strategy": "default",
      "default": "8000"
    },
    {
      "key": "SILLYTAVERN_LISTEN",
      "service": "SillyTavern-v2",
      "description": "Binds to all network interfaces. Required for Railway's proxy to reach the container at all — without it, SillyTavern only listens on localhost.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "SILLYTAVERN_WHITELISTMODE",
      "service": "SillyTavern-v2",
      "description": "Disables SillyTavern's own IP whitelist. Railway's edge network isn't a fixed IP you can whitelist, so this must be off for the app to be reachable publicly.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "SILLYTAVERN_ADMIN_PASSWORD",
      "service": "SillyTavern-v2",
      "description": "Auto-generated on deploy. Applied to the default-user account on first boot via SillyTavern's own recover.js script — this is the fix for the \"no password by default\" gap present in the reference template. Log in with this value, then change it from inside the UI if you want.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "SILLYTAVERN_ENABLEUSERACCOUNTS",
      "service": "SillyTavern-v2",
      "description": "Turns on SillyTavern's multi-user login system. Needed for the password fix above to mean anything — without it, there's no auth at all.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "SILLYTAVERN_ENABLEDISCREETLOGIN",
      "service": "SillyTavern-v2",
      "description": "When true, hides the account picker on the login screen (you type your username instead of selecting it from a list). Cosmetic/privacy toggle only.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "sillytavern-llm"
      }
    },
    "cli": "railway deploy --template sillytavern-llm",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "e69b541d-263a-491f-9516-fe7f1968a199",
            "serializedConfig": {
              "buckets": {},
              "services": {
                "3fb082c2-e76d-42e8-aadb-cc4841665eb5": {
                  "icon": "https://sillytavern.app/img/logo.png",
                  "name": "SillyTavern-v2",
                  "deploy": {
                    "startCommand": "sh -c 'PERSIST_DIR=\"/home/node/app/persist\"; mkdir -p \"$PERSIST_DIR\"; for d in config data plugins; do if [ ! -L \"./$d\" ]; then if [ -e \"$PERSIST_DIR/$d\" ]; then rm -rf \"./$d\"; else mv \"./$d\" \"$PERSIST_DIR/$d\"; fi; ln -s \"$PERSIST_DIR/$d\" \"./$d\"; fi; done; [ -e \"config/config.yaml\" ] || cp default/config.yaml config/config.yaml; npm run init; if [ -n \"$SILLYTAVERN_ADMIN_PASSWORD\" ]; then node recover.js default-user \"$SILLYTAVERN_ADMIN_PASSWORD\"; fi; exec ./docker-entrypoint.sh'",
                    "healthcheckPath": "/login",
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "ghcr.io/sillytavern/sillytavern:latest"
                  },
                  "variables": {
                    "SILLYTAVERN_PORT": {
                      "isOptional": false,
                      "description": "The port SillyTavern's server listens on inside the container. Matches the image's default — don't change unless you also update the service's public networking to match.",
                      "defaultValue": "8000"
                    },
                    "SILLYTAVERN_LISTEN": {
                      "isOptional": false,
                      "description": "Binds to all network interfaces. Required for Railway's proxy to reach the container at all — without it, SillyTavern only listens on localhost.",
                      "defaultValue": "true"
                    },
                    "SILLYTAVERN_WHITELISTMODE": {
                      "isOptional": false,
                      "description": "Disables SillyTavern's own IP whitelist. Railway's edge network isn't a fixed IP you can whitelist, so this must be off for the app to be reachable publicly.",
                      "defaultValue": "false"
                    },
                    "SILLYTAVERN_ADMIN_PASSWORD": {
                      "isOptional": false,
                      "description": "Auto-generated on deploy. Applied to the default-user account on first boot via SillyTavern's own recover.js script — this is the fix for the \"no password by default\" gap present in the reference template. Log in with this value, then change it from inside the UI if you want.",
                      "defaultValue": "{{SILLYTAVERN_ADMIN_PASSWORD}}"
                    },
                    "SILLYTAVERN_ENABLEUSERACCOUNTS": {
                      "isOptional": false,
                      "description": "Turns on SillyTavern's multi-user login system. Needed for the password fix above to mean anything — without it, there's no auth at all.",
                      "defaultValue": "true"
                    },
                    "SILLYTAVERN_ENABLEDISCREETLOGIN": {
                      "isOptional": true,
                      "description": "When true, hides the account picker on the login screen (you type your username instead of selecting it from a list). Cosmetic/privacy toggle only.",
                      "defaultValue": "false"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8000": {
                        "port": 8000
                      }
                    }
                  },
                  "volumeMounts": {
                    "3fb082c2-e76d-42e8-aadb-cc4841665eb5": {
                      "mountPath": "/home/node/app/persist"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "SillyTavern-v2",
      "method": "GET",
      "path": "/login",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 1,
    "needs_volume": true
  },
  "generated_at": "2026-10-06T04:14:42.626Z",
  "generator_version": "0.1.0",
  "status": "degraded",
  "validated_at": "2026-10-04T06:02:02.567Z",
  "success_rate_30d": 0,
  "validation": {
    "last_run_id": "run_3bc3c62828c444a5a0ae",
    "checks": [
      {
        "name": "workflow_completed",
        "passed": true
      },
      {
        "name": "all_services_deployed",
        "passed": false,
        "detail": "FAILED"
      }
    ]
  }
}
