diff --git a/docs/REGISTRY_API.md b/docs/REGISTRY_API.md index c701c7d2..770e71af 100644 --- a/docs/REGISTRY_API.md +++ b/docs/REGISTRY_API.md @@ -27,11 +27,32 @@ to GitHub. No workflow enables or purchases a Workers plan. ## Publish the first feed +### Current Free-plan deployment + +Live acceptance on 2026-10-11 verified 6,842 skills from 81 repositories, +all selectors and pagination, and a 292,366-character body reconstructed from +10 hash-verified segments. Cloudflare measured API CPU P50 of 1.43 ms and P99 +of 560 ms, and isolate memory P99 of approximately 83 MiB. These are sampled +metrics from the acceptance requests, including cold loads. + +The account is on Workers Free. Cold loads and snapshot refreshes exceed its +10 ms CPU allowance; repeated refresh testing returned resource-limit 503s. +The maintainer explicitly deferred a paid upgrade. The current deployment is +therefore available for evaluation but cannot guarantee successful cold loads +or refreshes. No subscription was changed. + +For a future approved Workers Paid deployment, configure +`"limits": { "cpu_ms": 1000 }` in `wrangler.jsonc`, deploy with Wrangler and +repeat cold-load/refresh acceptance. The Paid subscription starts at US$5/month; +see [Cloudflare pricing](https://developers.cloudflare.com/workers/platform/pricing/). +Free accounts reject this CPU-limit configuration, so it is intentionally absent +from the current config. + The first release requires a manual “Deploy Skill Catalog” run with `bootstrap_feed=true`. The existing directory becomes the baseline, with no historical notification flood. Future runs read `feed-state.json` from the last successful Pages publication and compare its source revision with the latest -successful `github-pages` deployment. A later missing/corrupt state cannot be +completed successful production publication workflow. A later missing/corrupt state cannot be bootstrapped again: recover the last published artifact instead. Production publishes are serialized through verification of the new public diff --git a/src/api/api.test.ts b/src/api/api.test.ts index 14334e32..4470a131 100644 --- a/src/api/api.test.ts +++ b/src/api/api.test.ts @@ -237,6 +237,7 @@ describe("catalog API", () => { `https://raw.githubusercontent.com/alpha/skills/${"a".repeat(40)}/SKILL.md`, ); expect(fetcher.mock.calls[1][1]?.headers).toBeUndefined(); + expect(fetcher.mock.calls[1][1]?.redirect).toBe("manual"); expect( ( await worker.fetch( @@ -250,7 +251,7 @@ describe("catalog API", () => { }); it("reports legacy provenance, unavailable upstream and mismatched hash", async () => { - for (const scenario of ["legacy", "missing", "hash", "timeout"]) { + for (const scenario of ["legacy", "missing", "redirect", "hash", "timeout"]) { const s = await fixtureSnapshot(); if (scenario === "legacy") delete s.skills[0].sourceCommit; const fetcher: typeof fetch = async (input) => { @@ -259,7 +260,7 @@ describe("catalog API", () => { if (scenario === "timeout") throw new DOMException("timeout", "TimeoutError"); return new Response("wrong", { - status: scenario === "missing" ? 404 : 200, + status: scenario === "missing" ? 404 : scenario === "redirect" ? 302 : 200, }); }; const response = await createWorker(fetcher).fetch( diff --git a/src/api/worker.ts b/src/api/worker.ts index 2e368396..23292ec3 100644 --- a/src/api/worker.ts +++ b/src/api/worker.ts @@ -115,7 +115,9 @@ export function createWorker( try { const response = await fetcher(sourceUrl, { signal: AbortSignal.timeout(10_000), - redirect: "error", + // workerd supports manual/follow only. Reject redirect status codes + // below to preserve the commit-pinned origin contract. + redirect: "manual", }); if (!response.ok || !response.body) throw new Error("Source unavailable"); const reader = response.body.getReader();