Skip to content

feat(emulate): add --slug option to namespace portless aliases#92

Open
gmackie wants to merge 1 commit intovercel-labs:mainfrom
gmackie:feat/portless-slug
Open

feat(emulate): add --slug option to namespace portless aliases#92
gmackie wants to merge 1 commit intovercel-labs:mainfrom
gmackie:feat/portless-slug

Conversation

@gmackie
Copy link
Copy Markdown

@gmackie gmackie commented Apr 30, 2026

Summary

Adds a --slug option to emulate start and emulate init that namespaces portless aliases per project.

Motivation

Each emulate instance holds mutable runtime state (issues, customers, messages, sessions, etc.) with no tenant isolation, so running multiple projects against a single instance causes state crosstalk. Running separate instances per project is a simple solution — tenant-level namespacing would also work but would be a significant overhaul to the project.

In port-based mode, separate instances are trivial — just use different --port ranges. But in --portless mode, aliases are derived solely from the service name (github.emulate, stripe.emulate). A second instance silently overwrites the first's aliases via --force, and shutdown of either instance removes the alias for both. --base-url can't help here — it's mutually exclusive with --portless and only affects advertised URLs, not alias registration.

--slug namespaces both the portless alias and the advertised URL, allowing concurrent instances without collisions.

Changes

  • Add --slug to start and init commands
  • Read slug from config file (slug top-level key), CLI flag takes precedence
  • Validate slug as a DNS label (lowercase alnum + hyphens, max 63 chars)
  • Register portless aliases after servers bind to avoid dangling aliases on port conflicts
  • Update init output to suggest --portless when slug is configured
  • Update README, docs site, and SKILL.md

Usage

# emulate.config.yaml
slug: myapp
tokens:
  # ...
github:
  # ...
# Or via CLI flag (overrides config)
npx emulate start --portless --slug myapp

# URLs: https://github.myapp.emulate.localhost
#        https://stripe.myapp.emulate.localhost

emulate init --slug myapp writes the slug field to the generated config. Without --slug, no slug is written and portless behaves as before.

Test plan

  • --portless --slug myapp registers aliases as github.myapp.emulate and serves at https://github.myapp.emulate.localhost
  • --portless without slug behaves identically to before
  • --slug without --portless errors with a clear message
  • Invalid slugs (spaces, uppercase, special chars) are rejected
  • Two instances with different slugs run concurrently without alias collisions

🤖 Generated with Claude Code

When running multiple projects with --portless, aliases like
github.emulate collide because they are derived only from the
service name. The second instance silently overwrites the first
via --force, and shutdown of either instance removes the alias
for both.

--slug <name> namespaces aliases to <service>.<slug>.emulate,
producing URLs like https://github.myapp.emulate.localhost.
The slug can be set via CLI flag, or as a top-level `slug` field
in emulate.config.yaml (CLI flag takes precedence). Without
--slug, behavior is unchanged.

Additional improvements:
- Validate slug as a DNS label (lowercase alnum + hyphens, max 63 chars)
- Register portless aliases after servers bind to avoid dangling aliases on port conflicts
- Update init command output to suggest --portless when slug is configured

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

vercel Bot commented Apr 30, 2026

@gmackie is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

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