{
  "id": "osc-stack-deploy-agent-written-app",
  "job": "Run the app your AI coding tool wrote (Claude Code, Cursor, Codex or any MCP client) in production, with the database, object storage and environment variables it needs, without writing a Dockerfile or opening a cloud console.",
  "aliases": [
    "deploy app built with claude code",
    "deploy vibe coded app",
    "host the app my agent wrote",
    "full-stack app with database from github"
  ],
  "inputs": [
    {
      "protocol": "git",
      "source": "a git repository (GitHub or any HTTPS git host) holding the app's code; append #branch to deploy a branch other than the default; private repositories use a pre-registered git credential"
    }
  ],
  "outputs": [
    {
      "protocol": "HTTPS",
      "consumer": "the app's users, at a stable URL that does not change across rebuilds or restarts",
      "runtimes": ["nodejs", "python", "wasm", "golang", "dotnet", "php"]
    }
  ],
  "recommendedPath": {
    "chain": [
      {
        "step": 1,
        "serviceId": "birme-osc-postgresql",
        "role": "relational database for the app",
        "requiredConfig": ["name", "PostgresPassword"],
        "requiresCredential": true,
        "note": "PostgresUser and PostgresDb are optional and default to 'postgres' / the username. Skip this step if the app has no database."
      },
      {
        "step": 2,
        "serviceId": "minio-minio",
        "role": "S3-compatible object storage for uploads and files",
        "requiredConfig": ["name"],
        "requiresCredential": false,
        "note": "Optional step. RootUser and RootPassword are optional at create time. Skip if the app stores no files."
      },
      {
        "step": 3,
        "mcpTool": "setup-parameter-store",
        "role": "parameter store holding the app's environment variables, for example DATABASE_URL",
        "requiredConfig": ["name", "enableSecrets"],
        "requiresCredential": false,
        "note": "One store can serve many apps. With enableSecrets: true the response carries a one-time download URL for the CONFIG_API_KEY, which cannot be retrieved later. Handle it before anything else, on one of two paths: (a) interactive session: show the URL to the human, wait until they confirm the key is saved, then call acknowledge-secret-saved with the token (the last path segment after /files/); (b) headless or autonomous agent with no human to confirm: call create-service-secret-from-download-token with { downloadToken, serviceId, secretName, confirmed: true } so the server writes the key into a service secret and the raw key never enters the agent's context, and skip acknowledge-secret-saved. Then call wait-for-parameter-store until the store is ready (setup-parameter-store returns ready: false and no URL), and only then write values with set-parameter, all BEFORE step 4 if the app needs them at build or start time. enableSecrets: false skips the key entirely and is the documented choice for non-secret config.",
        "oneTimeSecret": {
          "name": "CONFIG_API_KEY",
          "retrievableLater": false,
          "headlessTool": "create-service-secret-from-download-token",
          "interactiveTool": "acknowledge-secret-saved"
        }
      },
      {
        "step": 4,
        "mcpTool": "create-my-app",
        "role": "build and run the app's code from its git repository as a private My App",
        "requiredConfig": ["name", "type"],
        "requiresCredential": false,
        "note": "Pass gitHubUrl or gitUrl, and configService set to the store from step 3 (create-my-app also accepts the store's configApiKey, write-only, when secret parameters must be decrypted). The app must listen on process.env.PORT (8080). OSC injects APP_URL, AUTH_URL and PORT; do not set them in the parameter store. Then call wait-for-app-ready until it returns ready: true; a 'building' response only means the request was accepted."
      }
    ],
    "tokensPerDay": null,
    "tokensPerDayMeasured": false,
    "measuredWallClockSeconds": null,
    "wallClockMeasured": false,
    "gapNote": "No author's own end-to-end run of this exact chain has been timed or costed. No number is invented here; this is a named gap, not a filled-in figure.",
    "agentFoundServiceUnaided": null
  },
  "configSurface": {
    "servicesInChain": 2,
    "mcpToolsInChain": 2,
    "totalRequiredFieldsAcrossChain": 7,
    "sensitiveFields": ["PostgresPassword", "RootPassword"],
    "note": "Neither birme-osc-postgresql nor minio-minio carries a serviceAssociation (both read live 2026-10-07: serviceAssociations []). The connection string reaches the app through the parameter store, written by hand or by the deploying agent, not wired by the platform.",
    "sensitiveFieldsNote": "RootPassword is minio-minio's optional admin password, marked sensitive in its live schema (2026-10-07); it is listed even though it is not required."
  },
  "setup": {
    "codeRequired": true,
    "knownDeployGotcha": "The app must read its port from process.env.PORT and listen on 8080. Next.js NEXT_PUBLIC_* values set in the parameter store are available at build time, because OSC injects parameter-store variables before the build runs. .osc.yaml files are not supported."
  },
  "cost": {
    "unit": "tokens/day",
    "ceiling": null,
    "ceilingMeasured": false,
    "note": "Not costed as a named stack yet. Named gap for the next author to close, not an invented figure."
  },
  "servicesVerifiedLive": {
    "date": "2026-10-07",
    "method": "osc_call_tool wrap (name=get-service-schema) on birme-osc-postgresql and minio-minio; osc_search_tools (name=create-my-app) for the My App input schema and runtime list; osc_search_tools query for setup-parameter-store. wait-for-parameter-store is named as the required next step in setup-parameter-store's own live description (2026-10-07). Step 3 CONFIG_API_KEY handling re-read 2026-10-08 from setup-parameter-store's live description (two paths: acknowledge-secret-saved or create-service-secret-from-download-token) and create-my-app's live input schema (configApiKey).",
    "catalogTotal": 196,
    "catalogTotalSource": "list-service-categories via osc_call_tool on 2026-10-07, per-category sum 10+27+13+31+64+25+19+7 = 196"
  },
  "gateCheck": {
    "gatedServiceId": "eyevinn-just-go-live",
    "usesGatedService": false,
    "note": "This stack does not name or link eyevinn-just-go-live; the P3 gate does not bind it."
  },
  "verifiedOn": "2026-10-07",
  "publishedAt": "https://www.osaas.io/tutorials/claude-code-fullstack-deploy",
  "descriptorUrl": "https://www.osaas.io/stacks/deploy-agent-written-app.json"
}
