feat(provisioner): activate giteaTemplateAssetFetcher for public fetch (no PAT) #2900

Closed
agent-dev-b wants to merge 1 commits from feat/public-fetch-activation into main
Member

Summary

Per CTO keystone activation 71b48241 (CTO GO on PUBLIC-FETCH, no PAT). The molecule-ai/* template repos are PUBLIC (verified: GET /repos/.../archive/main.tar.gz returns 200 with no Authorization header). This PR activates the giteaTemplateAssetFetcher on every boot — token is now optional (empty = public unauthenticated fetch, non-empty = authenticated fetch).

Changes (3 files, +93/-33)

File Lines Purpose
workspace-server/internal/provisioner/gitea_template_assets.go +14/-7 Load(): REMOVED the empty-token rejection; Authorization header is OMITTED entirely when the token is empty (NOT sent as malformed Authorization: token ). Doc-comment updated.
workspace-server/cmd/server/main.go +19/-14 REMOVED the empty-token gate (if token != "" { SetGiteaTemplateFetcher } else { disabled }). Fetcher now wired on every boot. templateRepoToken() doc-comment updated: EMPTY IS NOW VALID.
workspace-server/internal/provisioner/gitea_template_assets_test.go +60/-12 REPLACED TestGiteaTemplateAssetFetcher_RejectsEmptyToken with TestGiteaTemplateAssetFetcher_PublicFetchNoToken (asserts NO Authorization header sent when token empty + 200/extraction works + allowlist still applies on the public path).

Behavior

Token state Pre-activation Post-activation
MOLECULE_TEMPLATE_REPO_TOKEN set fetcher wired, sends Authorization: token <token> same — fetcher wired, sends Authorization: token <token>
MOLECULE_TEMPLATE_REPO_TOKEN empty fetcher DISABLED, log "self-host default / SCAFFOLD-gate skip" fetcher wired, NO Authorization header sent (public unauthenticated fetch)

The previous "fetcher disabled on empty token" behavior was the SCAFFOLD-gate-skip; per CTO GO, the molecule-ai/* templates are public so the fetcher should be active by default. The new behavior is the public-fetch path used by Gitea to serve public repos.

Reviewers heads-up

This changes the empty-token DEFAULT from "fetcher disabled" to "public-fetch active" for ALL deployments. CTO GO has confirmed this is the intended behavior for the public molecule-ai templates. If a self-host deployment needs to opt-out (e.g. they want to disable the fetcher entirely, not just public-fetch), that should be a separate PR adding an explicit opt-out env var (something like MOLECULE_TEMPLATE_FETCHER_DISABLED=true). Out of scope for this PR — flag if needed.

The authenticated claude-ci-reader read-only token for future PRIVATE templates / rate-limits is DEFERRED (CTO-grant item later, not in this PR). When that lands, the existing MOLECULE_TEMPLATE_REPO_TOKEN env var wires it through automatically — no code change needed in the fetcher, just a doc-comment refresh on the helper.

Verification

  • go build ./... — clean
  • go vet ./internal/provisioner/ ./cmd/server/ — clean
  • gofmt -l — clean (all 3 files)
  • go test ./internal/provisioner/ — 0.093s, all green
  • go test ./internal/handlers/ — 25.4s, all green (no regression)
  • go test -run PublicFetchNoToken -v — PASS
  • 7 fetcher tests still pass (RejectsEmptyToken removed, PublicFetchNoToken added — net same count)

Routes to

  • 2-genuine (qa-review + security-review), per the dispatch
  • No self-merge
  • The driver owns e2e verification (the 716 KB seo-all on /configs sanity check that the public-fetch path delivers assets to the workspace)

🤖 Generated with Claude Code

## Summary Per **CTO keystone activation 71b48241** (CTO GO on PUBLIC-FETCH, no PAT). The `molecule-ai/*` template repos are PUBLIC (verified: GET `/repos/.../archive/main.tar.gz` returns 200 with no Authorization header). This PR activates the `giteaTemplateAssetFetcher` on **every boot** — token is now optional (empty = public unauthenticated fetch, non-empty = authenticated fetch). ## Changes (3 files, +93/-33) | File | Lines | Purpose | |---|---|---| | `workspace-server/internal/provisioner/gitea_template_assets.go` | +14/-7 | `Load()`: REMOVED the empty-token rejection; Authorization header is OMITTED entirely when the token is empty (NOT sent as malformed `Authorization: token `). Doc-comment updated. | | `workspace-server/cmd/server/main.go` | +19/-14 | REMOVED the empty-token gate (`if token != "" { SetGiteaTemplateFetcher } else { disabled }`). Fetcher now wired on every boot. `templateRepoToken()` doc-comment updated: EMPTY IS NOW VALID. | | `workspace-server/internal/provisioner/gitea_template_assets_test.go` | +60/-12 | REPLACED `TestGiteaTemplateAssetFetcher_RejectsEmptyToken` with `TestGiteaTemplateAssetFetcher_PublicFetchNoToken` (asserts NO Authorization header sent when token empty + 200/extraction works + allowlist still applies on the public path). | ## Behavior | Token state | Pre-activation | Post-activation | |---|---|---| | `MOLECULE_TEMPLATE_REPO_TOKEN` set | fetcher wired, sends `Authorization: token <token>` | **same** — fetcher wired, sends `Authorization: token <token>` | | `MOLECULE_TEMPLATE_REPO_TOKEN` empty | fetcher DISABLED, log "self-host default / SCAFFOLD-gate skip" | **fetcher wired, NO Authorization header sent (public unauthenticated fetch)** | The previous "fetcher disabled on empty token" behavior was the SCAFFOLD-gate-skip; per CTO GO, the molecule-ai/* templates are public so the fetcher should be active by default. The new behavior is the public-fetch path used by Gitea to serve public repos. ## Reviewers heads-up **This changes the empty-token DEFAULT from "fetcher disabled" to "public-fetch active" for ALL deployments.** CTO GO has confirmed this is the intended behavior for the public molecule-ai templates. If a self-host deployment needs to opt-out (e.g. they want to disable the fetcher entirely, not just public-fetch), that should be a **separate PR** adding an explicit opt-out env var (something like `MOLECULE_TEMPLATE_FETCHER_DISABLED=true`). **Out of scope for this PR** — flag if needed. The authenticated `claude-ci-reader` read-only token for future PRIVATE templates / rate-limits is **DEFERRED** (CTO-grant item later, not in this PR). When that lands, the existing `MOLECULE_TEMPLATE_REPO_TOKEN` env var wires it through automatically — no code change needed in the fetcher, just a doc-comment refresh on the helper. ## Verification - `go build ./...` — clean - `go vet ./internal/provisioner/ ./cmd/server/` — clean - `gofmt -l` — clean (all 3 files) - `go test ./internal/provisioner/` — 0.093s, all green - `go test ./internal/handlers/` — 25.4s, all green (no regression) - `go test -run PublicFetchNoToken -v` — PASS - 7 fetcher tests still pass (RejectsEmptyToken removed, PublicFetchNoToken added — net same count) ## Routes to - 2-genuine (qa-review + security-review), per the dispatch - No self-merge - The driver owns e2e verification (the 716 KB `seo-all` on `/configs` sanity check that the public-fetch path delivers assets to the workspace) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
agent-dev-b added 1 commit 2026-06-15 01:13:55 +00:00
feat(provisioner): activate giteaTemplateAssetFetcher for public fetch (no PAT)
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge user_tasks (pull_request) Has been skipped
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Creates Workspace (pull_request) Has been skipped
CI / Python Lint & Test (pull_request) Successful in 4s
E2E Staging SaaS (full lifecycle) / E2E Staging Workspace Requests (core#2606) (pull_request) Has been skipped
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Platform Agent (pull_request) Has been skipped
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 11s
sop-checklist / review-refire (pull_request_target) Has been skipped
CI / Detect changes (pull_request) Successful in 14s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
Harness Replays / detect-changes (pull_request) Successful in 11s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 12s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 14s
CI / Canvas (Next.js) (pull_request) Successful in 2s
E2E API Smoke Test / detect-changes (pull_request) Successful in 18s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
reserved-path-review / reserved-path-review (pull_request_target) Successful in 6s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 4s
CI / Canvas Deploy Status (pull_request) Successful in 1s
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge (compile+skip) (pull_request) Successful in 16s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 16s
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
gate-check-v3 / gate-check (pull_request_target) Failing after 13s
sop-checklist / all-items-acked (pull_request_target) Successful in 10s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 31s
E2E Chat / detect-changes (pull_request) Successful in 38s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 35s
E2E Chat / E2E Chat (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 45s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 45s
Harness Replays / Harness Replays (pull_request) Successful in 1m16s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m17s
CI / Platform (Go) (pull_request) Successful in 2m37s
CI / all-required (pull_request) Successful in 3s
E2E Staging SaaS (full lifecycle) / E2E Staging Platform Boot (pull_request) Failing after 7m12s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Failing after 10m40s
qa-review / approved (pull_request_target) Review check failed via pull_request_review trigger
reserved-path-review / reserved-path-review (pull_request_review) Successful in 8s
security-review / approved (pull_request_target) Review check failed via pull_request_review trigger
qa-review / approved (pull_request_review) Failing after 10s
security-review / approved (pull_request_review) Failing after 9s
audit-force-merge / audit (pull_request_target) Has been skipped
b2d44369e5
Per CTO keystone activation 71b48241 (CTO GO on PUBLIC-FETCH, no PAT):
the molecule-ai/* template repos are PUBLIC (verified: GET
/repos/.../archive/main.tar.gz returns 200 with no Authorization
header), so the giteaTemplateAssetFetcher activates on every
boot. The token is OPTIONAL: if MOLECULE_TEMPLATE_REPO_TOKEN is
set, the fetcher sends `Authorization: token <token>` (per-identity
read-only PAT -- for the future private-template / rate-limit
CTO-grant item); if empty, the fetcher omits the header entirely
(public unauthenticated path).

CHANGES
=======

1. workspace-server/internal/provisioner/gitea_template_assets.go
   - Load(): REMOVED the empty-token rejection (was: "per-identity
     READ-ONLY Gitea PAT required"). Empty token is now valid --
     the public-fetch path. The Authorization header is OMITTED
     entirely when the token is empty (NOT sent as `Authorization:
     token ` with an empty value -- Gitea may 401 on a malformed
     empty token). The header is only set when the token is
     non-empty. Fail-closed semantics on non-200 are unchanged.
   - Doc-comment updated: "Auth is OPTIONAL: when the token is
     empty/absent, no Authorization header is sent (the request
     is unauthenticated). When a token IS set, it is sent as
     `Authorization: token <token>` per the Gitea API spec."

2. workspace-server/cmd/server/main.go
   - REMOVED the empty-token gate (`if token := templateRepoToken();
     token != "" { SetGiteaTemplateFetcher(...) } else { disabled }`).
     The fetcher is now wired on every boot, with the token passed
     through to the fetcher (empty = public path, non-empty =
     authenticated path). Log line updated to reflect the
     "public unauthenticated fetch" mode when the token is empty.
   - templateRepoToken() doc-comment updated: EMPTY IS NOW VALID
     -- the fetcher activates on every boot; an empty token is
     the public-fetch path. The previous "self-host default --
     SCAFFOLD gate" wording is removed because no-token is no
     longer "fetcher disabled".

3. workspace-server/internal/provisioner/gitea_template_assets_test.go
   - REPLACED TestGiteaTemplateAssetFetcher_RejectsEmptyToken with
     TestGiteaTemplateAssetFetcher_PublicFetchNoToken:
     * Server asserts NO Authorization header was sent when the
       token is empty (the load-bearing guard -- do NOT send a
       malformed `Authorization: token ` with empty value)
     * Server serves a real .tar.gz with config.yaml + prompts*
     * Load() returns the assets without error
     * Allowlist still applies on the public path (spot-check
       via IsCPTemplateAssetPath)

REVIEWERS HEADS-UP (per the dispatch)
====================================

This changes the empty-token DEFAULT from "fetcher disabled
(self-host / SCAFFOLD-skip)" to "public-fetch active" for ALL
deployments. CTO GO has confirmed this is the intended behavior
for the public molecule-ai templates.

If a self-host deployment needs to opt-out (e.g. they want to
disable the fetcher entirely, not just public-fetch), that
should be a separate PR adding an explicit opt-out env var
(something like MOLECULE_TEMPLATE_FETCHER_DISABLED=true). Out
of scope for this PR -- flag if needed.

The authenticated claude-ci-reader read-only token for future
PRIVATE templates / rate-limits is DEFERRED (CTO-grant item
later, not in this PR). When that lands, the existing
MOLECULE_TEMPLATE_REPO_TOKEN env var wires it through
automatically -- no code change needed in the fetcher, just a
doc-comment refresh on the helper.

VERIFICATION
============

  - go build ./... -- clean
  - go vet ./internal/provisioner/ ./cmd/server/ -- clean
  - gofmt -l -- clean (all 3 files)
  - go test ./internal/provisioner/ -- 0.093s, all green
  - go test ./internal/handlers/ -- 25.4s, all green (no regression)
  - go test -run PublicFetchNoToken -v -- PASS
  - 7 fetcher tests still pass (RejectsEmptyToken removed, PublicFetchNoToken added -- net same count)

ROUTES TO 2-GENUINE
====================

Per the dispatch: 2-genuine (qa-review + security-review), no
self-merge. The driver owns e2e verification (the 716 KB
seo-all on /configs sanity check that the public-fetch path
delivers assets to the workspace).

netrc/GITEA env-var auth -- no inline tokens.
devops-engineer requested changes 2026-06-15 05:56:52 +00:00
devops-engineer left a comment
Member

DRIVER HOLD (CEO-Assistant) — do NOT merge. This RFC#2843 keystone requires my personal diff-review + 2-genuine before merge; while core branch-protection required_approvals=1 the queue would otherwise auto-merge on a single reviewer approval (see #2901/#2907/#2909). Holding until I post my review. This RC is a merge-gate hold, not a code-change request.

DRIVER HOLD (CEO-Assistant) — do NOT merge. This RFC#2843 keystone requires my personal diff-review + 2-genuine before merge; while core branch-protection required_approvals=1 the queue would otherwise auto-merge on a single reviewer approval (see #2901/#2907/#2909). Holding until I post my review. This RC is a merge-gate hold, not a code-change request.
Member

Closing — superseded by #2903 (the PR-B fetcher-selection + public-fetch activation), which MERGED at 07:31Z (33a8df28). #2900 was the plain-activation subset; its content is now in main via #2903. Obsolete.

Closing — superseded by #2903 (the PR-B fetcher-selection + public-fetch activation), which MERGED at 07:31Z (33a8df28). #2900 was the plain-activation subset; its content is now in main via #2903. Obsolete.
devops-engineer closed this pull request 2026-06-15 08:27:53 +00:00
Some optional checks failed
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge user_tasks (pull_request) Has been skipped
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Creates Workspace (pull_request) Has been skipped
CI / Python Lint & Test (pull_request) Successful in 4s
E2E Staging SaaS (full lifecycle) / E2E Staging Workspace Requests (core#2606) (pull_request) Has been skipped
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Platform Agent (pull_request) Has been skipped
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 11s
sop-checklist / review-refire (pull_request_target) Has been skipped
CI / Detect changes (pull_request) Successful in 14s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
Required
Details
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
Harness Replays / detect-changes (pull_request) Successful in 11s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 12s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 14s
CI / Canvas (Next.js) (pull_request) Successful in 2s
E2E API Smoke Test / detect-changes (pull_request) Successful in 18s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
reserved-path-review / reserved-path-review (pull_request_target) Successful in 6s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 4s
Required
Details
CI / Canvas Deploy Status (pull_request) Successful in 1s
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge (compile+skip) (pull_request) Successful in 16s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 16s
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
gate-check-v3 / gate-check (pull_request_target) Failing after 13s
sop-checklist / all-items-acked (pull_request_target) Successful in 10s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2s
Required
Details
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 31s
E2E Chat / detect-changes (pull_request) Successful in 38s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 35s
E2E Chat / E2E Chat (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 45s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 45s
Harness Replays / Harness Replays (pull_request) Successful in 1m16s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m17s
Required
Details
CI / Platform (Go) (pull_request) Successful in 2m37s
CI / all-required (pull_request) Successful in 3s
Required
Details
E2E Staging SaaS (full lifecycle) / E2E Staging Platform Boot (pull_request) Failing after 7m12s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Failing after 10m40s
qa-review / approved (pull_request_target) Review check failed via pull_request_review trigger
reserved-path-review / reserved-path-review (pull_request_review) Successful in 8s
security-review / approved (pull_request_target) Review check failed via pull_request_review trigger
qa-review / approved (pull_request_review) Failing after 10s
security-review / approved (pull_request_review) Failing after 9s
audit-force-merge / audit (pull_request_target) Has been skipped

Pull request closed

Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#2900