chadtmiller.com/knotless
git that lives in your Atmosphere account
1
10
deploy: knotless.dev's DNS is kept in the infra repository
deploy/.gitignore+0−5
| @@ -1,6 +1 @@ | ||
.terraform/ | ||
*.tfstate | ||
*.tfstate.* | ||
*.tfvars | ||
!*.tfvars.example | ||
local.env | ||
deploy/.terraform.lock.hcldeleted+0−24
| @@ -1,24 +0,0 @@ | ||
# This file is maintained automatically by "terraform init". | ||
# Manual edits may be lost in future updates. | ||
| ||
provider "registry.terraform.io/cloudflare/cloudflare" { | ||
version = "4.52.9" | ||
constraints = "~> 4.0" | ||
hashes = [ | ||
"h1:sIoAt5vaxgFBrst1Esfm7ixQQ/T7/JNPZu0m4bw+Bc4=", | ||
"zh:0826cad9b57368f06466ec3e1f766086f44a373288753e144f681ddc0e61ab49", | ||
"zh:15bf8d8abdcd202652466f3ef91110f143a7fad13c6173c7180e3dc0dbe9254b", | ||
"zh:1a60d1d2e55a17942902b1ccc91e8f6cb150c035843d3eefc074f1291a0e7d5f", | ||
"zh:23373a63fe06bbfa90c6c105c08429031719bedafa2458973cbdad32a384b5a7", | ||
"zh:256a97b79cedee2575002f77ac3bf18888ce0fc97170ae8a357db406bb0a88e3", | ||
"zh:3dd9f93c167cd59491ae77781032491d36a80843a1e44f9ee68b41b3f8e3ab8e", | ||
"zh:5182d2d61930dd83584d8feb56102614f80ddeec532b939b58085ad74b796a6e", | ||
"zh:5e3ca1888ba3b3bd8abd0f7dfcdd82fb5a8819affee899ed7fd891a33c17e0cb", | ||
"zh:7a3ce2119e39fb3a90a6a459a26863851309dbc92e0735336cbc98692539cb4d", | ||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", | ||
"zh:c095dc680a646cc078c10bb0551e2ea8f08e7abdbcbc58cfd332c56b58788631", | ||
"zh:d2400e010e069c19916c99136e200cf873eba6db2b18d3eb9541b38eb1f565fa", | ||
"zh:dbc2f19f09aaefbd69334005bca2bcb0c9b690de1ed76f7ba97ce8e59a6e85df", | ||
"zh:e685225f432da3e23b8717848de8c0ad0e22363450b2ac92cd24a56739ab5e9b", | ||
] | ||
} | ||
deploy/main.tfdeleted+0−81
| @@ -1,81 +0,0 @@ | ||
# The names knotless-serve answers at: the apex, and www, which the proxy | ||
# redirects to it. | ||
| ||
data "cloudflare_zone" "main" { | ||
name = var.zone | ||
} | ||
| ||
# DNS-only, not proxied: the gateway is the zone's did:web, whose document | ||
# and OAuth client metadata PDSes fetch from the origin, and git clones want | ||
# bodies Cloudflare's proxy would cap. The reverse proxy on the host holds | ||
# the certificate. | ||
resource "cloudflare_record" "apex_a" { | ||
zone_id = data.cloudflare_zone.main.id | ||
name = "@" | ||
type = "A" | ||
content = var.host_ipv4 | ||
ttl = 1 | ||
proxied = false | ||
comment = "knotless-serve" | ||
} | ||
| ||
resource "cloudflare_record" "apex_aaaa" { | ||
count = var.host_ipv6 == "" ? 0 : 1 | ||
zone_id = data.cloudflare_zone.main.id | ||
name = "@" | ||
type = "AAAA" | ||
content = var.host_ipv6 | ||
ttl = 1 | ||
proxied = false | ||
comment = "knotless-serve" | ||
} | ||
| ||
resource "cloudflare_record" "www" { | ||
zone_id = data.cloudflare_zone.main.id | ||
name = "www" | ||
type = "CNAME" | ||
content = var.zone | ||
ttl = 1 | ||
proxied = false | ||
comment = "redirects to the apex" | ||
} | ||
| ||
# The handles of the service's own accounts under the zone, each a | ||
# `_atproto.<name>` TXT record naming its DID: the runner hosts', the prod | ||
# host's runner's (ci), the lexicon account's, the reviewer's, the triager's. A handle whose name also serves | ||
# /.well-known/atproto-did answers there first; these answer any resolver | ||
# that reads DNS. The `_lexicon` records are publish-lexicons.mjs's. | ||
resource "cloudflare_record" "handle" { | ||
for_each = var.handles | ||
zone_id = data.cloudflare_zone.main.id | ||
name = "_atproto.${each.key}" | ||
type = "TXT" | ||
content = "\"did=${each.value}\"" | ||
ttl = 300 | ||
proxied = false | ||
comment = "atproto handle ${each.key}.${var.zone}" | ||
} | ||
| ||
# The runner host, apart from this one, where its bot logs in at | ||
# /oauth/bot through its Caddy (deploy/runner-host). | ||
resource "cloudflare_record" "runner_a" { | ||
count = var.runner_ipv4 == "" ? 0 : 1 | ||
zone_id = data.cloudflare_zone.main.id | ||
name = "runner" | ||
type = "A" | ||
content = var.runner_ipv4 | ||
ttl = 300 | ||
proxied = false | ||
comment = "the knotless runner host: its bot login, through Caddy" | ||
} | ||
| ||
resource "cloudflare_record" "runner_aaaa" { | ||
count = var.runner_ipv6 == "" ? 0 : 1 | ||
zone_id = data.cloudflare_zone.main.id | ||
name = "runner" | ||
type = "AAAA" | ||
content = var.runner_ipv6 | ||
ttl = 300 | ||
proxied = false | ||
comment = "the knotless runner host: its bot login, through Caddy" | ||
} | ||
deploy/outputs.tfdeleted+0−7
| @@ -1,7 +0,0 @@ | ||
output "url" { | ||
value = "https://${var.zone}" | ||
} | ||
| ||
output "gateway_did" { | ||
value = "did:web:${var.zone}" | ||
} | ||
deploy/README.md+8−8
| @@ -12,20 +12,20 @@ One Docker host, a reverse proxy in front of it for TLS, and a domain. | ||
| `rollout-test.sh` | tries `rollout.sh` on a local docker behind a Caddy, counting failed requests | | ||
| `release.sh` | builds the client for macOS and Linux here and puts the builds on the host, for `/install.sh` | | ||
| `setup.sh` | makes a fresh Ubuntu 24.04 host ready for `deploy.sh`: docker, a Caddy of its own for the domain, a firewall, upgrades and pruning | | ||
| `*.tf` | the domain's DNS records at Cloudflare: the host, the runner host, and the handles of the service's own accounts (`handles`) | | ||
| ||
```sh | ||
deploy/setup.sh root@host git.example.com # once, on a fresh host | ||
just deploy root@host git.example.com # build on the host and restart; or put both in deploy/local.env | ||
cd deploy && terraform apply # DNS, once (terraform.tfvars from the .example) | ||
``` | ||
| ||
The Cloudflare token is allowed from one IPv4 address: where terraform | ||
would reach Cloudflare over IPv6, run it through a proxy that dials IPv4 | ||
(`HTTPS_PROXY`). A worker that serves an account's handle at its own name, | ||
as the reviewer's pds.js does at `review.knotless.dev`, holds that name as a | ||
custom domain in its `wrangler.toml`, which makes the record: terraform | ||
keeps only its `_atproto` TXT. | ||
The domain needs an A (and AAAA) record for the host, and an `_atproto` | ||
TXT record for each of the service's own accounts with a handle under it. | ||
knotless.dev's are kept by the `stacks/edge` Terraform in the infra | ||
repository, beside `stacks/knotless`, which makes the host. A worker that | ||
serves an account's handle at its own name, as the reviewer's pds.js does | ||
at `review.knotless.dev`, holds that name as a custom domain in its | ||
`wrangler.toml`, which makes the record: the infra repository keeps only | ||
its `_atproto` TXT. | ||
| ||
### From a push to main to the site running it | ||
| ||
deploy/terraform.tfvars.exampledeleted+0−14
| @@ -1,14 +0,0 @@ | ||
cloudflare_api_token = "…" # Zone:DNS:Edit on the zone | ||
zone = "example.com" | ||
host_ipv4 = "203.0.113.10" | ||
host_ipv6 = "2001:db8::10" | ||
| ||
# Optional: the service's own accounts' handles, and the runner host. | ||
handles = { | ||
runner = "did:plc:…" | ||
lexicons = "did:plc:…" | ||
review = "did:plc:…" | ||
ci = "did:plc:…" | ||
} | ||
runner_ipv4 = "203.0.113.20" | ||
runner_ipv6 = "2001:db8::20" | ||
deploy/variables.tfdeleted+0−39
| @@ -1,39 +0,0 @@ | ||
variable "cloudflare_api_token" { | ||
description = "A token with Zone:DNS:Edit on the zone." | ||
type = string | ||
sensitive = true | ||
} | ||
| ||
variable "zone" { | ||
description = "The Cloudflare zone knotless-serve answers at the apex of." | ||
type = string | ||
} | ||
| ||
variable "host_ipv4" { | ||
description = "The host's IPv4 address, where the reverse proxy listens." | ||
type = string | ||
} | ||
| ||
variable "host_ipv6" { | ||
description = "The host's IPv6 address, or empty for none." | ||
type = string | ||
default = "" | ||
} | ||
| ||
variable "handles" { | ||
description = "Accounts with a handle under the zone, by name: runner = \"did:plc:…\" gives runner.<zone>." | ||
type = map(string) | ||
default = {} | ||
} | ||
| ||
variable "runner_ipv4" { | ||
description = "The runner host's IPv4 address, for runner.<zone>, or empty for none." | ||
type = string | ||
default = "" | ||
} | ||
| ||
variable "runner_ipv6" { | ||
description = "The runner host's IPv6 address, or empty for none." | ||
type = string | ||
default = "" | ||
} | ||
deploy/versions.tfdeleted+0−14
| @@ -1,14 +0,0 @@ | ||
terraform { | ||
required_version = ">= 1.5" | ||
| ||
required_providers { | ||
cloudflare = { | ||
source = "cloudflare/cloudflare" | ||
version = "~> 4.0" | ||
} | ||
} | ||
} | ||
| ||
provider "cloudflare" { | ||
api_token = var.cloudflare_api_token | ||
} | ||