Frontend Deployment

The app can be deployed to AWS and Azure as a static Observable Framework frontend. This is intentionally separate from cloud backend setup: public website resources serve dist/, while file/document artifacts remain in the local object directory, private S3 buckets, or private Azure Blob containers behind the coordinating API.

Use this page for both AWS and Azure frontend deployment. Provider-specific approval notes are kept here so AWS and Azure remain symmetrical in the documentation; the old Azure appendix is retained only as a historical first-deployment record.

Deployment Sequence

Use this order when bringing up a new environment:

  1. Run locally with npm run dev:local.
  2. Confirm the local API writes metadata to .wm-data/workspace.sqlite and file bytes to .wm-data/objects/.
  3. Create application accounts for clients, businesses, departments, firms, or personal workspaces.
  4. Provision AWS, Azure, or both private data backends.
  5. Add provider connection manifests to the coordinating node.
  6. Sync local project data to the connected backend surfaces.
  7. Build and deploy the static frontend when users need browser access beyond the local workstation.

When both AWS and Azure are connected, the coordinating node syncs the provider-neutral project model across configured surfaces. The public static website bucket or container should not be treated as file or document-artifact storage.

Build

npm run build

Current Development Deployment

As of July 2, 2026, the development frontend has been published to both cloud static hosting targets:

Cloud Hosting resource Public development URL CDN/DNS status
AWS S3 static website bucket workspace.categori.se in account 977060200223 http://workspace.categori.se.s3-website-us-east-1.amazonaws.com CloudFront distribution E2N4VCNIOGFEIE serves https://workspace.categori.se.
Azure Storage static website account wme97ea6bcsite in resource group wm-site-rg https://wme97ea6bcsite.z13.web.core.windows.net/ No Azure Front Door/CDN or custom DNS is attached yet.

Both targets are development static frontends. They do not store private project files. Private file and document-artifact storage should remain in the selected backend object store and should be accessed only through authenticated API flows.

Independence Contract

AWS and Azure deploy independently. The AWS script reads only AWS_* settings and uploads to an S3 static website bucket. The Azure script reads only AZURE_* settings and uploads to the Azure Storage $web container. The only shared artifact is the local dist/ directory produced by npm run build.

Do not reuse one cloud's public hosting bucket/container, DNS name, or CDN distribution for the other cloud. Keep DNS/CDN front doors separate until a deliberate traffic-management layer is introduced.

AWS Frontend

AWS deployment uses a dedicated public S3 website bucket. AWS documents S3 static website hosting for static pages and client-side scripts, and the AWS CLI s3 sync command recursively copies changed files from dist/ to S3.

export AWS_REGION=us-east-1
export AWS_PROFILE=categorise
export AWS_SITE_BUCKET=workspace.categori.se
npm run deploy:aws

AWS_SITE_BUCKET defaults to workspace.categori.se when unset. Use AWS_PROFILE=categorise for account 977060200223. Set AWS_SITE_DELETE=true only when you want the script to remove remote files that are no longer in dist/.

The script also uploads extensionless HTML aliases, for example docs/deployment.html is also published as docs/deployment, so Observable Framework's clean routes work on plain S3 website hosting. When AWS_SITE_DOMAIN or AWS_SITE_DISTRIBUTION_ID is set, deploy:aws creates a CloudFront invalidation after upload unless AWS_SITE_INVALIDATE=false.

AWS Auth Gateway

The workspace frontend uses the same application-side gateway pattern as the other categori.se apps. Unauthenticated users see a centered sign-in panel with Sign in, Register, Demo, and Recover account actions. Sign-in, registration, password recovery, and sign-out use Cognito Hosted UI with PKCE.

The shared gateway UI and actions live in src/components/auth-gateway.js. The mounted workspace routes use it from src/components/workspace-management-app.js, and the site-wide Observable head script in src/workspace-chrome.js uses the same component to guard static pages such as / and /docs/*. ?demo=1 or ?preview=1 opens the public demo route without a Cognito session.

Public Demo Account

The Demo action opens a dedicated browser-local Workspace Demo account instead of bypassing auth with the production seed. The seed lives in cloneDemoWorkspaceSeed(), and the daily browser store and quotas live in src/lib/demo-workspace.js.

Demo behavior:

Current auth resources:

Setting Value
AWS account 977060200223
User pool us-east-1_KXUwaZnuQ (auth)
Hosted UI domain auth.categori.se
App client 56jt8rqj4r0i2hafjper8ljsmb (workspace-management-spa)
Callback/logout URLs https://workspace.categori.se/, https://d28y8aivxdwrn7.cloudfront.net/, local ports 3000-3002

The gateway protects application routes and static documentation entry points from casual unauthenticated browsing, and keeps private workspace flows behind a Cognito session before workspace data hydration. The static S3 website bucket remains a public origin because this deployment uses S3 website hosting; backend APIs and private file/object access still need server-side authorization checks.

AWS HTTPS Custom Domain

Use setup:aws:site-domain after the S3 website bucket has been created by deploy:aws. The script creates or reports one CloudFront distribution for the requested alias, uses the S3 website endpoint as the origin, redirects HTTP viewers to HTTPS, and maps 403/404 responses back to index.html for clean Observable routes.

For the current workspace.categori.se deployment:

export AWS_REGION=us-east-1
export AWS_PROFILE=categorise
export AWS_SITE_BUCKET=workspace.categori.se
export AWS_SITE_DOMAIN=workspace.categori.se
export AWS_SITE_CERTIFICATE_ARN=arn:aws:acm:us-east-1:977060200223:certificate/79ce96a2-6b18-48ac-b334-abdc0a5dc25d
npm run deploy:aws
npm run setup:aws:site-domain

The active target-account CloudFront distribution is:

Setting Value
Account 977060200223
Distribution id E2N4VCNIOGFEIE
Distribution domain d28y8aivxdwrn7.cloudfront.net
Alias workspace.categori.se
Origin workspace.categori.se.s3-website-us-east-1.amazonaws.com
Certificate arn:aws:acm:us-east-1:977060200223:certificate/79ce96a2-6b18-48ac-b334-abdc0a5dc25d

Because categori.se is not hosted in Route53 in this AWS account, manage DNS in DNSMadeEasy or the active DNS provider. The production DNS record should point the subdomain to CloudFront:

workspace CNAME d28y8aivxdwrn7.cloudfront.net

Keep this ACM validation record in place so the certificate can renew automatically:

_bdede6a9b74260a9787d5e37958bf04e.workspace CNAME _9343714ced34b51e35ddc54df1b60b24.jkddzztszm.acm-validations.aws.

Current DNS and certificate findings:

If the intended hostname is exactly workspace.catgori.se, request and validate a separate us-east-1 ACM certificate and create DNS validation records in the authoritative DNS provider before running setup:aws:site-domain.

AWS Approval Notes

Ask IT to approve or provide:

The AWS frontend bucket is separate from the private S3 document bucket created by npm run setup:aws.

Azure Frontend

Azure deployment uses a dedicated StorageV2 account with static website hosting enabled and uploads dist/ into the $web container.

az login
az account set --subscription "<subscription-id-or-name>"

export AZURE_RESOURCE_GROUP=wm-site-rg
export AZURE_LOCATION=eastus
export AZURE_SITE_STORAGE_ACCOUNT=<globally-unique-lowercase-name>
npm run deploy:azure

The script defaults Blob uploads to AZURE_STORAGE_AUTH_MODE=key, which lets the deployment account use the storage account key immediately after creating the account. Set AZURE_STORAGE_AUTH_MODE=login only after assigning the deploying identity a Blob Data role on the storage account. Set AZURE_SITE_DELETE=true only when you want the script to clear $web before uploading.

The script also uploads extensionless HTML aliases, for example docs/deployment.html is also published as docs/deployment, so Observable Framework's clean routes work on Azure Storage static website hosting.

Azure Approval Notes

Ask IT to approve or provide:

The Azure frontend $web container is separate from the private Blob container created by npm run setup:azure. The first Azure deployment record is retained in Azure Backend Setup.

Deployment Approval Summary

Use this summary when requesting approval for either cloud.

Concern AWS frontend Azure frontend
Public hosting resource S3 static website bucket StorageV2 static website $web container
Deploy command npm run deploy:aws npm run deploy:azure
Cloud CLI aws az
Environment variables AWS_* AZURE_*
Least-privilege deploy identity IAM user or role scoped to the frontend bucket User, service principal, or managed identity with Blob data-plane write access
Public access model Public website endpoint unless fronted by an access layer Public anonymous $web endpoint unless fronted by an access layer
HTTPS/custom domain npm run setup:aws:site-domain creates CloudFront; DNS record is external when the zone is not in Route53 Azure Front Door, Azure CDN, or approved front door
Private file/artifact storage Separate private S3 bucket Separate private Blob container

Backend Baselines

Use these when you also want to create the private data resources:

npm run setup:aws
npm run setup:azure

The setup scripts create baseline private object storage and metadata databases. They do not deploy API compute, custom domains, HTTPS CDN fronts, identity callback apps, or email sender verification.

Internal Corporate Deployment

When the corporation already has cloud storage, ask IT to provide or approve a dedicated static-site storage target and a separate private file/artifact storage target.

For the frontend static site, request:

For private project data, request:

Do not use the public static website bucket/container for private files or document artifacts.

Command Separation Audit

The deployment scripts are intentionally independent.

Deployment Script Cloud CLI Public hosting resource Environment prefix
AWS frontend scripts/deploy-aws-site.sh aws S3 static website bucket AWS_
AWS custom domain scripts/setup-aws-site-domain.sh aws CloudFront distribution in front of S3 website bucket AWS_
Azure frontend scripts/deploy-azure-site.sh az Azure Storage static website $web AZURE_

The only shared step is the local static build:

npm run build

After that, AWS uploads to S3 and Azure uploads to the $web container. Neither deployment script calls the other cloud CLI, reads the other cloud's environment variables, or writes to the other cloud's public hosting resource.

The backend baseline scripts are also separate:

Backend Script Private object store Metadata database
AWS scripts/setup-aws-backend.sh S3 DynamoDB
Azure scripts/setup-azure-backend.sh Azure Blob Storage Cosmos DB

Production Notes

For AWS production hosting, prefer an approved HTTPS front door such as CloudFront or Amplify Hosting in front of S3 for CDN behavior and custom-domain TLS.

For Azure production hosting, use Azure Front Door or Azure CDN when you need custom-domain HTTPS, caching rules, and edge routing in front of the static website endpoint.

References