Skip to content

feat(sdk): accept a localhost app origin in every environment - #1381

Merged
callumalpass merged 2 commits into
mainfrom
feat/localhost-app-development
Nov 10, 2026
Merged

callumalpass merged 2 commits into
mainfrom
feat/localhost-app-development

Conversation

@callumalpass

Copy link
Copy Markdown
Contributor

Summary

Lets an outside developer build an app on http://localhost against production (or staging), with no flag, allowlist entry or separate SDK build.

A loopback HTTP origin (http://localhost:<port>, http://127.0.0.1:<port>, http://[::1]:<port>) is now an admissible application origin in every environment. Connect's own origins (control plane, log, portal) stay HTTPS outside LAB and isolated local builds. No other plain-HTTP origin is an application origin anywhere.

What changes

Layer Change
Protocol One predicate, isLoopbackHttpOrigin, used by the SDK and the server.
SDK The declaration origin, storage, asset and return-URL checks accept a loopback application origin in every environment. selectAppEnvironment still requires HTTPS for the control plane and log outside LAB/local builds.
Server POST /v1/apps/register, /v1/apps/validate and installation pairing START parse manifests with loopback allowed. Pairing accepts a loopback origin only when it is the origin the manifest's homepage or redirect URIs declare. A loopback origin listed only under installation_origins is still refused in staging and production.
Portal The approval screen describes a loopback application as "an app running on this computer", marks its identity unverified, and does not show its name or icon as branding.
connect-dev Unchanged: validate-manifest stays strict unless --allow-local is passed, so a loopback homepage in a production manifest is caught before it ships.

Also fixed, because it blocked the same developer: the npm-installed SDK failed under vite dev with "The app couldn't load" (application_packaged_assets_unavailable). Vite serves the SDK's module with a ?v=<hash> query and packagedApplicationAssets refused any module URL with a query. First-party apps link the workspace package and never met it. The query is now ignored; the files resolve beside the module on the app's origin and their bytes are still checked against the packaged pins.

Replaced and deleted

  • MDBASE_CONNECT_ALLOW_INSECURE_MANIFESTS is removed from the server, compose files and scripts. The server always parses with loopback allowed.
  • The SDK's allowNativeLoopbackHttp option and the platform argument to selectAppEnvironment / packagedApplicationAssets are removed. Storage modules no longer take allowLoopbackHttp for the application origin; it remains only where it relaxes HTTPS for Connect's own origins in LAB.
  • The "native platform on 127.0.0.1" special case becomes the general rule.

Net: 86 files, +402 / −300, most of it deleting the parameter plumbing.

Why this is safe

  • A remote page cannot give itself a loopback origin. The browser sets Origin, and requesting or framing a loopback URL does not transfer that origin or its storage. The party that can serve a loopback origin is a program already running on the person's computer.
  • Production already accepted http://127.0.0.1:<port> for manifests declaring platform: "native". This extends the same admission to localhost and [::1] and to web declarations, and requires the origin to be the one the manifest declares.
  • Registration was already open to any HTTPS origin and identifies no publisher. A loopback declaration gains nothing an HTTPS lookalike could not already register, and it cannot be reached by a link.
  • The approval screen never presents a loopback application's name or icon as branding. Account approval, collection consent and per-collection capability grants are unchanged, and each grant is bound to the exact origin and port.
  • Account-origin cookie privileges and CORS credentials are decided by the configured account origins and are unaffected.

A development manifest (loopback homepage) and the published manifest (HTTPS homepage) are separate registered applications, so approvals and device storage made on localhost stay on localhost.

Full write-up: docs/local-app-development.md.

Checks

  • pnpm ci:local (native plan): all node gates (lint, typecheck, unit tests, package audit, builds, architecture, public API inventories), cargo fmt --check, the feature check, clippy -D warnings and the CI script tests pass.
  • Rust workspace tests: the ci:local run of this gate stalled linking on the build VM and was stopped. Rerun on its own as rcargo test --locked --workspace --no-fail-fast -j 8: 806 passed, 0 failed, 189 ignored. No Rust source changes in this PR.
  • pnpm e2e (runtime daemon request path): pass.
  • pnpm check:changelog: pass.
  • SDK: new unit tests for a loopback application origin's storage and for a module URL with a query; an npm-packed build of this SDK was run in a Vite + React app under vite dev and vite build on http://localhost, up to the signed-out and work-before-connecting screens.

Not exercised: a real sign-in from http://localhost against production or staging, which needs this change deployed. The server-side registration and pairing paths are covered by the Postgres-backed tests in services/server.

Follow-ups (not in this PR)

  • mdbase-cloud-ops render.yaml still sets MDBASE_CONNECT_ALLOW_INSECURE_MANIFESTS. The server now ignores it; the line can go.
  • test/upgrade/server-retained-v2.sh still passes the flag, deliberately: it starts the retained old image, which reads it.
  • The server's app_platform / platform response fields are no longer read by the portal.
  • No test yet covers readiness for a pack that carries only a seed type.
  • The SDK guide rewrite that documents this is Rework the SDK guide: example app, localhost development, generated API reference mdbase.dev#100 (draft until an SDK release contains this change).

A loopback HTTP origin (localhost, 127.0.0.1 or [::1], any port) is now an
admissible application origin in LAB, staging and production. The SDK accepts
it for the declaration, storage, assets and return URL; the server registers
and pairs a manifest whose homepage is that origin; the approval screen
describes it as an app running on this computer with an unverified identity.
Connect's own origins stay HTTPS outside LAB and local builds.

This replaces the native-platform 127.0.0.1 special case and removes
MDBASE_CONNECT_ALLOW_INSECURE_MANIFESTS, allowNativeLoopbackHttp and the
platform argument that carried it.

Also let the npm-installed SDK start under a development server: a version
query on the SDK's module URL no longer makes it refuse its packaged files.
…est apps

The provider system suite registers its applications on loopback origins. The
approval screen now presents every loopback application as an unverified app
on this computer, so assert that heading and the note naming the app.
@callumalpass

Copy link
Copy Markdown
Contributor Author

CI caught one failure from this change: the provider system suite registers its test apps on loopback origins and asserted the approval heading was the app's name. Updated the two assertions to the local-app identity (heading, origin line and the note naming the app). pnpm test:system -- --suite provider passes locally with the fix.

@callumalpass
callumalpass added this pull request to the merge queue Nov 10, 2026
Merged via the queue into main with commit af93628 Nov 10, 2026
65 checks passed
@callumalpass
callumalpass deleted the feat/localhost-app-development branch November 10, 2026 11:57
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