Creating a cloud-first static website generator using AWS services such as S3 and Lambda. Transforms markdown files into HTML.
  • Kotlin 65.4%
  • Svelte 17.3%
  • CSS 14.9%
  • TypeScript 1.4%
  • Handlebars 0.4%
  • Other 0.5%
Find a file
2026-06-25 19:53:18 +01:00
.devcontainer Upgrade Java and Kotlin versions to 21 and 2.2.0 respectively; update project version to 0.1.0. 2025-07-19 10:53:35 +01:00
.github/workflows An attempt to get cdk/gradlew to work on both windows and linux 2025-09-04 21:39:05 +01:00
.idea Merge branch 'master' into 153-bespoke-page-metadata 2026-05-24 08:27:23 +01:00
.junie Implement a HandleBars template loader that loads from S3, and a TemplateCache which strips yaml frontmatter from template strings. 2025-08-31 10:24:31 +01:00
API Added unit test for new page tree function. 2025-11-18 19:58:46 +00:00
CloudfrontRewriteURI Set up Cloudfront lambda@edge function to rewrite URI based on host. Set default environment on the main stack. 2024-02-14 22:26:51 +00:00
DevDiary Latest copy of website content, just in case. 2024-02-22 19:04:33 +00:00
FileUploadHandler Fixed custom attributes test. Include custom attributes in upsert command for pages and posts. 2025-09-24 07:38:23 +01:00
gradle Pin version of localstack to last community edition. Update testcontainers runner due to docker upgrade issue. 2026-02-16 07:24:33 +00:00
ImageProcessor Fixed image deletion on the backend at least. 2025-09-09 17:42:05 +01:00
MarkdownProcessor Image processor now puts generated images into the destination bucket. But not the website bucket. 2025-09-06 13:48:58 +01:00
OpenAPISchemaAnnotations Version bump to 0.1.13 2025-08-25 20:00:26 +01:00
OpenAPISchemaGenerator Version bump to 0.1.13 2025-08-25 20:00:26 +01:00
SharedModels Pin version of localstack to last community edition. Update testcontainers runner due to docker upgrade issue. 2026-02-16 07:24:33 +00:00
src Allow Profile OAuth scope for app client. 2025-09-14 16:14:44 +01:00
TemplateProcessor Created a calculateDestinationKey function for ContentNode. Works for pages, posts, and probably statics and images. 2025-09-06 11:27:24 +01:00
testEvents Moved source files for tidiness. 2023-02-26 10:48:50 +00:00
Web Gitignore update 2024-03-09 08:25:37 +00:00
WebEditor Refined page and folder counting logic in +page.svelte to use recursive countTreeItems function. 2026-05-24 09:15:27 +01:00
.gitignore Fixed merge issue with layout.svelte 2025-07-19 11:09:17 +01:00
API Requirements.md About a million hacks to make page regeneration work again, and template regeneration. 2023-08-10 22:00:28 +01:00
build.gradle.kts Version bump to 0.1.13 2025-08-25 20:00:26 +01:00
cdk.context.json Version number bump to remind myself how to build and deploy this. 2025-07-19 08:00:33 +01:00
cdk.json An attempt to get cdk/gradlew to work on both windows and linux 2025-09-04 21:39:05 +01:00
CLAUDE.md Added an outline CLAUDE.md for agent use 2026-05-24 08:15:44 +01:00
gradle.properties Wrote the getPages and rebuildPages methods. There's a lot of code duplication, unfortunately. And model duplication. 2023-03-13 20:51:11 +00:00
gradlew Force java17 build, to align with the kotlin version. 2025-06-11 20:24:36 +01:00
gradlew.bat Force java17 build, to align with the kotlin version. 2025-06-11 20:24:36 +01:00
Planning diagrams.epgz Update tag 2023-11-05 09:32:24 +00:00
README.md Merge pull request #158 from v79/153-bespoke-page-metadata 2026-06-25 19:53:18 +01:00
settings.gradle.kts BROKEN BUILD: 2023-12-09 18:25:39 +00:00

Cantilever

Project Retired

Cantilever is being retired. For further details and an explanation, see https://www.liamjd.org/2026/bascule-and-cantilevers.html.

Project Plan

An AWS Lambda driven static site generator written in Kotlin. Source markdown files will be converted to HTML using the Flexmark-java library, and further processed using handlebars. java, in AWS Lambda functions triggered by PUT events on a source S3 bucket.

The resultant HTML will be written to a separate S3 bucket, configured as a static website.

I haven't quite worked out how to handle site navigation / internal links, but my current thinking is this will be fully browser-rendered in javascript, based on a generated site map file (or database?).

Longer term, I'd like to build several interfaces to creating and editing the markdown files, including a website and an Android phone app.

Technology

I will be using AWS Cloudformation and the CDK to build this project. Although this ties me to AWS as a Cloud provider, I'm largely OK with that limitation.

I am writing primarily in Kotlin as it is my language of choice. I know that JVM-based Lambda functions are not the best performing, especially with start-up times, but I am not sure that performance is a priority for me. Perhaps with GraalVM is can be improved. I considered using a nodeJS/Javascript markdown-to-html Lambda function, but I am not a Javascript developer and have very little experience of the nodeJS ecosystem.

Project structure

The root project will use AWS CDK to build the AWS Cloudformation templates, to set up S3 buckets, Lambda functions, API gateway, and so on. I have a little experience in using CDK for this.

Sub-modules in the project will provide the Lambda functions for HTML generation, templating, and other actions.

  • I have also included a Web front-end in this project, but I think that is a mistake. Front-ends should be in a separate project and a separate repository. While it might be nice to share data classes between front and back ends, I could probably do that by building a separate .jar file from my SharedModels module.
  • The long-term goal is to have Web, Android and Desktop front-ends, but I haven't decided on the approach for that. It makes sense to use Jetpack Compose for the desktop and android interfaces, but I don't think that's a good fit for web. For now, I am writing a web front end using sveltekit. But I really dislike writing Javascript, and Typescript doesn't improve my mood either.

Motivation

This could be considered a successor to my bascule static site generator, a command-line driven application which got weighed down by poor program design and my own lack of experience. I have been keen to rebuild bascule as web-first application for several years, frustrated by my inability to make website updates away from my PC.

Deployment instructions

cdk deploy --context env=<dev|prod> --all to deploy the stack.cdk

Build WebEditor with npm run build

Project Status

There's a lot in flux and it's quite hard to maintain progress. The current goal is to enable Cantiver to build the liamjd.org website in order to fully replace bascule. But that is not going well.

  • liamjd.org migration
    • Being tracked under issue 130
    • Key gap is creating and managing partial templates (which are included using the {{> includes/headLinks}} syntax)
  • Web editor
    • File and folder management/tree is very fragile - work in progress under issue 155 adding a new api route /pages/tree
    • Web session management pretty much non-existent issue 39
    • Currently using Cognito for authentication; I think I'd like to move this to Clerk issue 156
  • Project architecture
    • The fully event-driven architecture is challenging. For instance, creating a new page means POST to an API, which writes a file to S3. Writing to S3 then triggers a lambda function which reads the file, persists its metadata to DynamoDB, and also triggers the markdown-to-HTML conversion lambda function. That then writes an HTML fragment to a separate S3 bucket, which in turn triggers the template processor lambda to build a complete HTML file to the destination bucket
    • Recreating bascule's comprehensive page-navigation functionality will be difficult under this model.
    • Error handling is not good - propagating a backend error to the front end is not easy issue 119 and issue 120
    • Something is very wrong with the SQS event bus; apparently I use about 800,000 messages a month even if the application isn't used at all?