Skip to content

Commit a1c011b

Browse files
Advance fullstack Gate 0 and alpha.4 (#1003)
Implements the first fullstack completion batch for #1002, including redundancy removal, Supabase-backed recipes, auth/admin/rate-limit hardening, and expanded provider smoke coverage.
1 parent 9bf4f74 commit a1c011b

52 files changed

Lines changed: 1812 additions & 326 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/fullstack-deploy-smoke.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,7 @@ jobs:
3434
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
3535
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
3636
run: >-
37-
deno run -A npm:wrangler@latest deploy .output-workers/server/index.mjs
38-
--name openelement-ref-starter
39-
--assets .output-workers/public
40-
--compatibility-date 2026-08-16
41-
--compatibility-flags nodejs_compat
37+
deno run -A npm:wrangler@latest deploy --config wrangler.jsonc
4238
--var "SUPABASE_URL:$SUPABASE_URL" "SUPABASE_ANON_KEY:$SUPABASE_ANON_KEY"
4339
4440
- name: Smoke the live worker

.github/workflows/supabase-project-smoke.yml

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,14 @@ jobs:
6969
run: |
7070
set -euo pipefail
7171
source .smoke/smoke.env
72-
curl -sf -o /dev/null -X POST "$SUPABASE_URL/rest/v1/notes" \
72+
response=$(curl -sf -X POST "$SUPABASE_URL/rest/v1/notes" \
7373
-H "apikey: $SUPABASE_SERVICE_ROLE_KEY" \
7474
-H "Authorization: Bearer $SUPABASE_SERVICE_ROLE_KEY" \
7575
-H "Content-Type: application/json" \
76-
-H "Prefer: return=minimal" \
77-
-d "{\"user_id\":\"$A_ID\",\"title\":\"smoke\",\"body\":\"$MARKER\"}"
76+
-H "Prefer: return=representation" \
77+
-d "{\"user_id\":\"$A_ID\",\"title\":\"smoke\",\"body\":\"$MARKER\"}")
78+
NOTE_ID=$(echo "$response" | jq -er '.[0].id')
79+
echo "NOTE_ID=$NOTE_ID" >> .smoke/smoke.env
7880
echo "seeded note with run-unique marker"
7981
8082
- name: Build and start the reference starter
@@ -122,6 +124,17 @@ jobs:
122124
echo "$page_a" | grep -q "signed-in:$A_EMAIL"
123125
record user-a-sees-own-row pass
124126
127+
# 3b. The application write action stamps user A, persists, and PRG redirects.
128+
CREATED_MARKER="created-$MARKER"
129+
code=$(curl -s -o /dev/null -w '%{http_code}' -b .smoke/jar-a \
130+
-X POST "$BASE/notes?/create" \
131+
-H 'Content-Type: application/x-www-form-urlencoded' \
132+
--data-urlencode "title=created by smoke" \
133+
--data-urlencode "body=$CREATED_MARKER")
134+
[ "$code" = "303" ]
135+
curl -s -b .smoke/jar-a "$BASE/notes" | grep -q "$CREATED_MARKER"
136+
record user-a-creates-note-via-action pass
137+
125138
# 4. User B sees none of A's rows (cross-user RLS).
126139
code=$(curl -s -o /dev/null -w '%{http_code}' -c .smoke/jar-b \
127140
-X POST "$BASE/login" \
@@ -137,6 +150,43 @@ jobs:
137150
fi
138151
record user-b-isolated-from-a pass
139152
153+
# 4b. Exercise database RLS directly with user B's JWT: attempts to
154+
# update/delete A's known row must affect zero rows.
155+
token_b=$(curl -sf -X POST "$SUPABASE_URL/auth/v1/token?grant_type=password" \
156+
-H "apikey: $SUPABASE_ANON_KEY" -H 'Content-Type: application/json' \
157+
-d "{\"email\":\"$B_EMAIL\",\"password\":\"$B_PASSWORD\"}" | jq -er '.access_token')
158+
changed=$(curl -sf -X PATCH "$SUPABASE_URL/rest/v1/notes?id=eq.$NOTE_ID" \
159+
-H "apikey: $SUPABASE_ANON_KEY" -H "Authorization: Bearer $token_b" \
160+
-H 'Content-Type: application/json' -H 'Prefer: return=representation' \
161+
-d '{"body":"cross-user overwrite"}')
162+
[ "$(echo "$changed" | jq 'length')" = "0" ]
163+
deleted=$(curl -sf -X DELETE "$SUPABASE_URL/rest/v1/notes?id=eq.$NOTE_ID" \
164+
-H "apikey: $SUPABASE_ANON_KEY" -H "Authorization: Bearer $token_b" \
165+
-H 'Prefer: return=representation')
166+
[ "$(echo "$deleted" | jq 'length')" = "0" ]
167+
record user-b-update-delete-a-denied pass
168+
169+
# 4c. Anonymous REST reads cannot observe any notes.
170+
anonymous_rows=$(curl -sf "$SUPABASE_URL/rest/v1/notes?select=id&limit=1" \
171+
-H "apikey: $SUPABASE_ANON_KEY")
172+
[ "$(echo "$anonymous_rows" | jq 'length')" = "0" ]
173+
record anonymous-direct-rest-denied pass
174+
175+
# 4d. Storage action is owner-scoped and collision-safe.
176+
printf 'smoke attachment' > .smoke/attachment.txt
177+
code=$(curl -s -o /dev/null -w '%{http_code}' -b .smoke/jar-a \
178+
-X POST "$BASE/upload?/upload" -F 'file=@.smoke/attachment.txt;type=text/plain')
179+
[ "$code" = "303" ]
180+
curl -s -b .smoke/jar-a "$BASE/upload" | grep -q 'attachment.txt'
181+
duplicate=$(curl -s -o .smoke/duplicate.html -w '%{http_code}' -b .smoke/jar-a \
182+
-X POST "$BASE/upload?/upload" -F 'file=@.smoke/attachment.txt;type=text/plain')
183+
[ "$duplicate" = "422" ]
184+
if curl -s -b .smoke/jar-b "$BASE/upload" | grep -q 'attachment.txt'; then
185+
record user-b-storage-isolated fail
186+
exit 1
187+
fi
188+
record storage-owner-and-collision-boundary pass
189+
140190
# 5. Logout clears the session: the next /notes read is denied again.
141191
# The action entry parses a form body unconditionally, so the POST
142192
# needs a form content type even with an empty body.

deno.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"nodeModulesDir": "manual",
3434
"tasks": {
3535
"dev": "cd www && deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi --allow-sys npm:vite --config vite.config.ts",
36+
"www:dev-smoke": "deno run --allow-net --allow-run tools/smoke-www-dev.ts",
3637
"build": "deno task generate:ui-manifest && (cd www && deno run --config ../deno.json --allow-read --allow-write --allow-net --allow-env --allow-ffi --allow-sys --allow-run ../packages/adapter-vite/src/cli/build.ts) && deno task www:pagefind && deno task www:check-artifact-truth",
3738
"www:pagefind": "cd www && deno run --config ../deno.json --allow-read --allow-write --allow-run --allow-env --allow-net --allow-ffi --allow-sys build-pagefind.ts",
3839
"preview": "cd www && deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi npm:vite preview --config vite.config.ts",
@@ -44,10 +45,11 @@
4445
"docs:check-strategy": "deno run --allow-read tools/check-docs-truth.ts --check=strategic",
4546
"docs:check-current": "deno run --allow-read tools/check-docs-truth.ts --check=current",
4647
"docs:check-claims": "deno run --allow-read tools/check-docs-truth.ts --check=claims",
48+
"docs:check-recipe-parity": "deno run --allow-read tools/check-supabase-recipe-parity.ts",
4749
"release:evidence:check": "deno run --allow-read --allow-run=git tools/check-docs-truth.ts --check=evidence",
4850
"release:state-machine:check": "deno run --allow-read --allow-run=git tools/check-release-state-machine.ts",
4951
"docs:check-version-anchors": "deno run --allow-read tools/check-version-anchors.ts",
50-
"docs:truth": "deno run --allow-read --allow-run=git tools/check-docs-truth.ts && deno task docs:check-version-anchors",
52+
"docs:truth": "deno run --allow-read --allow-run=git tools/check-docs-truth.ts && deno task docs:check-version-anchors && deno task docs:check-recipe-parity",
5153
"www:check-current-truth": "deno run --allow-read tools/check-docs-truth.ts --check=www",
5254
"www:check-theme-tokens": "deno run --allow-read tools/check-www-theme-tokens.ts",
5355
"www:check-artifact-truth": "deno run --allow-read tools/check-docs-truth.ts --check=www --artifacts",

deno.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ADR-0130: Retire the Duplicate `/_data` Loader Endpoint
2+
3+
- Status: Accepted
4+
- Date: 2026-08-17
5+
- Amends: ADR-0122 §2/§4
6+
- Related: #987, #1002
7+
8+
## Context
9+
10+
The generated request-time entry contained two ways to execute a page loader:
11+
the canonical page GET handler and a generated `/_data/<route>` handler. The
12+
browser router does not consume the latter; it imports the route module and
13+
invokes the same loader through its in-process request cache. No maintained
14+
consumer, public recipe, or compatibility test uses `/_data`.
15+
16+
Keeping an unused second endpoint duplicated route matching and loader dispatch
17+
inside the ADR-0122 frozen entry generator. It also created an attractive but
18+
false protocol surface whose params, Cookie propagation, redirects, problem
19+
responses and cache headers would have needed permanent parity coverage.
20+
21+
## Decision
22+
23+
Remove generation and orchestration of the private `/_data` route map and
24+
endpoint. There is no replacement network endpoint. Browser navigation keeps
25+
using the existing in-process loader path; request-time/native navigation keeps
26+
using the canonical page GET handler.
27+
28+
This amendment changes only the set of generated internal endpoints. It does
29+
not change ADR-0122's frozen contracts:
30+
31+
- loader/action signatures and `fail()`/`redirect()` algebra are unchanged;
32+
- action negotiation, PRG revalidation and native/enhanced symmetry are unchanged;
33+
- CSRF defaults and POST dispatch are unchanged;
34+
- Cookie/response-header propagation and cache behavior remain on canonical handlers;
35+
- pure-static projects and the documented `build` then `start` path are unchanged.
36+
37+
## Consequences
38+
39+
- One loader has one maintained request-time network representation.
40+
- Generated output must not contain `/_data` or `__dataRouteMap`; a regression
41+
test pins this absence.
42+
- A future serialized loader-data endpoint is a new public protocol proposal,
43+
not permission to restore the retired private implementation.
44+
- Removing any other generated route under the frozen entry pipeline requires
45+
its own compatibility analysis; this amendment is intentionally narrow.

docs/adr/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ git history.
172172
| 0121 | 0.42 Action Protocol Hardening Amendment (Audit Round 1) | Accepted |
173173
| 0122 | 0.42.0 Stable Scope Freeze — WC Light Fullstack | Proposed (TP-6) |
174174
| 0123 | Standards as Seams — Evolution Plan | Proposed (alpha.13) |
175+
| 0130 | Retire the Duplicate `/_data` Loader Endpoint | Accepted |
175176

176177
## Superseded / Historical
177178

docs/current/VERSION_PLAN.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
> Current source package line: `v0.43.0-alpha.1`\
44
> Current npm registry line: `v0.43.0-alpha.1` (published 2026-08-15, dist-tag `alpha`)\
5-
> In-flight work: the 0.43.0-alpha.1 foundation train on the `v0.43.0`
6-
> line; TP-6 closed 2026-08-14 with ADR-0122 accepted (#962) and the WC
7-
> light-fullstack stable cut shipped\
8-
> Active release target: `v0.43.0-alpha.1`\
5+
> Latest landed train: `v0.43.0-alpha.2` (admission visibility, on main)\
6+
> Active release target: `v0.43.0-alpha.2`\
7+
> Next planned train: `v0.43.0-alpha.3`\
8+
> In-flight work: close the alpha.2 Gate 0 correctness set and release
9+
> evidence while the fullstack production plan proceeds under #1002\
910
> Planning release target: `v0.43.0` (Universal WC SSR — this plan)\
1011
> Next release line: `v0.44.0` (production runtime)\
1112
> Current maturity stage: stable (the 0.42 line, WC light fullstack frozen

0 commit comments

Comments
 (0)