{
  "manifest_version": "1.0.0",
  "template": {
    "id": "103794be-3713-4a61-8941-68db10623dfe",
    "slug": "hexclave-or-self-hosted-auth-storage",
    "name": "Hexclave — Self-Hosted Auth, S3 Storage & Webhooks",
    "description": "Self-hosted auth, teams, analytics, and file uploads with built-in S3.",
    "url": "https://railway.com/deploy/hexclave-or-self-hosted-auth-storage",
    "upstream": {
      "image": "minio/minio:latest"
    }
  },
  "status": "unvalidated",
  "validated_at": null,
  "success_rate_30d": null,
  "services": [
    {
      "name": "minio-init",
      "source": {
        "image": "minio/mc:latest"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "svix-db",
      "source": {
        "image": "postgres:16"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql/data",
      "http": false
    },
    {
      "name": "MinIO",
      "source": {
        "image": "minio/minio:latest"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "http": true
    },
    {
      "name": "svix-server",
      "source": {
        "image": "svix/svix-server"
      },
      "needs_volume": false,
      "http": true
    },
    {
      "name": "hexclave-cron",
      "source": {
        "image": "curlimages/curl:latest"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "hexclave",
      "source": {
        "image": "stackauth/server:ebe7877"
      },
      "needs_volume": false,
      "http": false
    },
    {
      "name": "hexclave-proxy",
      "source": {
        "image": "caddy:2-alpine"
      },
      "needs_volume": false,
      "http": true
    },
    {
      "name": "Postgres",
      "source": {
        "image": "ghcr.io/railwayapp-templates/postgres-ssl:18"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql/data",
      "http": false
    },
    {
      "name": "ClickHouse",
      "source": {
        "image": "clickhouse/clickhouse-server:25.10"
      },
      "needs_volume": false,
      "http": false
    }
  ],
  "required_inputs": [
    {
      "key": "PUBLIC_BUCKET",
      "service": "minio-init",
      "description": "Name of the public bucket for browser-readable uploads like profile pictures.",
      "secret": false,
      "strategy": "default",
      "default": "hexclave-public"
    },
    {
      "key": "MINIO_ENDPOINT",
      "service": "minio-init",
      "description": "Internal address of the MinIO service, used to create buckets over Railway's private network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PRIVATE_BUCKET",
      "service": "minio-init",
      "description": "Name of the private bucket for session recordings and deployment source.",
      "secret": false,
      "strategy": "default",
      "default": "hexclave-private"
    },
    {
      "key": "MINIO_ROOT_USER",
      "service": "minio-init",
      "description": "MinIO root access key, referenced from the MinIO service.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "MINIO_ROOT_PASSWORD",
      "service": "minio-init",
      "description": "MinIO root secret key, referenced from the MinIO service.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGDATA",
      "service": "svix-db",
      "description": "Provide a value for PGDATA.",
      "secret": false,
      "strategy": "default",
      "default": "/var/lib/postgresql/data/pgdata"
    },
    {
      "key": "POSTGRES_DB",
      "service": "svix-db",
      "description": "Provide a value for POSTGRES_DB.",
      "secret": false,
      "strategy": "default",
      "default": "svix"
    },
    {
      "key": "POSTGRES_USER",
      "service": "svix-db",
      "description": "Provide a value for POSTGRES_USER.",
      "secret": false,
      "strategy": "default",
      "default": "postgres"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "svix-db",
      "description": "Provide a value for POSTGRES_PASSWORD.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "PORT",
      "service": "MinIO",
      "description": "Port MinIO's S3 API listens on. Set so Railway's healthcheck targets the right port; keep it aligned with the public domain's target port (9000).",
      "secret": false,
      "strategy": "default",
      "default": "9000"
    },
    {
      "key": "MINIO_ROOT_USER",
      "service": "MinIO",
      "description": "Root access key for MinIO. Auto-generated — used as the S3 access key by the hexclave service.",
      "secret": false,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "MINIO_SITE_REGION",
      "service": "MinIO",
      "description": "Region MinIO reports; must match the S3 region the hexclave service signs requests with.",
      "secret": false,
      "strategy": "default",
      "default": "us-east-1"
    },
    {
      "key": "MINIO_ROOT_PASSWORD",
      "service": "MinIO",
      "description": "Root secret key for MinIO. Auto-generated — used as the S3 secret key by the hexclave service. Do not share.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "PORT",
      "service": "svix-server",
      "description": "Provide a value for PORT.",
      "secret": false,
      "strategy": "default",
      "default": "8071"
    },
    {
      "key": "WAIT_FOR",
      "service": "svix-server",
      "description": "Provide a value for WAIT_FOR.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "SVIX_DB_DSN",
      "service": "svix-server",
      "description": "Provide a value for SVIX_DB_DSN.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "SVIX_REDIS_DSN",
      "service": "svix-server",
      "description": "Provide a value for SVIX_REDIS_DSN.",
      "secret": false,
      "strategy": "default",
      "default": "redis://localhost:6379"
    },
    {
      "key": "SVIX_CACHE_TYPE",
      "service": "svix-server",
      "description": "Provide a value for SVIX_CACHE_TYPE.",
      "secret": false,
      "strategy": "default",
      "default": "memory"
    },
    {
      "key": "SVIX_JWT_SECRET",
      "service": "svix-server",
      "description": "Provide a value for SVIX_JWT_SECRET.",
      "secret": true,
      "strategy": "default",
      "default": "lFSGu4YD8DlYOZHpJ2UhVvw4X1NvhjIX3fbHPXwqjL_6qfTPs_qnZGiKL9-KNRfg"
    },
    {
      "key": "SVIX_QUEUE_TYPE",
      "service": "svix-server",
      "description": "Provide a value for SVIX_QUEUE_TYPE.",
      "secret": false,
      "strategy": "default",
      "default": "memory"
    },
    {
      "key": "SVIX_LISTEN_ADDRESS",
      "service": "svix-server",
      "description": "Provide a value for SVIX_LISTEN_ADDRESS.",
      "secret": false,
      "strategy": "default",
      "default": "0.0.0.0:8071"
    },
    {
      "key": "API_URL",
      "service": "hexclave-cron",
      "description": "Base URL of the hexclave service's API, reached over Railway's private network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "CRON_SECRET",
      "service": "hexclave-cron",
      "description": "Shared secret used to authenticate this job's requests to hexclave's internal endpoints.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PORT",
      "service": "hexclave",
      "description": "Port the dashboard process listens on inside the container.",
      "secret": false,
      "strategy": "default",
      "default": "8101"
    },
    {
      "key": "CRON_SECRET",
      "service": "hexclave",
      "description": "Shared secret hexclave-cron uses to call this service's internal maintenance endpoints.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "HEXCLAVE_S3_BUCKET",
      "service": "hexclave",
      "description": "Public bucket name; must match minio-init's PUBLIC_BUCKET.",
      "secret": false,
      "strategy": "default",
      "default": "hexclave-public"
    },
    {
      "key": "HEXCLAVE_S3_REGION",
      "service": "hexclave",
      "description": "S3 region hexclave signs storage requests with; must match MinIO's MINIO_SITE_REGION.",
      "secret": false,
      "strategy": "default",
      "default": "us-east-1"
    },
    {
      "key": "STACK_SVIX_API_KEY",
      "service": "hexclave",
      "description": "Provide a value for STACK_SVIX_API_KEY.",
      "secret": true,
      "strategy": "default",
      "default": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDAwMDAwMDAsIm5iZiI6MTcwMDAwMDAwMCwiZXhwIjoyMDAwMDAwMDAwLCJpc3MiOiJzdml4LXNlcnZlciIsInN1YiI6Im9yZ19IZXhjbGF2ZVJhaWx3YXlTZWxmSG9zdDAxIn0.rE2LDP5CP5ubMvr3aaZwah5EkIdZZkfpFmQH9Z-26WQ"
    },
    {
      "key": "HEXCLAVE_S3_ENDPOINT",
      "service": "hexclave",
      "description": "S3 API endpoint. Uses MinIO's public domain because hexclave hands the browser presigned upload/download URLs bound to this host.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "STACK_SVIX_SERVER_URL",
      "service": "hexclave",
      "description": "Provide a value for STACK_SVIX_SERVER_URL.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "HEXCLAVE_SERVER_SECRET",
      "service": "hexclave",
      "description": "High-entropy secret used to encrypt sessions and derive internal keys. Keep it stable — changing it invalidates sessions.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "HEXCLAVE_TRUSTED_PROXY",
      "service": "hexclave",
      "description": "Tells hexclave it's behind Railway's reverse proxy so it can trust forwarded-for headers.",
      "secret": false,
      "strategy": "default",
      "default": "generic"
    },
    {
      "key": "HEXCLAVE_CLICKHOUSE_URL",
      "service": "hexclave",
      "description": "URL of the ClickHouse service used for analytics data.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "HEXCLAVE_S3_ACCESS_KEY_ID",
      "service": "hexclave",
      "description": "S3 access key, referenced from the MinIO service.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "HEXCLAVE_SKIP_SEED_SCRIPT",
      "service": "hexclave",
      "description": "IMPORTANT: after your FIRST successful deploy, set this to true. On its second run the image's seed script crashes the whole container (an upstream bug), so the app will crash-loop on the first restart/redeploy until you set this to true. This is the ONLY variable that fixes that crash — it is NOT the same as the HEXCLAVE_SEED_INTERNAL_* variables. Leave false for the very first deploy so the admin account and database get seeded.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "STACK_DISABLE_PLAN_LIMITS",
      "service": "hexclave",
      "description": "Provide a value for STACK_DISABLE_PLAN_LIMITS.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "HEXCLAVE_EMAILABLE_API_KEY",
      "service": "hexclave",
      "description": "Email validation provider key. Defaults to disabling validation — set a real Emailable key to enable it.",
      "secret": true,
      "strategy": "default",
      "default": "disable_email_validation"
    },
    {
      "key": "HEXCLAVE_S3_PRIVATE_BUCKET",
      "service": "hexclave",
      "description": "Private bucket name for session recordings and deployment source; must match minio-init's PRIVATE_BUCKET.",
      "secret": false,
      "strategy": "default",
      "default": "hexclave-private"
    },
    {
      "key": "HEXCLAVE_S3_PUBLIC_ENDPOINT",
      "service": "hexclave",
      "description": "Base URL for direct public-bucket object links (e.g. profile pictures). Points at the public bucket on MinIO's public domain.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "HEXCLAVE_CLICKHOUSE_DATABASE",
      "service": "hexclave",
      "description": "ClickHouse database hexclave uses for analytics, must match ClickHouse's CLICKHOUSE_DB.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "NEXT_PUBLIC_HEXCLAVE_API_URL",
      "service": "hexclave",
      "description": "Public URL for hexclave's API, automatically served through the hexclave-proxy service.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "HEXCLAVE_S3_SECRET_ACCESS_KEY",
      "service": "hexclave",
      "description": "S3 secret key, referenced from the MinIO service.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "HEXCLAVE_CLICKHOUSE_ADMIN_USER",
      "service": "hexclave",
      "description": "ClickHouse admin username, matches ClickHouse's CLICKHOUSE_USER.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "HEXCLAVE_EXTERNAL_DB_SYNC_DIRECT",
      "service": "hexclave",
      "description": "Runs external DB sync inline instead of queuing through Upstash QStash, which this template doesn't configure. Leave true unless you set up QStash yourself.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "NEXT_PUBLIC_STACK_SVIX_SERVER_URL",
      "service": "hexclave",
      "description": "Provide a value for NEXT_PUBLIC_STACK_SVIX_SERVER_URL.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "HEXCLAVE_CLICKHOUSE_ADMIN_PASSWORD",
      "service": "hexclave",
      "description": "ClickHouse admin password, matches ClickHouse's CLICKHOUSE_PASSWORD.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "NEXT_PUBLIC_HEXCLAVE_DASHBOARD_URL",
      "service": "hexclave",
      "description": "Public URL for the hexclave dashboard, automatically served through the hexclave-proxy service.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "HEXCLAVE_DATABASE_CONNECTION_STRING",
      "service": "hexclave",
      "description": "Postgres connection string hexclave uses for its own data.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "NEXT_PUBLIC_HEXCLAVE_SVIX_SERVER_URL",
      "service": "hexclave",
      "description": "Provide a value for NEXT_PUBLIC_HEXCLAVE_SVIX_SERVER_URL.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "HEXCLAVE_CLICKHOUSE_EXTERNAL_PASSWORD",
      "service": "hexclave",
      "description": "Password for the limited external ClickHouse user hexclave creates for analytics queries.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "HEXCLAVE_SEED_INTERNAL_PROJECT_USER_EMAIL",
      "service": "hexclave",
      "description": "Email for the admin account seeded on first boot — this is what you'll sign in to the dashboard with.",
      "secret": false,
      "strategy": "ask_user"
    },
    {
      "key": "HEXCLAVE_SEED_INTERNAL_PROJECT_USER_PASSWORD",
      "service": "hexclave",
      "description": "Password for the seeded admin account. Auto-generated — find it in this service's Variables tab after deploy.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "HEXCLAVE_SEED_INTERNAL_PROJECT_SIGN_UP_ENABLED",
      "service": "hexclave",
      "description": "Whether new users can sign themselves up to the internal dashboard project. Leave false and use the seeded admin account instead.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "HEXCLAVE_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS",
      "service": "hexclave",
      "description": "Grants the seeded admin user access to hexclave's own internal dashboard project.",
      "secret": false,
      "strategy": "default",
      "default": "true"
    },
    {
      "key": "PROXY_PORT",
      "service": "hexclave-proxy",
      "description": "Port this proxy listens on internally. Also the target port for its public domain — don't change one without the other.",
      "secret": false,
      "strategy": "default",
      "default": "8080"
    },
    {
      "key": "API_UPSTREAM",
      "service": "hexclave-proxy",
      "description": "Internal address of the hexclave service's API port, reached over Railway's private network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "DASHBOARD_UPSTREAM",
      "service": "hexclave-proxy",
      "description": "Internal address of the hexclave service's dashboard port, reached over Railway's private network.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PGDATA",
      "service": "Postgres",
      "description": "Path where Postgres stores data. Must be a subdirectory under the volume mount point, not the mount point itself.",
      "secret": false,
      "strategy": "default",
      "default": "/var/lib/postgresql/data/pgdata"
    },
    {
      "key": "PGHOST",
      "service": "Postgres",
      "description": "Internal hostname Postgres listens on (its own private Railway domain).",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PGPORT",
      "service": "Postgres",
      "description": "Port Postgres listens on inside the container.",
      "secret": false,
      "strategy": "default",
      "default": "5432"
    },
    {
      "key": "PGUSER",
      "service": "Postgres",
      "description": "Postgres role used by the server and other services to connect.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGDATABASE",
      "service": "Postgres",
      "description": "Name of the default database, mirrors POSTGRES_DB.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGPASSWORD",
      "service": "Postgres",
      "description": "Password for the Postgres role, mirrors POSTGRES_PASSWORD.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_DB",
      "service": "Postgres",
      "description": "Name of the database created on first boot.",
      "secret": false,
      "strategy": "default",
      "default": "railway"
    },
    {
      "key": "DATABASE_URL",
      "service": "Postgres",
      "description": "Full Postgres connection string other services should use to connect.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "POSTGRES_USER",
      "service": "Postgres",
      "description": "Username for the default Postgres role.",
      "secret": false,
      "strategy": "default",
      "default": "postgres"
    },
    {
      "key": "SSL_CERT_DAYS",
      "service": "Postgres",
      "description": "Validity period, in days, for the self-signed SSL certificate Postgres generates.",
      "secret": false,
      "strategy": "default",
      "default": "820"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "Postgres",
      "description": "Password for the default Postgres role. Auto-generated — you shouldn't need to set this.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "RAILWAY_DEPLOYMENT_DRAINING_SECONDS",
      "service": "Postgres",
      "description": "Grace period Railway waits before stopping the old instance during a deploy.",
      "secret": false,
      "strategy": "default",
      "default": "60"
    },
    {
      "key": "CLICKHOUSE_DB",
      "service": "ClickHouse",
      "description": "Name of the ClickHouse database Hexclave's analytics features use.",
      "secret": false,
      "strategy": "default",
      "default": "analytics"
    },
    {
      "key": "CLICKHOUSE_USER",
      "service": "ClickHouse",
      "description": "Admin username ClickHouse is initialized with.",
      "secret": false,
      "strategy": "default",
      "default": "stackframe"
    },
    {
      "key": "CLICKHOUSE_PASSWORD",
      "service": "ClickHouse",
      "description": "Admin password ClickHouse is initialized with. Auto-generated.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT",
      "service": "ClickHouse",
      "description": "Enables SQL-driven user/role management, required so Hexclave's migrations can create its own ClickHouse users.",
      "secret": false,
      "strategy": "default",
      "default": "1"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "hexclave-or-self-hosted-auth-storage"
      }
    },
    "cli": "railway deploy --template hexclave-or-self-hosted-auth-storage",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "103794be-3713-4a61-8941-68db10623dfe",
            "serializedConfig": {
              "services": {
                "16d4a159-cb09-4280-b000-03882ea3b5f3": {
                  "icon": "https://avatars.githubusercontent.com/u/695951?s=200&v=4",
                  "name": "minio-init",
                  "deploy": {
                    "startCommand": "sh -c 'until mc alias set local \"$MINIO_ENDPOINT\" \"$MINIO_ROOT_USER\" \"$MINIO_ROOT_PASSWORD\" >/dev/null 2>&1; do echo \"waiting for minio...\"; sleep 2; done; mc mb --ignore-existing local/\"$PUBLIC_BUCKET\"; mc mb --ignore-existing local/\"$PRIVATE_BUCKET\"; mc anonymous set download local/\"$PUBLIC_BUCKET\"; echo \"buckets ready\"'",
                    "restartPolicyType": "NEVER"
                  },
                  "source": {
                    "image": "minio/mc:latest"
                  },
                  "variables": {
                    "PUBLIC_BUCKET": {
                      "isOptional": false,
                      "description": "Name of the public bucket for browser-readable uploads like profile pictures.",
                      "defaultValue": "hexclave-public"
                    },
                    "MINIO_ENDPOINT": {
                      "isOptional": false,
                      "description": "Internal address of the MinIO service, used to create buckets over Railway's private network.",
                      "defaultValue": "http://${{MinIO.RAILWAY_PRIVATE_DOMAIN}}:9000"
                    },
                    "PRIVATE_BUCKET": {
                      "isOptional": false,
                      "description": "Name of the private bucket for session recordings and deployment source.",
                      "defaultValue": "hexclave-private"
                    },
                    "MINIO_ROOT_USER": {
                      "isOptional": false,
                      "description": "MinIO root access key, referenced from the MinIO service.",
                      "defaultValue": "${{MinIO.MINIO_ROOT_USER}}"
                    },
                    "MINIO_ROOT_PASSWORD": {
                      "isOptional": false,
                      "description": "MinIO root secret key, referenced from the MinIO service.",
                      "defaultValue": "${{MinIO.MINIO_ROOT_PASSWORD}}"
                    }
                  }
                },
                "1e961cb3-8cc1-4506-8033-12e912cbbcc8": {
                  "name": "svix-db",
                  "source": {
                    "image": "postgres:16"
                  },
                  "variables": {
                    "PGDATA": {
                      "defaultValue": "/var/lib/postgresql/data/pgdata"
                    },
                    "POSTGRES_DB": {
                      "defaultValue": "svix"
                    },
                    "POSTGRES_USER": {
                      "defaultValue": "postgres"
                    },
                    "POSTGRES_PASSWORD": {
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    }
                  },
                  "volumeMounts": {
                    "svix-db-volume": {
                      "mountPath": "/var/lib/postgresql/data"
                    }
                  }
                },
                "38b5befc-c942-4026-8ca7-58801bc1806e": {
                  "icon": "https://avatars.githubusercontent.com/u/695951?s=200&v=4",
                  "name": "MinIO",
                  "deploy": {
                    "startCommand": "minio server /data --console-address \":9001\"",
                    "healthcheckPath": "/minio/health/live"
                  },
                  "source": {
                    "image": "minio/minio:latest"
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "Port MinIO's S3 API listens on. Set so Railway's healthcheck targets the right port; keep it aligned with the public domain's target port (9000).",
                      "defaultValue": "9000"
                    },
                    "MINIO_ROOT_USER": {
                      "isOptional": false,
                      "description": "Root access key for MinIO. Auto-generated — used as the S3 access key by the hexclave service.",
                      "defaultValue": "{{MINIO_ROOT_USER}}"
                    },
                    "MINIO_SITE_REGION": {
                      "isOptional": false,
                      "description": "Region MinIO reports; must match the S3 region the hexclave service signs requests with.",
                      "defaultValue": "us-east-1"
                    },
                    "MINIO_ROOT_PASSWORD": {
                      "isOptional": false,
                      "description": "Root secret key for MinIO. Auto-generated — used as the S3 secret key by the hexclave service. Do not share.",
                      "defaultValue": "{{MINIO_ROOT_PASSWORD}}"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>": {
                        "port": 9000
                      }
                    }
                  },
                  "volumeMounts": {
                    "minio-volume": {
                      "mountPath": "/data"
                    }
                  }
                },
                "7fa490ac-b21a-42dc-b5a2-54d02b3ab1ef": {
                  "name": "svix-server",
                  "source": {
                    "image": "svix/svix-server"
                  },
                  "variables": {
                    "PORT": {
                      "defaultValue": "8071"
                    },
                    "WAIT_FOR": {
                      "defaultValue": "true"
                    },
                    "SVIX_DB_DSN": {
                      "defaultValue": "postgres://postgres:${{svix-db.POSTGRES_PASSWORD}}@${{svix-db.RAILWAY_PRIVATE_DOMAIN}}:5432/svix"
                    },
                    "SVIX_REDIS_DSN": {
                      "defaultValue": "redis://localhost:6379"
                    },
                    "SVIX_CACHE_TYPE": {
                      "defaultValue": "memory"
                    },
                    "SVIX_JWT_SECRET": {
                      "defaultValue": "lFSGu4YD8DlYOZHpJ2UhVvw4X1NvhjIX3fbHPXwqjL_6qfTPs_qnZGiKL9-KNRfg"
                    },
                    "SVIX_QUEUE_TYPE": {
                      "defaultValue": "memory"
                    },
                    "SVIX_LISTEN_ADDRESS": {
                      "defaultValue": "0.0.0.0:8071"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>": {
                        "port": 8071
                      }
                    }
                  }
                },
                "805e840f-667d-472b-9663-30be6596f0f0": {
                  "icon": "https://raw.githubusercontent.com/hexclave/hexclave/ebe7877cc1f81a23d0797272b6dc89f804b3103e/apps/dashboard/public/hexclave-icon.svg",
                  "name": "hexclave-cron",
                  "deploy": {
                    "startCommand": "sh -c 'for p in email-queue-step external-db-sync/sequencer external-db-sync/poller; do (curl -fsS -m 290 -o /dev/null -w \"$p -> %{http_code} in %{time_total}s\\n\" -H \"Authorization: Bearer $CRON_SECRET\" \"$API_URL/api/latest/internal/$p\" || echo \"$p FAILED\") & done; wait'",
                    "healthcheckPath": null
                  },
                  "source": {
                    "image": "curlimages/curl:latest"
                  },
                  "variables": {
                    "API_URL": {
                      "isOptional": false,
                      "description": "Base URL of the hexclave service's API, reached over Railway's private network.",
                      "defaultValue": "http://${{hexclave.RAILWAY_PRIVATE_DOMAIN}}:8102"
                    },
                    "CRON_SECRET": {
                      "isOptional": false,
                      "description": "Shared secret used to authenticate this job's requests to hexclave's internal endpoints.",
                      "defaultValue": "${{hexclave.CRON_SECRET}}"
                    }
                  }
                },
                "ac4aa618-1a78-47e9-b968-3cadba557790": {
                  "icon": "https://raw.githubusercontent.com/hexclave/hexclave/ebe7877cc1f81a23d0797272b6dc89f804b3103e/apps/dashboard/public/hexclave-icon.svg",
                  "name": "hexclave",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "stackauth/server:ebe7877"
                  },
                  "variables": {
                    "PORT": {
                      "isOptional": false,
                      "description": "Port the dashboard process listens on inside the container.",
                      "defaultValue": "8101"
                    },
                    "CRON_SECRET": {
                      "isOptional": false,
                      "description": "Shared secret hexclave-cron uses to call this service's internal maintenance endpoints.",
                      "defaultValue": "{{CRON_SECRET}}"
                    },
                    "HEXCLAVE_S3_BUCKET": {
                      "isOptional": false,
                      "description": "Public bucket name; must match minio-init's PUBLIC_BUCKET.",
                      "defaultValue": "hexclave-public"
                    },
                    "HEXCLAVE_S3_REGION": {
                      "isOptional": false,
                      "description": "S3 region hexclave signs storage requests with; must match MinIO's MINIO_SITE_REGION.",
                      "defaultValue": "us-east-1"
                    },
                    "STACK_SVIX_API_KEY": {
                      "defaultValue": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDAwMDAwMDAsIm5iZiI6MTcwMDAwMDAwMCwiZXhwIjoyMDAwMDAwMDAwLCJpc3MiOiJzdml4LXNlcnZlciIsInN1YiI6Im9yZ19IZXhjbGF2ZVJhaWx3YXlTZWxmSG9zdDAxIn0.rE2LDP5CP5ubMvr3aaZwah5EkIdZZkfpFmQH9Z-26WQ"
                    },
                    "HEXCLAVE_S3_ENDPOINT": {
                      "isOptional": false,
                      "description": "S3 API endpoint. Uses MinIO's public domain because hexclave hands the browser presigned upload/download URLs bound to this host.",
                      "defaultValue": "https://${{MinIO.RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "STACK_SVIX_SERVER_URL": {
                      "defaultValue": "http://${{svix-server.RAILWAY_PRIVATE_DOMAIN}}:8071"
                    },
                    "HEXCLAVE_SERVER_SECRET": {
                      "isOptional": false,
                      "description": "High-entropy secret used to encrypt sessions and derive internal keys. Keep it stable — changing it invalidates sessions.",
                      "defaultValue": "{{HEXCLAVE_SERVER_SECRET}}"
                    },
                    "HEXCLAVE_TRUSTED_PROXY": {
                      "isOptional": false,
                      "description": "Tells hexclave it's behind Railway's reverse proxy so it can trust forwarded-for headers.",
                      "defaultValue": "generic"
                    },
                    "HEXCLAVE_CLICKHOUSE_URL": {
                      "isOptional": false,
                      "description": "URL of the ClickHouse service used for analytics data.",
                      "defaultValue": "http://${{ClickHouse.RAILWAY_PRIVATE_DOMAIN}}:8123"
                    },
                    "HEXCLAVE_S3_ACCESS_KEY_ID": {
                      "isOptional": false,
                      "description": "S3 access key, referenced from the MinIO service.",
                      "defaultValue": "${{MinIO.MINIO_ROOT_USER}}"
                    },
                    "HEXCLAVE_SKIP_SEED_SCRIPT": {
                      "isOptional": false,
                      "description": "IMPORTANT: after your FIRST successful deploy, set this to true. On its second run the image's seed script crashes the whole container (an upstream bug), so the app will crash-loop on the first restart/redeploy until you set this to true. This is the ONLY variable that fixes that crash — it is NOT the same as the HEXCLAVE_SEED_INTERNAL_* variables. Leave false for the very first deploy so the admin account and database get seeded.",
                      "defaultValue": "false"
                    },
                    "STACK_DISABLE_PLAN_LIMITS": {
                      "defaultValue": "true"
                    },
                    "HEXCLAVE_EMAILABLE_API_KEY": {
                      "isOptional": false,
                      "description": "Email validation provider key. Defaults to disabling validation — set a real Emailable key to enable it.",
                      "defaultValue": "disable_email_validation"
                    },
                    "HEXCLAVE_S3_PRIVATE_BUCKET": {
                      "isOptional": false,
                      "description": "Private bucket name for session recordings and deployment source; must match minio-init's PRIVATE_BUCKET.",
                      "defaultValue": "hexclave-private"
                    },
                    "HEXCLAVE_S3_PUBLIC_ENDPOINT": {
                      "isOptional": false,
                      "description": "Base URL for direct public-bucket object links (e.g. profile pictures). Points at the public bucket on MinIO's public domain.",
                      "defaultValue": "https://${{MinIO.RAILWAY_PUBLIC_DOMAIN}}/hexclave-public"
                    },
                    "HEXCLAVE_CLICKHOUSE_DATABASE": {
                      "isOptional": false,
                      "description": "ClickHouse database hexclave uses for analytics, must match ClickHouse's CLICKHOUSE_DB.",
                      "defaultValue": "${{ClickHouse.CLICKHOUSE_DB}}"
                    },
                    "NEXT_PUBLIC_HEXCLAVE_API_URL": {
                      "isOptional": false,
                      "description": "Public URL for hexclave's API, automatically served through the hexclave-proxy service.",
                      "defaultValue": "https://${{hexclave-proxy.RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "HEXCLAVE_S3_SECRET_ACCESS_KEY": {
                      "isOptional": false,
                      "description": "S3 secret key, referenced from the MinIO service.",
                      "defaultValue": "${{MinIO.MINIO_ROOT_PASSWORD}}"
                    },
                    "HEXCLAVE_CLICKHOUSE_ADMIN_USER": {
                      "isOptional": false,
                      "description": "ClickHouse admin username, matches ClickHouse's CLICKHOUSE_USER.",
                      "defaultValue": "${{ClickHouse.CLICKHOUSE_USER}}"
                    },
                    "HEXCLAVE_EXTERNAL_DB_SYNC_DIRECT": {
                      "isOptional": false,
                      "description": "Runs external DB sync inline instead of queuing through Upstash QStash, which this template doesn't configure. Leave true unless you set up QStash yourself.",
                      "defaultValue": "true"
                    },
                    "NEXT_PUBLIC_STACK_SVIX_SERVER_URL": {
                      "defaultValue": "https://${{svix-server.RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "HEXCLAVE_CLICKHOUSE_ADMIN_PASSWORD": {
                      "isOptional": false,
                      "description": "ClickHouse admin password, matches ClickHouse's CLICKHOUSE_PASSWORD.",
                      "defaultValue": "${{ClickHouse.CLICKHOUSE_PASSWORD}}"
                    },
                    "NEXT_PUBLIC_HEXCLAVE_DASHBOARD_URL": {
                      "isOptional": false,
                      "description": "Public URL for the hexclave dashboard, automatically served through the hexclave-proxy service.",
                      "defaultValue": "https://${{hexclave-proxy.RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "HEXCLAVE_DATABASE_CONNECTION_STRING": {
                      "isOptional": false,
                      "description": "Postgres connection string hexclave uses for its own data.",
                      "defaultValue": "${{Postgres.DATABASE_URL}}"
                    },
                    "NEXT_PUBLIC_HEXCLAVE_SVIX_SERVER_URL": {
                      "defaultValue": "https://${{svix-server.RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "HEXCLAVE_CLICKHOUSE_EXTERNAL_PASSWORD": {
                      "isOptional": false,
                      "description": "Password for the limited external ClickHouse user hexclave creates for analytics queries.",
                      "defaultValue": "{{HEXCLAVE_CLICKHOUSE_EXTERNAL_PASSWORD}}"
                    },
                    "HEXCLAVE_SEED_INTERNAL_PROJECT_USER_EMAIL": {
                      "isOptional": false,
                      "description": "Email for the admin account seeded on first boot — this is what you'll sign in to the dashboard with.",
                      "defaultValue": "{{HEXCLAVE_SEED_INTERNAL_PROJECT_USER_EMAIL}}"
                    },
                    "HEXCLAVE_SEED_INTERNAL_PROJECT_USER_PASSWORD": {
                      "isOptional": false,
                      "description": "Password for the seeded admin account. Auto-generated — find it in this service's Variables tab after deploy.",
                      "defaultValue": "{{HEXCLAVE_SEED_INTERNAL_PROJECT_USER_PASSWORD}}"
                    },
                    "HEXCLAVE_SEED_INTERNAL_PROJECT_SIGN_UP_ENABLED": {
                      "isOptional": false,
                      "description": "Whether new users can sign themselves up to the internal dashboard project. Leave false and use the seeded admin account instead.",
                      "defaultValue": "false"
                    },
                    "HEXCLAVE_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS": {
                      "isOptional": false,
                      "description": "Grants the seeded admin user access to hexclave's own internal dashboard project.",
                      "defaultValue": "true"
                    }
                  },
                  "networking": {
                    "serviceDomains": null
                  }
                },
                "cd7d2754-6417-44ef-b826-e36f9c457b14": {
                  "icon": "https://raw.githubusercontent.com/hexclave/hexclave/ebe7877cc1f81a23d0797272b6dc89f804b3103e/apps/dashboard/public/hexclave-icon.svg",
                  "name": "hexclave-proxy",
                  "deploy": {
                    "startCommand": "sh -c 'cat > /etc/caddy/Caddyfile <<EOF\n:$PROXY_PORT {\n handle /caddy-health {\n respond \"ok\" 200\n }\n handle /api/* {\n reverse_proxy $API_UPSTREAM\n }\n handle {\n reverse_proxy $DASHBOARD_UPSTREAM\n }\n handle_errors {\n respond \"Hexclave is still starting up — first boot takes a few minutes. Refresh this page shortly.\" 503\n }\n}\nEOF\nexec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile'",
                    "healthcheckPath": "/caddy-health"
                  },
                  "source": {
                    "image": "caddy:2-alpine"
                  },
                  "variables": {
                    "PROXY_PORT": {
                      "isOptional": false,
                      "description": "Port this proxy listens on internally. Also the target port for its public domain — don't change one without the other.",
                      "defaultValue": "8080"
                    },
                    "API_UPSTREAM": {
                      "isOptional": false,
                      "description": "Internal address of the hexclave service's API port, reached over Railway's private network.",
                      "defaultValue": "${{hexclave.RAILWAY_PRIVATE_DOMAIN}}:8102"
                    },
                    "DASHBOARD_UPSTREAM": {
                      "isOptional": false,
                      "description": "Internal address of the hexclave service's dashboard port, reached over Railway's private network.",
                      "defaultValue": "${{hexclave.RAILWAY_PRIVATE_DOMAIN}}:8101"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>": {
                        "port": 8080
                      }
                    }
                  }
                },
                "db29658c-50fd-4997-aadf-144ce6183958": {
                  "icon": "https://devicons.railway.app/i/postgresql.svg",
                  "name": "Postgres",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "ghcr.io/railwayapp-templates/postgres-ssl:18"
                  },
                  "variables": {
                    "PGDATA": {
                      "isOptional": false,
                      "description": "Path where Postgres stores data. Must be a subdirectory under the volume mount point, not the mount point itself.",
                      "defaultValue": "/var/lib/postgresql/data/pgdata"
                    },
                    "PGHOST": {
                      "isOptional": false,
                      "description": "Internal hostname Postgres listens on (its own private Railway domain).",
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "PGPORT": {
                      "isOptional": false,
                      "description": "Port Postgres listens on inside the container.",
                      "defaultValue": "5432"
                    },
                    "PGUSER": {
                      "isOptional": false,
                      "description": "Postgres role used by the server and other services to connect.",
                      "defaultValue": "${{POSTGRES_USER}}"
                    },
                    "PGDATABASE": {
                      "isOptional": false,
                      "description": "Name of the default database, mirrors POSTGRES_DB.",
                      "defaultValue": "${{POSTGRES_DB}}"
                    },
                    "PGPASSWORD": {
                      "isOptional": false,
                      "description": "Password for the Postgres role, mirrors POSTGRES_PASSWORD.",
                      "defaultValue": "${{POSTGRES_PASSWORD}}"
                    },
                    "POSTGRES_DB": {
                      "isOptional": false,
                      "description": "Name of the database created on first boot.",
                      "defaultValue": "railway"
                    },
                    "DATABASE_URL": {
                      "isOptional": false,
                      "description": "Full Postgres connection string other services should use to connect.",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:5432/${{PGDATABASE}}"
                    },
                    "POSTGRES_USER": {
                      "isOptional": false,
                      "description": "Username for the default Postgres role.",
                      "defaultValue": "postgres"
                    },
                    "SSL_CERT_DAYS": {
                      "isOptional": false,
                      "description": "Validity period, in days, for the self-signed SSL certificate Postgres generates.",
                      "defaultValue": "820"
                    },
                    "POSTGRES_PASSWORD": {
                      "isOptional": false,
                      "description": "Password for the default Postgres role. Auto-generated — you shouldn't need to set this.",
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    },
                    "RAILWAY_DEPLOYMENT_DRAINING_SECONDS": {
                      "isOptional": false,
                      "description": "Grace period Railway waits before stopping the old instance during a deploy.",
                      "defaultValue": "60"
                    }
                  },
                  "volumeMounts": {
                    "db29658c-50fd-4997-aadf-144ce6183958": {
                      "mountPath": "/var/lib/postgresql/data"
                    }
                  }
                },
                "ed34b055-b95b-4e49-ac5d-8bb9f5f511a3": {
                  "icon": "https://devicons.railway.app/i/clickhouse",
                  "name": "ClickHouse",
                  "deploy": {
                    "startCommand": "",
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "image": "clickhouse/clickhouse-server:25.10"
                  },
                  "variables": {
                    "CLICKHOUSE_DB": {
                      "isOptional": false,
                      "description": "Name of the ClickHouse database Hexclave's analytics features use.",
                      "defaultValue": "analytics"
                    },
                    "CLICKHOUSE_USER": {
                      "isOptional": false,
                      "description": "Admin username ClickHouse is initialized with.",
                      "defaultValue": "stackframe"
                    },
                    "CLICKHOUSE_PASSWORD": {
                      "isOptional": false,
                      "description": "Admin password ClickHouse is initialized with. Auto-generated.",
                      "defaultValue": "{{CLICKHOUSE_PASSWORD}}"
                    },
                    "CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT": {
                      "isOptional": false,
                      "description": "Enables SQL-driven user/role management, required so Hexclave's migrations can create its own ClickHouse users.",
                      "defaultValue": "1"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "MinIO",
      "method": "GET",
      "path": "/minio/health/live",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 9,
    "needs_volume": true
  },
  "generated_at": "2026-09-14T22:14:43.073Z",
  "generator_version": "0.1.0"
}
