Skip to content

feat(draft): allow fast preview on local dev hosts and the dev tunnel - #1238

Merged
guitavano merged 2 commits into
mainfrom
guitavano/fast-preview-draft-dominios
Sep 22, 2026
Merged

guitavano merged 2 commits into
mainfrom
guitavano/fast-preview-draft-dominios

Conversation

@guitavano

@guitavano guitavano commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The ?__draft= fast preview was inert during local development. isDraftHostAllowed only rendered drafts on:

  • hosts named in the site block's previewHosts or DECO_ALLOWED_PREVIEW_HOSTS
  • the inferred <site>.deco.site (exact) and envs-<site>--<hash>.decocdn.com (per-deploy) domains

But deno task start serves the local app on localhost:<port> and on the per-developer dev tunnel <env>--<site>.deco.{host,site} (see daemon/tunnel.ts) — e.g. tavano--farmrio.deco.host. Neither matched, so a draft never previewed locally without extra config.

Change

engine/decofile/draft.ts:

  • Local dev hosts always allowed — localhost, 127.0.0.1, *.localhost, with any port (isLocalDevHost).
  • Dev tunnel inferred from the site name — matchesDevTunnel matches <env>--<site>.deco.host (the decoHost tunnel) and <env>--<site>.deco.site (the simpletunnel fallback). The <env> label is pinned to a single DNS label, the same anti-widening constraint as the per-deploy <hash>, so nothing like x.evil--<site>.deco.host can widen the match.
  • previewConfig now surfaces localAllowed and the resolved site; the early network-touch guards use isDraftPreviewEnabled instead of an empty-allowlist check.
  • Kill switch unchanged — DECO_ALLOWED_PREVIEW_HOSTS=none disables everything, local and tunnel included.

Security

The signed ?__draft= grant remains the actual capability; host-scoping only bounds blast radius. Loopback hosts are only reachable on the dev machine. The tunnel/local match slightly widens the direct-to-origin surface (an attacker with a valid signed grant spoofing x-forwarded-host: localhost), consistent with the threat model already documented in the module. none kills it without a deploy.

Note

The ?__draft= feature lives in the current @deco/deco line; a site on an older version (e.g. 1.202.1) picks up this behavior only after bumping the package.

Tests

Updated engine/decofile/draft.test.ts: the old "local requires config" assertions became "local always allowed"; new coverage for arbitrary ports, look-alike non-loopback hosts (localhost.evil.example, notlocalhost), the dev tunnel (both apexes, single-label enforcement, wrong site/apex), and the kill switch disabling local + tunnel. All passing.

🤖 Generated with Claude Code


Summary by cubic

Makes the ?__draft= fast preview work out of the box during local development, so deno task start can render drafts without extra config.

  • localhost, 127.0.0.1, *.localhost, and the IPv6 loopback (::1 / [::1], any port) now always allow drafts.
  • The per-developer dev tunnel <env>--<site>.deco.host is inferred from the site name, with the <env> label pinned to a single DNS label to prevent widening; the .deco.site tunnel fallback is deliberately not matched since it shares an apex with the production domain.
  • DECO_ALLOWED_PREVIEW_HOSTS=none still disables everything, including local hosts and the tunnel.
  • Sites on older @deco/deco versions pick up the change only after bumping the package.

Written for commit ebb6704. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Draft previews are enabled by default during local development.
    • Localhost and IPv6 loopback environments are supported on any port.
    • Developer tunnel hosts using the <env>--<site>.deco.host format can access draft previews.
    • Non-local hosts require an exact configured allowlist match, including port and capitalization.
  • Configuration

    • Set DECO_ALLOWED_PREVIEW_HOSTS=none to disable draft previews, including local access.

The `?__draft=` fast preview was inert during local development: it only
rendered drafts on hosts named in `previewHosts` / `DECO_ALLOWED_PREVIEW_HOSTS`
or the deco-hosted `<site>.deco.site` / `envs-<site>--<hash>.decocdn.com`
domains. But `deno task start` serves on `localhost:<port>` and on the
per-developer dev tunnel `<env>--<site>.deco.{host,site}` (daemon/tunnel.ts),
so a draft never previewed locally without extra config.

Now local dev hosts (`localhost`, `127.0.0.1`, `*.localhost`, any port) are
always allowed, and the dev tunnel is inferred from the resolved site name with
its `<env>` label pinned to a single DNS label — the same anti-widening
constraint as the per-deploy `<hash>`. `DECO_ALLOWED_PREVIEW_HOSTS=none` still
kills everything, local and tunnel included. The signed `?__draft=` grant
remains the actual capability; host-scoping only bounds blast radius.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Tagging Options

Should a new tag be published when this PR is merged?

  • 👍 for Patch 1.211.2 update
  • 🎉 for Minor 1.212.0 update
  • 🚀 for Major 2.0.0 update

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: d2ce142e-c40a-408b-8914-a6e62bd937a3

📥 Commits

Reviewing files that changed from the base of the PR and between 40efa50 and ebb6704.

📒 Files selected for processing (2)
  • engine/decofile/draft.test.ts
  • engine/decofile/draft.ts

Included review availability: Your plan provides up to 4 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

Draft preview is enabled for local development by default. Local hosts and valid per-developer .deco.host tunnels are allowed. Configured non-local hosts still require exact matches. The "none" value disables every preview path.

Changes

Draft preview gating

Layer / File(s) Summary
Host matching and preview configuration
engine/decofile/draft.ts, engine/decofile/draft.test.ts
Preview configuration enables local development by default. Local hosts, including IPv6 loopback forms, are accepted. Only valid <env>--<site>.deco.host tunnels are inferred. .deco.site hosts are not matched. Non-local allowlist matches remain exact.
Request gating and preview resolution
engine/decofile/draft.ts, engine/decofile/draft.test.ts
Draft resolution, cookie application, and request handling retain the isDraftPreviewEnabled gate. The "none" kill switch disables local hosts and inferred tunnels. Tests cover empty configuration, the kill switch, and missing site names.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Request
  participant resolveDraftForRequest
  participant isDraftPreviewEnabled
  participant isDraftHostAllowed
  Request->>resolveDraftForRequest: provide request host
  resolveDraftForRequest->>isDraftPreviewEnabled: check preview enablement
  isDraftPreviewEnabled-->>resolveDraftForRequest: return enabled or disabled
  resolveDraftForRequest->>isDraftHostAllowed: validate request host
  isDraftHostAllowed-->>resolveDraftForRequest: return allowed or rejected
  resolveDraftForRequest-->>Request: resolve draft or deny access
Loading

Merge Risk: ⚪ Minimal · up to ebb67

Draft previews support local development and valid dev tunnels while preserving production-domain protection and the global kill switch. The change is ready to merge with normal checks.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: enabling draft fast preview for local development hosts and the development tunnel.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread engine/decofile/draft.ts Outdated
Comment thread engine/decofile/draft.ts
…loopback

Two issues from PR review:

1. The dev-tunnel match no longer accepts the `<env>--<site>.deco.site`
   simpletunnel fallback: `.deco.site` is also the stable production apex, so
   matching it widened the draft gate on production, not just dev machines. Only
   `<env>--<site>.deco.host` (the `DECO_HOST` default) is matched now; the rare
   `DECO_HOST=false` opt-out previews via localhost or an explicit allowlist.

2. `isLocalDevHost` now treats the IPv6 loopback as local — `::1`, `[::1]`, and
   `[::1]:port` — which the port-splitting previously mangled to a non-match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@guitavano

Copy link
Copy Markdown
Contributor Author

Both cubic findings were valid — fixed in ebb6704:

1. <env>--<site>.deco.site matched on production (P3) — dropped the .deco.site tunnel apex entirely. It's the stable production apex, so matching --<site>.deco.site widened the gate beyond dev machines. .deco.host is the DECO_HOST default (daemon/daemon.ts:34), so the real deno task start flow is unaffected; the rare DECO_HOST=false simpletunnel opt-out previews via localhost or an explicit DECO_ALLOWED_PREVIEW_HOSTS entry. The dev tunnel now stays fully inert on production.

2. IPv6 loopback not matched (P3) — isLocalDevHost now treats ::1, [::1], and [::1]:port as local; the previous split(":")[0] mangled them. Added test coverage (including a [::2] negative).

@guitavano
guitavano merged commit 180dd68 into main Sep 22, 2026
4 checks passed
@guitavano
guitavano deleted the guitavano/fast-preview-draft-dominios branch September 22, 2026 11:15
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