npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

headroom-cms

v0.3.1

Published

Headroom CMS — Serverless headless CMS for AWS, deployed with SST

Downloads

280

Readme

Headroom CMS

Serverless headless CMS for AWS, deployed with SST v4.

Headroom provisions a complete CMS stack — DynamoDB, S3, Lambda, CloudFront, Cognito, and an admin UI — from a single SST component. No Go toolchain or build step required; pre-compiled binaries and a pre-built admin UI are included in the package.

Beta: Headroom is in active development and currently in beta. Expect breaking changes to the configuration interface, infrastructure, and APIs between minor versions. Not yet recommended for production workloads without pinning to an exact version.

Quick Start

npm create headroom my-cms
cd my-cms
npx sst deploy --stage production
./scripts/create-admin.sh [email protected] 'YourPassword123!'

After deploying, the CLI prints four URLs:

  • API — the Lambda function URL for the content API
  • API CDN — the CloudFront distribution for /v1/* and /health (cached, edge-authenticated)
  • Media CDN — the CloudFront distribution for /media/* and /img/* (public assets)
  • Admin — the admin UI

Open the Admin URL and sign in with the credentials you just created.

Prerequisites

  • Node.js 22+ and pnpm (or npm)
  • AWS credentials configured (aws configure or environment variables)
  • SST v4 (installed automatically as a dev dependency)
  • AWS CLI (for the create-admin.sh and get-token.sh helper scripts)
  • jq (used by helper scripts to read SST outputs)

Configuration Reference

Your sst.config.ts imports HeadroomCMS and passes a configuration object:

/// <reference path="./.sst/platform/config.d.ts" />
import { HeadroomCMS } from "headroom-cms";

export default $config({
  app(input) {
    return {
      name: "my-cms",
      removal: input?.stage === "production" ? "retain" : "remove",
      protect: input?.stage === "production",
      home: "aws",
    };
  },
  async run() {
    const cms = new HeadroomCMS("CMS", {
      senderEmail: "[email protected]",

      // All options below are optional
      domain: {
        name: "api.mycompany.com",
        certificateArn: "arn:aws:acm:us-east-1:...:certificate/...",
      },
      adminDomain: {
        name: "cms.mycompany.com",
        certificateArn: "arn:aws:acm:us-east-1:...:certificate/...",
      },
      priceClass: "PriceClass_100",
      apiCacheTtl: 3600,
      passwordPolicy: {
        minimumLength: 12,
        requireLowercase: true,
        requireUppercase: true,
        requireNumbers: true,
        requireSymbols: true,
      },
    });

    return cms.outputs;
  },
});

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | senderEmail | string | (required) | Email for admin invitations and password resets. Must be verified in SES (see Troubleshooting). | | domain | { name, certificateArn } | CloudFront default | Custom domain for the API CDN (/v1/*, /health). | | mediaDomain | { name, certificateArn } | CloudFront default | Custom domain for the media CDN (/media/*, /img/*). | | adminDomain | { name, certificateArn } | CloudFront default | Custom domain for the admin UI. | | priceClass | "PriceClass_100" | "PriceClass_200" | "PriceClass_All" | "PriceClass_100" | CloudFront price class. 100 = US/Canada/Europe, 200 adds Asia/Africa, All = global. | | apiCacheTtl | number | 3600 | API response cache TTL in seconds at the CloudFront edge. | | passwordPolicy | object | See below | Cognito password policy overrides. |

Password Policy Defaults

| Field | Default | |-------|---------| | minimumLength | 12 | | requireLowercase | true | | requireUppercase | true | | requireNumbers | true | | requireSymbols | true |

Outputs

The component returns these outputs (available in .sst/outputs.json after deploy):

| Output | Description | |--------|-------------| | api | Lambda function URL for the Go API | | apiCdn | API CloudFront distribution URL (/v1/*, /health — cached, edge-authenticated) | | mediaCdn | Media CloudFront distribution URL (/media/*, /img/* — public assets). Use this in public HTML so the API host stays out of <img src> and og:image tags. | | admin | Admin UI URL | | userPoolId | Cognito User Pool ID | | userPoolClientId | Cognito User Pool Client ID |

The API and media CDNs are deliberately split. apiCdn is the host your server-to-server code calls for /v1/* reads and writes; mediaCdn is the host that should appear in any public HTML, RSS feed, or og:image meta tag your site emits. Keeping the API host out of casually-scraped public content makes scraping, key probing, and accidental reverse-engineering of the admin endpoints noticeably harder.

Custom Domains

To use custom domains for the API CDN, media CDN, or admin UI:

  1. Request an ACM certificate in us-east-1 (required for CloudFront):

    aws acm request-certificate \
      --domain-name api.mycompany.com \
      --validation-method DNS \
      --region us-east-1
  2. Validate the certificate by adding the DNS records ACM provides (check the AWS Console or use aws acm describe-certificate).

  3. Add the domain config to your sst.config.ts:

    const cms = new HeadroomCMS("CMS", {
      senderEmail: "[email protected]",
      domain: {
        name: "api.mycompany.com",
        certificateArn: "arn:aws:acm:us-east-1:123456789:certificate/abc-123",
      },
    });
  4. Deploy, then create a CNAME (or alias) DNS record pointing your domain to the CloudFront distribution domain shown in the deploy output.

Repeat for mediaDomain (media CDN, serves /media/* and /img/*) and adminDomain (admin UI) if you want custom domains for those. Each certificate must be in us-east-1.

Updating

pnpm update headroom-cms
npx sst deploy --stage production

SST detects changes in the component and updates Lambda code, admin UI assets, and any infrastructure changes automatically.

Version Policy

Headroom follows semantic versioning:

  • Patch (0.1.x) — Bug fixes, no infrastructure changes
  • Minor (0.x.0) — New features, backward-compatible infrastructure additions
  • Major (x.0.0) — Breaking changes to config interface or infrastructure that may require manual steps

Troubleshooting

SES Email Verification

Headroom creates an SES email identity for your senderEmail automatically, but AWS requires you to verify it. Check the inbox for that address and click the verification link. Until verified, admin invitation emails won't send.

If your AWS account is still in the SES sandbox, you can only send to verified email addresses. Request production access in the AWS Console under SES > Account dashboard.

IAM Permissions

The deploying user/role needs broad permissions to create DynamoDB tables, S3 buckets, Lambda functions, CloudFront distributions, Cognito user pools, SQS queues, and IAM roles. If you get AccessDenied errors during deploy, ensure your credentials have AdministratorAccess or an equivalent policy.

Deploy Errors

  • "Resource already exists" — You may have resources from a previous deployment in the same AWS account/region. Use a different SST stage name or clean up the old resources.
  • "CREATE_FAILED ... custom-message Lambda" — This usually means the Node.js runtime version isn't available in your region. Ensure you're deploying to a standard region.
  • Timeout during deploy — CloudFront distribution creation can take 5-15 minutes on first deploy. This is normal.

Helper Scripts

The scaffolded project includes two helper scripts in scripts/:

  • create-admin.sh <email> <password> — Creates a Cognito admin user. Password must be 12+ characters with uppercase, lowercase, numbers, and symbols.
  • get-token.sh <email> <password> — Returns a JWT access token for testing admin API endpoints.

Both scripts read from .sst/outputs.json, so you must deploy before running them.

What Gets Deployed

A single HeadroomCMS component creates:

  • 13 DynamoDB tables (sites, content, draft content, blocks, media, collections, block types, admin audit, relationships, site users, webhooks, webhook deliveries, scheduler lock)
  • 1 S3 bucket (media files and content bodies)
  • 1 CloudFront KVS (edge auth cache and site version tracking)
  • 1 Cognito User Pool + Client (admin authentication)
  • 1 SES Email Identity (admin invitations)
  • 5 Lambda functions (API, webhook worker, image transform, custom message, scheduler)
  • 1 EventBridge schedule (drives the scheduler Lambda)
  • 1 CloudFront Distribution with 2 CloudFront Functions (edge auth, media URL rewrite)
  • 1 Admin UI (static site on CloudFront)
  • 2 SQS queues (webhook delivery + dead letter queue)

Advanced: Ejecting

Ejection to a full source checkout — for customizing Go API handlers, adding admin UI pages, changing DynamoDB schemas, or swapping out subsystems — will be supported in a future release. For now, the packaged component is the only supported entry point; track the changelog for updates.

License

PolyForm Noncommercial 1.0.0