Skip to content

feat(deploy): Phase-1 signed-out go-live for agentculture.org/learn#6

Merged
OriNachum merged 1 commit into
mainfrom
feat/learn-golive-signedout
Jul 11, 2026
Merged

feat(deploy): Phase-1 signed-out go-live for agentculture.org/learn#6
OriNachum merged 1 commit into
mainfrom
feat/learn-golive-signedout

Conversation

@OriNachum

Copy link
Copy Markdown
Contributor

What this is

The signed-out learning experience is now live in production at
https://agentculture.org/learn/ — same-origin, org's design system, no
whole-site reload. This PR records the deploy and fixes the one real config
bug the cutover surfaced.

Deployed with the repo's .env Cloudflare token (Pages: Edit + Workers
Scripts: Edit):

  • Cloudflare Pages project agentculture-learn — the built site-astro/
    tree wrapped under /learn/.
  • learn-api Worker as a proxy owning the agentculture.org/learn/* zone
    route. /learn/api/* is handled by the worker (returns 401 without a
    session, zero inference); every other path reverse-proxies to the Pages
    origin. No KV, no D1, no secrets — the signed-out tier touches no storage.

Verification

LIVE_ORIGIN=https://agentculture.org bash tools/launch-gate/run.sh -> 38/38 PASS,
including the production signed-out walk (phone + desktop) asserting a story
page fires only GET /learn/api/me. Root agentculture.org/ is untouched
(still org); the route is scoped strictly to /learn/*. Worker node suite 39/39.

Fix

wrangler.toml: the routes array was positioned after [vars], so TOML
scoped it as vars.routes and wrangler silently bound no route ("No
targets deployed"). Moved it above [vars]; filled the now-known vars
(PAGES_ORIGIN etc.) from the live deploy. Added wrangler.signedout.toml
(the committed Phase-1 config) and a two-phase README with the exact token
permissions each phase requires.

Not in this PR — Phase 2 (signed-in), pending two operator credentials

  1. Broaden the Cloudflare token with Workers KV Storage: Edit + D1:
    Edit
    (the .env token lacks both — proven by code: 10000 on
    kv namespace create / d1 create). Then I can provision KV + D1 and
    deploy the full wrangler.toml autonomously.
  2. Create the GitHub OAuth app (device flow; callback
    https://agentculture.org/learn/api/auth/callback) — no GitHub API creates
    OAuth apps, so this is UI-only. Its client id + secret light up sign-in.

org's Learn-nav link (org PR #8) stays held until Phase 2, so /learn has no
public inbound link yet — the incomplete sign-in button has no surface.

  • learn-cli (Claude)

Cut over the signed-out learning experience to production, same-origin
at agentculture.org/learn:

- Deployed Cloudflare Pages project `agentculture-learn` (the built
  site-astro/ tree wrapped under /learn/).
- Deployed the learn-api Worker as a proxy owning the
  `agentculture.org/learn/*` zone route: /learn/api/* handled by the
  worker (401 without a session, zero inference), everything else
  reverse-proxied to the Pages origin. No KV/D1/secrets — the
  signed-out tier touches no storage.
- Verified 38/38 by `LIVE_ORIGIN=https://agentculture.org` launch gate
  (signed-out phone+desktop walk against the deployed site).

Fixes a real config trap and documents the phased rollout:

- wrangler.toml: `routes` moved ABOVE [vars]. Under [vars], TOML scoped
  it as vars.routes and wrangler silently bound no route ("No targets
  deployed"). Vars/PAGES_ORIGIN now filled from the live deploy.
- Add wrangler.signedout.toml — the committed Phase-1 config.
- README: two-phase go-live table + the exact token permissions each
  phase needs (Pages+Workers Scripts now; +KV+D1 for signed-in).
- .gitignore: repo-root .wrangler/ (wrangler pages deploy scratch).

Phase 2 (signed-in) remains: broaden the token with Workers KV + D1,
create the GitHub OAuth app, deploy the full wrangler.toml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jkvh4uxSi6AsBJYPjiurze
@OriNachum

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

qodo-code-review Bot commented Jul 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Go-live signed-out /learn via Cloudflare Worker proxy + phase docs

✨ Enhancement 🐞 Bug fix 📝 Documentation ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Bind agentculture.org/learn/* to a proxy Worker that serves signed-out /learn in prod.
• Fix wrangler route binding by moving routes above [vars] and filling live vars.
• Document Phase 1 vs Phase 2 deploy requirements and commit Phase-1 config.
Diagram

graph TD
U(["User browser"]) --> R["agentculture.org/learn/*"] --> W["learn-api Worker"] -->|"reverse proxy"| P["Pages origin (agentculture-learn)"] --> S["Static /learn site"]
W --> A["/learn/api/*"] --> E["401 (no session)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use a dedicated subdomain (learn.agentculture.org) instead of a /learn path mount
  • ➕ Avoids Worker-based path proxying complexity and route-binding pitfalls
  • ➕ Simplifies cache/origin configuration (Pages can be first-class on a hostname)
  • ➖ Breaks the stated same-origin requirement for cookies/credentials and design integration
  • ➖ Requires DNS + potentially different auth/CORS posture
2. Cloudflare Transform Rules/Redirects to Pages origin (no Worker for non-API paths)
  • ➕ Less custom compute for purely static routing
  • ➕ Potentially simpler operational footprint
  • ➖ Does not cleanly support conditional handling where /learn/api/* must be owned by code
  • ➖ Harder to keep API and static under one consistent, testable request pipeline
3. Add a CI guardrail to validate wrangler.toml structure (routes must be top-level)
  • ➕ Prevents recurrence of silent non-binding deploys
  • ➕ Can be automated with a small TOML schema/linters check
  • ➖ Additional CI maintenance; doesn’t replace understanding of Cloudflare routing behavior

Recommendation: The Worker-as-proxy approach is the right fit for same-origin /learn with an API namespace that must short-circuit signed-out requests. Keep this architecture, but strongly consider adding an automated validation step (or a small deploy script) that asserts routes is top-level and that wrangler deploy reports bound targets, to avoid repeating the silent "No targets deployed" failure mode.

Files changed (5) +126 / -26

Bug fix (1) +22 / -15
wrangler.tomlFix route binding by moving routes above [vars] and filling live vars +22/-15

Fix route binding by moving routes above [vars] and filling live vars

• Moves the 'routes' array to the top level (before '[vars]') to avoid TOML scoping it under vars and wrangler silently ignoring it. Fills in the now-known production values for PUBLIC_URL/APP_URL/CORS_ORIGIN/PAGES_ORIGIN and adds explanatory comments about the path-mount proxy architecture.

workers/learn-api/wrangler.toml

Documentation (2) +61 / -10
CHANGELOG.mdRecord Phase-1 signed-out /learn production go-live and config fix +15/-0

Record Phase-1 signed-out /learn production go-live and config fix

• Adds a 0.5.2 release entry documenting the signed-out production deploy, the new Phase-1 wrangler config file, the README rollout documentation, and the wrangler route-binding fix.

CHANGELOG.md

README.mdDocument two-phase rollout and exact Cloudflare token permissions +46/-10

Document two-phase rollout and exact Cloudflare token permissions

• Replaces the prior generic provisioning section with a Phase 1 (live, signed-out) vs Phase 2 (pending, signed-in) table, including which wrangler config to deploy and required token permissions. Clarifies the live OAuth callback URL and adds a remote D1 schema application command for Phase 2.

workers/learn-api/README.md

Other (2) +43 / -1
pyproject.tomlBump learn-cli version to 0.5.2 +1/-1

Bump learn-cli version to 0.5.2

• Updates the project version to align with the recorded 0.5.2 deploy and changelog entry.

pyproject.toml

wrangler.signedout.tomlAdd committed Phase-1 (signed-out) Worker deploy config +42/-0

Add committed Phase-1 (signed-out) Worker deploy config

• Introduces a dedicated wrangler config for the signed-out production posture: binds the /learn zone route and sets only non-secret vars needed for reverse proxying to the Pages origin. Explicitly omits KV/D1/secrets and documents how Phase 2 differs.

workers/learn-api/wrangler.signedout.toml

@sonarqubecloud

Copy link
Copy Markdown

@OriNachum OriNachum merged commit 539977e into main Jul 11, 2026
8 checks passed
@OriNachum OriNachum deleted the feat/learn-golive-signedout branch July 11, 2026 04:38
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 9 rules

Grey Divider


Action required

1. OAuth callback missing /learn 🐞 Bug ≡ Correctness
Description
With the worker now bound only on the /learn route, handleLogin() still generates the GitHub
redirect_uri as https://agentculture.org/api/auth/callback (no /learn), so the OAuth callback will
land outside this worker and the web login flow cannot complete.
Code

workers/learn-api/wrangler.toml[R28-31]

+routes = [
+  { pattern = "agentculture.org/learn/*", zone_name = "agentculture.org" },
+  { pattern = "agentculture.org/learn", zone_name = "agentculture.org" },
+]
Evidence
wrangler.toml binds the worker only to /learn and /learn/*; index.js routes /learn/api/* into /api/*
handlers but handleLogin still builds redirect_uri at /api/auth/callback, while the README now
documents the callback as /learn/api/auth/callback.

workers/learn-api/wrangler.toml[18-31]
workers/learn-api/src/index.js[68-79]
workers/learn-api/src/index.js[125-133]
workers/learn-api/README.md[97-104]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The worker is now mounted at `agentculture.org/learn/*`, but `handleLogin()` constructs the GitHub OAuth `redirect_uri` at `/api/auth/callback` (no `/learn` prefix). This will cause GitHub to redirect to a path that does not hit this Worker route.

## Issue Context
- Requests arrive as `/learn/api/auth/login`, but the router strips `/learn` only for internal dispatch.
- GitHub must be configured to call back into the mounted worker path: `/learn/api/auth/callback`.

## Fix Focus Areas
- workers/learn-api/src/index.js[63-79]
- workers/learn-api/src/index.js[125-133]
- workers/learn-api/src/index.js[310-312]
- workers/learn-api/README.md[97-104]

## Notes
Implement a mount-aware callback builder, e.g. derive a `mountPrefix` from the incoming request pathname (`/learn` when mounted) and build:
- `redirectUri = `${publicOrigin(env,url)}${mountPrefix}/api/auth/callback``
Then ensure README and GitHub OAuth app config remain aligned.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Signed-out config hits GitHub 🐞 Bug ☼ Reliability
Description
wrangler.signedout.toml sets a non-empty placeholder GITHUB_CLIENT_ID, so phase-1 deployments will
still allow direct requests to /learn/api/auth/login and /learn/api/auth/device which attempt GitHub
OAuth/device-flow network calls instead of being cleanly disabled.
Code

workers/learn-api/wrangler.signedout.toml[R31-36]

+[vars]
+# Placeholder client id: harmless in phase 1. The sign-in button is only
+# reachable by direct navigation (org's Learn nav link is held until phase 2),
+# so a non-functional OAuth start has no public surface yet.
+GITHUB_CLIENT_ID = "REPLACE_ME_github_oauth_client_id"
+PUBLIC_URL   = "https://agentculture.org"
Evidence
The signed-out config provides GITHUB_CLIENT_ID; index.js exposes /api/auth/login and
/api/auth/device without requireAuth; github.js implements those handlers via outbound fetches to
GitHub endpoints.

workers/learn-api/wrangler.signedout.toml[31-40]
workers/learn-api/src/index.js[81-90]
workers/learn-api/src/index.js[125-188]
workers/learn-api/src/github.js[25-35]
workers/learn-api/src/github.js[79-90]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Phase-1 (signed-out) config includes a placeholder `GITHUB_CLIENT_ID`, which keeps the public auth endpoints callable and triggers outbound GitHub requests if hit directly.

## Issue Context
Even if the UI link is withheld, these endpoints remain reachable by URL and can create noisy failures and unexpected external traffic in the signed-out tier.

## Fix Focus Areas
- workers/learn-api/wrangler.signedout.toml[31-40]
- workers/learn-api/src/index.js[125-188]
- workers/learn-api/src/github.js[25-35]
- workers/learn-api/src/github.js[79-90]

## Notes
Preferred low-effort fix: remove `GITHUB_CLIENT_ID` from `wrangler.signedout.toml` (or set it empty) so `requireConfig()` returns a controlled 503 and no outbound GitHub calls occur.
Alternative: add a phase flag (e.g. `SIGNED_OUT=1`) and have the router return 401/404 for `/api/auth/*` and `/api/health` as desired in phase 1.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Prod vars hinder wrangler dev 🐞 Bug ⚙ Maintainability
Description
wrangler.toml now hard-codes PUBLIC_URL/APP_URL/CORS_ORIGIN to production, so wrangler dev will emit
production OAuth redirect URIs and CORS headers (breaking localhost auth/browser testing) unless
developers override these vars.
Code

workers/learn-api/wrangler.toml[R41-44]

+PUBLIC_URL   = "https://agentculture.org"          # origin used to build the OAuth callback
+APP_URL      = "https://agentculture.org/learn/"   # where the web callback redirects post-login
+CORS_ORIGIN  = "https://agentculture.org"          # allow the /learn site to call the API with creds
+PAGES_ORIGIN = "https://agentculture-learn.pages.dev"  # static-site origin the /learn zone mount proxies to
Evidence
wrangler.toml sets production PUBLIC_URL/APP_URL/CORS_ORIGIN; index.js uses PUBLIC_URL to build
redirect_uri and CORS_ORIGIN to set Access-Control-Allow-Origin; README instructs using
wrangler.toml for wrangler dev.

workers/learn-api/wrangler.toml[33-45]
workers/learn-api/README.md[64-66]
workers/learn-api/src/index.js[125-133]
workers/learn-api/src/index.js[310-312]
workers/learn-api/src/index.js[322-335]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Production defaults for `PUBLIC_URL`, `APP_URL`, and `CORS_ORIGIN` in `wrangler.toml` make local `wrangler dev` behave like production (wrong OAuth redirect URIs and CORS allow-origin), which is error-prone for contributors.

## Issue Context
README directs developers to use `wrangler.toml` for `wrangler dev`, and the runtime code prefers these env vars over the request origin.

## Fix Focus Areas
- workers/learn-api/wrangler.toml[33-46]
- workers/learn-api/README.md[64-66]
- workers/learn-api/src/index.js[125-133]
- workers/learn-api/src/index.js[322-335]

## Notes
Options:
1) Add a `wrangler.dev.toml` (or `wrangler.local.toml`) with localhost values and document `wrangler dev -c wrangler.dev.toml`.
2) Document explicitly that `.dev.vars` should also override these non-secret vars for local runs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +28 to +31
routes = [
{ pattern = "agentculture.org/learn/*", zone_name = "agentculture.org" },
{ pattern = "agentculture.org/learn", zone_name = "agentculture.org" },
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Oauth callback missing /learn 🐞 Bug ≡ Correctness

With the worker now bound only on the /learn route, handleLogin() still generates the GitHub
redirect_uri as https://agentculture.org/api/auth/callback (no /learn), so the OAuth callback will
land outside this worker and the web login flow cannot complete.
Agent Prompt
## Issue description
The worker is now mounted at `agentculture.org/learn/*`, but `handleLogin()` constructs the GitHub OAuth `redirect_uri` at `/api/auth/callback` (no `/learn` prefix). This will cause GitHub to redirect to a path that does not hit this Worker route.

## Issue Context
- Requests arrive as `/learn/api/auth/login`, but the router strips `/learn` only for internal dispatch.
- GitHub must be configured to call back into the mounted worker path: `/learn/api/auth/callback`.

## Fix Focus Areas
- workers/learn-api/src/index.js[63-79]
- workers/learn-api/src/index.js[125-133]
- workers/learn-api/src/index.js[310-312]
- workers/learn-api/README.md[97-104]

## Notes
Implement a mount-aware callback builder, e.g. derive a `mountPrefix` from the incoming request pathname (`/learn` when mounted) and build:
- `redirectUri = `${publicOrigin(env,url)}${mountPrefix}/api/auth/callback``
Then ensure README and GitHub OAuth app config remain aligned.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +31 to +36
[vars]
# Placeholder client id: harmless in phase 1. The sign-in button is only
# reachable by direct navigation (org's Learn nav link is held until phase 2),
# so a non-functional OAuth start has no public surface yet.
GITHUB_CLIENT_ID = "REPLACE_ME_github_oauth_client_id"
PUBLIC_URL = "https://agentculture.org"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Signed-out config hits github 🐞 Bug ☼ Reliability

wrangler.signedout.toml sets a non-empty placeholder GITHUB_CLIENT_ID, so phase-1 deployments will
still allow direct requests to /learn/api/auth/login and /learn/api/auth/device which attempt GitHub
OAuth/device-flow network calls instead of being cleanly disabled.
Agent Prompt
## Issue description
Phase-1 (signed-out) config includes a placeholder `GITHUB_CLIENT_ID`, which keeps the public auth endpoints callable and triggers outbound GitHub requests if hit directly.

## Issue Context
Even if the UI link is withheld, these endpoints remain reachable by URL and can create noisy failures and unexpected external traffic in the signed-out tier.

## Fix Focus Areas
- workers/learn-api/wrangler.signedout.toml[31-40]
- workers/learn-api/src/index.js[125-188]
- workers/learn-api/src/github.js[25-35]
- workers/learn-api/src/github.js[79-90]

## Notes
Preferred low-effort fix: remove `GITHUB_CLIENT_ID` from `wrangler.signedout.toml` (or set it empty) so `requireConfig()` returns a controlled 503 and no outbound GitHub calls occur.
Alternative: add a phase flag (e.g. `SIGNED_OUT=1`) and have the router return 401/404 for `/api/auth/*` and `/api/health` as desired in phase 1.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +41 to +44
PUBLIC_URL = "https://agentculture.org" # origin used to build the OAuth callback
APP_URL = "https://agentculture.org/learn/" # where the web callback redirects post-login
CORS_ORIGIN = "https://agentculture.org" # allow the /learn site to call the API with creds
PAGES_ORIGIN = "https://agentculture-learn.pages.dev" # static-site origin the /learn zone mount proxies to

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

3. Prod vars hinder wrangler dev 🐞 Bug ⚙ Maintainability

wrangler.toml now hard-codes PUBLIC_URL/APP_URL/CORS_ORIGIN to production, so wrangler dev will emit
production OAuth redirect URIs and CORS headers (breaking localhost auth/browser testing) unless
developers override these vars.
Agent Prompt
## Issue description
Production defaults for `PUBLIC_URL`, `APP_URL`, and `CORS_ORIGIN` in `wrangler.toml` make local `wrangler dev` behave like production (wrong OAuth redirect URIs and CORS allow-origin), which is error-prone for contributors.

## Issue Context
README directs developers to use `wrangler.toml` for `wrangler dev`, and the runtime code prefers these env vars over the request origin.

## Fix Focus Areas
- workers/learn-api/wrangler.toml[33-46]
- workers/learn-api/README.md[64-66]
- workers/learn-api/src/index.js[125-133]
- workers/learn-api/src/index.js[322-335]

## Notes
Options:
1) Add a `wrangler.dev.toml` (or `wrangler.local.toml`) with localhost values and document `wrangler dev -c wrangler.dev.toml`.
2) Document explicitly that `.dev.vars` should also override these non-secret vars for local runs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant