NNO Docs
Concepts

CF Pages Direct Upload

Documentation for CF Pages Direct Upload

Replaces: Per-client GitHub repository model for platform shell builds Phase: [Phase 3] Status: Blueprint — not yet implemented


Overview

Phase 1 provisions a private GitHub repository per client platform (\{platformId\}-platform-console) and uses GitHub Actions to build and deploy the platform shell to Cloudflare Pages. At scale this model has several structural problems:

  • GitHub Actions minutes consumed per-client (cost scales linearly)
  • GitHub REST API rate limits (5,000 req/hour) become a ceiling at burst provisioning volume
  • GitHub App requires org-wide installation access for every new repo
  • CF_API_TOKEN / CF_ACCOUNT_ID must be org-level secrets — rotation affects all platform repos simultaneously
  • All client repos live in one org with no per-client isolation
  • Repo management overhead grows with client count

Phase 3 replaces this with CF Pages Direct Upload — no per-client GitHub repo, no per-client GitHub Actions workflow. A single central build runner handles all platforms.


Current Architecture [Phase 1]

Provisioning: trigger_shell_rebuild step
  → CLI Service: POST /platforms/{id}/features/activate
  → GitHub Contents API: commit features.config.ts to {platformId}-platform-console
  → GitHub Actions (per-repo deploy.yml): triggered on git push
      1. Checkout repo
      2. pnpm install && pnpm build
      3. wrangler pages deploy ./dist --project-name={platformId}-{env}-portal
  → CF Pages: deployment via GitHub source connection

CF Pages projects are created with source.type = "github" — each platform project is connected to its own GitHub repo.


Target Architecture [Phase 3]

Provisioning: trigger_shell_rebuild step
  → CLI Service: POST /platforms/{id}/features/activate
  → GitHub API: POST /repos/neutrino-io/nno-platform-builder/actions/workflows/build.yml/dispatches
      { inputs: { platformId, env } }
  → GitHub Actions (central nno-platform-builder repo):
      1. Checkout nno-stack-starter
      2. GET /api/v1/platforms/{platformId}/config (NNO API — auth: NNO_INTERNAL_API_KEY)
      3. Write features.config.ts, env.config.ts, auth.config.ts from API response
      4. pnpm install && pnpm build
      5. wrangler pages deploy ./dist --project-name={platformId}-{env}-portal
  → CF Pages: Direct Upload deployment (no git source connection per platform)

CF Pages projects are created without a source field — Direct Upload projects receive deployments only via wrangler pages deploy.


Implementation Gaps

IDComponentDescriptionPriority
G1CLI Service POST /platformsRemove per-client repo creation; store initial config in NNO storage; trigger workflow_dispatch insteadHigh
G2Platform config storageD1 or R2 storage layer for features.config.ts, env.config.ts, auth.config.ts + GET /platforms/\{id\}/config APIHigh
G3Central builder repoCreate neutrino-io/nno-platform-builder with workflow_dispatch build workflowHigh
G4Provisioning trigger_shell_rebuildSwitch from git-push to workflow_dispatch on nno-platform-builder across all executors (activate-feature, deactivate-feature, provision-stack, deactivate-stack)Medium
G5CF Pages project creationSwitch project creation from source.type = "github" to Direct Upload (no source field)Medium
G6NNO config API endpointGET /api/v1/cli/platforms/\{platformId\}/config — authenticated with NNO_INTERNAL_API_KEY, returns build config JSONMedium
G7Build status trackingCF Pages status polling continues to work for Direct Upload; optionally add GitHub Actions run status polling for pre-deploy visibilityLow
G8nno-stack-starter cleanupRemove deploy.yml; update README to reflect dev-only template roleLow

G2 — Config Storage Options

Platform build config (features.config.ts, env.config.ts, auth.config.ts) must move from GitHub repo files to NNO-managed storage:

  • Registry D1: New platform_configs table or columns on the platforms record
  • R2: Objects at configs/\{platformId\}/features.config.ts etc.

The central build runner fetches config via:

GET /api/v1/platforms/{platformId}/config
Authorization: Bearer {NNO_INTERNAL_API_KEY}

Response: { featuresConfig: string, envConfig: string, authConfig: string }

G3 — Central Builder Repo Workflow Skeleton

# neutrino-io/nno-platform-builder — .github/workflows/build.yml
name: Build and Deploy Platform
on:
  workflow_dispatch:
    inputs:
      platformId:
        description: NNO platform ID
        required: true
      env:
        description: Deployment environment (dev/stg/prod)
        required: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          repository: neutrino-io/nno-stack-starter

      - name: Fetch platform config
        run: |
          curl -sf \
            -H "Authorization: Bearer $NNO_INTERNAL_API_KEY" \
            "$NNO_API_URL/api/v1/platforms/${{ inputs.platformId }}/config" \
            -o platform-config.json
        env:
          NNO_INTERNAL_API_KEY: ${{ secrets.NNO_INTERNAL_API_KEY }}
          NNO_API_URL: ${{ secrets.NNO_API_URL }}

      - name: Write config files
        run: node scripts/write-platform-config.js platform-config.json

      - uses: pnpm/action-setup@v3
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - run: pnpm install
      - run: pnpm build

      - name: Deploy to CF Pages
        run: |
          npx wrangler pages deploy ./dist \
            --project-name="${{ inputs.platformId }}-${{ inputs.env }}-portal"
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}

Required secrets on nno-platform-builder: NNO_INTERNAL_API_KEY, NNO_API_URL, CF_API_TOKEN, CF_ACCOUNT_ID.


Long-term Path

Once Cloudflare Workers Builds (currently in beta) matures:

  • Replace GitHub Actions central runner with CF Workers Builds
  • Remove GitHub dependency entirely — everything stays within the Cloudflare ecosystem
  • G3 (central builder repo) and G4 (workflow_dispatch) become irrelevant

Monitor: Cloudflare Workers Builds


On this page