1---2name: infra3description: Infra — Index4---56# Infra — Index78Persimmon infra = Railway (app + DB + bucket in one project), S3-compatible storage with presigned uploads, resumable background jobs, and a minimal GitHub Actions CI. This mother is a map; follow the child for the actual work.910## Trigger1112- "Deploy this" / "custom domain" / "env vars"13- "File upload" / "presigned URL" / "bucket CORS"14- "Background job" / "long-running task" / "queue / cron"15- "Set up CI"1617## The child skills1819| Skill | When to use | Owns |20|---|---|---|21| `infra-railway-deploy` | Project creation, deploy, domains | Railway project setup, env vars, custom domain, container port (8080), staged-config drift recovery |22| `infra-s3-uploads` | Any file upload flow | Presigned PUT flow, bucket CORS, multipart, content-type validation (Tigris/R2/S3) |23| `infra-background-jobs` | Work that can't finish in a request | Idempotent resumable pipelines on Railway (pg-boss / Inngest / cron) |24| `infra-github-ci` | CI pipeline | Minimal Next.js CI — lint, typecheck, `prisma validate`, build |2526## How to route27281. **Deploying / hosting?** → `infra-railway-deploy`.292. **Uploads?** → `infra-s3-uploads` (presigned PUT — never proxy bytes through Next).303. **Long job?** → `infra-background-jobs` (idempotent + resumable).314. **CI?** → `infra-github-ci`.3233## Persimmon infra defaults — one-screen summary3435- **Railway container port is `8080`** (from `PORT`). Public domain `targetPort` must match — not 3000.36- **Uploads**: server issues presigned URL → client PUTs directly to the bucket.37- **Bucket CORS** must include every uploading origin (`http://localhost:3000`, preview URLs, prod domains) via `PutBucketCorsCommand`, or uploads fail silently with `net::ERR_FAILED`.38- **Railway managed bucket** stays staged until the dashboard "Deploy" is committed once.39- **Staged-config drift**: committing an older draft reverts live mutations; Settings → Source → Disconnect → Reconnect drains the draft.40- **CD**: Railway auto-deploys on push to `main`. **CI**: lint, typecheck, `prisma validate`, build.4142## Anti-patterns banned4344- Public domain `targetPort` mismatched with `8080`45- Proxying upload bytes through Next instead of presigned PUT46- Missing CORS origins on the bucket47- Non-idempotent background jobs that can't resume48- Committing secrets — use Railway/GitHub env, never code4950## Relationship to other mothers5152| Mother | Connection |53|---|---|54| `stack` | Deployed app code follows `stack` conventions |55| `data` | Railway hosts Postgres + pgvector |56| `security` | `infra-github-ci` runs the checks `security-review` relies on |