founder reference to the Organization node in the Infrastructure package.
Website Template
A template for the platform's websites, shipping as a complete, buildable
reference site under the placeholder identity Site / site. Generating
a real site takes two steps: create a fresh repository, then run the bootstrap
script to stamp in the display name, slug, and canonical URL.
What's in here
| Path | Role |
|---|---|
Services/Website |
The Hummingbird website service — executable, pages, static assets, Docker/Compose deployment. Its README is the operational reference. |
Packages/Infrastructure |
Shared Hummingbird toolkit: declarative routing, the hardened middlewares, the Page/Asset scaffolding, the head-metadata types (SocialCard, StructuredData, Analytics), and the pre-rendered localized responses. |
Packages/Localization |
String Catalog lookups, Accept-Language negotiation, and the catalog-derived language list. |
Packages/Persistence |
The Fluent data layer: driver selection (in-memory SQLite or PostgreSQL), migration registration, and the readiness probe. |
Packages/Utility |
Dependency-free helpers shared across the services. |
Site.xcodeproj |
The umbrella Xcode project (renamed to your site by bootstrap). |
Scripts/bootstrap |
The generator. Rewrites placeholders, then deletes itself. |
Makefile |
Bootstrap convenience only (make bootstrap); removed by the script. |
All four packages are vendored, not fetched: the service depends on them by relative path, so every generated repository owns its copy and can diverge freely. The trade-off is that engine fixes reach existing sites only by hand.
Requirements
- Swift 6.3 toolchain (every manifest is
swift-tools-version:6.3). - Docker — optional: the containerized run/deploy workflow, the local PostgreSQL
instance (
make db-mount), and the asset-optimization preview. - Hummingbird CLI (
hb) — optional, only formake site-run.
Configuration keys, persistence, testing, and deployment are documented in the service README.
Generating a new site
1. Create a repository from this template
On Gitea, mark this repository as a template once (Settings → Template → “Template”); new sites then come from the “Use this template” button, each with its own history.
No Gitea button? Clone this repo,
rm -rf .git, and continue — bootstrap offers to re-initialise git.
2. Bootstrap
From the new repository's root:
make bootstrap # or: ./Scripts/bootstrap
| Prompt | Example | Rewrites |
|---|---|---|
| Site display name (PascalCase) | Loud |
Server name (LoudWebsite), Loud.xcodeproj, README title. |
| Project slug (lowercase) | loud-ams |
Container owner, database name/user, Compose project name. |
| Canonical site URL (scheme + host) | https://loud.amsterdam |
The Sitemap: reference in robots.txt and the <loc> entry in sitemap.xml. |
- The URL defaults to the reserved
https://site.example.com; leaving it is allowed (the script warns) and can be corrected later in the two crawler files. - Keeping the default display name
Siteis allowed too: the Xcode rename is skipped and the placeholders stay in place. - The script refuses to run outside the repository root and validates the URL before writing, so a rejected value leaves the tree untouched.
- It rewrites in place, renames
Site.xcodeproj, optionally starts a fresh git history, then deletes itself, this file, and the rootMakefile.
3. Fill in the content
- Copy & localization —
Services/Website/Sources/Library/Catalogs/Localizable.xcstrings(English only out of the box) and the landing / 404 pages underServices/Website/Sources/Library. - Static assets —
Services/Website/Resources/Static(CSS, JS, favicon, icons). The manifest'sname/short_nameare empty and are not rewritten by bootstrap. - Head metadata — the pages ship no
summary,canonicalURL,socialCard, orstructuredData: no meta description, no link preview, no JSON-LD until you supply them. See Page metadata. - Analytics — the cookieless Umami tracker ships off
(
analytics.websiteIDis empty, so no third-party script is requested). Enabling it, in order: pointString.Analytics.originat the instance you report to, allow that origin insecurity.contentSecurityPolicy(.env.localalready does;docker-compose.ymlfalls back to a'self'-only policy), then setANALYTICS_WEBSITE_ID.ANALYTICS_DOMAINSlimits which hosts report;ANALYTICS_RECORDERopts into the session recorder. See Analytics. - Secrets — set a real
DATABASE_PASSWORDin a git-ignoredServices/Website/.env; the committed.env.localis an example and defaults the password to the slug. - Domain models — replace the
Persistencepackage's sampleExampleRecord/ExampleRepository. The default driver is in-memory SQLite, migrated on every boot; a real site setsDATABASE_DRIVER=postgresand migrates out of band (swift run Website --database-migrate). See Persistence.
4. Build & run
cd Services/Website
make site-run # watch + rebuild
# or
make site-mount # docker compose up --build
What bootstrap does NOT change
- Organisation (
Röck+Cöde VoF) and XcodeDEVELOPMENT_TEAM— shared across all sites. - The package architecture, middlewares, security headers, and persistence layer (PostgreSQL, behind the in-memory SQLite default).
- The analytics origin:
String.Analytics.originand theContent-Security-Policyin.env.localthat allows it both name the platform's shared Umami instance. Tracking stays off until a site setsANALYTICS_WEBSITE_ID. - The generic
websiteimage name and theMakefiletarget names (site-run,site-mount, …). - The four
Packages/*/README.mdfiles and the package sources — no rewrite line covers them, so whatever site name they carry ships as-is.Packages/Persistence's PostgreSQL integration test defaults itsPOSTGRES_TEST_*credentials tosite.
Maintaining the template itself
Edit and test the reference site directly — it builds and its tests pass with the
Site / site placeholders in place:
(cd Services/Website && make pkg-test) # the service's own two test targets
(cd Packages/Infrastructure && swift test) # and likewise for each vendored package
No external infrastructure is needed: Packages/Persistence's PostgreSQL
integration test skips itself unless POSTGRES_TEST_HOST is set. To run it,
start the database (cd Services/Website && make db-mount), then
POSTGRES_TEST_HOST=127.0.0.1 swift test.
Keep the placeholders intact so the bootstrap rewrites keep matching:
| Placeholder | Appears in |
|---|---|
Site |
Xcode project name and project.pbxproj, the service README title |
SiteWebsite |
String+Constants.swift, .env.local, docker-compose.yml, the service README |
site |
String+Constants.swift defaults, HOST_OWNER / DATABASE_* in .env.local and both Compose files, the site-platform Compose project, the Makefile db-shell fallbacks |
https://site.example.com |
Resources/Static/robots.txt, Resources/Static/sitemap.xml |
A new site-specific value needs a placeholder from this set and a matching
rewrite line in Scripts/bootstrap; anything else ships verbatim into every
generated site.