Skip to content

feat(local-mode): local-first developer experience with zero-ceremony setup#2544

Merged
vibegui merged 30 commits intomainfrom
feat/local-first-dx-core
Mar 5, 2026
Merged

feat(local-mode): local-first developer experience with zero-ceremony setup#2544
vibegui merged 30 commits intomainfrom
feat/local-first-dx-core

Conversation

@viktormarinho
Copy link
Contributor

@viktormarinho viktormarinho commented Mar 2, 2026

Summary

Zero-config local-first developer experience for MCP Mesh. Run npx decocms and get a fully functional MCP gateway on your machine in seconds.

Local mode (MESH_LOCAL_MODE=true)

  • First-run wizard prompts for data directory (defaults to ~/deco/)
  • Auto-generates and persists BETTER_AUTH_SECRET + ENCRYPTION_KEY in secrets.json (chmod 0600)
  • Seeds admin user from OS username + default org on fresh databases
  • Browser auto-logs in without a login form (loopback-only, socket-level IP check)
  • Home route skips org selection and redirects straight to the first org
  • Local filesystem object storage (/mcp/dev-assets) in local mode
  • CLI flags: --home <path>, --no-local-mode

decocms npm package

  • npx decocms / npm i -g decocms && deco — user-facing CLI
  • Thin wrapper in packages/studio/ that depends on @decocms/mesh
  • CI workflow auto-publishes in lockstep with mesh version bumps
  • Product README with features, quick start, and MCP client config

OAuth behind proxies

  • /api/config exposes internalUrl only in local mode for OAuth redirect URIs
  • setOAuthRedirectOrigin() in mesh-sdk lets the client use localhost:PORT for redirects
  • Cross-origin postMessage support between .localhost variants (e.g. tokyo.localhostlocalhost:3000)
  • Better Auth trusts localhost:PORT origin only when isLocalMode() is true

Security hardening

  • /local-session restricted to loopback IPs via getConnInfo from hono/bun (socket-level, non-spoofable)
  • server passed as Hono env in Bun.serve() for getConnInfo to work
  • Seed readiness gate (markSeedComplete/waitForSeed) prevents race between seeding and auto-login
  • DATABASE_URL respects user-provided value (no silent overwrite)
  • Tilde expansion only handles ~ and ~/path, not ~user
  • Random ENCRYPTION_KEY generated if not provided

Other

  • bun run dev renamed to bun run dev:saas; new bun run dev runs scripts/dev.ts
  • HTML title set to "Deco Studio"
  • Fresh chat per page load with stable in-session UUID
  • internalUrl removed from production /api/config responses
  • Technical debt tracked in DEBT.md

Test plan

  • Fresh install: npx decocms → prompted for data dir → browser opens, lands in org without login
  • Restart: no prompt, same session works
  • --no-local-mode: login form shown as normal
  • --home ~/custom-dir: data stored there
  • /local-session returns 403 from non-loopback IP
  • /local-session returns 503 before seed completes, then succeeds
  • OAuth popup flow works behind proxy (e.g. tokyo.localhost)
  • /api/config omits internalUrl when MESH_LOCAL_MODE is not set
  • bun test passes (except pre-existing Playwright/bun conflict)

🤖 Generated with Claude Code

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

🧪 Benchmark

Should we run the Virtual MCP strategy benchmark for this PR?

React with 👍 to run the benchmark.

Reaction Action
👍 Run quick benchmark (10 & 128 tools)

Benchmark will run on the next push after you react.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

Release Options

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

React with an emoji to vote on the release type:

Reaction Type Next Version
👍 Prerelease 2.136.2-alpha.1
🎉 Patch 2.136.2
❤️ Minor 2.137.0
🚀 Major 3.0.0

Current version: 2.136.1

Deployment

  • Deploy to production (triggers ArgoCD sync after Docker image is published)

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

6 issues found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/api/routes/auth.ts">

<violation number="1" location="apps/mesh/src/api/routes/auth.ts:126">
P1: This unauthenticated endpoint grants full admin access with no credentials. If the server is ever bound to `0.0.0.0` (or another non-loopback address), any machine on the local network can obtain an admin session. Add a host/origin check to restrict this endpoint to loopback requests only (`127.0.0.1` / `::1` / `localhost`).</violation>
</file>

<file name="apps/mesh/scripts/dev.ts">

<violation number="1" location="apps/mesh/scripts/dev.ts:75">
P2: Tilde expansion drops the home directory for inputs like "~/dir" because `answer.slice(1)` starts with `/`. Strip the leading slash before joining so the resolved path stays under the user home.</violation>

<violation number="2" location="apps/mesh/scripts/dev.ts:119">
P2: Generate a random ENCRYPTION_KEY when none is provided instead of storing an empty string; an empty key is hashed into a deterministic value, so all installs share the same derived encryption key.</violation>
</file>

<file name="apps/mesh/src/index.ts">

<violation number="1" location="apps/mesh/src/index.ts:74">
P2: Local-mode seeding runs as a detached async task after the server starts, so `/api/auth/custom/local-session` can be called before the admin user is created and returns a 500. Gate local-session until seeding finishes or wait for the seed before exposing auto-login.</violation>
</file>

<file name="apps/mesh/src/cli.ts">

<violation number="1" location="apps/mesh/src/cli.ts:184">
P1: DATABASE_URL is unconditionally overwritten, silently ignoring user-provided values (e.g., PostgreSQL URLs). The help text documents it as a configurable env var with a default, but the code always replaces it. This breaks the PostgreSQL deployment path described in the project README.

Respect the user's DATABASE_URL when explicitly set, and only default to the SQLite path inside MESH_HOME.</violation>
</file>

<file name="apps/mesh/src/auth/local-mode.ts">

<violation number="1" location="apps/mesh/src/auth/local-mode.ts:14">
P1: Hardcoding a known admin password makes the seeded account trivially guessable. Use a generated or environment-provided secret so local-mode credentials aren’t predictable if this mode is accidentally enabled outside a dev machine.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@vibegui vibegui force-pushed the feat/local-first-dx-core branch from 2e2e14a to 993a4fe Compare March 4, 2026 19:54
@vibegui
Copy link
Contributor

vibegui commented Mar 4, 2026

@cubic-dev-ai can you re-review?

@cubic-dev-ai
Copy link
Contributor

cubic-dev-ai bot commented Mar 4, 2026

@cubic-dev-ai can you re-review?

@vibegui I have started the AI code review. It will take a few minutes to complete.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

6 issues found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/auth/local-mode.ts">

<violation number="1" location="apps/mesh/src/auth/local-mode.ts:86">
P2: Organization slug is derived from raw OS username without normalization, so invalid slug characters can be persisted.</violation>
</file>

<file name="apps/mesh/src/cli.ts">

<violation number="1" location="apps/mesh/src/cli.ts:160">
P1: Missing non-TTY guard: if the CLI is run non-interactively (Docker, CI, systemd) without `--home` and without an existing `~/deco` directory, the `prompt()` call will hang forever. `scripts/dev.ts` correctly checks `process.stdin.isTTY` and falls back to a default, but `cli.ts` does not.</violation>
</file>

<file name="apps/mesh/scripts/dev.ts">

<violation number="1" location="apps/mesh/scripts/dev.ts:41">
P3: This file duplicates substantial startup/secrets logic from `src/cli.ts`; extract a shared module to prevent divergence and double-maintenance.</violation>

<violation number="2" location="apps/mesh/scripts/dev.ts:119">
P1: `ENCRYPTION_KEY` is initialized to an empty string, which results in a predictable global encryption key instead of a unique secret.</violation>

<violation number="3" location="apps/mesh/scripts/dev.ts:127">
P2: `secrets.json` is written without explicitly restricting file permissions, which can expose auth/encryption secrets on permissive environments.</violation>
</file>

<file name="apps/mesh/src/web/routes/login.tsx">

<violation number="1" location="apps/mesh/src/web/routes/login.tsx:75">
P2: Auto-login should retry transient local-session failures instead of failing permanently on the first 5xx response, otherwise users can get stuck on an error screen during initial startup/seeding.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/web/components/chat/task/use-task-manager.ts">

<violation number="1" location="apps/mesh/src/web/components/chat/task/use-task-manager.ts:152">
P2: This initializer resets the active task on every hook initialization/remount, causing unstable chat selection and unnecessary localStorage churn. Preserve an existing value in the initializer so only first-time initialization generates a UUID.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@vibegui vibegui force-pushed the feat/local-first-dx-core branch from 422e1d8 to 01e38af Compare March 5, 2026 17:52
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 13 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/auth/index.ts">

<violation number="1" location="apps/mesh/src/auth/index.ts:328">
P1: The localhost trusted origin is added in all environments (and forced to `http`), which unintentionally broadens `trustedOrigins` outside development.</violation>
</file>

<file name="packages/studio/scripts/publish.sh">

<violation number="1" location="packages/studio/scripts/publish.sh:63">
P2: `package.json` restoration is not guaranteed on failure; the script can exit early and leave the file patched.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/scripts/dev.ts">

<violation number="1" location="apps/mesh/scripts/dev.ts:75">
P2: Tilde expansion trims one extra character for `~<name>` inputs because `slice(2)` is used with a broad `startsWith("~")` check.</violation>
</file>

<file name="apps/mesh/src/cli.ts">

<violation number="1" location="apps/mesh/src/cli.ts:177">
P2: Interactive `~` path expansion removes one extra character (`slice(2)`), producing incorrect directories for inputs like `~mydir` and diverging from `--home` behavior.</violation>
</file>

<file name="apps/mesh/src/api/routes/auth.ts">

<violation number="1" location="apps/mesh/src/api/routes/auth.ts:132">
P0: Loopback check relies on spoofable client headers (`x-forwarded-for`, `x-real-ip`), making it trivially bypassable. Any LAN client can set `X-Forwarded-For: 127.0.0.1` to gain admin access. Additionally, the `remoteAddr === ""` fallback means any direct connection (no proxy) passes the check regardless of origin.

Use `getConnInfo` from `hono/bun` to read the actual socket remote address instead of trusting client-supplied headers.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

8 issues found across 14 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/docs/client/src/content/draft/pt-br/introduction.mdx">

<violation number="1" location="apps/docs/client/src/content/draft/pt-br/introduction.mdx:31">
P3: The quickstart command now conflicts with its own heading (`npx` vs `bunx`), which can confuse users following the setup instructions.</violation>
</file>

<file name="apps/mesh/src/cli.ts">

<violation number="1" location="apps/mesh/src/cli.ts:64">
P2: CLI help text now points users to `bunx decocms`, which conflicts with the documented `npx decocms` entrypoint and can misdirect users without Bun installed.</violation>
</file>

<file name="apps/docs/client/src/content/latest/en/introduction.mdx">

<violation number="1" location="apps/docs/client/src/content/latest/en/introduction.mdx:32">
P2: The quickstart command now contradicts the `(npx)` option label and documented onboarding path; this may block users without Bun installed. Keep this command as `npx decocms` (or rename the option to Bun explicitly).</violation>
</file>

<file name="apps/docs/client/src/content/latest/pt-br/mcp-mesh/quickstart.mdx">

<violation number="1" location="apps/docs/client/src/content/latest/pt-br/mcp-mesh/quickstart.mdx:16">
P3: The quickstart instruction is inconsistent: the section says "(npx)" but the command uses `bunx`, which can confuse users about the expected tool.</violation>
</file>

<file name="apps/docs/client/src/content/latest/pt-br/introduction.mdx">

<violation number="1" location="apps/docs/client/src/content/latest/pt-br/introduction.mdx:32">
P2: The quick-start command was changed to `bunx decocms`, which conflicts with the documented `npx` flow and adds an unintended Bun prerequisite for users.</violation>
</file>

<file name="apps/docs/client/src/content/latest/en/mcp-mesh/quickstart.mdx">

<violation number="1" location="apps/docs/client/src/content/latest/en/mcp-mesh/quickstart.mdx:16">
P3: The quickstart command conflicts with the surrounding "(npx)" instructions; use `npx decocms` here to keep the documented setup path consistent.</violation>
</file>

<file name=".github/workflows/release.yaml">

<violation number="1" location=".github/workflows/release.yaml:154">
P2: The release output’s “Install via npm” command was changed to `bunx`, which requires Bun and conflicts with npm-targeted install instructions. Keep npm instructions on `npx` to avoid breaking copy/paste setup for npm users.</violation>
</file>

<file name="apps/docs/client/src/content/draft/pt-br/mcp-mesh/quickstart.mdx">

<violation number="1" location="apps/docs/client/src/content/draft/pt-br/mcp-mesh/quickstart.mdx:16">
P3: The command now contradicts the section label `(npx)`, which can mislead users about which runner to use. Keep this line consistent with the documented option.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@vibegui
Copy link
Contributor

vibegui commented Mar 5, 2026

@cubic-dev-ai anything else?

@cubic-dev-ai
Copy link
Contributor

cubic-dev-ai bot commented Mar 5, 2026

@cubic-dev-ai anything else?

@vibegui The core logic for the zero-ceremony setup is robust, especially the security boundary around the local session endpoint. I have a few minor observations for scripts/dev.ts to ensure it's as flexible as the CLI:

  • Respect existing DATABASE_URL: In apps/mesh/scripts/dev.ts, you’re currently hard-overwriting process.env.DATABASE_URL. To align with the logic you added to the CLI, it should probably respect an existing environment variable if the user is explicitly pointing to a different database for development:
    process.env.DATABASE_URL = process.env.DATABASE_URL ?? `file:${join(meshHome, "mesh.db")}`;
  • Cross-platform path handling: In dev.ts, the line import.meta.dir.replace("/scripts", "") might be brittle on Windows if Bun provides backslashes in the path. Using join(import.meta.dir, "..") or a more robust path utility would be safer.
  • Seeding Gate: The use of waitForSeed() in the /local-session route is a great addition. It effectively prevents the "500 on first load" race condition that often plagues auto-login implementations where the DB isn't fully ready yet.
  • Loopback Check: Using getConnInfo (socket-level IP) instead of headers is the correct way to secure this. The inclusion of the IPv4-mapped IPv6 loopback (::ffff:127.0.0.1) is also a nice catch for compatibility.

The move to track the NATS and OpenRouter onboarding in DEBT.md is also a good call to keep this PR's scope focused while ensuring those UX improvements aren't forgotten.

AI Review Settings | Manage Learnings | Documentation

viktormarinho and others added 16 commits March 5, 2026 16:53
… setup

Running `bunx @decocms/mesh` (or `bun run dev`) now works with no config:
- Prompts for a data directory on first run (defaults to ~/deco/)
- Auto-generates and persists BETTER_AUTH_SECRET + ENCRYPTION_KEY
- Seeds an admin user from the OS username (e.g. viktor@localhost.mesh)
  and a default "Viktor Local" org on fresh databases
- Auto-logs in the browser without a login form (MESH_LOCAL_MODE=true)
- Skips org selection and redirects straight to the first org
- Enables local filesystem object storage (/mcp/dev-assets) for assets
- Adds --home and --no-local-mode CLI flags; renames old dev to dev:saas

New files:
- apps/mesh/scripts/dev.ts — mirrors CLI setup for `bun run dev`
- apps/mesh/src/auth/local-mode.ts — seedLocalMode, getLocalAdminUser
- POST /api/auth/custom/local-session — auto-sign-in endpoint (local only)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
… fixes

- Generate random ENCRYPTION_KEY instead of empty string (cli.ts, dev.ts)
- Restrict secrets.json file permissions to 0o600 (cli.ts, dev.ts)
- Add non-TTY guard to prevent prompt() hang in Docker/CI (cli.ts)
- Normalize org slug from OS username to valid [a-z0-9-] (local-mode.ts)
- Retry auto-login on 5xx with exponential backoff (login.tsx)
- Always start with fresh chat on page load (use-task-manager.ts)
- Autofocus chat input on load (tiptap/input.tsx)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use module-level UUID constant so the fresh-chat behavior is stable
within a session while still generating a new chat on each page load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix TS2345 errors: add non-null assertions for Hono route params
  (threadId, toolName) that are always present in route patterns
- Respect MESH_LOCAL_MODE env var in dev.ts instead of hardcoding true
- Set MESH_LOCAL_MODE=false in e2e workflow so signup form is shown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add localhost:PORT to Better Auth trusted origins for proxy hostnames
- Add getInternalUrl() for server-to-server connections (mcp/self, dev-assets)
- Use BASE_URL in startup logs and CLI banner
- Simplify no-LLM empty state (remove tasks panel when no model connected)
- Change HTML title to "Deco Studio"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Transitional package so `bunx @decocms/studio` works while the canonical
package remains @decocms/mesh. Includes local publish script and CI workflow
that auto-publishes with the same version as mesh.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
External OAuth servers (e.g. OpenRouter MCP) reject .localhost subdomains
as redirect URIs. When running behind a proxy (tokyo.localhost → localhost:3000),
the OAuth flow now uses localhost:PORT for the redirect URI.

- Add setOAuthRedirectOrigin() to mesh-sdk for configuring redirect origin
- Expose internalUrl in /api/config public endpoint
- Set redirect origin from config on app init in ThemeProvider
- Accept cross-origin postMessage between .localhost variants in local dev

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n on failure

- Only add localhost:PORT to Better Auth trusted origins in local mode
- Add EXIT trap to studio publish script to restore package.json on failure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Restrict /local-session endpoint to loopback requests only (127.0.0.1/::1)
  to prevent LAN access when server is bound to 0.0.0.0
- Fix tilde expansion in dev.ts and cli.ts: ~/dir was resolving to /dir
  instead of $HOME/dir (slice(1) → slice(2) to skip the ~/)
- Gate local-session behind seed completion to prevent 500s on race
- Respect user-provided DATABASE_URL in CLI instead of always overwriting
- Add security comment documenting that loopback check (not password)
  is the security boundary for local-mode auto-login

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update test mocks to include hostname/port/protocol on window.location
and adjust assertion for postMessage target origin ("*" in local dev).

Also add DEBT.md tracking technical debt for local-first DX work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace spoofable X-Forwarded-For header check with Bun's getConnInfo()
  (socket-level requestIP) for the local-session loopback guard
- Pass Bun server as env to Hono so getConnInfo can access requestIP
- Fix tilde expansion: only expand bare ~ and ~/path, not ~user inputs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Publish as `decocms` on npm so users can run `npx decocms` or
`npm i -g decocms && deco`. Renamed bin from studio.js to deco.js
and updated CI workflow, publish script, README, and DEBT.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace internal wrapper docs with a proper product README showing
features, quick start, and MCP client config. Add descriptive keywords
and update main README to reference `npx decocms`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Production deployments don't need the localhost URL in /api/config.
Gate it behind isLocalMode() so it's only sent when running locally
behind a proxy (e.g. tokyo.localhost).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
vibegui and others added 8 commits March 5, 2026 16:53
Rewrite to lead with agents and tools, not just MCP plumbing.
Matches the landing page: hire agents, connect 50+ integrations,
track costs, run locally or scale to teams.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@decocms/mesh is resolved via require() in bin/deco.js at runtime,
which knip can't trace. Add ignoreDependencies for packages/studio.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update all user-facing references: CLI help text, release workflow,
docs quickstart pages (en + pt-br), dev script comment, and OAuth
client name to use "Deco Studio" / "npx decocms" / "deco".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The built CLI uses Bun APIs (Bun.file, Bun.serve), so the wrapper
must run it with bun, not node. Auto-detects bun availability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update startup banners, prompts, and subtitle in cli.ts and dev.ts
to show "Deco Studio" instead of "MCP Mesh".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The built CLI uses Bun APIs so bun is a hard requirement. Show a
clear error with install instructions if bun is missing. Update all
docs, CLI help, README, and release workflow from npx to bunx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update "(npx)" headings to "(bunx)" in quickstart docs and change
"Install via npm" to "Install via Bun" in release workflow to match
the actual bunx decocms commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use nullish coalescing for DATABASE_URL to not override user-provided
  values (e.g., PostgreSQL connection strings), matching cli.ts behavior
- Replace brittle string replace with join(import.meta.dir, "..") for
  cross-platform path handling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vibegui vibegui force-pushed the feat/local-first-dx-core branch from 15fe181 to 89fb8ed Compare March 5, 2026 19:53
… prod guard

- Extract shared bootstrap module (scripts/bootstrap.ts) from cli.ts and dev.ts
- Generate per-install LOCAL_ADMIN_PASSWORD in secrets.json instead of hardcoded value
- Fix seed gate: resolve immediately when not in local mode (prevents hang)
- Add .catch() to dynamic import chain in index.ts
- Tighten postMessage target from "*" to getOAuthRedirectOrigin()
- Refuse local mode in production (NODE_ENV=production) unless explicitly overridden

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 7 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/index.ts">

<violation number="1" location="apps/mesh/src/index.ts:38">
P1: Use an explicit boolean-string check for `MESH_ALLOW_LOCAL_PROD`; the current truthiness check can unintentionally allow local mode in production.</violation>

<violation number="2" location="apps/mesh/src/index.ts:103">
P2: Handle local-mode module import failure as fatal (or otherwise resolve the seed gate); currently failures can leave `waitForSeed()` unresolved and break local-session auth.</violation>
</file>

<file name="apps/mesh/scripts/dev.ts">

<violation number="1" location="apps/mesh/scripts/dev.ts:36">
P1: Non-interactive runs can incorrectly use `~/deco` instead of the CI-safe `.mesh-dev` path.</violation>
</file>

<file name="packages/mesh-sdk/src/lib/mcp-oauth.ts">

<violation number="1" location="packages/mesh-sdk/src/lib/mcp-oauth.ts:560">
P2: Using getOAuthRedirectOrigin() as the postMessage target in local dev can break OAuth callbacks behind proxies, because the opener origin (e.g. proxy.localhost) won’t match the redirect origin (localhost). The message is dropped when targetOrigin mismatches, so the parent window never receives the OAuth code. Keep the permissive target for local dev or otherwise ensure the target matches the opener’s origin.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

vibegui and others added 5 commits March 5, 2026 17:28
…cking

- Remove hardcoded "admin@mesh" fallback password — throw on missing secrets.json
- Fix seed gate hang when local-mode module import fails (markSeedComplete in catch)
- Fix directory traversal in sanitizeKey() — use path.resolve() + containment check
- Unify DEV_ASSETS_BASE_DIR to use MESH_HOME in both dev-assets files
- Use crypto.timingSafeEqual() for HMAC signature verification
- Remove production console.log leaking taskId on every stream resume
- Cherry-pick OAuth flow extraction from feat/oauth-flow-extraction
- Update DEBT.md with deferred review issues from merged PRs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…proxy setups

- Use `!== "true"` instead of truthiness check for MESH_ALLOW_LOCAL_PROD
  to prevent unintended bypass with values like "false"
- Use "*" as postMessage target in local dev so OAuth callbacks work
  behind proxies where opener origin differs from redirect origin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
getLocalAdminPassword() now checks the default ~/deco directory as a
fallback when MESH_HOME is not set. This fixes auto-login when running
via `bun run dev:server` directly (which doesn't set MESH_HOME), while
still refusing to use a hardcoded credential.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…veMeshHome

Swap the order so CI/non-TTY environments always use the CI-safe
.mesh-dev path, even when ~/deco exists from a prior local run.
Previously, existsSync(~/deco) was checked first, causing CI on
shared runners to use the developer's real data directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The LLM was self-identifying as "decoCopilot" which reads as "decoco"
in Portuguese. Use properly spaced "Deco Pilot" and "Deco Studio".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vibegui vibegui merged commit 158a038 into main Mar 5, 2026
8 checks passed
@vibegui vibegui deleted the feat/local-first-dx-core branch March 5, 2026 21:28
@viktormarinho viktormarinho restored the feat/local-first-dx-core branch March 5, 2026 22:14
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.

2 participants