Skip to main content

Deployment

About 15 minutes. You need a Cloudflare account and the ability to add a DNS record. Nothing in this project needs a server — it is a static site.

Before you start, decide the hostname, for example oss.example.com. You cannot use a path on someone else's site; it must be a hostname you control.

Step 0 — Build the clean output folder​

cd oss-compliance-analyzer
npm run build:static

This creates dist/ containing only index.html, _headers, assets/, src/, samples/ and image/ (281 KB). It deliberately excludes tools/ — the CLI and MCP server are Node processes with filesystem and stdio access, and publishing them to a corporate URL would be unnecessary exposure. This is ADR-013.

Checkpoint: ls dist should contain no tools directory.

Step 1 — Get the files onto Cloudflare​

Pick one route.

Route A — Wrangler from your machine (fastest, no Git needed)​

npx wrangler login # opens a browser, authorises the CLI
npm run deploy:pages

The first run asks you to confirm the project name and prints the preview URL, https://oss-compliance.pages.dev.

Route B — Git integration (re-deploys on every push)​

  1. Push the project to GitHub or GitLab.

  2. Cloudflare dashboard → Workers & Pages → Create → Pages → Connect to Git.

  3. Select the repository, then set:

    SettingValue
    Framework presetNone
    Build commandnpm run build:static
    Build output directorydist
  4. Save and deploy.

note

Cloudflare has been merging Pages into Workers. If your dashboard only offers Workers, use npx wrangler deploy with static assets instead, or create the Pages project via Wrangler (Route A) — both end in the same place.

Checkpoint: open the *.pages.dev URL and click Demo milestone diff. If the diff card appears, the upload is good.

Step 2 — Add your custom domain​

  1. Open the Pages project → Custom domains → Set up a custom domain.
  2. Enter your hostname → Continue.

Then it depends on where your DNS lives.

Case 1 — the domain already uses Cloudflare DNS​

Cloudflare adds the DNS record itself. Nothing to do. Wait for the certificate: the status goes Initializing → Active, usually under five minutes, occasionally up to an hour.

Case 2 — DNS is with another provider, or another team owns the apex​

Pages needs the zone in your Cloudflare account. Two options:

  • Move the whole domain to Cloudflare DNS — Cloudflare walks you through the nameserver change. Cleanest, but it affects everything on the domain, so get the DNS owner's sign-off.

  • Delegate only the subdomain — add these records at your current provider:

    oss.example.com. NS <name1>.ns.cloudflare.com.
    oss.example.com. NS <name2>.ns.cloudflare.com.

    then add oss.example.com as a zone in Cloudflare. This touches nothing else — a good option in a company where apex DNS is locked down.

Using the bare apex (example.com with no subdomain) works only if the domain is on Cloudflare DNS, because Cloudflare flattens the CNAME at the root. With external DNS, use a subdomain.

Checkpoint: https://oss.example.com loads the app and the padlock is valid.

No SBOM is ever uploaded — parsing happens in the browser — so a public URL leaks no data. But this is internal tooling; keep it off the open internet.

  1. Cloudflare dashboard → Zero Trust → Access → Applications → Add an application → Self-hosted.
  2. Application domain: oss.example.com (all paths).
  3. Add a policy: Allow → Emails ending in → @yourcompany.com. Save.

Now do the same for oss-compliance.pages.dev, or the login bypasses your policy by using the default URL. Alternatively add a Bulk Redirect from oss-compliance.pages.dev to your custom domain.

Also worth setting on the zone: SSL/TLS → Overview → Full (strict).

Step 4 — Verify​

curl -I https://oss.example.com
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' https://oss.example.com/src/app.js

Expect 200 and text/javascript. If src/app.js returns text/plain or text/html, ES modules will be blocked and the page renders blank.

Then in the browser, load Demo milestone diff and confirm the diff card appears.

Security headers​

_headers sets the following, and must sit in the deployed root (dist/_headers):

/*
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
X-Frame-Options: DENY

/index.html
Cache-Control: no-cache

/src/*
Cache-Control: no-cache

no-cache on HTML and JS is deliberate. There is no content hashing in the build, so aggressive caching would leave users on stale code after a redeploy. Cloudflare supports up to 100 rules in this file.

Troubleshooting​

SymptomCause and fix
Blank page, console shows a module MIME errorsrc/*.js not served as text/javascript. Confirm the deploy included src/ and that no rule rewrites content types.
525 / 526 on the custom domainCertificate still issuing. Wait; check status on the Custom domains tab.
Works on pages.dev, fails on the custom domainDNS record missing or not proxied — should be a CNAME to <project>.pages.dev with the orange cloud on.
Old version after a redeployBrowser cached src/. _headers sets no-cache for JS/HTML; hard-reload once.
_headers seems ignoredIt must sit in the deployed root.
App works but demo buttons failsamples/ missing from the upload.

Re-deploying and removing​

  • Route A: npm run deploy:pages. Route B: push to the connected branch.
  • Removing: Pages project → Manage deployment → Delete project, then remove the custom domain and the DNS record it created.

What cannot be deployed​

tools/cli.mjs and tools/mcp-server.mjs are Node processes — they need a filesystem and stdio, which Workers do not provide. Run them locally or in your build agents.

If you later want a hosted API, wrap src/risk-engine.js in a Worker with a fetch handler — the engine is pure and has no Node dependencies. Do not try to reuse the stdio MCP server on Workers; there is no stdin and no filesystem.