personalPersonal
Only the publication owner can enter after CMUX sign-in.
One canonical domain for every published VM port—generated or customer-owned—with Freestyle TLS and CMUX personal, team, or public access policy.
personalOnly the publication owner can enter after CMUX sign-in.
teamAny current member of the selected CMUX team can enter.
publicAnyone with the URL can enter. No authorization call.
Freestyle keeps its existing multi-certificate proxy and wake-on-request path.
CMUX applies account and team policy, including sign-in and the signed-in denial state.
Local traffic skips the public hostname, TLS edge, relay, and browser authorization entirely.
A publication maps one VM port to one canonical HTTPS hostname:
hostname -> CMUX publication -> Freestyle VM + port
Every publication has exactly one access mode:
| Mode | Viewer policy |
|---|---|
personal | Only the CMUX user who owns the publication |
team | Any current member of the selected CMUX team |
public | Anyone with the URL |
There are no viewer grants, access requests, approvals, or per-domain share lists. A person either satisfies the current publication policy or does not.
The unauthorized browser states are deliberately small:
Freestyle is the data plane. It owns DNS-facing routing, customer certificates, TLS termination, VM wake-up, and proxying to the selected VM port.
CMUX is the control and policy plane. It owns publications, their personal | team | public mode, CMUX identity, current team membership, browser authorization transactions, and publication sessions.
CMUX does not terminate customer-domain TLS and the VM never receives CMUX authentication secrets.
The owner's tunnel path is separate and simpler: while connected to the account VPC, the owner reaches the service directly at the VM's private IP and port. It does not resolve or traverse the public publication hostname.
https://<publication-host>/
-> Freestyle TLS edge
|
| public
| -> wake VM if needed
| -> proxy to VM:port
|
| personal or team
| -> bodyless forwardAuth request to CMUX
| -> 2xx: proxy to VM:port
| -> 3xx: return sign-in/callback redirect to browser
| -> 4xx: return unauthorized response
| -> timeout, network failure, or 5xx: return 503
|
-> strip CMUX cookies and trusted edge metadata before VM delivery
Freestyle calls one reusable CMUX forwardAuth configuration from every protected public rule. Public rules omit forwardAuth entirely.
const cmuxAuth = await freestyle.tls.forwardAuth.create({
url: "https://cmux.com/api/freestyle/forward-auth",
headers: { authorization: `Bearer ${serviceToken}` },
timeoutMs: 1500,
protectedCookies: [
"__Host-cmux-preview",
"__Host-cmux-preview-tx",
],
});
await freestyle.tls.rules.create({
action: "allow",
domain: publication.hostname,
protocol: "http",
source: { public: true },
destination: {
vmId: publication.providerVmId,
port: publication.port,
},
forwardAuth: publication.accessMode === "public"
? undefined
: { id: cmuxAuth.id },
});
Freestyle does not follow authorization redirects. It returns them to the browser. Protected cookies are visible to CMUX during the authorization check, removed before the request reaches the VM, and protected from VM response writes.
personal compares the viewer with the owner. team checks current Stack team membership.200 from CMUX and Freestyle proxies the original request.GET or HEAD is redirected to the CMUX access page and normal Stack sign-in.401 or 403.After successful CMUX sign-in and policy evaluation:
CMUX creates a random, short-lived, single-use code bound to the user, publication, exact callback URI, PKCE challenge, state, and relative return path.
CMUX redirects the browser to the publication hostname:
https://<publication-host>/_cmux/auth/callback?code=<code>&state=<state>
Freestyle intercepts the callback through forwardAuth; the VM never sees it.
CMUX atomically consumes the transaction and code, issues a host-only publication session, clears the transaction cookie, and redirects to the validated relative path.
The repeated request is authorized and reaches the VM.
Cookie contract:
__Host-cmux-preview=<opaque random token>
Secure; HttpOnly; SameSite=Lax; Path=/; no Domain
__Host-cmux-preview-tx=<opaque transaction token>
Secure; HttpOnly; SameSite=Lax; Path=/; no Domain
short expiry
Only token hashes are stored. Every check compares the session's routing revision with the active publication and reloads current team membership, so team removal, access-mode changes, and unpublishing take effect on the next request.
cloud_vm_domains
id
owner_user_id
hostname verified zone base, for example mydomain.com
kind generated | custom
provider
provider_domain_id
verification_state
certificate_state
verification_records
created_at
updated_at
cloud_vm_publications
id
owner_user_id
vm_id
domain_id
hostname exact canonical publication hostname
hostname_claimed_at nullable
port
access_mode personal | team | public
team_id nullable
provider_tls_rule_id nullable
provider_forward_auth_id nullable
routing_revision
state
created_at
updated_at
disabled_at
cloud_vm_publication_auth_transactions
transaction_hash
publication_id
callback_uri
pkce_verifier
state_hash
return_path
expires_at
consumed_at
cloud_vm_publication_auth_codes
code_hash
publication_id
user_id
callback_uri
pkce_challenge
state_hash
return_path
expires_at
consumed_at
cloud_vm_publication_sessions
token_hash
publication_id
user_id
routing_revision
expires_at
revoked_at
Server-owned records are authoritative for VM ownership, provider VM id, private network attachment, port, TLS rule id, domain state, and publication state.
Generated *.style.dev names need no customer DNS proof. CMUX reserves a name and reconciles its TLS rule immediately.
For a customer zone and its publication hostnames:
mydomain.com._acme-challenge.mydomain.com NS delegation, plus the exact publication hostname's routing record.*.mydomain.com wildcard certificate and polls it to readiness.mydomain.com, app.mydomain.com, or docs.mydomain.com. A deeper name needs its own covering zone because one wildcard covers one label.Freestyle verification is provider-account scoped and parent ownership covers subdomains. CMUX therefore never infers one CMUX user's ownership from Freestyle's domain list, parent coverage, an existing certificate, or TLS-rule success. The stored challenge ID and base domain identify the CMUX owner; only that same owner can reuse the zone for wildcard publication hosts.
The CLI surface is:
cmux cloud domains list
cmux cloud domains publish <vm> <port> [--domain <hostname>]
[--access personal|team|public] [--team <team-id>]
cmux cloud domains verify <publication-id>
cmux cloud domains access <publication-id> <personal|team|public>
[--team <team-id>]
cmux cloud domains rm <publication-id>
public -> personal/team: attach forwardAuth at Freestyle first, then commit the protected CMUX policy and increment routing_revision.personal/team -> public: commit the public CMUX policy first, then remove forwardAuth from the Freestyle rule.personal <-> team: commit the new CMUX policy and increment routing_revision; the shared Freestyle forwardAuth id stays attached.Ambiguous provider deletion fails closed and is safe to retry. A reconciler repairs active routing drift and never exposes a protected publication whose Freestyle rule lacks forwardAuth.
The public/custom hostname is an Internet-sharing endpoint only. It always
resolves to Freestyle's public TLS edge and always follows its configured
personal | team | public policy, even when the viewer also has a CMUX tunnel.
The owner's authenticated WireGuard tunnel already places their computer on
the same per-account VPC as every owned VM. Local tools therefore connect
straight to the VM's recorded private VPC IP and selected port, using whatever
HTTP or HTTPS protocol the application itself serves. This path needs no
Freestyle TLS rule, certificate, forward-auth call, relay VM, local DNS
override, or new VPC IP lookup API: CMUX already receives each VM's private
address in VmData.vpcs[].ipv4/ipv6 and persists it with the VM.
2xx decision.personal, current-member team, and unauthenticated public behavior in fresh browsers.mydomain.com once, then publish two independent one-label hosts using the same wildcard certificate; reject an uncovered deeper host.personal, team, or public;