No description
  • Lua 80.2%
  • Go 6.3%
  • Rust 5.1%
  • Shell 4.8%
  • CSS 0.9%
  • Other 2.7%
Find a file
72043bd2 1fd80b7398
Some checks failed
Tests / lua-tests (push) Failing after 8s
compost: rename --with-groundwork → --with-perennials; scope display to true perennials
"Groundwork" over-claimed: it's the whole structural kind (apply +
verify, no deploy), most of which is on-node (qos, os_updates, backup
timer) and dies with the nodes. The compost flag only ever tears down
the DURABLE, provider-side groundwork — buckets today, floating IPs /
held volumes / owned DNS zones tomorrow — the things that outlive a
compost. Those are the perennials; the flag is now named for what it
actually destroys.

Mechanically a perennial == a groundwork with a destroy() function
(on-node groundwork carries none). The compost section now FILTERS its
enumeration on destroy(), so the "Perennials" header, the per-item
destroy/keep lines, and the count describe only true perennials — the
rename had turned the old vague "Keeping qos" line under a "Groundwork"
header into an actual misstatement under a "Perennials" one.

No groundwork participant changed — not apply, verify, or destroy. What
gets torn down is identical (object_storage_bucket is still the only
thing with a destroy()). Only the flag name and which items earn a line
under the section header changed. Suite 1300 green.
2026-06-16 06:20:40 +00:00
.forgejo/workflows press: PUBLISH_TOKEN, not FORGEJO_TOKEN (reserved-prefix block) 2026-05-16 15:34:22 +00:00
ci builder: deploy CGI requires Bearer auth + image origin validation 2026-05-03 04:33:43 +00:00
garden compost: rename --with-groundwork → --with-perennials; scope display to true perennials 2026-06-16 06:20:40 +00:00
keepers Rename stewards → keepers 2026-04-19 01:01:58 +00:00
lua compost: rename --with-groundwork → --with-perennials; scope display to true perennials 2026-06-16 06:20:40 +00:00
plant docs: stop asserting Hetzner S3 keys are region-scoped — unconfirmed 2026-05-21 20:44:58 +00:00
seeds restore image descriptions in seeds 2026-04-28 23:47:26 +00:00
weeds domains: move per-garden under .gardens/<g>/domains, positional ownership 2026-05-20 22:10:10 +00:00
.dev-keys.example Add --dev flag for installing dev SSH keys 2026-01-16 02:49:34 +00:00
.gitignore domains: move per-garden under .gardens/<g>/domains, positional ownership 2026-05-20 22:10:10 +00:00
.pressignore domains: move per-garden under .gardens/<g>/domains, positional ownership 2026-05-20 22:10:10 +00:00
_AUDIT-2026-06-13.md docs: prune the graveyard — retire one done plan, fix dead pointers 2026-06-15 16:50:05 +00:00
_BUILDING.md rename: ctx → context throughout Lua 2026-05-09 18:20:54 +00:00
_BYOH.md converge: remove dead firewall + rename_node methods across soil providers (B6) 2026-06-14 06:01:57 +00:00
_CONVERGENCE.md docs: sync _CONVERGENCE status to ground truth (smenor.phd); fix stale plant guidance 2026-06-15 20:36:03 +00:00
_DIGITALOCEAN.md Seed template: drop combination count, use FQDN, rename clone dir 2026-03-28 03:26:51 +00:00
_GARDEN.md Remove startup framing from docs, fix plant.lua arg parsing 2026-04-12 18:45:26 +00:00
_GOTCHAS.md sync docs to reality 2026-04-28 08:01:53 +00:00
_LEGAL.md move governance documents to rhizome/governance 2026-04-26 05:34:51 +00:00
_MIRRORING.md Mirroring: pull-mirror topology, trust policy, upstream chain 2026-04-25 03:12:02 +00:00
_PLAN-assets.md plan: garden abstraction design + planning doc audit 2026-05-03 18:03:18 +00:00
_PLAN-constraint-restore.md compost: backup-before-destroy via garden-scoped snapshot_all (Slice 2) 2026-06-09 23:49:46 +00:00
_PLAN-externals.md docs: mark externals slice 4, DNS manifest, walk fix as done 2026-05-25 09:25:32 +00:00
_PLAN-garden-abstraction.md docs: delete _PLAN-capability-deploy.md (work fully landed) 2026-05-15 07:43:05 +00:00
_PLAN-garden-migration.md migration: Phase 2 DONE — .garden migrated onto the planner, zero data loss 2026-06-13 23:27:01 +00:00
_PLAN-groundwork.md compost: rename --with-groundwork → --with-perennials; scope display to true perennials 2026-06-16 06:20:40 +00:00
_PLAN-node-identity.md converge: remove verified dead code (B1,B2,B5,B7 from audit) 2026-06-14 06:01:57 +00:00
_PLAN-node-orchestration.md doc: capture the clock-axis design, the two-writers trap, and why it waited 2026-06-16 04:45:44 +00:00
_PLAN-qos.md docs: record os_updates fold + partial-reachability across the three plans 2026-06-16 00:54:29 +00:00
_PLAN-substrate-dissolution.md docs: record os_updates fold + partial-reachability across the three plans 2026-06-16 00:54:29 +00:00
_PLAN-test-suite.md doc: scoped plan for test-suite health (split, de-flake, tier) 2026-06-14 22:28:43 +00:00
_PRESS.md Remove startup framing from docs, fix plant.lua arg parsing 2026-04-12 18:45:26 +00:00
_QUICKSTART.md domains: move per-garden under .gardens/<g>/domains, positional ownership 2026-05-20 22:10:10 +00:00
_RECOVERY.md sync docs to reality 2026-04-28 08:01:53 +00:00
_SMELLS.md docs: prune the graveyard — retire one done plan, fix dead pointers 2026-06-15 16:50:05 +00:00
_STATE-MODEL.md dns: retire the two design throwaways — code is the truth now 2026-06-08 09:39:44 +00:00
_TODO.md todo: git-db false-banner has a verify-side twin; both want one topology-aware fix 2026-06-13 09:38:10 +00:00
_TRUST-SURFACE.md compost: rename --with-groundwork → --with-perennials; scope display to true perennials 2026-06-16 06:20:40 +00:00
_WELL-KNOWN.md well-known convention, status banner, env export fix 2026-04-09 19:37:28 +00:00
about.md Rewrite index opening, drop stale 7k line count everywhere 2026-03-27 17:59:50 +00:00
config.lua.example sites/press_sites: resolve per-garden, close multi-garden bleed 2026-05-31 21:57:19 +00:00
index.md Update index.md 2026-06-14 03:05:41 +00:00
LICENSE.txt Update LICENSE.txt 2026-01-12 06:00:28 +00:00
plant.lua os_updates: fold into a groundwork participant — state-driven, self-healing 2026-06-16 00:53:10 +00:00
prepare.sh Hetzner default in plant guide + DigitalOcean alternative doc 2026-03-26 05:22:03 +00:00
privacy.md privacy: backup retention split, identity privacy link, policy additions 2026-04-09 01:47:56 +00:00
README.md domains: move per-garden under .gardens/<g>/domains, positional ownership 2026-05-20 22:10:10 +00:00
social-contract.md Update social-contract.md 2026-04-05 00:24:45 +00:00
tend.lua qos: tend qos command surface 2026-06-14 21:34:30 +00:00
test.lua state: classify SSH-unreachable nodes as rebooting vs down (stuck-vs-transient) 2026-06-16 01:21:37 +00:00

Sweetgrass

Community infrastructure you can understand, modify, and share.


What this is

Sweetgrass is a set of tools for planting and tending your own corner of the internet — websites, publishing, email, git hosting, video calls — on small, affordable servers that you and your friends control.

Think of it like a community garden. You bring the seeds (your domain, your writing, your projects). The tools help you plant and tend. The infrastructure takes care of itself most of the time, and when it doesn't, you can read the code and fix it. All of it.

Where we are

We're building this right now. The tools work — we've planted multiple gardens and each planting gets smoother. Every one teaches us something new.

This is a barn raising, not a finished house. If that appeals to you — if you want to build something with people instead of buying something from a company — you're welcome here.

What you get

Three scripts named for what they do:

plant.lua    Create infrastructure from scratch
tend.lua     Deploy, update, backup, maintain
compost.lua  Tear everything down (safely, backups first)

The cycle is: plant, tend, compost, plant. Data survives destruction by default.

When you plant, you get a self-organizing cluster with:

your site ── static, always on
  identity ── passkeys and single sign-on
    git ──── your repos (Forgejo)
      press ── push markdown, get a website
        meet ── video conferencing (Jitsi)
  mail ──── email + webmail (docker-mailserver)

Automatic SSL. Encrypted daily backups. Rolling updates with zero downtime. Self-healing via Docker Swarm. The core stack (static through press) fits on a single small server. A 3-node cluster gives you failover and room for meet.

Two ways in

However you join, the social contract is the same: resilience through interdependence, no extraction, invite-based trust, portability.

Plant your own. Get together with friends. Pick your domains. Set up a few small servers. This is the way sweetgrass is meant to grow — many small gardens tended by the people who use them, sharing improvements through the same tools. See QUICKSTART.md.

Grow in the community garden. If you don't want to manage your own infrastructure, you're welcome in ours. It's free, it's invite-only, and your stuff is yours — you can transplant to your own garden or a friend's whenever you want. See GARDEN.md.

The philosophy

No magic. Every operation is a readable Lua script. No hidden state, no declarative DSLs, no frameworks. Just scripts that do things you could do by hand, reliably.

No lock-in. Your domains transplant between gardens. Your content is markdown files in git repos. Your infrastructure is standard Docker on standard servers. Nothing traps you here.

No extraction. We want you not to pay us. We want you to plant your own garden with friends and share what you grow. The code is AGPL — improve it, share it back. The more gardens, the more resilient every garden becomes.

Boring on purpose. The infrastructure is meant to be dull. Pages are simple and lightweight. Servers quietly do what they're supposed to. The most exciting thing about sweetgrass is that it works and you can understand why.

By planting sweetgrass, you're doing something quietly subversive. You're building infrastructure that's democratic by design and resistant to the patterns that turn tools into platforms and communities into products. It doesn't look like much. That's the point.

How it works

The tools call standard unix utilities — ssh, rsync, curl — plus your cloud provider's CLI (hcloud, doctl, or none for BYOH). Docker Swarm handles orchestration. We picked Swarm over Kubernetes because you can understand Swarm in an afternoon.

plant.lua              Create and configure infrastructure
tend.lua               Deploy, update, backup, maintain
lua/compost.lua        Full teardown (backups first)

lua/deploy/            Stack deployment, template rendering
lua/backup/            Encrypted backup to S3-compatible storage (restic)
lua/maintain/          Rolling updates, health, scaling
lua/intake.lua         SSH/rsync gateway for static content
lua/roots/             Swarm, DNS, and network primitives
lua/soil/              Provider layer (Hetzner, DigitalOcean, BYOH)

garden/<name>/conf.lua             Stack definitions (self-describing)
.gardens/<g>/domains/<d>.lua       Domain configurations (per garden)

For the full technical story — why Lua, why Swarm, how the pieces fit, what we've learned — see BUILDING.md.

For the publishing system at the heart of it, see PRESS.md.

The name

Sweetgrass is a plant sacred to many Indigenous peoples of North America. It spreads through underground rhizomes, connecting in ways you can't see, binding the soil together. You can't eradicate it once it's established.

We chose the name because that's what we want this to be. Infrastructure that spreads through communities, that's impossible to shut down, that binds people together. Many small gardens, connected underground.

Contributing

This is community infrastructure. If you see something that could be better, make it better. The whole thing is readable. Start with BUILDING.md.

License

AGPL-3.0 — If you improve it, share it back.


Infrastructure doesn't have to be complicated. It doesn't have to be corporate. It can be ours.