Pulumi code for my server setup
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add pds ascii art and age assurance bypass

+124 -72
services/haring/atproto/knot.ts services/haring/atproto/tangled/knot.ts
-65
services/haring/atproto/pds.ts
··· 1 - import { DnsRecord } from "@pulumi/cloudflare"; 2 - import { getEnv } from "~lib/env"; 3 - import { fetchRelays } from "~lib/relay-hosts"; 4 - import { confMount } from "~lib/service/mounts"; 5 - import { ContainerService } from "~lib/service/service"; 6 - 7 - export const pdsService = new ContainerService("pds", { 8 - image: "ghcr.io/bluesky-social/pds", 9 - servicePort: 3000, 10 - mounts: [confMount("pds", "/pds")], 11 - envs: { 12 - PDS_HOSTNAME: "pds.bas.sh", 13 - PDS_JWT_SECRET: getEnv("PDS_JWT_SECRET"), 14 - PDS_ADMIN_PASSWORD: getEnv("PDS_ADMIN_PASSWORD"), 15 - PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: getEnv("PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX"), 16 - PDS_DATA_DIRECTORY: "/pds", 17 - PDS_BLOBSTORE_DISK_LOCATION: "/pds/blocks", 18 - PDS_BLOB_UPLOAD_LIMIT: "2147483648", 19 - PDS_RATE_LIMITS_ENABLED: false, 20 - PDS_DID_PLC_URL: "https://plc.directory", 21 - PDS_BSKY_APP_VIEW_URL: "https://api.bsky.app", 22 - PDS_BSKY_APP_VIEW_DID: "did:web:api.bsky.app", 23 - PDS_REPORT_SERVICE_URL: "https://mod.bsky.app", 24 - PDS_REPORT_SERVICE_DID: "did:plc:ar7c4by46qjdydhdevvrndac", 25 - PDS_CRAWLERS: fetchRelays(), 26 - LOG_ENABLED: "true", 27 - PDS_EMAIL_SMTP_URL: getEnv("PDS_SMTP_AUTH_URI"), 28 - PDS_EMAIL_FROM_ADDRESS: "PDS <pds@bas.sh>", 29 - }, 30 - labels: { 31 - "traefik.http.middlewares.pds-favicon.redirectregex.regex": 32 - "^https://pds\\.bas\\.sh/favicon\\.ico$", 33 - "traefik.http.middlewares.pds-favicon.redirectregex.replacement": 34 - "https://tranquil.bas.sh/favicon.ico", 35 - "traefik.http.routers.pds-favicon.entrypoints": "https", 36 - "traefik.http.routers.pds-favicon.rule": "Host(`pds.bas.sh`) && Path(`/favicon.ico`)", 37 - "traefik.http.routers.pds-favicon.middlewares": "cloudflare,pds-favicon", 38 - 39 - "traefik.http.middlewares.pds-age-assurance.plugin.staticresponse.statuscode": "200", 40 - "traefik.http.middlewares.pds-age-assurance.plugin.staticresponse.body": 41 - '{"state":{"lastInitiatedAt":"2025-07-14T14:22:43.912Z","status":"assured","access":"full"},"metadata":{"accountCreatedAt":"2022-11-17T00:35:16.391Z"}}', 42 - "traefik.http.routers.pds-age-assurance.rule": 43 - "Host(`pds.bas.sh`) && Path(`/xrpc/app.bsky.ageassurance.getState`)", 44 - "traefik.http.routers.pds-age-assurance.entrypoints": "https", 45 - "traefik.http.routers.pds-age-assurance.middlewares": "cloudflare,cors,json,pds-age-assurance", 46 - }, 47 - }); 48 - 49 - export const pdsDnsRecord = new DnsRecord("pds", { 50 - zoneId: getEnv("CLOUDFLARE_ZONE_ID"), 51 - name: "pds.bas.sh", 52 - ttl: 1, 53 - type: "CNAME", 54 - content: "haring.bas.sh", 55 - proxied: false, 56 - }); 57 - 58 - export const pdsWildcardDnsRecord = new DnsRecord("pds-wildcard", { 59 - zoneId: getEnv("CLOUDFLARE_ZONE_ID"), 60 - name: "*.pds.bas.sh", 61 - ttl: 1, 62 - type: "CNAME", 63 - content: "pds.bas.sh", 64 - proxied: false, 65 - });
+23
services/haring/atproto/pds/motd.txt
··· 1 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀ 2 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⠙⠻⢶⣄⡀⠀⠀⠀⢀⣤⠶⠛⠛⡇⠀⠀⠀ 3 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣇⠀⠀⣙⣿⣦⣤⣴⣿⣁⠀⠀⣸⠇⠀⠀⠀ 4 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣡⣾⣿⣿⣿⣿⣿⣿⣿⣷⣌⠋⠀⠀⠀⠀ 5 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣷⣄⡈⢻⣿⡟⢁⣠⣾⣿⣦⠀⠀⠀⠀ 6 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣿⣿⣿⠘⣿⠃⣿⣿⣿⣿⡏⠀⠀⠀⠀ 7 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠈⠛⣰⠿⣆⠛⠁⠀⡀⠀⠀⠀⠀⠀ 8 + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣦⠀⠘⠛⠋⠀⣴⣿⠁⠀⠀⠀⠀⠀ 9 + ⠀⠀⠀⠀⠀⠀⣀⣤⣶⣾⣿⣿⣿⣿⡇⠀⠀⠀⢸⣿⣏⠀⠀⠀⠀⠀⠀ 10 + ⠀⠀⠀⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠀⠀⠀⠾⢿⣿⠀⠀⠀⠀⠀⠀ 11 + ⠀⣠⣿⣿⣿⣿⣿⣿⡿⠟⠋⣁⣠⣤⣤⡶⠶⠶⣤⣄⠈⠀⠀⠀⠀⠀⠀ 12 + ⢰⣿⣿⣮⣉⣉⣉⣤⣴⣶⣿⣿⣋⡥⠄⠀⠀⠀⠀⠉⢻⣄⠀⠀⠀⠀⠀ 13 + ⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣋⣁⣤⣀⣀⣤⣤⣤⣤⣄⣿⡄⠀⠀⠀⠀ 14 + ⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠛⠋⠉⠁⠀⠀⠀⠀⠈⠛⠃⠀⠀⠀⠀ 15 + ⠀⠀⠀⠀⠉⠉⠉⠉⠉ 16 + 17 + This is an AT Protocol Personal Data Server (aka, an atproto PDS) 18 + 19 + Most API routes are under /xrpc/ 20 + 21 + Code: https://github.com/bluesky-social/atproto 22 + Self-Host: https://github.com/bluesky-social/pds 23 + Protocol: https://atproto.com
+94
services/haring/atproto/pds/pds.ts
··· 1 + import { DnsRecord } from "@pulumi/cloudflare"; 2 + import { remote } from "@pulumi/command"; 3 + import { asset, interpolate, output } from "@pulumi/pulumi"; 4 + import path from "path"; 5 + import { getEnv } from "~lib/env"; 6 + import { fetchRelays } from "~lib/relay-hosts"; 7 + import { confMount, ssdcacheMount } from "~lib/service/mounts"; 8 + import { ContainerService, defaultConnection } from "~lib/service/service"; 9 + 10 + export const pdsService = new ContainerService("pds", { 11 + image: "ghcr.io/bluesky-social/pds", 12 + servicePort: 3000, 13 + mounts: [confMount("pds", "/pds")], 14 + envs: { 15 + PDS_HOSTNAME: "pds.bas.sh", 16 + PDS_JWT_SECRET: getEnv("PDS_JWT_SECRET"), 17 + PDS_ADMIN_PASSWORD: getEnv("PDS_ADMIN_PASSWORD"), 18 + PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: getEnv("PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX"), 19 + PDS_DATA_DIRECTORY: "/pds", 20 + PDS_BLOBSTORE_DISK_LOCATION: "/pds/blocks", 21 + PDS_BLOB_UPLOAD_LIMIT: "2147483648", 22 + PDS_RATE_LIMITS_ENABLED: false, 23 + PDS_DID_PLC_URL: "https://plc.directory", 24 + PDS_BSKY_APP_VIEW_URL: "https://api.bsky.app", 25 + PDS_BSKY_APP_VIEW_DID: "did:web:api.bsky.app", 26 + PDS_REPORT_SERVICE_URL: "https://mod.bsky.app", 27 + PDS_REPORT_SERVICE_DID: "did:plc:ar7c4by46qjdydhdevvrndac", 28 + PDS_CRAWLERS: fetchRelays(), 29 + LOG_ENABLED: "true", 30 + PDS_EMAIL_SMTP_URL: getEnv("PDS_SMTP_AUTH_URI"), 31 + PDS_EMAIL_FROM_ADDRESS: "PDS <pds@bas.sh>", 32 + }, 33 + labels: { 34 + "traefik.http.middlewares.pds-favicon.redirectregex.regex": 35 + "^https://pds\\.bas\\.sh/favicon\\.ico$", 36 + "traefik.http.middlewares.pds-favicon.redirectregex.replacement": 37 + "https://tranquil.bas.sh/favicon.ico", 38 + "traefik.http.routers.pds-favicon.entrypoints": "https", 39 + "traefik.http.routers.pds-favicon.rule": "Host(`pds.bas.sh`) && Path(`/favicon.ico`)", 40 + "traefik.http.routers.pds-favicon.middlewares": "cloudflare,pds-favicon", 41 + }, 42 + }); 43 + 44 + const webMount = ssdcacheMount("web/pds", "/var/www"); 45 + 46 + const MOTD_FILE_NAME = "motd.txt"; 47 + const motdFile = new asset.FileAsset(path.join(import.meta.dirname, MOTD_FILE_NAME)); 48 + const copyMotdFile = new remote.CopyToRemote("pds-motd", { 49 + connection: defaultConnection, 50 + source: motdFile, 51 + remotePath: output(webMount.source).apply((dir) => path.join(dir, MOTD_FILE_NAME)), 52 + }); 53 + 54 + const CADDYFILE = ` 55 + :80 { 56 + handle /xrpc/app.bsky.ageassurance.getState { 57 + respond {"state":{"lastInitiatedAt":"2025-07-14T14:22:43.912Z","status":"assured","access":"full"},"metadata":{"accountCreatedAt":"2022-11-17T00:35:16.391Z"}} 200 58 + } 59 + 60 + handle / { 61 + try_files /${MOTD_FILE_NAME} 62 + file_server 63 + } 64 + } 65 + `; 66 + 67 + export const pdsCaddyService = new ContainerService(`pds-web`, { 68 + image: "caddy", 69 + servicePort: 80, 70 + hostRule: "Host(`pds.bas.sh`) && (Path(`/`) || Path(`/xrpc/app.bsky.ageassurance.getState`))", 71 + hostRulePriority: 1000, 72 + command: ["/bin/sh", "-c", `echo '${CADDYFILE}' | caddy run --config - --adapter caddyfile`], 73 + mounts: [webMount], 74 + workingDir: "/var/www", 75 + middlewares: ["cors"], 76 + }); 77 + 78 + export const pdsDnsRecord = new DnsRecord("pds", { 79 + zoneId: getEnv("CLOUDFLARE_ZONE_ID"), 80 + name: "pds.bas.sh", 81 + ttl: 1, 82 + type: "CNAME", 83 + content: "haring.bas.sh", 84 + proxied: false, 85 + }); 86 + 87 + export const pdsWildcardDnsRecord = new DnsRecord("pds-wildcard", { 88 + zoneId: getEnv("CLOUDFLARE_ZONE_ID"), 89 + name: "*.pds.bas.sh", 90 + ttl: 1, 91 + type: "CNAME", 92 + content: "pds.bas.sh", 93 + proxied: false, 94 + });
+1 -1
services/haring/atproto/pegasus.ts
··· 63 63 }); 64 64 65 65 const CADDYFILE = ` 66 - http://horse.pegasus.bas.sh:80 { 66 + :80 { 67 67 respond <<HORSE 68 68 69 69 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠨⣧⡀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
services/haring/atproto/spindle.ts services/haring/atproto/tangled/spindle.ts
+3 -3
services/haring/index.ts
··· 58 58 59 59 export * from "./atproto/tranquil/tranquil"; 60 60 export * from "./atproto/pegasus"; 61 - export * from "./atproto/pds"; 61 + export * from "./atproto/pds/pds"; 62 62 export * from "./atproto/relay"; 63 63 // export * from "./atproto/social-app"; 64 - export * from "./atproto/knot"; 65 - export * from "./atproto/spindle"; 64 + export * from "./atproto/tangled/knot"; 65 + export * from "./atproto/tangled/spindle"; 66 66 67 67 export * from "./communication/ergo"; 68 68 export * from "./communication/thelounge";
+1 -1
services/haring/web/caddy.ts
··· 5 5 const SUBDOMAINS = ["get", "static", "files", "f", "i"]; 6 6 7 7 const CADDYFILE = ` 8 - ${SUBDOMAINS.map((sub) => `http://${sub}.bas.sh:80`).join(" ")} { 8 + :80 { 9 9 file_server browse { 10 10 } 11 11 header X-Robots-Tag "noindex"
+2 -2
services/haring/web/dani/dani.ts
··· 2 2 import { ContainerService } from "~lib/service/service"; 3 3 4 4 const CADDYFILE = ` 5 - danimutiara.nl:80 {} 5 + :80 {} 6 6 `; 7 7 8 8 export const caddyFileserverService = new ContainerService(`caddy-dani`, { ··· 10 10 servicePort: 80, 11 11 subdomain: "dani", 12 12 command: ["/bin/sh", "-c", `echo '${CADDYFILE}' | caddy run --config - --adapter caddyfile`], 13 - mounts: [ssdcacheMount("web/dani", "/var/www"), ssdcacheMount()], 13 + mounts: [ssdcacheMount("web/dani", "/var/www")], 14 14 workingDir: "/var/www", 15 15 });