From 8e5ea7d516065ad20a6fd312c51e7a62f9a1f655 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 13 Jan 2026 21:05:10 +0000 Subject: [PATCH 1/3] Add custom token support to exposePort documentation - Add token parameter to exposePort() method signature - Document token requirements (1-16 chars, lowercase alphanumeric + hyphens/underscores) - Add examples showing custom tokens for stable URLs vs auto-generated tokens - Explain use cases for stable URLs across deployments and restarts - Update TypeScript examples with both usage patterns Ref: cloudflare/sandbox-sdk PR #329 --- src/content/docs/sandbox/api/ports.mdx | 57 +++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/src/content/docs/sandbox/api/ports.mdx b/src/content/docs/sandbox/api/ports.mdx index 53e789c109ac55a..0670842ba7a6d5a 100644 --- a/src/content/docs/sandbox/api/ports.mdx +++ b/src/content/docs/sandbox/api/ports.mdx @@ -29,29 +29,74 @@ const response = await sandbox.exposePort(port: number, options: ExposePortOptio - `options`: - `hostname` - Your Worker's domain name (e.g., `'example.com'`). Required to construct preview URLs with wildcard subdomains like `https://8080-sandbox-abc123.example.com`. Cannot be a `.workers.dev` domain as it doesn't support wildcard DNS patterns. - `name` - Friendly name for the port (optional) + - `token` - Custom token for stable preview URLs (optional). Must be 1-16 characters containing only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_). If not provided, a random 16-character token will be generated automatically. **Returns**: `Promise` with `port`, `url` (preview URL), `name` -``` +```ts // Extract hostname from request const { hostname } = new URL(request.url); +// Basic usage - auto-generated token (random URL each time) await sandbox.startProcess('python -m http.server 8000'); const exposed = await sandbox.exposePort(8000, { hostname }); -console.log('Available at:', exposed.exposedAt); -// https://8000-abc123.yourdomain.com +console.log('Available at:', exposed.url); +// https://8000-sandbox-id-abc123random4567.yourdomain.com -// Multiple services with names +// Custom token for stable URLs across deployments await sandbox.startProcess('node api.js'); -const api = await sandbox.exposePort(3000, { hostname, name: 'api' }); +const api = await sandbox.exposePort(3000, { + hostname, + name: 'api', + token: 'my-api-v1' +}); + +console.log('Stable URL:', api.url); +// https://3000-sandbox-id-my-api-v1.yourdomain.com +// URL stays the same across container restarts and deployments +// Multiple services with custom tokens await sandbox.startProcess('npm run dev'); -const frontend = await sandbox.exposePort(5173, { hostname, name: 'frontend' }); +const frontend = await sandbox.exposePort(5173, { + hostname, + name: 'frontend', + token: 'dev-ui' +}); +``` + + +#### Custom Tokens for Stable URLs + +Use custom tokens to create predictable preview URLs that remain consistent across container restarts and deployments. This is especially useful for: + +- Sharing URLs with users or team members +- Maintaining stable references in production environments +- Testing with consistent endpoints + + +```ts +// With custom token - URL stays the same across restarts +const { url } = await sandbox.exposePort(8080, { + hostname: 'example.com', + token: 'my-token-v1' +}); +// url: https://8080-sandbox-id-my-token-v1.example.com + +// Without token - generates random 16-character token (existing behavior) +const { url } = await sandbox.exposePort(8080, { + hostname: 'example.com' +}); +// url: https://8080-sandbox-id-abc123random4567.example.com ``` +**Token Requirements**: +- Length: 1-16 characters +- Characters: Lowercase letters (a-z), numbers (0-9), hyphens (-), underscores (_) only +- Must be unique per port within the same sandbox + :::note[Local development] When using `wrangler dev`, you must add `EXPOSE` directives to your Dockerfile for each port. See [Expose Services guide](/sandbox/guides/expose-services/#local-development) for details. ::: From a8a15999ffca40bb4e17f1e8a3cfafb939ce761d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 13 Jan 2026 21:17:19 +0000 Subject: [PATCH 2/3] Add custom token support to exposePort for stable preview URLs - Add optional token parameter to exposePort() API documentation - Include examples showing auto-generated vs custom token usage - Document token validation rules (1-16 chars, lowercase alphanumeric + hyphens/underscores) - Update URL format examples to include tokens in preview URLs - Add stable URL use cases for production deployments and URL sharing - Update guides with custom token examples for consistent URLs across restarts Sync documentation for cloudflare/sandbox-sdk PR #329: Add custom token support to exposePort for stable preview URLs --- src/content/docs/sandbox/api/ports.mdx | 52 ++++++++++++++- .../docs/sandbox/concepts/preview-urls.mdx | 66 ++++++++++++++----- .../docs/sandbox/guides/expose-services.mdx | 61 +++++++++++++++-- 3 files changed, 156 insertions(+), 23 deletions(-) diff --git a/src/content/docs/sandbox/api/ports.mdx b/src/content/docs/sandbox/api/ports.mdx index 0670842ba7a6d5a..e7a5d731c75213a 100644 --- a/src/content/docs/sandbox/api/ports.mdx +++ b/src/content/docs/sandbox/api/ports.mdx @@ -29,7 +29,7 @@ const response = await sandbox.exposePort(port: number, options: ExposePortOptio - `options`: - `hostname` - Your Worker's domain name (e.g., `'example.com'`). Required to construct preview URLs with wildcard subdomains like `https://8080-sandbox-abc123.example.com`. Cannot be a `.workers.dev` domain as it doesn't support wildcard DNS patterns. - `name` - Friendly name for the port (optional) - - `token` - Custom token for stable preview URLs (optional). Must be 1-16 characters containing only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_). If not provided, a random 16-character token will be generated automatically. + - `token` - Custom token for the preview URL (optional). Must be 1-16 characters containing only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_). If not provided, a random 16-character token is automatically generated. Use custom tokens to maintain stable URLs across container restarts and deployments. **Returns**: `Promise` with `port`, `url` (preview URL), `name` @@ -38,18 +38,28 @@ const response = await sandbox.exposePort(port: number, options: ExposePortOptio // Extract hostname from request const { hostname } = new URL(request.url); +<<<<<<< HEAD // Basic usage - auto-generated token (random URL each time) +======= +// Basic usage with auto-generated token +>>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) await sandbox.startProcess('python -m http.server 8000'); const exposed = await sandbox.exposePort(8000, { hostname }); +console.log('Available at:', exposed.url); +// https://8000-sandbox-id-abc123random4567.yourdomain.com +<<<<<<< HEAD console.log('Available at:', exposed.url); // https://8000-sandbox-id-abc123random4567.yourdomain.com +======= +>>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) // Custom token for stable URLs across deployments await sandbox.startProcess('node api.js'); const api = await sandbox.exposePort(3000, { hostname, name: 'api', +<<<<<<< HEAD token: 'my-api-v1' }); @@ -57,12 +67,23 @@ console.log('Stable URL:', api.url); // https://3000-sandbox-id-my-api-v1.yourdomain.com // URL stays the same across container restarts and deployments +======= + token: 'api-v1' // URL stays the same across restarts +}); +console.log('API at:', api.url); +// https://3000-sandbox-id-api-v1.yourdomain.com + +>>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) // Multiple services with custom tokens await sandbox.startProcess('npm run dev'); const frontend = await sandbox.exposePort(5173, { hostname, name: 'frontend', +<<<<<<< HEAD token: 'dev-ui' +======= + token: 'web-prod' +>>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) }); ``` @@ -101,6 +122,35 @@ const { url } = await sandbox.exposePort(8080, { When using `wrangler dev`, you must add `EXPOSE` directives to your Dockerfile for each port. See [Expose Services guide](/sandbox/guides/expose-services/#local-development) for details. ::: +## Custom tokens for stable URLs + +Custom tokens enable consistent preview URLs that survive container restarts and deployments. This is useful for: + +- **Production deployments** where URL stability is important +- **Sharing URLs** with users or external services +- **Integration testing** with predictable endpoints + +**Token requirements:** +- 1-16 characters long +- Lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_) only +- Must be unique within each sandbox (collision detection prevents reuse across ports) + + +```ts +// Without custom token - URL changes on restart +const { url } = await sandbox.exposePort(8080, { hostname: 'example.com' }); +// https://8080-sandbox-id-random16chars12.example.com + +// With custom token - URL stays consistent +const { url } = await sandbox.exposePort(8080, { + hostname: 'example.com', + token: 'my-api-v2' +}); +// https://8080-sandbox-id-my-api-v2.example.com +// Same URL after container restart ✓ +``` + + ### `unexposePort()` Remove an exposed port and close its preview URL. diff --git a/src/content/docs/sandbox/concepts/preview-urls.mdx b/src/content/docs/sandbox/concepts/preview-urls.mdx index 1d98a3b8e41d48c..e67e4cb9481f176 100644 --- a/src/content/docs/sandbox/concepts/preview-urls.mdx +++ b/src/content/docs/sandbox/concepts/preview-urls.mdx @@ -18,21 +18,56 @@ const { hostname } = new URL(request.url); await sandbox.startProcess("python -m http.server 8000"); const exposed = await sandbox.exposePort(8000, { hostname }); -console.log(exposed.exposedAt); -// Production: https://8000-abc123.yourdomain.com +console.log(exposed.url); +// Production: https://8000-sandbox-id-abc123random4567.yourdomain.com // Local dev: http://localhost:8787/... ``` ## URL Format -**Production**: `https://{port}-{sandbox-id}.yourdomain.com` +**Production**: `https://{port}-{sandbox-id}-{token}.yourdomain.com` -- Port 8080: `https://8080-abc123.yourdomain.com` -- Port 3000: `https://3000-abc123.yourdomain.com` +- With auto-generated token: `https://8080-abc123-random16chars12.yourdomain.com` +- With custom token: `https://8080-abc123-my-api-v1.yourdomain.com` **Local development**: `http://localhost:8787/...` -Preview URLs remain stable while a port is exposed and can be shared during that time. However, if you unexpose and re-expose a port, a new random token is generated and the URL changes. For persistent URLs, keep ports exposed for the duration you need them accessible. +## Token Types + +### Auto-generated tokens (default) + +When no custom token is specified, a random 16-character token is generated: + +```typescript +const exposed = await sandbox.exposePort(8000, { hostname }); +// https://8000-sandbox-id-abc123random4567.yourdomain.com +``` + +URLs with auto-generated tokens change when you unexpose and re-expose a port. + +### Custom tokens for stable URLs + +For production deployments or shared URLs, specify a custom token to maintain consistency across container restarts: + +```typescript +const stable = await sandbox.exposePort(8000, { + hostname, + token: 'api-v1' +}); +// https://8000-sandbox-id-api-v1.yourdomain.com +// Same URL every time ✓ +``` + +**Token requirements:** +- 1-16 characters long +- Lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_) only +- Must be unique within each sandbox + +**Use cases for custom tokens:** +- Production APIs with stable endpoints +- Sharing demo URLs with external users +- Documentation with consistent examples +- Integration testing with predictable URLs ## ID Case Sensitivity @@ -45,7 +80,7 @@ Preview URLs extract the sandbox ID from the hostname to route requests. Since h const sandbox = getSandbox(env.Sandbox, 'MyProject-123'); // Durable Object ID: "MyProject-123" await sandbox.exposePort(8080, { hostname }); -// Preview URL: 8000-myproject-123.yourdomain.com +// Preview URL: 8080-myproject-123-token123.yourdomain.com // Routes to: "myproject-123" (different DO - doesn't exist!) ``` @@ -56,7 +91,7 @@ const sandbox = getSandbox(env.Sandbox, 'MyProject-123', { normalizeId: true }); // Durable Object ID: "myproject-123" (lowercased) -// Preview URL: 8000-myproject-123.yourdomain.com +// Preview URL: 8080-myproject-123-token123.yourdomain.com // Routes to: "myproject-123" (same DO - works!) ``` @@ -101,9 +136,9 @@ await sandbox.startProcess("node admin.js"); // Port 3001 const api = await sandbox.exposePort(3000, { hostname, name: "api" }); const admin = await sandbox.exposePort(3001, { hostname, name: "admin" }); -// Each gets its own URL: -// https://3000-abc123.yourdomain.com -// https://3001-abc123.yourdomain.com +// Each gets its own URL with unique tokens: +// https://3000-abc123-random16chars01.yourdomain.com +// https://3001-abc123-random16chars02.yourdomain.com ``` ## What Works @@ -131,10 +166,10 @@ const { hostname } = new URL(request.url); // Start a WebSocket server await sandbox.startProcess("bun run ws-server.ts 8080"); -const { exposedAt } = await sandbox.exposePort(8080, { hostname }); +const { url } = await sandbox.exposePort(8080, { hostname }); // Clients connect using WebSocket protocol -// Browser: new WebSocket('wss://8080-abc123.yourdomain.com') +// Browser: new WebSocket('wss://8080-abc123-token123.yourdomain.com') // Your Worker routes automatically export default { @@ -155,9 +190,10 @@ Preview URLs are publicly accessible by default, but require a valid access toke **Built-in security**: -- **Token-based access** - Each exposed port gets a unique token in the URL (for example, `https://8080-sandbox-abc123token.yourdomain.com`) +- **Token-based access** - Each exposed port gets a unique token in the URL (for example, `https://8080-sandbox-abc123token456.yourdomain.com`) - **HTTPS in production** - All traffic is encrypted with automatic TLS -- **Unpredictable URLs** - Tokens are randomly generated and difficult to guess +- **Unpredictable URLs** - Auto-generated tokens are randomly generated and difficult to guess +- **Token collision prevention** - Custom tokens are validated to ensure uniqueness within each sandbox **Add application-level authentication**: diff --git a/src/content/docs/sandbox/guides/expose-services.mdx b/src/content/docs/sandbox/guides/expose-services.mdx index ee82fb05d96dbe8..13ab454c7ec7c20 100644 --- a/src/content/docs/sandbox/guides/expose-services.mdx +++ b/src/content/docs/sandbox/guides/expose-services.mdx @@ -88,6 +88,45 @@ This lowercases the ID during creation so it matches preview URL routing. Withou See [Sandbox options](/sandbox/configuration/sandbox-options/#normalizeid) for details. ::: +## Stable URLs with custom tokens + +For production deployments or when sharing URLs with users, use custom tokens to maintain consistent preview URLs across container restarts: + + +``` +// Extract hostname from request +const { hostname } = new URL(request.url); + +// Without custom token - URL changes on restart +const exposed = await sandbox.exposePort(8080, { hostname }); +// https://8080-sandbox-id-random16chars12.yourdomain.com + +// With custom token - URL stays the same across restarts +const stable = await sandbox.exposePort(8080, { + hostname, + token: 'api-v1' +}); +// https://8080-sandbox-id-api-v1.yourdomain.com +// Same URL after container restart ✓ + +return Response.json({ + 'Temporary URL (changes on restart)': exposed.url, + 'Stable URL (consistent)': stable.url +}); +``` + + +**Token requirements:** +- 1-16 characters long +- Lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_) only +- Must be unique within each sandbox + +**Use cases:** +- Production APIs with stable endpoints +- Sharing demo URLs with external users +- Integration testing with predictable URLs +- Documentation with consistent examples + ## Name your exposed ports When exposing multiple ports, use names to stay organized: @@ -97,15 +136,23 @@ When exposing multiple ports, use names to stay organized: // Extract hostname from request const { hostname } = new URL(request.url); -// Start and expose API server +// Start and expose API server with stable token await sandbox.startProcess('node api.js', { env: { PORT: '8080' } }); await new Promise(resolve => setTimeout(resolve, 2000)); -const api = await sandbox.exposePort(8080, { hostname, name: 'api' }); +const api = await sandbox.exposePort(8080, { + hostname, + name: 'api', + token: 'api-prod' +}); -// Start and expose frontend +// Start and expose frontend with stable token await sandbox.startProcess('npm run dev', { env: { PORT: '5173' } }); await new Promise(resolve => setTimeout(resolve, 2000)); -const frontend = await sandbox.exposePort(5173, { hostname, name: 'frontend' }); +const frontend = await sandbox.exposePort(5173, { + hostname, + name: 'frontend', + token: 'web-app' +}); console.log('Services:'); console.log('- API:', api.exposedAt); @@ -330,10 +377,10 @@ See [Sandbox options - normalizeId](/sandbox/configuration/sandbox-options/#norm ## Preview URL Format -**Production**: `https://{port}-{sandbox-id}.yourdomain.com` +**Production**: `https://{port}-{sandbox-id}-{token}.yourdomain.com` -- Port 8080: `https://8080-abc123.yourdomain.com` -- Port 5173: `https://5173-abc123.yourdomain.com` +- Auto-generated token: `https://8080-abc123-random16chars12.yourdomain.com` +- Custom token: `https://8080-abc123-my-api-v1.yourdomain.com` **Local development**: `http://localhost:8787/...` From edd6cd1b2c8d15d928203a0eb46f4bafc5627097 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 13 Jan 2026 21:23:32 +0000 Subject: [PATCH 3/3] Clean up custom token documentation - resolve merge conflicts Fix merge conflicts in ports.mdx API documentation and provide clean, comprehensive coverage of the custom token feature for exposePort(). Includes validatePortToken() method documentation and complete examples for stable preview URLs across deployments. --- src/content/docs/sandbox/api/ports.mdx | 139 ++++++++++++------------- 1 file changed, 67 insertions(+), 72 deletions(-) diff --git a/src/content/docs/sandbox/api/ports.mdx b/src/content/docs/sandbox/api/ports.mdx index e7a5d731c75213a..861aede7a3757fd 100644 --- a/src/content/docs/sandbox/api/ports.mdx +++ b/src/content/docs/sandbox/api/ports.mdx @@ -17,7 +17,7 @@ Expose services running in your sandbox via public preview URLs. See [Preview UR ### `exposePort()` -Expose a port and get a preview URL. +Expose a port and get a preview URL for accessing services running in the sandbox. ```ts const response = await sandbox.exposePort(port: number, options: ExposePortOptions): Promise @@ -27,9 +27,9 @@ const response = await sandbox.exposePort(port: number, options: ExposePortOptio - `port` - Port number to expose (1024-65535) - `options`: - - `hostname` - Your Worker's domain name (e.g., `'example.com'`). Required to construct preview URLs with wildcard subdomains like `https://8080-sandbox-abc123.example.com`. Cannot be a `.workers.dev` domain as it doesn't support wildcard DNS patterns. + - `hostname` - Your Worker's domain name (e.g., `'example.com'`). Required to construct preview URLs with wildcard subdomains like `https://8080-sandbox-abc123token.example.com`. Cannot be a `.workers.dev` domain as it doesn't support wildcard DNS patterns. - `name` - Friendly name for the port (optional) - - `token` - Custom token for the preview URL (optional). Must be 1-16 characters containing only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_). If not provided, a random 16-character token is automatically generated. Use custom tokens to maintain stable URLs across container restarts and deployments. + - `token` - Custom token for the preview URL (optional). Must be 1-16 characters containing only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_). If not provided, a random 16-character token is generated automatically. **Returns**: `Promise` with `port`, `url` (preview URL), `name` @@ -38,116 +38,111 @@ const response = await sandbox.exposePort(port: number, options: ExposePortOptio // Extract hostname from request const { hostname } = new URL(request.url); -<<<<<<< HEAD -// Basic usage - auto-generated token (random URL each time) -======= // Basic usage with auto-generated token ->>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) await sandbox.startProcess('python -m http.server 8000'); const exposed = await sandbox.exposePort(8000, { hostname }); -console.log('Available at:', exposed.url); -// https://8000-sandbox-id-abc123random4567.yourdomain.com -<<<<<<< HEAD console.log('Available at:', exposed.url); // https://8000-sandbox-id-abc123random4567.yourdomain.com -======= ->>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) -// Custom token for stable URLs across deployments +// With custom token for stable URLs across deployments await sandbox.startProcess('node api.js'); const api = await sandbox.exposePort(3000, { hostname, name: 'api', -<<<<<<< HEAD - token: 'my-api-v1' + token: 'prod-api-v1' // URL stays same across restarts }); -console.log('Stable URL:', api.url); -// https://3000-sandbox-id-my-api-v1.yourdomain.com -// URL stays the same across container restarts and deployments - -======= - token: 'api-v1' // URL stays the same across restarts -}); -console.log('API at:', api.url); -// https://3000-sandbox-id-api-v1.yourdomain.com +console.log('Stable API URL:', api.url); +// https://3000-sandbox-id-prod-api-v1.yourdomain.com ->>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) // Multiple services with custom tokens await sandbox.startProcess('npm run dev'); const frontend = await sandbox.exposePort(5173, { hostname, name: 'frontend', -<<<<<<< HEAD - token: 'dev-ui' -======= - token: 'web-prod' ->>>>>>> 4f59e72 (Add custom token support to exposePort for stable preview URLs) + token: 'dev-ui' }); ``` -#### Custom Tokens for Stable URLs +:::note[Local development] +When using `wrangler dev`, you must add `EXPOSE` directives to your Dockerfile for each port. See [Expose Services guide](/sandbox/guides/expose-services/#local-development) for details. +::: -Use custom tokens to create predictable preview URLs that remain consistent across container restarts and deployments. This is especially useful for: +## Custom Tokens for Stable URLs -- Sharing URLs with users or team members -- Maintaining stable references in production environments -- Testing with consistent endpoints +Custom tokens enable consistent preview URLs across container restarts and deployments. This is useful for: + +- **Production environments** - Share stable URLs with users or teams +- **Development workflows** - Maintain bookmarks and integrations +- **CI/CD pipelines** - Reference consistent URLs in tests or deployment scripts + +**Token Requirements:** +- 1-16 characters in length +- Only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_) +- Must be unique per sandbox (cannot reuse tokens across different ports) ```ts -// With custom token - URL stays the same across restarts +// Production API with stable URL const { url } = await sandbox.exposePort(8080, { - hostname: 'example.com', - token: 'my-token-v1' + hostname: 'api.example.com', + token: 'v1-stable' // Always the same URL }); -// url: https://8080-sandbox-id-my-token-v1.example.com -// Without token - generates random 16-character token (existing behavior) -const { url } = await sandbox.exposePort(8080, { - hostname: 'example.com' -}); -// url: https://8080-sandbox-id-abc123random4567.example.com +// Error: Token collision prevention +await sandbox.exposePort(8081, { hostname, token: 'v1-stable' }); +// Throws: Token 'v1-stable' is already in use by port 8080 + +// Success: Re-exposing same port with same token (idempotent) +await sandbox.exposePort(8080, { hostname, token: 'v1-stable' }); +// Works - same port, same token ``` -**Token Requirements**: -- Length: 1-16 characters -- Characters: Lowercase letters (a-z), numbers (0-9), hyphens (-), underscores (_) only -- Must be unique per port within the same sandbox +### `validatePortToken()` -:::note[Local development] -When using `wrangler dev`, you must add `EXPOSE` directives to your Dockerfile for each port. See [Expose Services guide](/sandbox/guides/expose-services/#local-development) for details. -::: +Validate if a token is authorized to access a specific exposed port. Useful for custom authentication or routing logic. -## Custom tokens for stable URLs +```ts +const isValid = await sandbox.validatePortToken(port: number, token: string): Promise +``` -Custom tokens enable consistent preview URLs that survive container restarts and deployments. This is useful for: +**Parameters**: -- **Production deployments** where URL stability is important -- **Sharing URLs** with users or external services -- **Integration testing** with predictable endpoints +- `port` - Port number to check +- `token` - Token to validate -**Token requirements:** -- 1-16 characters long -- Lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_) only -- Must be unique within each sandbox (collision detection prevents reuse across ports) +**Returns**: `Promise` - `true` if token is valid for the port, `false` otherwise ```ts -// Without custom token - URL changes on restart -const { url } = await sandbox.exposePort(8080, { hostname: 'example.com' }); -// https://8080-sandbox-id-random16chars12.example.com - -// With custom token - URL stays consistent -const { url } = await sandbox.exposePort(8080, { - hostname: 'example.com', - token: 'my-api-v2' -}); -// https://8080-sandbox-id-my-api-v2.example.com -// Same URL after container restart ✓ +// Custom validation in your Worker +export default { + async fetch(request: Request, env: Env): Promise { + const url = new URL(request.url); + + // Extract token from custom header or query param + const customToken = request.headers.get('x-access-token'); + + if (customToken) { + const sandbox = getSandbox(env.Sandbox, 'my-sandbox'); + const isValid = await sandbox.validatePortToken(8080, customToken); + + if (!isValid) { + return new Response('Invalid token', { status: 403 }); + } + } + + // Handle preview URL routing + const proxyResponse = await proxyToSandbox(request, env); + if (proxyResponse) return proxyResponse; + + // Your application routes + return new Response('Not found', { status: 404 }); + } +}; ``` @@ -232,4 +227,4 @@ export default { ## Related resources - [Preview URLs concept](/sandbox/concepts/preview-urls/) - How preview URLs work -- [Commands API](/sandbox/api/commands/) - Start background processes +- [Commands API](/sandbox/api/commands/) - Start background processes \ No newline at end of file