Deno Subhosting, Run User Submitted Javascript Securely

Deno Subhosting, Run User Submitted Javascript Securely

Subhosting simplifies the process of securely running untrusted JavaScript from various customers in a hosted, sandboxed environment. This service is perfect for scenarios such as providing edge functions to users, hosting ecommerce storefronts near customers, and more—all while ensuring security and minimal production infrastructure maintenance.

For those hosting user code, it's often desirable to make deployments publicly accessible via custom domains like user1.yourcompany.com. With the latest update, managing these custom domains across user deployments has become more straightforward thanks to the new flexible domain associations. This feature allows for programmatic domain management and the attachment of custom domains to deployments via the Subhosting API.

Key Features

Organization-wide Wildcard Subdomains

Assign different subdomains under a single wildcard domain to various deployments. For example, with the wildcard domain *.example.com, you can allocate foo.example.com to one deployment and bar.example.com to another. This enhanced flexibility supports more sophisticated deployment strategies and simplifies resource management.

Variables for Simplified Domain Management

To streamline the programmatic management and referencing of user deployments, two variables are now available when specifying a domain name to associate with a deployment:

  • {deployment.id}: The unique identifier of the deployment.
  • {project.name}: The name of the project associated with the deployment.

These variables can be combined with arbitrary strings, provided they result in valid domains under the registered wildcard domain. Examples include:

  • {deployment.id}.example.com
  • {project.name}.example.com
  • {project.name}-{deployment.id}.example.com
  • foo-{deployment.id}.example.com
  • foo-{deployment.id}-{project.name}.example.com

When using the deno.dev domain, the allowed formats are limited to:

  • {project.name}-{deployment.id}.deno.dev
  • {project.name}.deno.dev

These improvements offer better customization and automation, making it easier to manage and reference deployments programmatically.

Practical Usage

Registering Custom Domains

Before attaching custom domains to deployments, you need to register the domain using the POST /organizations/{organizationId}/domains endpoint:

import { assert } from "jsr:@std/assert/assert";

const orgId = "your-organization-id";
const orgToken = "your-organization-token";

const res = await fetch(
  `https://api.deno.com/v1/organizations/${orgId}/domains`,
  {
    method: "POST",
    body: JSON.stringify({
      domain: "*.example.com",
    }),
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);
The response includes a dnsRecords field, listing DNS records for setting up a name server.

Issuing TLS Certificates
Next, provision TLS certificates for the domain using the POST /domains/{domainId}/certificates/provision endpoint:

javascript
Copy code
import { assert } from "jsr:@std/assert/assert";

const orgToken = "your-organization-token";
// Domain ID from the previous step
const domainId = "your-domain-id";

const res = await fetch(
  `https://api.deno.com/v1/domains/${domainId}/certificates/provision`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);
Creating a Deployment
Create a new deployment with POST /projects/{projectId}/deployments:

javascript
Copy code
import { assert } from "jsr:@std/assert/assert";

const projectId = "your-project-id";
const orgToken = "your-organization-token";

const res = await fetch(
  `https://api.deno.com/v1/projects/${projectId}/deployments`,
  {
    method: "POST",
    body: JSON.stringify({
      entryPointUrl: "main.ts",
      assets: {
        "main.ts": {
          kind: "file",
          content: 'Deno.serve(() => new Response("hello"));',
        },
      },
      envVars: {},
      domains: [
        "foo.example.com",
        "{deployment.id}.example.com",
        "{project.name}-{deployment.id}.deno.dev",
      ],
    }),
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

The domains field in the payload specifies the custom domains attached to the deployment. For a deployment ID chonky-dog-57 under the project my-project, the following domains will route to this deployment:

https://foo.example.com
https://chonky-dog-57.example.com
https://my-project-chonky-dog-57.deno.dev

Attaching Custom Domains to Existing Deployments
To attach a custom domain to an existing deployment, use PUT /deployments/{deploymentId}/domains/{domain}:

import { assert } from "jsr:@std/assert/assert";

const deploymentId = "chonky-dog-57";
const orgToken = "your-organization-token";

const extraDomain = "prefix-{project.name}.example.com";

const res = await fetch(`/deployments/${deploymentId}/domains/${extraDomain}`, {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${orgToken}`,
  },
});

assert(res.ok);

This will direct https://prefix-my-project.example.com to the chonky-dog-57 deployment. Note that attaching a domain to a deployment automatically detaches it from any previous deployment.

Detaching Domains from Deployments
To detach a domain from a deployment, use DELETE /deployments/{deploymentId}/domains/{domain}:

import { assert } from "jsr:@std/assert/assert";

const deploymentId = "chonky-dog-57";
const orgToken = "your-organization-token";

const res = await fetch(
  `/deployments/${deploymentId}/domains/foo.example.com`,
  {
    method: "DELETE",
    headers: {
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

After this, the domain https://foo.example.com will no longer direct to the chonky-dog-57 deployment.

What’s Next
Deno subhosting may be a top choice for organizations looking to run user code securely without the hassle of maintaining production infrastructure. We are committed to enhancing Subhosting, making it the easiest way to securely run third-party untrusted code, so you can focus on delivering value to your users.

As Deno continues to evolve, it's becoming a formidable competitor to Node.js, offering unique features that cater to modern development needs. One notable advancement is the integration of npm packages, which greatly expands Deno's capabilities and compatibility with existing JavaScript ecosystems. For developers interested in leveraging npm packages within Deno for their private projects, check out this insightful article on Deno npm packages. This integration signifies a major step forward in Deno's mission to provide a secure, efficient, and versatile runtime for JavaScript and TypeScript applications.

Read more