diff --git a/lib/host/blog.sx b/lib/host/blog.sx index 0b2498fb..c2efd087 100644 --- a/lib/host/blog.sx +++ b/lib/host/blog.sx @@ -199,3 +199,11 @@ (dream-post "/posts" (host/blog--protect resolve host/blog-create)) (dream-put "/posts/:slug" (host/blog--protect resolve host/blog-update-handler)) (dream-delete "/posts/:slug" (host/blog--protect resolve host/blog-delete-handler))))) + +;; EXPERIMENTAL: create-only, UNGUARDED — POST /new form ingest with error +;; trapping but NO auth, for validating the editor->host publish loop on the +;; experimental subdomain. Create-only by design (no PUT/DELETE), so the worst +;; case is junk posts, not overwrite/delete. GATE before any real use. +(define host/blog-open-create-routes + (list + (dream-post "/new" (host/pipeline (list host/wrap-errors) host/blog-form-submit)))) diff --git a/lib/host/serve.sh b/lib/host/serve.sh index 9ffc1d24..0d7d8680 100755 --- a/lib/host/serve.sh +++ b/lib/host/serve.sh @@ -103,5 +103,8 @@ EPOCH=1 # post detail (blog-routes LAST — the :slug catch-all must not shadow the rest). # Guarded write groups (auth/ACL or internal-HMAC) are added here once their # injected policy is supplied at wiring time. - echo "(eval \"(host/serve $PORT (list host/feed-routes host/relations-routes host/blog-routes))\")" + # EXPERIMENTAL: host/blog-open-create-routes mounts POST /new UNGUARDED (no + # auth) so the editor can publish end-to-end on the experimental subdomain. + # Create-only (no PUT/DELETE). GATE (Caddy basicauth / sessions) before real use. + echo "(eval \"(host/serve $PORT (list host/feed-routes host/relations-routes host/blog-open-create-routes host/blog-routes))\")" } | exec "$SX_SERVER" diff --git a/lib/host/tests/blog.sx b/lib/host/tests/blog.sx index 65076017..bf36e9e1 100644 --- a/lib/host/tests/blog.sx +++ b/lib/host/tests/blog.sx @@ -124,6 +124,17 @@ (dream-status (host-bl-wapp (host-bl-send "DELETE" "/posts/ghost" "Bearer good" "" ""))) 404) +;; -- experimental unguarded create-only route (POST /new, no auth) -- +(define host-bl-oapp (host/make-app (list host/blog-open-create-routes host/blog-routes))) +(host/blog-use-store! (persist/open)) +(host-bl-test "open create no auth -> 303" + (dream-status (host-bl-oapp (host-bl-send "POST" "/new" nil + "application/x-www-form-urlencoded" "title=Open+Post&sx_content=(p+%22o%22)&status=published"))) + 303) +(host-bl-test "open-created post renders" + (contains? (dream-resp-body (host-bl-oapp (host-bl-req "/open-post/"))) "

o

") + true) + (define host-bl-tests-run! (fn () diff --git a/plans/host-on-sx.md b/plans/host-on-sx.md index 993ba3c3..afb2711b 100644 --- a/plans/host-on-sx.md +++ b/plans/host-on-sx.md @@ -319,6 +319,16 @@ symbols (`deps-check`, candidate pre-commit gate) → fail-loud runner (done) behavioural tests. A `deps-check`-style "binding shadows a special form" lint would catch the reserved-name class before runtime — a worthwhile follow-up. +## ⚠ Experimental: unguarded create live on blog.rose-ash.com + +`host/blog-open-create-routes` mounts **`POST /new` with NO auth** (create-only, +error-trapped) so the SX editor can publish end-to-end. **Validated live**: an +editor-style form POST → 303 → the post renders at `//` and lists on `/`. +This is a deliberate, short-lived public write hole (create-only — no PUT/DELETE +exposed; obscure subdomain). **MUST be gated before real use** — Caddy basicauth +on `/new` (the `/root/caddy/auth` dir exists) or session auth once identity lands. +Swap `host/blog-open-create-routes` → `host/blog-write-routes ` to gate. + ## Blockers - **Live wiring to the native OCaml HTTP server** (Phase 3/4): the prod server in