This repository is a small Rust web application designed so that every change flows from commit to production without manual intervention.
Infrastructure, CI/CD, and security are defined alongside the code and evolve with it as a single system. There are no hidden steps and no out-of-band processes.
The focus is not the technology itself, but the shape of the system: making correct change the path of least resistance.
- Rust for building and
running the app locally. Install with
rustup, which providescargoandrustc. - devloop for the primary
local development workflow. Install with
cargo install --git https://github.com/pasunboneleve/devloop.git. - cloudflared for the shareable public tunnel used to validate social cards during local development. Install it from Cloudflare's package or binary distribution for your platform.
- Node.js or Bun to install the Tailwind CLI.
- Tailwind CLI
for CSS compilation during local development. Install with
npm install -g @tailwindcss/clior the equivalent Bun workflow. - direnv to auto-load repo environment variables.
Install it from your system package manager and run
direnv allowin the repo. - bacon only if you want the fallback
direct-repo workflow instead of
devloop. Install withcargo install bacon. - Docker for containerization
gcloudCLI configured with your GCP project- OpenTofu/Terraform for infrastructure management
-
Prepare deployment variables if you need them locally:
cp .env.template .env # Edit .env with your actual GCP project valuesThe checked-in
.envrcdoes not automatically load.env. Use this file as a template for manual exports or extend your localdirenvsetup if you want it loaded automatically. -
Configure infrastructure variables:
cp infra/prod.tfvars.template infra/prod.tfvars # Edit infra/prod.tfvars with your GCP project details📝 Template file:
infra/prod.tfvars.template -
Create GitHub Personal Access Token:
- Go to GitHub Settings > Developer settings > Personal access tokens > Tokens (classic)
- Click "Generate new token (classic)"
- Set Expiration as needed (e.g., 90 days)
- Select repo scope (full control of private repositories)
- Click "Generate token" and copy the token
- Add the token to your
infra/prod.tfvarsfile asgithub_token
⚠️ Important: Store this token securely - GitHub won't show it again!
Recommended:
- use the external
devloopsupervisor for the full Rust + CSS + content + cloudflared workflow - use the repo-local
devloop.tomlas the working client config - keep repo-specific helper scripts in
scripts/build-css.shandscripts/current-browser-path.sh
Primary local workflow:
direnv allow
devloop runThat gives you one supervised loop for Rust rebuilds, content-triggered server restarts, CSS recompilation, browser refresh notifications, and copy/paste-ready public post URLs for card validation.
Fallback direct-repo workflow:
# Terminal 1: Rust server with restart-on-change
bacon run
# Terminal 2: one-shot CSS build when needed
./scripts/build-css.sh
# Check and format code
cargo check
cargo fmt
cargo clippy- Blog posts: Add Markdown files in
content/posts/as<slug>.md - Access posts: Visit
/posts/<slug>in your browser - Site header: Customize
content/banner.htmlfor navigation and branding
├── src/ # Axum app, content loading, markdown, metadata
├── content/
│ ├── banner.html # Site header with navigation
│ ├── layout.html # Shared page shell
│ ├── home.md # Home page content
│ └── posts/
│ └── <slug>.md # Blog post content
├── infra/ # OpenTofu/Terraform infrastructure
├── .github/workflows/ # CI/CD automation
└── Dockerfile # Multi-stage container build
This project implements a cloud-native, security-first architecture:
- Application: Modular Rust web server using Axum
- Content: File-based blog posts in Markdown format
- Rendering: Markdown, KaTeX math, Mermaid diagrams, and social metadata
- Infrastructure: Fully managed with OpenTofu/Terraform
- Deployment: Automated CI/CD with GitHub Actions and Workload Identity Federation
- Security: Least-privilege service accounts and non-root containers
- DNS: Managed through Google Cloud DNS with OpenTofu
At startup, the app loads site configuration, HTML templates, the home
page, the 404 page, and post Markdown from content/. Requests are
served from that loaded content plus static assets under
content/static/.
To deploy your own instance:
- Fork this repository
- Configure infrastructure:
cp infra/prod.tfvars.template infra/prod.tfvars # Edit infra/prod.tfvars with your GCP project details and GitHub token - Deploy infrastructure:
This automatically:
cd infra tofu init tofu apply -var-file="prod.tfvars"
- Sets up GCP Workload Identity Federation
- Configures all required GitHub repository secrets
- Provisions infrastructure components
- Deploy: Push to main branch triggers automatic deployment
The deploy workflow uses two build modes:
- Full build: Runs when application/infrastructure code changes. Builds and pushes:
:${GITHUB_SHA}(deploy image):app-base(binary/runtime base image)
- Content-only build: Runs when all changed files are under
content/.- Reuses
:app-base - Builds a lightweight overlay image that only updates
/app/content - Pushes
:${GITHUB_SHA}and deploys it
- Reuses
If :app-base does not exist yet, the workflow automatically falls back to a full build and publishes it for future content-only deployments.
- Security Architecture - Service accounts, IAM, and security best practices
- Infrastructure Guide - Deployment, DNS, and infrastructure management
✅ Infrastructure as Code - Everything managed with OpenTofu
✅ Secure CI/CD - GitHub Actions with Workload Identity Federation
✅ Least Privilege - Dedicated service accounts with minimal permissions
✅ Automated DNS - Domain management through code
✅ Container Security - Multi-stage builds, non-root containers
✅ Observability - Structured logging with tracing
✅ Faster content deploys - Content-only changes reuse a prebuilt app base image
PORT- Server port (default: 8080, required for Cloud Run)RUST_LOG- Log level (default: "info")
This project is open source and available under the MIT License.