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_IDmust 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 connectionCF 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
| ID | Component | Description | Priority |
|---|---|---|---|
| G1 | CLI Service POST /platforms | Remove per-client repo creation; store initial config in NNO storage; trigger workflow_dispatch instead | High |
| G2 | Platform config storage | D1 or R2 storage layer for features.config.ts, env.config.ts, auth.config.ts + GET /platforms/\{id\}/config API | High |
| G3 | Central builder repo | Create neutrino-io/nno-platform-builder with workflow_dispatch build workflow | High |
| G4 | Provisioning trigger_shell_rebuild | Switch from git-push to workflow_dispatch on nno-platform-builder across all executors (activate-feature, deactivate-feature, provision-stack, deactivate-stack) | Medium |
| G5 | CF Pages project creation | Switch project creation from source.type = "github" to Direct Upload (no source field) | Medium |
| G6 | NNO config API endpoint | GET /api/v1/cli/platforms/\{platformId\}/config — authenticated with NNO_INTERNAL_API_KEY, returns build config JSON | Medium |
| G7 | Build status tracking | CF Pages status polling continues to work for Direct Upload; optionally add GitHub Actions run status polling for pre-deploy visibility | Low |
| G8 | nno-stack-starter cleanup | Remove deploy.yml; update README to reflect dev-only template role | Low |
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_configstable or columns on theplatformsrecord - R2: Objects at
configs/\{platformId\}/features.config.tsetc.
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
Related
- services/cli-service.md — CLI Service that triggers builds and manages CF Pages
- services/provisioning.md —
trigger_shell_rebuildstep in provisioning executors - concepts/module-federation.md — Phase 3 parallel track: remote Module Federation eliminates builds for feature activation