{
  "manifest_version": "1.0.0",
  "template": {
    "id": "eac4b595-4409-4062-9ad6-a51f2494577d",
    "slug": "trug",
    "name": "Trug",
    "description": "Trug is a self-hosted shared shopping list for one household.",
    "url": "https://railway.com/deploy/trug",
    "upstream": {
      "image": "ghcr.io/maxdraki/trug:latest"
    }
  },
  "services": [
    {
      "name": "trug",
      "source": {
        "image": "ghcr.io/maxdraki/trug:latest"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "http": true
    }
  ],
  "required_inputs": [
    {
      "key": "TRUG_RP_ID",
      "service": "trug",
      "description": "Passkey relying-party ID — resolved automatically from your generated domain. Only change it if you attach a custom domain (set it to the bare hostname).",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "TRUG_ORIGIN",
      "service": "trug",
      "description": "Full origin for passkeys — resolved automatically. With a custom domain, set it to `https://<your-domain>`.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "TRUG_TOKEN_MCP",
      "service": "trug",
      "description": "Machine token for token-based MCP clients. The MCP connector's OAuth flow does not need it. Ignore it unless you use token auth.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "TRUG_TOKEN_RING",
      "service": "trug",
      "description": "Machine token for the webhook capture endpoint (`POST /api/capture`) — smart rings, Home Assistant, and similar. Ignore it unless you use those.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "TRUG_BOOTSTRAP_TOKEN",
      "service": "trug",
      "description": "One-time token to claim the FIRST account. Copy it, open your Trug URL, and paste it at the gate. After that it is inert — everyone else joins via invite links.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "TRUG_TRUSTED_PROXY_HOPS",
      "service": "trug",
      "description": "Railway terminates TLS in front of the app, so this tells the rate limiter to key on the real client IP instead of the shared proxy address. Leave it at 1.",
      "secret": false,
      "strategy": "default",
      "default": "1"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "trug"
      }
    },
    "cli": "railway deploy --template trug",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "eac4b595-4409-4062-9ad6-a51f2494577d",
            "serializedConfig": {
              "services": {
                "f1daa2cf-dbf3-4042-8c4c-5163479a58e0": {
                  "icon": "https://raw.githubusercontent.com/maxdraki/trug/refs/heads/main/icon/trug-logo.svg",
                  "name": "trug",
                  "deploy": {
                    "healthcheckPath": "/healthz"
                  },
                  "source": {
                    "image": "ghcr.io/maxdraki/trug:latest"
                  },
                  "variables": {
                    "LLM_MODEL": {
                      "isOptional": true,
                      "description": "Optional. Model name for your LLM provider.",
                      "defaultValue": ""
                    },
                    "TRUG_RP_ID": {
                      "description": "Passkey relying-party ID — resolved automatically from your generated domain. Only change it if you attach a custom domain (set it to the bare hostname).",
                      "defaultValue": "${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "LLM_API_KEY": {
                      "isOptional": true,
                      "description": "Optional. Bring your own LLM key for smarter item icons and aisles. Trug works fully without it.",
                      "defaultValue": ""
                    },
                    "TRUG_ORIGIN": {
                      "description": "Full origin for passkeys — resolved automatically. With a custom domain, set it to `https://<your-domain>`.",
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "LLM_BASE_URL": {
                      "isOptional": true,
                      "description": "Optional. OpenAI-compatible base URL (for example Ollama or a proxy).",
                      "defaultValue": ""
                    },
                    "TRUG_TOKEN_MCP": {
                      "description": "Machine token for token-based MCP clients. The MCP connector's OAuth flow does not need it. Ignore it unless you use token auth.",
                      "defaultValue": "{{TRUG_TOKEN_MCP}}"
                    },
                    "TRUG_TOKEN_RING": {
                      "description": "Machine token for the webhook capture endpoint (`POST /api/capture`) — smart rings, Home Assistant, and similar. Ignore it unless you use those.",
                      "defaultValue": "{{TRUG_TOKEN_RING}}"
                    },
                    "TRUG_BOOTSTRAP_TOKEN": {
                      "description": "One-time token to claim the FIRST account. Copy it, open your Trug URL, and paste it at the gate. After that it is inert — everyone else joins via invite links.",
                      "defaultValue": "{{TRUG_BOOTSTRAP_TOKEN}}"
                    },
                    "TRUG_TRUSTED_PROXY_HOPS": {
                      "description": "Railway terminates TLS in front of the app, so this tells the rate limiter to key on the real client IP instead of the shared proxy address. Leave it at 1.",
                      "defaultValue": "1"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8080": {
                        "port": 8080
                      }
                    }
                  },
                  "volumeMounts": {
                    "f1daa2cf-dbf3-4042-8c4c-5163479a58e0": {
                      "mountPath": "/data"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "trug",
      "method": "GET",
      "path": "/healthz",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 1,
    "needs_volume": true
  },
  "generated_at": "2026-09-10T16:14:52.212Z",
  "generator_version": "0.1.0",
  "status": "validated",
  "validated_at": "2026-09-05T00:21:22.378Z",
  "success_rate_30d": 1,
  "validation": {
    "last_run_id": "run_dc716762b33a41d1a378",
    "checks": [
      {
        "name": "workflow_completed",
        "passed": true
      },
      {
        "name": "all_services_deployed",
        "passed": true
      },
      {
        "name": "healthcheck",
        "passed": true
      },
      {
        "name": "stays_up",
        "passed": true
      }
    ],
    "typical_ready_seconds": 57,
    "typical_build_seconds": 0,
    "typical_start_seconds": 10,
    "slowest_service": "trug"
  }
}
