How info.tomas.im works

A zero-config project hosting system. Drop a folder, deploy, get a subdomain. Here's what happens under the hood.

01 The idea

One repo, many projects, automatic subdomains.

Every folder inside projects/ becomes its own site at <name>.info.tomas.im. No configuration needed — the system discovers projects at build time and routes traffic at the edge.

Static HTML? Just drop the files in. React or Vite app? It detects package.json, runs the build, and deploys the output. Want a redirect? A single redirect.json is all it takes.

📁

Static

Plain HTML/CSS/JS. Copied as-is to the CDN.

⚙️

Buildable

Has package.json. Auto-built, output deployed.

↗️

Redirect

Has redirect.json. 301 redirect at the edge.

🏠

Landing page

Auto-generated at info.tomas.im from project metadata.

02 Request flow

What happens when you visit a project subdomain.

1

Browser → Cloudflare DNS

A wildcard CNAME record (*.info.tomas.im) points all subdomains to the CloudFront distribution. DNS is managed automatically via SST + Cloudflare.

2

CloudFront edge → Function

A CloudFront Function runs on every request. It reads the Host header, extracts the subdomain, and decides what to do — all in under 1ms.

3

Route decision

The function checks three cases: if it's the root domain, serve the landing page. If the subdomain is in the redirect map, return a 301. Otherwise, rewrite the path to /projects/<subdomain>/.

4

S3 origin → Response

The rewritten request hits S3, which serves the project files through CloudFront's global CDN. Cache headers ensure fast repeat visits.

03 The edge function

The core routing logic, running at 400+ edge locations worldwide.

CloudFront Functions use the cloudfront-js-1.0 runtime — a stripped-down JavaScript environment. No const or let, no arrow functions, no async. Just var and ES5. This is what makes them sub-millisecond.

cloudfront-function.js
// Extract subdomain from Host header
var host = event.request.headers.host.value;
var parts = host.split(".");

// Root domain → serve landing page
if (host === "info.tomas.im") {
  return event.request;
}

var subdomain = parts[0];

// Check redirect map (built at deploy time)
if (redirects[subdomain]) {
  return { statusCode: 301, headers: {
    location: { value: redirects[subdomain] }
  }};
}

// Rewrite path → /projects/<subdomain>/...
event.request.uri = "/projects/" + subdomain + uri;
return event.request;

04 Build pipeline

What bun run build does before deployment.

1

Scan

Read the projects/ directory. Classify each folder as static, buildable, or redirect based on which files it contains.

2

Validate

Check every folder name: lowercase, alphanumeric, hyphens only. Catch duplicates. Fail fast on errors.

3

Build

Static projects are copied as-is. Buildable projects get npm install && bun run build, then the output (dist/, build/, or out/) is copied. Redirects just go into the redirect map.

4

Generate

Read project.json from each project, generate the landing page HTML with cards, create placeholder thumbnails, and write the redirect map JSON.

05 Tech stack

Everything that powers this system.

06 Add a project

It takes about 30 seconds.

terminal
# 1. Create a folder
mkdir projects/my-cool-site

# 2. Add your files
cp index.html styles.css projects/my-cool-site/

# 3. (Optional) Add metadata for the landing page
cat > projects/my-cool-site/project.json << 'EOF'
{
  "description": "A cool thing I made",
  "tags": ["html", "demo"]
}
EOF

# 4. Deploy
bun run deploy

# → my-cool-site.info.tomas.im ✓