Monorepo for Tangled tangled.org

local-infra: local, sandboxed atmosphere infra

Signed-off-by: Seongmin Lee <git@boltless.me>

boltless.me 006a6526 38b00a4a

verified
+54
local-infra/Caddyfile
··· 1 + { 2 + storage file_system /data/ 3 + debug 4 + pki { 5 + ca localtangled { 6 + name "LocalTangledCA" 7 + } 8 + } 9 + auto_https disable_redirects 10 + } 11 + 12 + plc.tngl.boltless.dev { 13 + tls { 14 + issuer internal { 15 + ca localtangled 16 + } 17 + } 18 + reverse_proxy http://plc:8080 19 + } 20 + 21 + *.pds.tngl.boltless.dev, pds.tngl.boltless.dev { 22 + tls { 23 + issuer internal { 24 + ca localtangled 25 + } 26 + } 27 + reverse_proxy http://pds:3000 28 + } 29 + 30 + jetstream.tngl.boltless.dev { 31 + tls { 32 + issuer internal { 33 + ca localtangled 34 + } 35 + } 36 + reverse_proxy http://jetstream:6008 37 + } 38 + 39 + http://knot.tngl.boltless.dev { 40 + reverse_proxy http://host.docker.internal:6000 41 + } 42 + 43 + https://knot.tngl.boltless.dev { 44 + tls { 45 + issuer internal { 46 + ca localtangled 47 + } 48 + } 49 + reverse_proxy http://host.docker.internal:6000 50 + } 51 + 52 + http://spindle.tngl.boltless.dev { 53 + reverse_proxy http://host.docker.internal:6555 54 + }
+12
local-infra/cert/localtangled/intermediate.crt
··· 1 + -----BEGIN CERTIFICATE----- 2 + MIIBuTCCAV+gAwIBAgIQR5mkZ/TBSWtRFqrMyeVrNDAKBggqhkjOPQQDAjApMScw 3 + JQYDVQQDEx5Mb2NhbFRhbmdsZWRDQSAtIDIwMjUgRUNDIFJvb3QwHhcNMjUxMTAz 4 + MTAyMDA0WhcNMjUxMTEwMTAyMDA0WjAsMSowKAYDVQQDEyFMb2NhbFRhbmdsZWRD 5 + QSAtIEVDQyBJbnRlcm1lZGlhdGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARm 6 + 892T608pFY+dmgkEMFdvq9hj+PlR7o7Vogc+Ca5LeHB846PrZJmxdvHW8Up67hP3 7 + ZpmNjnZQvgOEEjLmquvio2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgw 8 + BgEB/wIBADAdBgNVHQ4EFgQUn8d4TdPCYP0r1Jc09QF4/GKkSSowHwYDVR0jBBgw 9 + FoAUKSXx08/YgAxM+u7pYQcs/WHJIRAwCgYIKoZIzj0EAwIDSAAwRQIgTZeKVo6k 10 + ZBZwx2sx+T46LyjYc5xK/DCQJbLWsgoc/lECIQDNtduyds5J/BfBvnVzO/oK9+0H 11 + oRvV+fcWRAQHGKF4Ew== 12 + -----END CERTIFICATE-----
+5
local-infra/cert/localtangled/intermediate.key
··· 1 + -----BEGIN EC PRIVATE KEY----- 2 + MHcCAQEEIOoepsyQeMkbA05rTh3EwvqHWs5tzTTib7r8fyt2fUo8oAoGCCqGSM49 3 + AwEHoUQDQgAEZvPdk+tPKRWPnZoJBDBXb6vYY/j5Ue6O1aIHPgmuS3hwfOOj62SZ 4 + sXbx1vFKeu4T92aZjY52UL4DhBIy5qrr4g== 5 + -----END EC PRIVATE KEY-----
+11
local-infra/cert/localtangled/root.crt
··· 1 + -----BEGIN CERTIFICATE----- 2 + MIIBlTCCATygAwIBAgIRAMDTcwNxYDMgtUNC5LkCeEQwCgYIKoZIzj0EAwIwKTEn 3 + MCUGA1UEAxMeTG9jYWxUYW5nbGVkQ0EgLSAyMDI1IEVDQyBSb290MB4XDTI1MTAx 4 + NzE2MTE0NVoXDTM1MDgyNjE2MTE0NVowKTEnMCUGA1UEAxMeTG9jYWxUYW5nbGVk 5 + Q0EgLSAyMDI1IEVDQyBSb290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7rFM 6 + 4oNfT0UMqMuc3L60TCLeTd58WFSUYnKl7R1HOHDWeWZhhoNdWguXJSHhFPiWmQ5E 7 + +fiI7KvDAVQGHzfUAqNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB 8 + Af8CAQEwHQYDVR0OBBYEFCkl8dPP2IAMTPru6WEHLP1hySEQMAoGCCqGSM49BAMC 9 + A0cAMEQCIFjSGjvie1gO/JuNtP2HqeUHQNEh82K1fXdks54up3KEAiBWQDaOYeZ2 10 + zVTiKe8ZQHpH3glXsIS0USsxeKaohMp0zA== 11 + -----END CERTIFICATE-----
+5
local-infra/cert/localtangled/root.key
··· 1 + -----BEGIN EC PRIVATE KEY----- 2 + MHcCAQEEIBqEj1iG3q+OLBgHjWQ3UkvKjq4sy5ej47syIYWn/Ql/oAoGCCqGSM49 3 + AwEHoUQDQgAE7rFM4oNfT0UMqMuc3L60TCLeTd58WFSUYnKl7R1HOHDWeWZhhoNd 4 + WguXJSHhFPiWmQ5E+fiI7KvDAVQGHzfUAg== 5 + -----END EC PRIVATE KEY-----
+75
local-infra/docker-compose.yml
··· 1 + name: tangled-local-infra 2 + services: 3 + caddy: 4 + container_name: caddy 5 + image: caddy:2 6 + depends_on: 7 + - pds 8 + restart: unless-stopped 9 + cap_add: 10 + - NET_ADMIN 11 + ports: 12 + - "80:80" 13 + - "443:443" 14 + - "443:443/udp" 15 + volumes: 16 + - ./Caddyfile:/etc/caddy/Caddyfile 17 + - ./cert/localtangled:/data/pki/authorities/localtangled 18 + - caddy_data:/data 19 + - caddy_config:/config 20 + 21 + plc: 22 + image: ghcr.io/bluesky-social/did-method-plc:plc-f2ab7516bac5bc0f3f86842fa94e996bd1b3815b 23 + # did-method-plc only provides linux/amd64 24 + platform: linux/amd64 25 + container_name: plc 26 + restart: unless-stopped 27 + depends_on: 28 + - plc_db 29 + environment: 30 + DEBUG_MODE: 1 31 + LOG_ENABLED: "true" 32 + LOG_LEVEL: "debug" 33 + LOG_DESTINATION: 1 34 + DB_CREDS_JSON: &DB_CREDS_JSON '{"username":"pg","password":"password","host":"plc_db","port":5432}' 35 + DB_MIGRATE_CREDS_JSON: *DB_CREDS_JSON 36 + PLC_VERSION: 0.0.1 37 + PORT: 8080 38 + 39 + plc_db: 40 + image: postgres:14.4-alpine 41 + container_name: plc_db 42 + environment: 43 + - POSTGRES_USER=pg 44 + - POSTGRES_PASSWORD=password 45 + - PGPORT=5432 46 + volumes: 47 + - plc:/var/lib/postgresql/data 48 + 49 + pds: 50 + container_name: pds 51 + image: ghcr.io/bluesky-social/pds:0.4 52 + restart: unless-stopped 53 + volumes: 54 + - pds:/pds 55 + env_file: 56 + - ./pds.env 57 + 58 + jetstream: 59 + container_name: jetstream 60 + image: ghcr.io/bluesky-social/jetstream:sha-0ab10bd 61 + restart: unless-stopped 62 + volumes: 63 + - jetstream:/data 64 + environment: 65 + - JETSTREAM_DATA_DIR=/data 66 + # livness check interval to restart when no events are received (default: 15sec) 67 + - JETSTREAM_LIVENESS_TTL=300s 68 + - JETSTREAM_WS_URL=ws://pds:3000/xrpc/com.atproto.sync.subscribeRepos 69 + 70 + volumes: 71 + caddy_config: 72 + caddy_data: 73 + plc: 74 + pds: 75 + jetstream:
+17
local-infra/pds.env
··· 1 + PDS_JWT_SECRET=8cae8bffcc73d9932819650791e4e89a 2 + PDS_ADMIN_PASSWORD=d6a902588cd93bee1af83f924f60cfd3 3 + PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=2e92e336a50a618458e1097d94a1db86ec3fd8829d7735020cbae80625c761d7 4 + 5 + LOG_ENABLED=true 6 + 7 + # PDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app 8 + # PDS_BSKY_APP_VIEW_URL=https://api.bsky.app 9 + 10 + PDS_DATA_DIRECTORY=/pds 11 + PDS_BLOBSTORE_DISK_LOCATION=/pds/blocks 12 + 13 + PDS_DID_PLC_URL=http://plc:8080 14 + PDS_HOSTNAME=pds.tngl.boltless.dev 15 + 16 + # PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac 17 + # PDS_REPORT_SERVICE_URL=https://mod.bsky.app
+9
local-infra/readme.md
··· 1 + run compose 2 + ``` 3 + docker compose up -d 4 + ``` 5 + 6 + trust the cert (macOS) 7 + ``` 8 + sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./local-infra/cert/localtangled/root.crt 9 + ```
+63
local-infra/scripts/create-test-account.sh
··· 1 + #!/bin/bash 2 + set -o errexit 3 + set -o nounset 4 + set -o pipefail 5 + 6 + source "$(dirname "$0")/../pds.env" 7 + 8 + # curl a URL and fail if the request fails. 9 + function curl_cmd_get { 10 + curl --fail --silent --show-error "$@" 11 + } 12 + 13 + # curl a URL and fail if the request fails. 14 + function curl_cmd_post { 15 + curl --fail --silent --show-error --request POST --header "Content-Type: application/json" "$@" 16 + } 17 + 18 + # curl a URL but do not fail if the request fails. 19 + function curl_cmd_post_nofail { 20 + curl --silent --show-error --request POST --header "Content-Type: application/json" "$@" 21 + } 22 + 23 + USERNAME="${1:-}" 24 + 25 + if [[ "${USERNAME}" == "" ]]; then 26 + read -p "Enter a username: " USERNAME 27 + fi 28 + 29 + if [[ "${USERNAME}" == "" ]]; then 30 + echo "ERROR: missing USERNAME parameter." >/dev/stderr 31 + echo "Usage: $0 ${SUBCOMMAND} <USERNAME>" >/dev/stderr 32 + exit 1 33 + fi 34 + 35 + PASSWORD="password" 36 + INVITE_CODE="$(curl_cmd_post \ 37 + --user "admin:${PDS_ADMIN_PASSWORD}" \ 38 + --data '{"useCount": 1}' \ 39 + "https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createInviteCode" | jq --raw-output '.code' 40 + )" 41 + RESULT="$(curl_cmd_post_nofail \ 42 + --data "{\"email\":\"${USERNAME}@${PDS_HOSTNAME}\", \"handle\":\"${USERNAME}.${PDS_HOSTNAME}\", \"password\":\"${PASSWORD}\", \"inviteCode\":\"${INVITE_CODE}\"}" \ 43 + "https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createAccount" 44 + )" 45 + 46 + DID="$(echo $RESULT | jq --raw-output '.did')" 47 + if [[ "${DID}" != did:* ]]; then 48 + ERR="$(echo ${RESULT} | jq --raw-output '.message')" 49 + echo "ERROR: ${ERR}" >/dev/stderr 50 + echo "Usage: $0 <EMAIL> <HANDLE>" >/dev/stderr 51 + exit 1 52 + fi 53 + 54 + echo 55 + echo "Account created successfully!" 56 + echo "-----------------------------" 57 + echo "Handle : ${USERNAME}.${PDS_HOSTNAME}" 58 + echo "DID : ${DID}" 59 + echo "Password : ${PASSWORD}" 60 + echo "-----------------------------" 61 + echo "This is a test account with an insecure password." 62 + echo "Make sure it's only used for development." 63 + echo
+5
nix/vm.nix
··· 79 79 }; 80 80 # This is fine because any and all ports that are forwarded to host are explicitly marked above, we don't need a separate guest firewall 81 81 networking.firewall.enable = false; 82 + services.dnsmasq.enable = true; 83 + services.dnsmasq.settings.address = "/tngl.boltless.dev/10.0.2.2"; 84 + security.pki.certificates = [ 85 + (builtins.readFile ../local-infra/cert/localtangled/root.crt) 86 + ]; 82 87 time.timeZone = "Europe/London"; 83 88 services.getty.autologinUser = "root"; 84 89 environment.systemPackages = with pkgs; [curl vim git sqlite litecli];