this repo has no description

Rename to tranquil PDS, sounds better than bullshit PDS

lewis a4d6d0fd 920994c7

+2 -2
.env.example
··· 30 30 # Security Secrets 31 31 # ============================================================================= 32 32 # These MUST be set in production (minimum 32 characters each) 33 - # In development, set BSPDS_ALLOW_INSECURE_SECRETS=1 to use defaults 33 + # In development, set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 to use defaults 34 34 # Server-wide secret for OAuth token signing (HS256) 35 35 # JWT_SECRET=your-secure-random-string-at-least-32-chars 36 36 # Secret for DPoP proof validation ··· 38 38 # Key for encrypting user signing keys at rest (AES-256-GCM) 39 39 # MASTER_KEY=your-secure-random-string-at-least-32-chars 40 40 # Set this ONLY in development to allow default/weak secrets 41 - # BSPDS_ALLOW_INSECURE_SECRETS=1 41 + # TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 42 42 # ============================================================================= 43 43 # PLC Directory 44 44 # =============================================================================
+22
.sqlx/query-1add22e111d5eff8beadbd832b4b8146d95da0a0ce8ce31dc9a2f930a26cc9ce.json
··· 1 + { 2 + "db_name": "PostgreSQL", 3 + "query": "SELECT takedown_ref FROM users WHERE did = $1", 4 + "describe": { 5 + "columns": [ 6 + { 7 + "ordinal": 0, 8 + "name": "takedown_ref", 9 + "type_info": "Text" 10 + } 11 + ], 12 + "parameters": { 13 + "Left": [ 14 + "Text" 15 + ] 16 + }, 17 + "nullable": [ 18 + true 19 + ] 20 + }, 21 + "hash": "1add22e111d5eff8beadbd832b4b8146d95da0a0ce8ce31dc9a2f930a26cc9ce" 22 + }
+61 -61
Cargo.lock
··· 930 930 ] 931 931 932 932 [[package]] 933 - name = "bspds" 934 - version = "0.1.0" 935 - dependencies = [ 936 - "aes-gcm", 937 - "anyhow", 938 - "async-trait", 939 - "aws-config", 940 - "aws-sdk-s3", 941 - "axum", 942 - "base32", 943 - "base64 0.22.1", 944 - "bcrypt", 945 - "bytes", 946 - "chrono", 947 - "cid", 948 - "ctor", 949 - "dotenvy", 950 - "ed25519-dalek", 951 - "futures", 952 - "governor", 953 - "hickory-resolver", 954 - "hkdf", 955 - "hmac", 956 - "image", 957 - "ipld-core", 958 - "iroh-car", 959 - "jacquard", 960 - "jacquard-axum", 961 - "jacquard-repo", 962 - "jsonwebtoken", 963 - "k256", 964 - "metrics", 965 - "metrics-exporter-prometheus", 966 - "multibase", 967 - "multihash", 968 - "p256 0.13.2", 969 - "p384", 970 - "rand 0.8.5", 971 - "redis", 972 - "reqwest", 973 - "serde", 974 - "serde_bytes", 975 - "serde_ipld_dagcbor", 976 - "serde_json", 977 - "sha2", 978 - "sqlx", 979 - "subtle", 980 - "testcontainers", 981 - "testcontainers-modules", 982 - "thiserror 2.0.17", 983 - "tokio", 984 - "tokio-tungstenite", 985 - "tower-http", 986 - "tracing", 987 - "tracing-subscriber", 988 - "urlencoding", 989 - "uuid", 990 - "wiremock", 991 - ] 992 - 993 - [[package]] 994 933 name = "btree-range-map" 995 934 version = "0.7.2" 996 935 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6221 6160 "proc-macro2", 6222 6161 "quote", 6223 6162 "syn 2.0.111", 6163 + ] 6164 + 6165 + [[package]] 6166 + name = "tranquil-pds" 6167 + version = "0.1.0" 6168 + dependencies = [ 6169 + "aes-gcm", 6170 + "anyhow", 6171 + "async-trait", 6172 + "aws-config", 6173 + "aws-sdk-s3", 6174 + "axum", 6175 + "base32", 6176 + "base64 0.22.1", 6177 + "bcrypt", 6178 + "bytes", 6179 + "chrono", 6180 + "cid", 6181 + "ctor", 6182 + "dotenvy", 6183 + "ed25519-dalek", 6184 + "futures", 6185 + "governor", 6186 + "hickory-resolver", 6187 + "hkdf", 6188 + "hmac", 6189 + "image", 6190 + "ipld-core", 6191 + "iroh-car", 6192 + "jacquard", 6193 + "jacquard-axum", 6194 + "jacquard-repo", 6195 + "jsonwebtoken", 6196 + "k256", 6197 + "metrics", 6198 + "metrics-exporter-prometheus", 6199 + "multibase", 6200 + "multihash", 6201 + "p256 0.13.2", 6202 + "p384", 6203 + "rand 0.8.5", 6204 + "redis", 6205 + "reqwest", 6206 + "serde", 6207 + "serde_bytes", 6208 + "serde_ipld_dagcbor", 6209 + "serde_json", 6210 + "sha2", 6211 + "sqlx", 6212 + "subtle", 6213 + "testcontainers", 6214 + "testcontainers-modules", 6215 + "thiserror 2.0.17", 6216 + "tokio", 6217 + "tokio-tungstenite", 6218 + "tower-http", 6219 + "tracing", 6220 + "tracing-subscriber", 6221 + "urlencoding", 6222 + "uuid", 6223 + "wiremock", 6224 6224 ] 6225 6225 6226 6226 [[package]]
+1 -1
Cargo.toml
··· 1 1 [package] 2 - name = "bspds" 2 + name = "tranquil-pds" 3 3 version = "0.1.0" 4 4 edition = "2024" 5 5 [dependencies]
+2 -2
Dockerfile
··· 16 16 RUN touch src/main.rs && cargo build --release 17 17 # Stage 3: Final image 18 18 FROM alpine:3.23 19 - COPY --from=builder /app/target/release/bspds /usr/local/bin/bspds 19 + COPY --from=builder /app/target/release/tranquil-pds /usr/local/bin/tranquil-pds 20 20 COPY --from=builder /app/migrations /app/migrations 21 21 COPY --from=frontend-builder /frontend/dist /app/frontend/dist 22 22 WORKDIR /app ··· 24 24 ENV SERVER_PORT=3000 25 25 ENV FRONTEND_DIR=/app/frontend/dist 26 26 EXPOSE 3000 27 - CMD ["bspds"] 27 + CMD ["tranquil-pds"]
+1 -1
README.md
··· 1 - # BSPDS 1 + # Tranquil PDS 2 2 3 3 A production-grade Personal Data Server (PDS) for the AT Protocol. Drop-in replacement for Bluesky's reference PDS, written in rust with postgres and s3-compatible blob storage. 4 4
+6 -6
deploy/quadlets/bspds-app.container deploy/quadlets/tranquil-pds-app.container
··· 1 1 [Unit] 2 - Description=BSPDS AT Protocol PDS 3 - After=bspds-db.service bspds-minio.service bspds-valkey.service 2 + Description=Tranquil PDS AT Protocol PDS 3 + After=tranquil-pds-db.service tranquil-pds-minio.service tranquil-pds-valkey.service 4 4 [Container] 5 - ContainerName=bspds-app 6 - Image=localhost/bspds:latest 7 - Pod=bspds.pod 8 - EnvironmentFile=/srv/bspds/config/bspds.env 5 + ContainerName=tranquil-pds-app 6 + Image=localhost/tranquil-pds:latest 7 + Pod=tranquil-pds.pod 8 + EnvironmentFile=/srv/tranquil-pds/config/tranquil-pds.env 9 9 Environment=SERVER_HOST=0.0.0.0 10 10 Environment=SERVER_PORT=3000 11 11 Environment=S3_ENDPOINT=http://localhost:9000
-20
deploy/quadlets/bspds-db.container
··· 1 - [Unit] 2 - Description=BSPDS postgres database 3 - [Container] 4 - ContainerName=bspds-db 5 - Image=docker.io/library/postgres:18-alpine 6 - Pod=bspds.pod 7 - Environment=POSTGRES_USER=bspds 8 - Environment=POSTGRES_DB=pds 9 - Secret=bspds-db-password,type=env,target=POSTGRES_PASSWORD 10 - Volume=/srv/bspds/postgres:/var/lib/postgresql/data:Z 11 - HealthCmd=pg_isready -U bspds -d pds 12 - HealthInterval=10s 13 - HealthTimeout=5s 14 - HealthRetries=5 15 - HealthStartPeriod=10s 16 - [Service] 17 - Restart=always 18 - RestartSec=10 19 - [Install] 20 - WantedBy=default.target
+5 -5
deploy/quadlets/bspds-minio.container deploy/quadlets/tranquil-pds-minio.container
··· 1 1 [Unit] 2 - Description=BSPDS minio object storage 2 + Description=Tranquil PDS minio object storage 3 3 [Container] 4 - ContainerName=bspds-minio 4 + ContainerName=tranquil-pds-minio 5 5 Image=docker.io/minio/minio:RELEASE.2025-10-15T17-29-55Z 6 - Pod=bspds.pod 6 + Pod=tranquil-pds.pod 7 7 Environment=MINIO_ROOT_USER=minioadmin 8 - Secret=bspds-minio-password,type=env,target=MINIO_ROOT_PASSWORD 9 - Volume=/srv/bspds/minio:/data:Z 8 + Secret=tranquil-pds-minio-password,type=env,target=MINIO_ROOT_PASSWORD 9 + Volume=/srv/tranquil-pds/minio:/data:Z 10 10 Exec=server /data --console-address :9001 11 11 HealthCmd=curl -f http://localhost:9000/minio/health/live || exit 1 12 12 HealthInterval=30s
-15
deploy/quadlets/bspds-nginx.container
··· 1 - [Unit] 2 - Description=BSPDS nginx reverse proxy 3 - After=bspds-app.service 4 - [Container] 5 - ContainerName=bspds-nginx 6 - Image=docker.io/library/nginx:1.28-alpine 7 - Pod=bspds.pod 8 - Volume=/srv/bspds/config/nginx.conf:/etc/nginx/nginx.conf:ro,Z 9 - Volume=/srv/bspds/certs:/etc/nginx/certs:ro,Z 10 - Volume=/srv/bspds/acme:/var/www/acme:ro,Z 11 - [Service] 12 - Restart=always 13 - RestartSec=10 14 - [Install] 15 - WantedBy=default.target
+4 -4
deploy/quadlets/bspds-valkey.container deploy/quadlets/tranquil-pds-valkey.container
··· 1 1 [Unit] 2 - Description=BSPDS valkey cache 2 + Description=Tranquil PDS valkey cache 3 3 [Container] 4 - ContainerName=bspds-valkey 4 + ContainerName=tranquil-pds-valkey 5 5 Image=docker.io/valkey/valkey:9-alpine 6 - Pod=bspds.pod 7 - Volume=/srv/bspds/valkey:/data:Z 6 + Pod=tranquil-pds.pod 7 + Volume=/srv/tranquil-pds/valkey:/data:Z 8 8 Exec=valkey-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru 9 9 HealthCmd=valkey-cli ping 10 10 HealthInterval=10s
+1 -1
deploy/quadlets/bspds.pod deploy/quadlets/tranquil-pds.pod
··· 1 1 [Pod] 2 - PodName=bspds 2 + PodName=tranquil-pds 3 3 PublishPort=80:80 4 4 PublishPort=443:443 5 5 [Install]
+20
deploy/quadlets/tranquil-pds-db.container
··· 1 + [Unit] 2 + Description=Tranquil PDS postgres database 3 + [Container] 4 + ContainerName=tranquil-pds-db 5 + Image=docker.io/library/postgres:18-alpine 6 + Pod=tranquil-pds.pod 7 + Environment=POSTGRES_USER=tranquil_pds 8 + Environment=POSTGRES_DB=pds 9 + Secret=tranquil-pds-db-password,type=env,target=POSTGRES_PASSWORD 10 + Volume=/srv/tranquil-pds/postgres:/var/lib/postgresql/data:Z 11 + HealthCmd=pg_isready -U tranquil_pds -d pds 12 + HealthInterval=10s 13 + HealthTimeout=5s 14 + HealthRetries=5 15 + HealthStartPeriod=10s 16 + [Service] 17 + Restart=always 18 + RestartSec=10 19 + [Install] 20 + WantedBy=default.target
+15
deploy/quadlets/tranquil-pds-nginx.container
··· 1 + [Unit] 2 + Description=Tranquil PDS nginx reverse proxy 3 + After=tranquil-pds-app.service 4 + [Container] 5 + ContainerName=tranquil-pds-nginx 6 + Image=docker.io/library/nginx:1.28-alpine 7 + Pod=tranquil-pds.pod 8 + Volume=/srv/tranquil-pds/config/nginx.conf:/etc/nginx/nginx.conf:ro,Z 9 + Volume=/srv/tranquil-pds/certs:/etc/nginx/certs:ro,Z 10 + Volume=/srv/tranquil-pds/acme:/var/www/acme:ro,Z 11 + [Service] 12 + Restart=always 13 + RestartSec=10 14 + [Install] 15 + WantedBy=default.target
+6 -6
docker-compose.prod.yml
··· 1 1 services: 2 - bspds: 2 + tranquil-pds: 3 3 build: 4 4 context: . 5 5 dockerfile: Dockerfile 6 - image: bspds:latest 6 + image: tranquil-pds:latest 7 7 restart: unless-stopped 8 8 ports: 9 9 - "127.0.0.1:3000:3000" ··· 11 11 SERVER_HOST: "0.0.0.0" 12 12 SERVER_PORT: "3000" 13 13 PDS_HOSTNAME: "${PDS_HOSTNAME:?PDS_HOSTNAME is required}" 14 - DATABASE_URL: "postgres://bspds:${DB_PASSWORD:?DB_PASSWORD is required}@db:5432/pds" 14 + DATABASE_URL: "postgres://tranquil_pds:${DB_PASSWORD:?DB_PASSWORD is required}@db:5432/pds" 15 15 S3_ENDPOINT: "http://minio:9000" 16 16 AWS_REGION: "us-east-1" 17 17 S3_BUCKET: "pds-blobs" ··· 46 46 image: postgres:18-alpine 47 47 restart: unless-stopped 48 48 environment: 49 - POSTGRES_USER: bspds 49 + POSTGRES_USER: tranquil_pds 50 50 POSTGRES_PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required}" 51 51 POSTGRES_DB: pds 52 52 volumes: 53 53 - postgres_data:/var/lib/postgresql/data 54 54 healthcheck: 55 - test: ["CMD-SHELL", "pg_isready -U bspds -d pds"] 55 + test: ["CMD-SHELL", "pg_isready -U tranquil_pds -d pds"] 56 56 interval: 10s 57 57 timeout: 5s 58 58 retries: 5 ··· 128 128 - ./certs:/etc/nginx/certs:ro 129 129 - acme_challenge:/var/www/acme:ro 130 130 depends_on: 131 - - bspds 131 + - tranquil-pds 132 132 healthcheck: 133 133 test: ["CMD", "nginx", "-t"] 134 134 interval: 30s
+1 -1
docker-compose.yaml
··· 3 3 build: 4 4 context: . 5 5 dockerfile: Dockerfile 6 - image: bspds 6 + image: tranquil-pds 7 7 ports: 8 8 - "3000:3000" 9 9 env_file:
+42 -45
docs/install-alpine.md
··· 1 - # BSPDS Production Installation on Alpine Linux 1 + # Tranquil PDS Production Installation on Alpine Linux 2 2 > **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified. 3 3 4 - This guide covers installing BSPDS on Alpine Linux 3.23 (current stable as of December 2025). 4 + This guide covers installing Tranquil PDS on Alpine Linux 3.23. 5 5 6 6 ## Prerequisites 7 7 - A VPS with at least 2GB RAM and 20GB disk ··· 20 20 source ~/.cargo/env 21 21 rustup default stable 22 22 ``` 23 - This installs the latest stable Rust (1.92+ as of December 2025). Alpine 3.23 also ships Rust 1.91 via `apk add rust cargo` if you prefer system packages. 23 + This installs the latest stable Rust. Alpine also ships Rust via `apk add rust cargo` if you prefer system packages. 24 24 ## 3. Install postgres 25 - Alpine 3.23 includes PostgreSQL 18: 26 25 ```sh 27 26 apk add postgresql postgresql-contrib 28 27 rc-update add postgresql 29 28 /etc/init.d/postgresql setup 30 29 rc-service postgresql start 31 - psql -U postgres -c "CREATE USER bspds WITH PASSWORD 'your-secure-password';" 32 - psql -U postgres -c "CREATE DATABASE pds OWNER bspds;" 33 - psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;" 30 + psql -U postgres -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';" 31 + psql -U postgres -c "CREATE DATABASE pds OWNER tranquil_pds;" 32 + psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;" 34 33 ``` 35 34 ## 4. Install minio 36 35 ```sh ··· 78 77 mc mb local/pds-blobs 79 78 ``` 80 79 ## 5. Install valkey 81 - Alpine 3.23 includes Valkey 9: 82 80 ```sh 83 81 apk add valkey 84 82 rc-update add valkey ··· 90 88 export PATH="$HOME/.deno/bin:$PATH" 91 89 echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile 92 90 ``` 93 - ## 7. Clone and Build BSPDS 91 + ## 7. Clone and Build Tranquil PDS 94 92 ```sh 95 93 mkdir -p /opt && cd /opt 96 - git clone https://tangled.org/lewis.moe/bspds-sandbox bspds 97 - cd bspds 94 + git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds 95 + cd tranquil-pds 98 96 cd frontend 99 97 deno task build 100 98 cd .. ··· 103 101 ## 8. Install sqlx-cli and Run Migrations 104 102 ```sh 105 103 cargo install sqlx-cli --no-default-features --features postgres 106 - export DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" 104 + export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" 107 105 sqlx migrate run 108 106 ``` 109 - ## 9. Configure BSPDS 107 + ## 9. Configure Tranquil PDS 110 108 ```sh 111 - mkdir -p /etc/bspds 112 - cp /opt/bspds/.env.example /etc/bspds/bspds.env 113 - chmod 600 /etc/bspds/bspds.env 109 + mkdir -p /etc/tranquil-pds 110 + cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.env 111 + chmod 600 /etc/tranquil-pds/tranquil-pds.env 114 112 ``` 115 - Edit `/etc/bspds/bspds.env` and fill in your values. Generate secrets with: 113 + Edit `/etc/tranquil-pds/tranquil-pds.env` and fill in your values. Generate secrets with: 116 114 ```sh 117 115 openssl rand -base64 48 118 116 ``` 119 117 ## 10. Create OpenRC Service 120 118 ```sh 121 - adduser -D -H -s /sbin/nologin bspds 122 - cp /opt/bspds/target/release/bspds /usr/local/bin/ 123 - mkdir -p /var/lib/bspds 124 - cp -r /opt/bspds/frontend/dist /var/lib/bspds/frontend 125 - chown -R bspds:bspds /var/lib/bspds 126 - cat > /etc/init.d/bspds << 'EOF' 119 + adduser -D -H -s /sbin/nologin tranquil-pds 120 + cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/ 121 + mkdir -p /var/lib/tranquil-pds 122 + cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend 123 + chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds 124 + cat > /etc/init.d/tranquil-pds << 'EOF' 127 125 #!/sbin/openrc-run 128 - name="bspds" 129 - description="BSPDS - AT Protocol PDS" 130 - command="/usr/local/bin/bspds" 131 - command_user="bspds" 126 + name="tranquil-pds" 127 + description="Tranquil PDS - AT Protocol PDS" 128 + command="/usr/local/bin/tranquil-pds" 129 + command_user="tranquil-pds" 132 130 command_background=true 133 131 pidfile="/run/${RC_SVCNAME}.pid" 134 - output_log="/var/log/bspds.log" 135 - error_log="/var/log/bspds.log" 132 + output_log="/var/log/tranquil-pds.log" 133 + error_log="/var/log/tranquil-pds.log" 136 134 depend() { 137 135 need net postgresql minio 138 136 } 139 137 start_pre() { 140 - export FRONTEND_DIR=/var/lib/bspds/frontend 141 - . /etc/bspds/bspds.env 138 + export FRONTEND_DIR=/var/lib/tranquil-pds/frontend 139 + . /etc/tranquil-pds/tranquil-pds.env 142 140 export SERVER_HOST SERVER_PORT PDS_HOSTNAME DATABASE_URL 143 141 export S3_ENDPOINT AWS_REGION S3_BUCKET AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY 144 142 export VALKEY_URL JWT_SECRET DPOP_SECRET MASTER_KEY CRAWLERS 145 143 } 146 144 EOF 147 - chmod +x /etc/init.d/bspds 148 - rc-update add bspds 149 - rc-service bspds start 145 + chmod +x /etc/init.d/tranquil-pds 146 + rc-update add tranquil-pds 147 + rc-service tranquil-pds start 150 148 ``` 151 149 ## 11. Install and Configure nginx 152 - Alpine 3.23 includes nginx 1.28: 153 150 ```sh 154 151 apk add nginx certbot certbot-nginx 155 - cat > /etc/nginx/http.d/bspds.conf << 'EOF' 152 + cat > /etc/nginx/http.d/tranquil-pds.conf << 'EOF' 156 153 server { 157 154 listen 80; 158 155 listen [::]:80; ··· 217 214 ``` 218 215 ## 14. Verify Installation 219 216 ```sh 220 - rc-service bspds status 217 + rc-service tranquil-pds status 221 218 curl -s https://pds.example.com/xrpc/_health 222 219 curl -s https://pds.example.com/.well-known/atproto-did 223 220 ``` 224 221 ## Maintenance 225 222 View logs: 226 223 ```sh 227 - tail -f /var/log/bspds.log 224 + tail -f /var/log/tranquil-pds.log 228 225 ``` 229 - Update BSPDS: 226 + Update Tranquil PDS: 230 227 ```sh 231 - cd /opt/bspds 228 + cd /opt/tranquil-pds 232 229 git pull 233 230 cd frontend && deno task build && cd .. 234 231 cargo build --release 235 - rc-service bspds stop 236 - cp target/release/bspds /usr/local/bin/ 237 - cp -r frontend/dist /var/lib/bspds/frontend 238 - DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" sqlx migrate run 239 - rc-service bspds start 232 + rc-service tranquil-pds stop 233 + cp target/release/tranquil-pds /usr/local/bin/ 234 + cp -r frontend/dist /var/lib/tranquil-pds/frontend 235 + DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run 236 + rc-service tranquil-pds start 240 237 ``` 241 238 Backup database: 242 239 ```sh
+77 -77
docs/install-containers.md
··· 1 - # BSPDS Containerized Production Deployment 1 + # Tranquil PDS Containerized Production Deployment 2 2 > **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified. 3 - This guide covers deploying BSPDS using containers with podman. 3 + This guide covers deploying Tranquil PDS using containers with podman. 4 4 - **Debian 13+**: Uses systemd quadlets (modern, declarative container management) 5 5 - **Alpine 3.23+**: Uses OpenRC service script with podman-compose 6 6 ## Prerequisites ··· 39 39 ## 2. Create Directory Structure 40 40 ```bash 41 41 mkdir -p /etc/containers/systemd 42 - mkdir -p /srv/bspds/{postgres,minio,valkey,certs,acme,config} 42 + mkdir -p /srv/tranquil-pds/{postgres,minio,valkey,certs,acme,config} 43 43 ``` 44 44 ## 3. Create Environment File 45 45 ```bash 46 - cp /opt/bspds/.env.example /srv/bspds/config/bspds.env 47 - chmod 600 /srv/bspds/config/bspds.env 46 + cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env 47 + chmod 600 /srv/tranquil-pds/config/tranquil-pds.env 48 48 ``` 49 - Edit `/srv/bspds/config/bspds.env` and fill in your values. Generate secrets with: 49 + Edit `/srv/tranquil-pds/config/tranquil-pds.env` and fill in your values. Generate secrets with: 50 50 ```bash 51 51 openssl rand -base64 48 52 52 ``` ··· 54 54 ## 4. Install Quadlet Definitions 55 55 Copy the quadlet files from the repository: 56 56 ```bash 57 - cp /opt/bspds/deploy/quadlets/*.pod /etc/containers/systemd/ 58 - cp /opt/bspds/deploy/quadlets/*.container /etc/containers/systemd/ 57 + cp /opt/tranquil-pds/deploy/quadlets/*.pod /etc/containers/systemd/ 58 + cp /opt/tranquil-pds/deploy/quadlets/*.container /etc/containers/systemd/ 59 59 ``` 60 60 Note: Systemd doesn't support shell-style variable expansion in `Environment=` lines. The quadlet files expect DATABASE_URL to be set in the environment file. 61 61 ## 5. Create nginx Configuration 62 62 ```bash 63 - cp /opt/bspds/deploy/nginx/nginx-quadlet.conf /srv/bspds/config/nginx.conf 63 + cp /opt/tranquil-pds/deploy/nginx/nginx-quadlet.conf /srv/tranquil-pds/config/nginx.conf 64 64 ``` 65 - ## 6. Build BSPDS Image 65 + ## 6. Build Tranquil PDS Image 66 66 ```bash 67 67 cd /opt 68 - git clone https://tangled.org/lewis.moe/bspds-sandbox bspds 69 - cd bspds 70 - podman build -t bspds:latest . 68 + git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds 69 + cd tranquil-pds 70 + podman build -t tranquil-pds:latest . 71 71 ``` 72 72 ## 7. Create Podman Secrets 73 73 ```bash 74 - source /srv/bspds/config/bspds.env 75 - echo "$DB_PASSWORD" | podman secret create bspds-db-password - 76 - echo "$MINIO_ROOT_PASSWORD" | podman secret create bspds-minio-password - 74 + source /srv/tranquil-pds/config/tranquil-pds.env 75 + echo "$DB_PASSWORD" | podman secret create tranquil-pds-db-password - 76 + echo "$MINIO_ROOT_PASSWORD" | podman secret create tranquil-pds-minio-password - 77 77 ``` 78 78 ## 8. Start Services and Initialize 79 79 ```bash 80 80 systemctl daemon-reload 81 - systemctl start bspds-db bspds-minio bspds-valkey 81 + systemctl start tranquil-pds-db tranquil-pds-minio tranquil-pds-valkey 82 82 sleep 10 83 83 ``` 84 84 85 85 Create the minio bucket: 86 86 ```bash 87 - podman run --rm --pod bspds \ 87 + podman run --rm --pod tranquil-pds \ 88 88 -e MINIO_ROOT_USER=minioadmin \ 89 89 -e MINIO_ROOT_PASSWORD=your-minio-password \ 90 90 docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \ ··· 94 94 Run migrations: 95 95 ```bash 96 96 cargo install sqlx-cli --no-default-features --features postgres 97 - DATABASE_URL="postgres://bspds:your-db-password@localhost:5432/pds" sqlx migrate run --source /opt/bspds/migrations 97 + DATABASE_URL="postgres://tranquil_pds:your-db-password@localhost:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations 98 98 ``` 99 99 ## 9. Obtain Wildcard SSL Certificate 100 100 User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation. ··· 102 102 Create temporary self-signed cert to start services: 103 103 ```bash 104 104 openssl req -x509 -nodes -days 1 -newkey rsa:2048 \ 105 - -keyout /srv/bspds/certs/privkey.pem \ 106 - -out /srv/bspds/certs/fullchain.pem \ 105 + -keyout /srv/tranquil-pds/certs/privkey.pem \ 106 + -out /srv/tranquil-pds/certs/fullchain.pem \ 107 107 -subj "/CN=pds.example.com" 108 - systemctl start bspds-app bspds-nginx 108 + systemctl start tranquil-pds-app tranquil-pds-nginx 109 109 ``` 110 110 111 111 Get a wildcard certificate using DNS validation: 112 112 ```bash 113 113 podman run --rm -it \ 114 - -v /srv/bspds/certs:/etc/letsencrypt:Z \ 114 + -v /srv/tranquil-pds/certs:/etc/letsencrypt:Z \ 115 115 docker.io/certbot/certbot:v5.2.2 certonly \ 116 116 --manual --preferred-challenges dns \ 117 117 -d pds.example.com -d '*.pds.example.com' \ ··· 123 123 124 124 Link certificates and restart: 125 125 ```bash 126 - ln -sf /srv/bspds/certs/live/pds.example.com/fullchain.pem /srv/bspds/certs/fullchain.pem 127 - ln -sf /srv/bspds/certs/live/pds.example.com/privkey.pem /srv/bspds/certs/privkey.pem 128 - systemctl restart bspds-nginx 126 + ln -sf /srv/tranquil-pds/certs/live/pds.example.com/fullchain.pem /srv/tranquil-pds/certs/fullchain.pem 127 + ln -sf /srv/tranquil-pds/certs/live/pds.example.com/privkey.pem /srv/tranquil-pds/certs/privkey.pem 128 + systemctl restart tranquil-pds-nginx 129 129 ``` 130 130 ## 10. Enable All Services 131 131 ```bash 132 - systemctl enable bspds-db bspds-minio bspds-valkey bspds-app bspds-nginx 132 + systemctl enable tranquil-pds-db tranquil-pds-minio tranquil-pds-valkey tranquil-pds-app tranquil-pds-nginx 133 133 ``` 134 134 ## 11. Configure Firewall 135 135 ```bash ··· 142 142 ## 12. Certificate Renewal 143 143 Add to root's crontab (`crontab -e`): 144 144 ``` 145 - 0 0 * * * podman run --rm -v /srv/bspds/certs:/etc/letsencrypt:Z -v /srv/bspds/acme:/var/www/acme:Z docker.io/certbot/certbot:v5.2.2 renew --quiet && systemctl reload bspds-nginx 145 + 0 0 * * * podman run --rm -v /srv/tranquil-pds/certs:/etc/letsencrypt:Z -v /srv/tranquil-pds/acme:/var/www/acme:Z docker.io/certbot/certbot:v5.2.2 renew --quiet && systemctl reload tranquil-pds-nginx 146 146 ``` 147 147 --- 148 148 # Alpine 3.23+ with OpenRC ··· 161 161 ``` 162 162 ## 2. Create Directory Structure 163 163 ```sh 164 - mkdir -p /srv/bspds/{data,config} 165 - mkdir -p /srv/bspds/data/{postgres,minio,valkey,certs,acme} 164 + mkdir -p /srv/tranquil-pds/{data,config} 165 + mkdir -p /srv/tranquil-pds/data/{postgres,minio,valkey,certs,acme} 166 166 ``` 167 167 ## 3. Clone Repository and Build 168 168 ```sh 169 169 cd /opt 170 - git clone https://tangled.org/lewis.moe/bspds-sandbox bspds 171 - cd bspds 172 - podman build -t bspds:latest . 170 + git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds 171 + cd tranquil-pds 172 + podman build -t tranquil-pds:latest . 173 173 ``` 174 174 ## 4. Create Environment File 175 175 ```sh 176 - cp /opt/bspds/.env.example /srv/bspds/config/bspds.env 177 - chmod 600 /srv/bspds/config/bspds.env 176 + cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env 177 + chmod 600 /srv/tranquil-pds/config/tranquil-pds.env 178 178 ``` 179 - Edit `/srv/bspds/config/bspds.env` and fill in your values. Generate secrets with: 179 + Edit `/srv/tranquil-pds/config/tranquil-pds.env` and fill in your values. Generate secrets with: 180 180 ```sh 181 181 openssl rand -base64 48 182 182 ``` 183 183 ## 5. Set Up Compose and nginx 184 184 Copy the production compose and nginx configs: 185 185 ```sh 186 - cp /opt/bspds/docker-compose.prod.yml /srv/bspds/docker-compose.yml 187 - cp /opt/bspds/nginx.prod.conf /srv/bspds/config/nginx.conf 186 + cp /opt/tranquil-pds/docker-compose.prod.yml /srv/tranquil-pds/docker-compose.yml 187 + cp /opt/tranquil-pds/nginx.prod.conf /srv/tranquil-pds/config/nginx.conf 188 188 ``` 189 - Edit `/srv/bspds/docker-compose.yml` to adjust paths if needed: 190 - - Update volume mounts to use `/srv/bspds/data/` paths 191 - - Update nginx cert paths to match `/srv/bspds/data/certs/` 192 - Edit `/srv/bspds/config/nginx.conf` to update cert paths: 189 + Edit `/srv/tranquil-pds/docker-compose.yml` to adjust paths if needed: 190 + - Update volume mounts to use `/srv/tranquil-pds/data/` paths 191 + - Update nginx cert paths to match `/srv/tranquil-pds/data/certs/` 192 + Edit `/srv/tranquil-pds/config/nginx.conf` to update cert paths: 193 193 - Change `/etc/nginx/certs/live/${PDS_HOSTNAME}/` to `/etc/nginx/certs/` 194 194 ## 6. Create OpenRC Service 195 195 ```sh 196 - cat > /etc/init.d/bspds << 'EOF' 196 + cat > /etc/init.d/tranquil-pds << 'EOF' 197 197 #!/sbin/openrc-run 198 - name="bspds" 199 - description="BSPDS AT Protocol PDS (containerized)" 198 + name="tranquil-pds" 199 + description="Tranquil PDS AT Protocol PDS (containerized)" 200 200 command="/usr/bin/podman-compose" 201 - command_args="-f /srv/bspds/docker-compose.yml up" 201 + command_args="-f /srv/tranquil-pds/docker-compose.yml up" 202 202 command_background=true 203 203 pidfile="/run/${RC_SVCNAME}.pid" 204 - directory="/srv/bspds" 204 + directory="/srv/tranquil-pds" 205 205 depend() { 206 206 need net podman 207 207 after firewall 208 208 } 209 209 start_pre() { 210 210 set -a 211 - . /srv/bspds/config/bspds.env 211 + . /srv/tranquil-pds/config/tranquil-pds.env 212 212 set +a 213 213 } 214 214 stop() { 215 215 ebegin "Stopping ${name}" 216 - cd /srv/bspds 216 + cd /srv/tranquil-pds 217 217 set -a 218 - . /srv/bspds/config/bspds.env 218 + . /srv/tranquil-pds/config/tranquil-pds.env 219 219 set +a 220 - podman-compose -f /srv/bspds/docker-compose.yml down 220 + podman-compose -f /srv/tranquil-pds/docker-compose.yml down 221 221 eend $? 222 222 } 223 223 EOF 224 - chmod +x /etc/init.d/bspds 224 + chmod +x /etc/init.d/tranquil-pds 225 225 ``` 226 226 ## 7. Initialize Services 227 227 Start services: 228 228 ```sh 229 - rc-service bspds start 229 + rc-service tranquil-pds start 230 230 sleep 15 231 231 ``` 232 232 233 233 Create the minio bucket: 234 234 ```sh 235 - source /srv/bspds/config/bspds.env 236 - podman run --rm --network bspds_default \ 235 + source /srv/tranquil-pds/config/tranquil-pds.env 236 + podman run --rm --network tranquil-pds_default \ 237 237 -e MINIO_ROOT_USER="$MINIO_ROOT_USER" \ 238 238 -e MINIO_ROOT_PASSWORD="$MINIO_ROOT_PASSWORD" \ 239 239 docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \ ··· 246 246 rustup-init -y 247 247 source ~/.cargo/env 248 248 cargo install sqlx-cli --no-default-features --features postgres 249 - DB_IP=$(podman inspect bspds-db-1 --format '{{.NetworkSettings.Networks.bspds_default.IPAddress}}') 250 - DATABASE_URL="postgres://bspds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/bspds/migrations 249 + DB_IP=$(podman inspect tranquil-pds-db-1 --format '{{.NetworkSettings.Networks.tranquil-pds_default.IPAddress}}') 250 + DATABASE_URL="postgres://tranquil_pds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations 251 251 ``` 252 252 ## 8. Obtain Wildcard SSL Certificate 253 253 User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation. ··· 255 255 Create temporary self-signed cert to start services: 256 256 ```sh 257 257 openssl req -x509 -nodes -days 1 -newkey rsa:2048 \ 258 - -keyout /srv/bspds/data/certs/privkey.pem \ 259 - -out /srv/bspds/data/certs/fullchain.pem \ 258 + -keyout /srv/tranquil-pds/data/certs/privkey.pem \ 259 + -out /srv/tranquil-pds/data/certs/fullchain.pem \ 260 260 -subj "/CN=pds.example.com" 261 - rc-service bspds restart 261 + rc-service tranquil-pds restart 262 262 ``` 263 263 264 264 Get a wildcard certificate using DNS validation: 265 265 ```sh 266 266 podman run --rm -it \ 267 - -v /srv/bspds/data/certs:/etc/letsencrypt \ 267 + -v /srv/tranquil-pds/data/certs:/etc/letsencrypt \ 268 268 docker.io/certbot/certbot:v5.2.2 certonly \ 269 269 --manual --preferred-challenges dns \ 270 270 -d pds.example.com -d '*.pds.example.com' \ ··· 274 274 275 275 Link certificates and restart: 276 276 ```sh 277 - ln -sf /srv/bspds/data/certs/live/pds.example.com/fullchain.pem /srv/bspds/data/certs/fullchain.pem 278 - ln -sf /srv/bspds/data/certs/live/pds.example.com/privkey.pem /srv/bspds/data/certs/privkey.pem 279 - rc-service bspds restart 277 + ln -sf /srv/tranquil-pds/data/certs/live/pds.example.com/fullchain.pem /srv/tranquil-pds/data/certs/fullchain.pem 278 + ln -sf /srv/tranquil-pds/data/certs/live/pds.example.com/privkey.pem /srv/tranquil-pds/data/certs/privkey.pem 279 + rc-service tranquil-pds restart 280 280 ``` 281 281 ## 9. Enable Service at Boot 282 282 ```sh 283 - rc-update add bspds 283 + rc-update add tranquil-pds 284 284 ``` 285 285 ## 10. Configure Firewall 286 286 ```sh ··· 305 305 ## 11. Certificate Renewal 306 306 Add to root's crontab (`crontab -e`): 307 307 ``` 308 - 0 0 * * * podman run --rm -v /srv/bspds/data/certs:/etc/letsencrypt -v /srv/bspds/data/acme:/var/www/acme docker.io/certbot/certbot:v5.2.2 renew --quiet && rc-service bspds restart 308 + 0 0 * * * podman run --rm -v /srv/tranquil-pds/data/certs:/etc/letsencrypt -v /srv/tranquil-pds/data/acme:/var/www/acme docker.io/certbot/certbot:v5.2.2 renew --quiet && rc-service tranquil-pds restart 309 309 ``` 310 310 --- 311 311 # Verification and Maintenance ··· 317 317 ## View Logs 318 318 **Debian:** 319 319 ```bash 320 - journalctl -u bspds-app -f 321 - podman logs -f bspds-app 320 + journalctl -u tranquil-pds-app -f 321 + podman logs -f tranquil-pds-app 322 322 ``` 323 323 **Alpine:** 324 324 ```sh 325 - podman-compose -f /srv/bspds/docker-compose.yml logs -f 326 - podman logs -f bspds-bspds-1 325 + podman-compose -f /srv/tranquil-pds/docker-compose.yml logs -f 326 + podman logs -f tranquil-pds-tranquil-pds-1 327 327 ``` 328 - ## Update BSPDS 328 + ## Update Tranquil PDS 329 329 ```sh 330 - cd /opt/bspds 330 + cd /opt/tranquil-pds 331 331 git pull 332 - podman build -t bspds:latest . 332 + podman build -t tranquil-pds:latest . 333 333 ``` 334 334 335 335 Debian: 336 336 ```bash 337 - systemctl restart bspds-app 337 + systemctl restart tranquil-pds-app 338 338 ``` 339 339 340 340 Alpine: 341 341 ```sh 342 - rc-service bspds restart 342 + rc-service tranquil-pds restart 343 343 ``` 344 344 ## Backup Database 345 345 **Debian:** 346 346 ```bash 347 - podman exec bspds-db pg_dump -U bspds pds > /var/backups/pds-$(date +%Y%m%d).sql 347 + podman exec tranquil-pds-db pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql 348 348 ``` 349 349 **Alpine:** 350 350 ```sh 351 - podman exec bspds-db-1 pg_dump -U bspds pds > /var/backups/pds-$(date +%Y%m%d).sql 351 + podman exec tranquil-pds-db-1 pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql 352 352 ```
+40 -43
docs/install-debian.md
··· 1 - # BSPDS Production Installation on Debian 1 + # Tranquil PDS Production Installation on Debian 2 2 > **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified. 3 3 4 - This guide covers installing BSPDS on Debian 13 "Trixie" (current stable as of December 2025). 4 + This guide covers installing Tranquil PDS on Debian 13 "Trixie". 5 5 6 6 ## Prerequisites 7 7 - A VPS with at least 2GB RAM and 20GB disk ··· 19 19 source ~/.cargo/env 20 20 rustup default stable 21 21 ``` 22 - This installs the latest stable Rust (1.92+ as of December 2025). 22 + This installs the latest stable Rust. 23 23 ## 3. Install postgres 24 - Debian 13 includes PostgreSQL 17: 25 24 ```bash 26 25 apt install -y postgresql postgresql-contrib 27 26 systemctl enable postgresql 28 27 systemctl start postgresql 29 - sudo -u postgres psql -c "CREATE USER bspds WITH PASSWORD 'your-secure-password';" 30 - sudo -u postgres psql -c "CREATE DATABASE pds OWNER bspds;" 31 - sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;" 28 + sudo -u postgres psql -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';" 29 + sudo -u postgres psql -c "CREATE DATABASE pds OWNER tranquil_pds;" 30 + sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;" 32 31 ``` 33 32 ## 4. Install minio 34 33 ```bash ··· 71 70 mc mb local/pds-blobs 72 71 ``` 73 72 ## 5. Install valkey 74 - Debian 13 includes Valkey 8: 75 73 ```bash 76 74 apt install -y valkey 77 75 systemctl enable valkey-server ··· 83 81 export PATH="$HOME/.deno/bin:$PATH" 84 82 echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc 85 83 ``` 86 - ## 7. Clone and Build BSPDS 84 + ## 7. Clone and Build Tranquil PDS 87 85 ```bash 88 86 cd /opt 89 - git clone https://tangled.org/lewis.moe/bspds-sandbox bspds 90 - cd bspds 87 + git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds 88 + cd tranquil-pds 91 89 cd frontend 92 90 deno task build 93 91 cd .. ··· 96 94 ## 8. Install sqlx-cli and Run Migrations 97 95 ```bash 98 96 cargo install sqlx-cli --no-default-features --features postgres 99 - export DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" 97 + export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" 100 98 sqlx migrate run 101 99 ``` 102 - ## 9. Configure BSPDS 100 + ## 9. Configure Tranquil PDS 103 101 ```bash 104 - mkdir -p /etc/bspds 105 - cp /opt/bspds/.env.example /etc/bspds/bspds.env 106 - chmod 600 /etc/bspds/bspds.env 102 + mkdir -p /etc/tranquil-pds 103 + cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.env 104 + chmod 600 /etc/tranquil-pds/tranquil-pds.env 107 105 ``` 108 - Edit `/etc/bspds/bspds.env` and fill in your values. Generate secrets with: 106 + Edit `/etc/tranquil-pds/tranquil-pds.env` and fill in your values. Generate secrets with: 109 107 ```bash 110 108 openssl rand -base64 48 111 109 ``` 112 110 ## 10. Create Systemd Service 113 111 ```bash 114 - useradd -r -s /sbin/nologin bspds 115 - cp /opt/bspds/target/release/bspds /usr/local/bin/ 116 - mkdir -p /var/lib/bspds 117 - cp -r /opt/bspds/frontend/dist /var/lib/bspds/frontend 118 - chown -R bspds:bspds /var/lib/bspds 119 - cat > /etc/systemd/system/bspds.service << 'EOF' 112 + useradd -r -s /sbin/nologin tranquil-pds 113 + cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/ 114 + mkdir -p /var/lib/tranquil-pds 115 + cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend 116 + chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds 117 + cat > /etc/systemd/system/tranquil-pds.service << 'EOF' 120 118 [Unit] 121 - Description=BSPDS - AT Protocol PDS 119 + Description=Tranquil PDS - AT Protocol PDS 122 120 After=network.target postgresql.service minio.service 123 121 [Service] 124 122 Type=simple 125 - User=bspds 126 - Group=bspds 127 - EnvironmentFile=/etc/bspds/bspds.env 128 - Environment=FRONTEND_DIR=/var/lib/bspds/frontend 129 - ExecStart=/usr/local/bin/bspds 123 + User=tranquil-pds 124 + Group=tranquil-pds 125 + EnvironmentFile=/etc/tranquil-pds/tranquil-pds.env 126 + Environment=FRONTEND_DIR=/var/lib/tranquil-pds/frontend 127 + ExecStart=/usr/local/bin/tranquil-pds 130 128 Restart=always 131 129 RestartSec=5 132 130 [Install] 133 131 WantedBy=multi-user.target 134 132 EOF 135 133 systemctl daemon-reload 136 - systemctl enable bspds 137 - systemctl start bspds 134 + systemctl enable tranquil-pds 135 + systemctl start tranquil-pds 138 136 ``` 139 137 ## 11. Install and Configure nginx 140 - Debian 13 includes nginx 1.26: 141 138 ```bash 142 139 apt install -y nginx certbot python3-certbot-nginx 143 - cat > /etc/nginx/sites-available/bspds << 'EOF' 140 + cat > /etc/nginx/sites-available/tranquil-pds << 'EOF' 144 141 server { 145 142 listen 80; 146 143 listen [::]:80; ··· 158 155 } 159 156 } 160 157 EOF 161 - ln -s /etc/nginx/sites-available/bspds /etc/nginx/sites-enabled/ 158 + ln -s /etc/nginx/sites-available/tranquil-pds /etc/nginx/sites-enabled/ 162 159 rm -f /etc/nginx/sites-enabled/default 163 160 nginx -t 164 161 systemctl reload nginx ··· 192 189 ``` 193 190 ## 14. Verify Installation 194 191 ```bash 195 - systemctl status bspds 192 + systemctl status tranquil-pds 196 193 curl -s https://pds.example.com/xrpc/_health | jq 197 194 curl -s https://pds.example.com/.well-known/atproto-did 198 195 ``` 199 196 ## Maintenance 200 197 View logs: 201 198 ```bash 202 - journalctl -u bspds -f 199 + journalctl -u tranquil-pds -f 203 200 ``` 204 - Update BSPDS: 201 + Update Tranquil PDS: 205 202 ```bash 206 - cd /opt/bspds 203 + cd /opt/tranquil-pds 207 204 git pull 208 205 cd frontend && deno task build && cd .. 209 206 cargo build --release 210 - systemctl stop bspds 211 - cp target/release/bspds /usr/local/bin/ 212 - cp -r frontend/dist /var/lib/bspds/frontend 213 - DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" sqlx migrate run 214 - systemctl start bspds 207 + systemctl stop tranquil-pds 208 + cp target/release/tranquil-pds /usr/local/bin/ 209 + cp -r frontend/dist /var/lib/tranquil-pds/frontend 210 + DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run 211 + systemctl start tranquil-pds 215 212 ``` 216 213 Backup database: 217 214 ```bash
+1 -1
docs/install-kubernetes.md
··· 1 - # BSPDS on Kubernetes 1 + # Tranquil PDS on Kubernetes 2 2 3 3 If you're reaching for kubernetes for this app, you're experienced enough to know how to spin up: 4 4
+36 -37
docs/install-openbsd.md
··· 1 - # BSPDS Production Installation on OpenBSD 1 + # Tranquil PDS Production Installation on OpenBSD 2 2 > **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified. 3 - This guide covers installing BSPDS on OpenBSD 7.8 (current release as of December 2025). 3 + This guide covers installing Tranquil PDS on OpenBSD 7.8. 4 4 ## Prerequisites 5 5 - A VPS with at least 2GB RAM and 20GB disk 6 6 - A domain name pointing to your server's IP ··· 16 16 ```sh 17 17 pkg_add rust 18 18 ``` 19 - OpenBSD 7.8 ships Rust 1.82+. For the latest stable (1.92+), use rustup: 19 + OpenBSD ships Rust in ports. For the latest stable, use rustup: 20 20 ```sh 21 21 pkg_add rustup 22 22 rustup-init -y ··· 24 24 rustup default stable 25 25 ``` 26 26 ## 3. Install postgres 27 - OpenBSD 7.8 includes PostgreSQL 17 (PostgreSQL 18 may not yet be in ports): 28 27 ```sh 29 28 pkg_add postgresql-server postgresql-client 30 29 mkdir -p /var/postgresql/data ··· 32 31 su - _postgresql -c "initdb -D /var/postgresql/data -U postgres -A scram-sha-256" 33 32 rcctl enable postgresql 34 33 rcctl start postgresql 35 - psql -U postgres -c "CREATE USER bspds WITH PASSWORD 'your-secure-password';" 36 - psql -U postgres -c "CREATE DATABASE pds OWNER bspds;" 37 - psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;" 34 + psql -U postgres -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';" 35 + psql -U postgres -c "CREATE DATABASE pds OWNER tranquil_pds;" 36 + psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;" 38 37 ``` 39 38 ## 4. Install minio 40 39 OpenBSD doesn't have a minio package. Options: ··· 93 92 export PATH="$HOME/.deno/bin:$PATH" 94 93 echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile 95 94 ``` 96 - ## 7. Clone and Build BSPDS 95 + ## 7. Clone and Build Tranquil PDS 97 96 ```sh 98 97 mkdir -p /opt && cd /opt 99 - git clone https://tangled.org/lewis.moe/bspds-sandbox bspds 100 - cd bspds 98 + git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds 99 + cd tranquil-pds 101 100 cd frontend 102 101 deno task build 103 102 cd .. ··· 106 105 ## 8. Install sqlx-cli and Run Migrations 107 106 ```sh 108 107 cargo install sqlx-cli --no-default-features --features postgres 109 - export DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" 108 + export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" 110 109 sqlx migrate run 111 110 ``` 112 - ## 9. Configure BSPDS 111 + ## 9. Configure Tranquil PDS 113 112 ```sh 114 - mkdir -p /etc/bspds 115 - cp /opt/bspds/.env.example /etc/bspds/bspds.conf 116 - chmod 600 /etc/bspds/bspds.conf 113 + mkdir -p /etc/tranquil-pds 114 + cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.conf 115 + chmod 600 /etc/tranquil-pds/tranquil-pds.conf 117 116 ``` 118 - Edit `/etc/bspds/bspds.conf` and fill in your values. Generate secrets with: 117 + Edit `/etc/tranquil-pds/tranquil-pds.conf` and fill in your values. Generate secrets with: 119 118 ```sh 120 119 openssl rand -base64 48 121 120 ``` 122 121 ## 10. Create rc.d Service 123 122 ```sh 124 - useradd -d /var/empty -s /sbin/nologin _bspds 125 - cp /opt/bspds/target/release/bspds /usr/local/bin/ 126 - mkdir -p /var/bspds 127 - cp -r /opt/bspds/frontend/dist /var/bspds/frontend 128 - chown -R _bspds:_bspds /var/bspds 129 - cat > /etc/rc.d/bspds << 'EOF' 123 + useradd -d /var/empty -s /sbin/nologin _tranquil_pds 124 + cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/ 125 + mkdir -p /var/tranquil-pds 126 + cp -r /opt/tranquil-pds/frontend/dist /var/tranquil-pds/frontend 127 + chown -R _tranquil_pds:_tranquil_pds /var/tranquil-pds 128 + cat > /etc/rc.d/tranquil_pds << 'EOF' 130 129 #!/bin/ksh 131 - daemon="/usr/local/bin/bspds" 132 - daemon_user="_bspds" 130 + daemon="/usr/local/bin/tranquil-pds" 131 + daemon_user="_tranquil_pds" 133 132 daemon_logger="daemon.info" 134 133 . /etc/rc.d/rc.subr 135 134 rc_pre() { 136 - export FRONTEND_DIR=/var/bspds/frontend 135 + export FRONTEND_DIR=/var/tranquil-pds/frontend 137 136 while IFS='=' read -r key value; do 138 137 case "$key" in 139 138 \#*|"") continue ;; 140 139 esac 141 140 export "$key=$value" 142 - done < /etc/bspds/bspds.conf 141 + done < /etc/tranquil-pds/tranquil-pds.conf 143 142 } 144 143 rc_cmd $1 145 144 EOF 146 - chmod +x /etc/rc.d/bspds 147 - rcctl enable bspds 148 - rcctl start bspds 145 + chmod +x /etc/rc.d/tranquil_pds 146 + rcctl enable tranquil_pds 147 + rcctl start tranquil_pds 149 148 ``` 150 149 ## 11. Install and Configure nginx 151 150 ```sh ··· 227 226 ``` 228 227 ## 14. Verify Installation 229 228 ```sh 230 - rcctl check bspds 229 + rcctl check tranquil_pds 231 230 ftp -o - https://pds.example.com/xrpc/_health 232 231 ftp -o - https://pds.example.com/.well-known/atproto-did 233 232 ``` ··· 236 235 ```sh 237 236 tail -f /var/log/daemon 238 237 ``` 239 - Update BSPDS: 238 + Update Tranquil PDS: 240 239 ```sh 241 - cd /opt/bspds 240 + cd /opt/tranquil-pds 242 241 git pull 243 242 cd frontend && deno task build && cd .. 244 243 cargo build --release 245 - rcctl stop bspds 246 - cp target/release/bspds /usr/local/bin/ 247 - cp -r frontend/dist /var/bspds/frontend 248 - DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" sqlx migrate run 249 - rcctl start bspds 244 + rcctl stop tranquil_pds 245 + cp target/release/tranquil-pds /usr/local/bin/ 246 + cp -r frontend/dist /var/tranquil-pds/frontend 247 + DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run 248 + rcctl start tranquil_pds 250 249 ``` 251 250 Backup database: 252 251 ```sh
+1 -1
frontend/index.html
··· 3 3 <head> 4 4 <meta charset="UTF-8" /> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 - <title>BSPDS</title> 6 + <title>Tranquil PDS</title> 7 7 <style> 8 8 html { background: #fafafa; } 9 9 @media (prefers-color-scheme: dark) { html { background: #1a1a1a; } }
+1 -1
frontend/package.json
··· 1 1 { 2 - "name": "bspds-frontend", 2 + "name": "tranquil-pds-frontend", 3 3 "private": true, 4 4 "version": "0.0.0", 5 5 "type": "module",
+8 -8
frontend/src/lib/api.ts
··· 255 255 signalNumber: string | null 256 256 signalVerified: boolean 257 257 }> { 258 - return xrpc('com.bspds.account.getNotificationPrefs', { token }) 258 + return xrpc('com.tranquil.account.getNotificationPrefs', { token }) 259 259 }, 260 260 261 261 async updateNotificationPrefs(token: string, prefs: { ··· 264 264 telegramUsername?: string 265 265 signalNumber?: string 266 266 }): Promise<{ success: boolean }> { 267 - return xrpc('com.bspds.account.updateNotificationPrefs', { 267 + return xrpc('com.tranquil.account.updateNotificationPrefs', { 268 268 method: 'POST', 269 269 token, 270 270 body: prefs, ··· 272 272 }, 273 273 274 274 async confirmChannelVerification(token: string, channel: string, code: string): Promise<{ success: boolean }> { 275 - return xrpc('com.bspds.account.confirmChannelVerification', { 275 + return xrpc('com.tranquil.account.confirmChannelVerification', { 276 276 method: 'POST', 277 277 token, 278 278 body: { channel, code }, ··· 289 289 body: string 290 290 }> 291 291 }> { 292 - return xrpc('com.bspds.account.getNotificationHistory', { token }) 292 + return xrpc('com.tranquil.account.getNotificationHistory', { token }) 293 293 }, 294 294 295 295 async getServerStats(token: string): Promise<{ ··· 298 298 recordCount: number 299 299 blobStorageBytes: number 300 300 }> { 301 - return xrpc('com.bspds.admin.getServerStats', { token }) 301 + return xrpc('com.tranquil.admin.getServerStats', { token }) 302 302 }, 303 303 304 304 async changePassword(token: string, currentPassword: string, newPassword: string): Promise<void> { 305 - await xrpc('com.bspds.account.changePassword', { 305 + await xrpc('com.tranquil.account.changePassword', { 306 306 method: 'POST', 307 307 token, 308 308 body: { currentPassword, newPassword }, ··· 317 317 isCurrent: boolean 318 318 }> 319 319 }> { 320 - return xrpc('com.bspds.account.listSessions', { token }) 320 + return xrpc('com.tranquil.account.listSessions', { token }) 321 321 }, 322 322 323 323 async revokeSession(token: string, sessionId: string): Promise<void> { 324 - await xrpc('com.bspds.account.revokeSession', { 324 + await xrpc('com.tranquil.account.revokeSession', { 325 325 method: 'POST', 326 326 token, 327 327 body: { sessionId },
+2 -2
frontend/src/lib/auth.svelte.ts
··· 1 1 import { api, type Session, type CreateAccountParams, type CreateAccountResult, ApiError } from './api' 2 2 import { startOAuthLogin, handleOAuthCallback, checkForOAuthCallback, clearOAuthCallbackParams, refreshOAuthToken } from './oauth' 3 3 4 - const STORAGE_KEY = 'bspds_session' 5 - const ACCOUNTS_KEY = 'bspds_accounts' 4 + const STORAGE_KEY = 'tranquil_pds_session' 5 + const ACCOUNTS_KEY = 'tranquil_pds_accounts' 6 6 7 7 export interface SavedAccount { 8 8 did: string
+2 -2
frontend/src/lib/oauth.ts
··· 1 - const OAUTH_STATE_KEY = 'bspds_oauth_state' 2 - const OAUTH_VERIFIER_KEY = 'bspds_oauth_verifier' 1 + const OAUTH_STATE_KEY = 'tranquil_pds_oauth_state' 2 + const OAUTH_VERIFIER_KEY = 'tranquil_pds_oauth_verifier' 3 3 4 4 interface OAuthState { 5 5 state: string
+1 -1
frontend/src/routes/Register.svelte
··· 3 3 import { navigate } from '../lib/router.svelte' 4 4 import { api, ApiError, type VerificationChannel } from '../lib/api' 5 5 6 - const STORAGE_KEY = 'bspds_pending_verification' 6 + const STORAGE_KEY = 'tranquil_pds_pending_verification' 7 7 8 8 let handle = $state('') 9 9 let email = $state('')
+1 -1
frontend/src/routes/Verify.svelte
··· 2 2 import { confirmSignup, resendVerification, getAuthState } from '../lib/auth.svelte' 3 3 import { navigate } from '../lib/router.svelte' 4 4 5 - const STORAGE_KEY = 'bspds_pending_verification' 5 + const STORAGE_KEY = 'tranquil_pds_pending_verification' 6 6 7 7 interface PendingVerification { 8 8 did: string
+3 -3
frontend/src/tests/Dashboard.test.ts
··· 10 10 setupAuthenticatedUser, 11 11 setupUnauthenticatedUser, 12 12 } from './mocks' 13 - const STORAGE_KEY = 'bspds_session' 13 + const STORAGE_KEY = 'tranquil_pds_session' 14 14 describe('Dashboard', () => { 15 15 beforeEach(() => { 16 16 clearMocks() ··· 38 38 await waitFor(() => { 39 39 expect(screen.getByRole('heading', { name: /dashboard/i })).toBeInTheDocument() 40 40 expect(screen.getByRole('heading', { name: /account overview/i })).toBeInTheDocument() 41 - expect(screen.getByText(/@testuser\.test\.bspds\.dev/)).toBeInTheDocument() 42 - expect(screen.getByText(/did:web:test\.bspds\.dev:u:testuser/)).toBeInTheDocument() 41 + expect(screen.getByText(/@testuser\.test\.tranquil\.dev/)).toBeInTheDocument() 42 + expect(screen.getByText(/did:web:test\.tranquil\.dev:u:testuser/)).toBeInTheDocument() 43 43 expect(screen.getByText('test@example.com')).toBeInTheDocument() 44 44 expect(screen.getByText('Verified')).toBeInTheDocument() 45 45 expect(screen.getByText('Verified')).toHaveClass('badge', 'success')
+2 -2
frontend/src/tests/Login.test.ts
··· 95 95 json: async () => ({ 96 96 error: 'AccountNotVerified', 97 97 message: 'Account not verified', 98 - did: 'did:web:test.bspds.dev:u:testuser', 98 + did: 'did:web:test.tranquil.dev:u:testuser', 99 99 }), 100 100 })) 101 101 render(Login) ··· 116 116 json: async () => ({ 117 117 error: 'AccountNotVerified', 118 118 message: 'Account not verified', 119 - did: 'did:web:test.bspds.dev:u:testuser', 119 + did: 'did:web:test.tranquil.dev:u:testuser', 120 120 }), 121 121 })) 122 122 render(Login)
+27 -27
frontend/src/tests/Notifications.test.ts
··· 28 28 describe('page structure', () => { 29 29 beforeEach(() => { 30 30 setupAuthenticatedUser() 31 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 31 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 32 32 jsonResponse(mockData.notificationPrefs()) 33 33 ) 34 34 }) ··· 48 48 setupAuthenticatedUser() 49 49 }) 50 50 it('shows loading text while fetching preferences', async () => { 51 - mockEndpoint('com.bspds.account.getNotificationPrefs', async () => { 51 + mockEndpoint('com.tranquil.account.getNotificationPrefs', async () => { 52 52 await new Promise(resolve => setTimeout(resolve, 100)) 53 53 return jsonResponse(mockData.notificationPrefs()) 54 54 }) ··· 61 61 setupAuthenticatedUser() 62 62 }) 63 63 it('displays all four channel options', async () => { 64 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 64 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 65 65 jsonResponse(mockData.notificationPrefs()) 66 66 ) 67 67 render(Notifications) ··· 73 73 }) 74 74 }) 75 75 it('email channel is always selectable', async () => { 76 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 76 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 77 77 jsonResponse(mockData.notificationPrefs()) 78 78 ) 79 79 render(Notifications) ··· 83 83 }) 84 84 }) 85 85 it('discord channel is disabled when not configured', async () => { 86 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 86 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 87 87 jsonResponse(mockData.notificationPrefs({ discordId: null })) 88 88 ) 89 89 render(Notifications) ··· 93 93 }) 94 94 }) 95 95 it('discord channel is enabled when configured', async () => { 96 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 96 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 97 97 jsonResponse(mockData.notificationPrefs({ discordId: '123456789' })) 98 98 ) 99 99 render(Notifications) ··· 103 103 }) 104 104 }) 105 105 it('shows hint for disabled channels', async () => { 106 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 106 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 107 107 jsonResponse(mockData.notificationPrefs()) 108 108 ) 109 109 render(Notifications) ··· 112 112 }) 113 113 }) 114 114 it('selects current preferred channel', async () => { 115 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 115 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 116 116 jsonResponse(mockData.notificationPrefs({ preferredChannel: 'email' })) 117 117 ) 118 118 render(Notifications) ··· 127 127 setupAuthenticatedUser() 128 128 }) 129 129 it('displays email as readonly with current value', async () => { 130 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 130 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 131 131 jsonResponse(mockData.notificationPrefs()) 132 132 ) 133 133 render(Notifications) ··· 138 138 }) 139 139 }) 140 140 it('displays all channel inputs with current values', async () => { 141 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 141 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 142 142 jsonResponse(mockData.notificationPrefs({ 143 143 discordId: '123456789', 144 144 telegramUsername: 'testuser', ··· 158 158 setupAuthenticatedUser() 159 159 }) 160 160 it('shows Primary badge for email', async () => { 161 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 161 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 162 162 jsonResponse(mockData.notificationPrefs()) 163 163 ) 164 164 render(Notifications) ··· 167 167 }) 168 168 }) 169 169 it('shows Verified badge for verified discord', async () => { 170 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 170 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 171 171 jsonResponse(mockData.notificationPrefs({ 172 172 discordId: '123456789', 173 173 discordVerified: true, ··· 180 180 }) 181 181 }) 182 182 it('shows Not verified badge for unverified discord', async () => { 183 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 183 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 184 184 jsonResponse(mockData.notificationPrefs({ 185 185 discordId: '123456789', 186 186 discordVerified: false, ··· 192 192 }) 193 193 }) 194 194 it('does not show badge when channel not configured', async () => { 195 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 195 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 196 196 jsonResponse(mockData.notificationPrefs()) 197 197 ) 198 198 render(Notifications) ··· 208 208 }) 209 209 it('calls updateNotificationPrefs with correct data', async () => { 210 210 let capturedBody: Record<string, unknown> | null = null 211 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 211 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 212 212 jsonResponse(mockData.notificationPrefs()) 213 213 ) 214 - mockEndpoint('com.bspds.account.updateNotificationPrefs', (_url, options) => { 214 + mockEndpoint('com.tranquil.account.updateNotificationPrefs', (_url, options) => { 215 215 capturedBody = JSON.parse((options?.body as string) || '{}') 216 216 return jsonResponse({ success: true }) 217 217 }) ··· 228 228 }) 229 229 }) 230 230 it('shows loading state while saving', async () => { 231 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 231 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 232 232 jsonResponse(mockData.notificationPrefs()) 233 233 ) 234 - mockEndpoint('com.bspds.account.updateNotificationPrefs', async () => { 234 + mockEndpoint('com.tranquil.account.updateNotificationPrefs', async () => { 235 235 await new Promise(resolve => setTimeout(resolve, 100)) 236 236 return jsonResponse({ success: true }) 237 237 }) ··· 244 244 expect(screen.getByRole('button', { name: /saving/i })).toBeDisabled() 245 245 }) 246 246 it('shows success message after saving', async () => { 247 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 247 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 248 248 jsonResponse(mockData.notificationPrefs()) 249 249 ) 250 - mockEndpoint('com.bspds.account.updateNotificationPrefs', () => 250 + mockEndpoint('com.tranquil.account.updateNotificationPrefs', () => 251 251 jsonResponse({ success: true }) 252 252 ) 253 253 render(Notifications) ··· 260 260 }) 261 261 }) 262 262 it('shows error when save fails', async () => { 263 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 263 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 264 264 jsonResponse(mockData.notificationPrefs()) 265 265 ) 266 - mockEndpoint('com.bspds.account.updateNotificationPrefs', () => 266 + mockEndpoint('com.tranquil.account.updateNotificationPrefs', () => 267 267 errorResponse('InvalidRequest', 'Invalid channel configuration', 400) 268 268 ) 269 269 render(Notifications) ··· 278 278 }) 279 279 it('reloads preferences after successful save', async () => { 280 280 let loadCount = 0 281 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => { 281 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => { 282 282 loadCount++ 283 283 return jsonResponse(mockData.notificationPrefs()) 284 284 }) 285 - mockEndpoint('com.bspds.account.updateNotificationPrefs', () => 285 + mockEndpoint('com.tranquil.account.updateNotificationPrefs', () => 286 286 jsonResponse({ success: true }) 287 287 ) 288 288 render(Notifications) ··· 301 301 setupAuthenticatedUser() 302 302 }) 303 303 it('enables discord channel after entering discord ID', async () => { 304 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 304 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 305 305 jsonResponse(mockData.notificationPrefs()) 306 306 ) 307 307 render(Notifications) ··· 314 314 }) 315 315 }) 316 316 it('allows selecting a configured channel', async () => { 317 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 317 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 318 318 jsonResponse(mockData.notificationPrefs({ 319 319 discordId: '123456789', 320 320 discordVerified: true, ··· 334 334 setupAuthenticatedUser() 335 335 }) 336 336 it('shows error when loading preferences fails', async () => { 337 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 337 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 338 338 errorResponse('InternalError', 'Database connection failed', 500) 339 339 ) 340 340 render(Notifications)
+2 -2
frontend/src/tests/Settings.test.ts
··· 176 176 it('displays current handle', async () => { 177 177 render(Settings) 178 178 await waitFor(() => { 179 - expect(screen.getByText(/current: @testuser\.test\.bspds\.dev/i)).toBeInTheDocument() 179 + expect(screen.getByText(/current: @testuser\.test\.tranquil\.dev/i)).toBeInTheDocument() 180 180 }) 181 181 }) 182 182 it('calls updateHandle with new handle', async () => { ··· 314 314 await waitFor(() => { 315 315 expect(capturedBody?.token).toBe('DEL123') 316 316 expect(capturedBody?.password).toBe('mypassword') 317 - expect(capturedBody?.did).toBe('did:web:test.bspds.dev:u:testuser') 317 + expect(capturedBody?.did).toBe('did:web:test.tranquil.dev:u:testuser') 318 318 }) 319 319 }) 320 320 it('navigates to login after successful deletion', async () => {
+8 -8
frontend/src/tests/mocks.ts
··· 85 85 } 86 86 export const mockData = { 87 87 session: (overrides?: Partial<Session>): Session => ({ 88 - did: 'did:web:test.bspds.dev:u:testuser', 89 - handle: 'testuser.test.bspds.dev', 88 + did: 'did:web:test.tranquil.dev:u:testuser', 89 + handle: 'testuser.test.tranquil.dev', 90 90 email: 'test@example.com', 91 91 emailConfirmed: true, 92 92 accessJwt: 'mock-access-jwt-token', ··· 102 102 code: 'test-invite-123', 103 103 available: 1, 104 104 disabled: false, 105 - forAccount: 'did:web:test.bspds.dev:u:testuser', 106 - createdBy: 'did:web:test.bspds.dev:u:testuser', 105 + forAccount: 'did:web:test.tranquil.dev:u:testuser', 106 + createdBy: 'did:web:test.tranquil.dev:u:testuser', 107 107 createdAt: new Date().toISOString(), 108 108 uses: [], 109 109 ...overrides, ··· 120 120 ...overrides, 121 121 }), 122 122 describeServer: () => ({ 123 - availableUserDomains: ['test.bspds.dev'], 123 + availableUserDomains: ['test.tranquil.dev'], 124 124 inviteCodeRequired: false, 125 125 links: { 126 126 privacyPolicy: 'https://example.com/privacy', ··· 128 128 }, 129 129 }), 130 130 describeRepo: (did: string) => ({ 131 - handle: 'testuser.test.bspds.dev', 131 + handle: 'testuser.test.tranquil.dev', 132 132 did, 133 133 didDoc: {}, 134 134 collections: ['app.bsky.feed.post', 'app.bsky.feed.like', 'app.bsky.graph.follow'], ··· 173 173 mockEndpoint('com.atproto.server.createInviteCode', () => 174 174 jsonResponse({ code: 'new-invite-' + Date.now() }) 175 175 ) 176 - mockEndpoint('com.bspds.account.getNotificationPrefs', () => 176 + mockEndpoint('com.tranquil.account.getNotificationPrefs', () => 177 177 jsonResponse(mockData.notificationPrefs()) 178 178 ) 179 - mockEndpoint('com.bspds.account.updateNotificationPrefs', () => 179 + mockEndpoint('com.tranquil.account.updateNotificationPrefs', () => 180 180 jsonResponse({ success: true }) 181 181 ) 182 182 mockEndpoint('com.atproto.server.requestEmailUpdate', () =>
+1 -1
justfile
··· 25 25 ./scripts/run-tests.sh --test {{file}} 26 26 # Run tests with testcontainers (slower, no shared infra) 27 27 test-standalone: 28 - BSPDS_ALLOW_INSECURE_SECRETS=1 cargo test 28 + TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 cargo test 29 29 # Manually manage test infrastructure (for debugging) 30 30 test-infra-start: 31 31 ./scripts/test-infra.sh start
+4 -4
nginx.prod.conf
··· 34 34 ssl_session_tickets off; 35 35 ssl_stapling on; 36 36 ssl_stapling_verify on; 37 - upstream bspds { 38 - server bspds:3000; 37 + upstream tranquil-pds { 38 + server tranquil-pds:3000; 39 39 keepalive 32; 40 40 } 41 41 server { ··· 57 57 ssl_certificate_key /etc/nginx/certs/live/${PDS_HOSTNAME}/privkey.pem; 58 58 client_max_body_size 100M; 59 59 location / { 60 - proxy_pass http://bspds; 60 + proxy_pass http://tranquil-pds; 61 61 proxy_http_version 1.1; 62 62 proxy_set_header Upgrade $http_upgrade; 63 63 proxy_set_header Connection "upgrade"; ··· 71 71 proxy_request_buffering off; 72 72 } 73 73 location /xrpc/com.atproto.sync.subscribeRepos { 74 - proxy_pass http://bspds; 74 + proxy_pass http://tranquil-pds; 75 75 proxy_http_version 1.1; 76 76 proxy_set_header Upgrade $http_upgrade; 77 77 proxy_set_header Connection "upgrade";
+1 -1
observability/prometheus.yml
··· 7 7 static_configs: 8 8 - targets: ['localhost:9090'] 9 9 10 - - job_name: 'bspds' 10 + - job_name: 'tranquil-pds' 11 11 static_configs: 12 12 - targets: ['app:3000'] 13 13 metrics_path: /metrics
+75 -75
scripts/install-debian.sh
··· 24 24 nuke_installation() { 25 25 log_warn "NUKING EXISTING INSTALLATION" 26 26 log_info "Stopping services..." 27 - systemctl stop bspds 2>/dev/null || true 28 - systemctl disable bspds 2>/dev/null || true 27 + systemctl stop tranquil-pds 2>/dev/null || true 28 + systemctl disable tranquil-pds 2>/dev/null || true 29 29 30 - log_info "Removing BSPDS files..." 31 - rm -rf /opt/bspds 32 - rm -rf /var/lib/bspds 33 - rm -f /usr/local/bin/bspds 34 - rm -f /usr/local/bin/bspds-sendmail 35 - rm -f /usr/local/bin/bspds-mailq 36 - rm -rf /var/spool/bspds-mail 37 - rm -f /etc/systemd/system/bspds.service 30 + log_info "Removing Tranquil PDS files..." 31 + rm -rf /opt/tranquil-pds 32 + rm -rf /var/lib/tranquil-pds 33 + rm -f /usr/local/bin/tranquil-pds 34 + rm -f /usr/local/bin/tranquil-pds-sendmail 35 + rm -f /usr/local/bin/tranquil-pds-mailq 36 + rm -rf /var/spool/tranquil-pds-mail 37 + rm -f /etc/systemd/system/tranquil-pds.service 38 38 systemctl daemon-reload 39 39 40 - log_info "Removing BSPDS configuration..." 41 - rm -rf /etc/bspds 40 + log_info "Removing Tranquil PDS configuration..." 41 + rm -rf /etc/tranquil-pds 42 42 43 43 log_info "Dropping postgres database and user..." 44 44 sudo -u postgres psql -c "DROP DATABASE IF EXISTS pds;" 2>/dev/null || true 45 - sudo -u postgres psql -c "DROP USER IF EXISTS bspds;" 2>/dev/null || true 45 + sudo -u postgres psql -c "DROP USER IF EXISTS tranquil_pds;" 2>/dev/null || true 46 46 47 47 log_info "Removing minio bucket..." 48 48 if command -v mc &>/dev/null; then ··· 54 54 rm -f /etc/default/minio 2>/dev/null || true 55 55 56 56 log_info "Removing nginx config..." 57 - rm -f /etc/nginx/sites-enabled/bspds 58 - rm -f /etc/nginx/sites-available/bspds 57 + rm -f /etc/nginx/sites-enabled/tranquil-pds 58 + rm -f /etc/nginx/sites-available/tranquil-pds 59 59 systemctl reload nginx 2>/dev/null || true 60 60 61 61 log_success "Previous installation nuked" 62 62 } 63 63 64 - if [[ -f /etc/bspds/bspds.env ]] || [[ -d /opt/bspds ]] || [[ -f /usr/local/bin/bspds ]]; then 64 + if [[ -f /etc/tranquil-pds/tranquil-pds.env ]] || [[ -d /opt/tranquil-pds ]] || [[ -f /usr/local/bin/tranquil-pds ]]; then 65 65 log_warn "Existing installation detected" 66 66 echo "" 67 67 echo "Options:" ··· 76 76 echo "" 77 77 log_warn "This will DELETE:" 78 78 echo " - PostgreSQL database 'pds' and all data" 79 - echo " - All BSPDS configuration and credentials" 80 - echo " - All source code in /opt/bspds" 79 + echo " - All Tranquil PDS configuration and credentials" 80 + echo " - All source code in /opt/tranquil-pds" 81 81 echo " - MinIO bucket 'pds-blobs' and all blobs" 82 82 echo "" 83 83 read -p "Type 'NUKE' to confirm: " CONFIRM_NUKE ··· 102 102 fi 103 103 104 104 echo "" 105 - log_info "BSPDS Installation Script for Debian" 105 + log_info "Tranquil PDS Installation Script for Debian" 106 106 echo "" 107 107 108 108 get_public_ips() { ··· 142 142 exit 0 143 143 fi 144 144 145 - CREDENTIALS_FILE="/etc/bspds/.credentials" 145 + CREDENTIALS_FILE="/etc/tranquil-pds/.credentials" 146 146 if [[ -f "$CREDENTIALS_FILE" ]]; then 147 147 log_info "Loading existing credentials..." 148 148 source "$CREDENTIALS_FILE" ··· 154 154 DB_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32) 155 155 MINIO_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32) 156 156 157 - mkdir -p /etc/bspds 157 + mkdir -p /etc/tranquil-pds 158 158 cat > "$CREDENTIALS_FILE" << EOF 159 159 JWT_SECRET="$JWT_SECRET" 160 160 DPOP_SECRET="$DPOP_SECRET" ··· 196 196 apt install -y postgresql postgresql-contrib 197 197 systemctl enable postgresql 198 198 systemctl start postgresql 199 - sudo -u postgres psql -c "CREATE USER bspds WITH PASSWORD '${DB_PASSWORD}';" 2>/dev/null || \ 200 - sudo -u postgres psql -c "ALTER USER bspds WITH PASSWORD '${DB_PASSWORD}';" 201 - sudo -u postgres psql -c "CREATE DATABASE pds OWNER bspds;" 2>/dev/null || true 202 - sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;" 199 + sudo -u postgres psql -c "CREATE USER tranquil_pds WITH PASSWORD '${DB_PASSWORD}';" 2>/dev/null || \ 200 + sudo -u postgres psql -c "ALTER USER tranquil_pds WITH PASSWORD '${DB_PASSWORD}';" 201 + sudo -u postgres psql -c "CREATE DATABASE pds OWNER tranquil_pds;" 2>/dev/null || true 202 + sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;" 203 203 log_success "postgres configured" 204 204 205 205 log_info "Installing valkey..." ··· 292 292 grep -q 'deno/bin' ~/.bashrc 2>/dev/null || echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc 293 293 fi 294 294 295 - log_info "Cloning BSPDS..." 296 - if [[ ! -d /opt/bspds ]]; then 297 - git clone https://tangled.org/lewis.moe/bspds-sandbox /opt/bspds 295 + log_info "Cloning Tranquil PDS..." 296 + if [[ ! -d /opt/tranquil-pds ]]; then 297 + git clone https://tangled.org/lewis.moe/bspds-sandbox /opt/tranquil-pds 298 298 else 299 - cd /opt/bspds && git pull 299 + cd /opt/tranquil-pds && git pull 300 300 fi 301 - cd /opt/bspds 301 + cd /opt/tranquil-pds 302 302 303 303 log_info "Building frontend..." 304 304 "$HOME/.deno/bin/deno" task build --filter=frontend 305 305 log_success "Frontend built" 306 306 307 - log_info "Building BSPDS (this takes a while)..." 307 + log_info "Building Tranquil PDS (this takes a while)..." 308 308 source "$HOME/.cargo/env" 309 309 if [[ $TOTAL_MEM_KB -lt 4000000 ]]; then 310 310 log_info "Low memory - limiting parallel jobs" ··· 312 312 else 313 313 cargo build --release 314 314 fi 315 - log_success "BSPDS built" 315 + log_success "Tranquil PDS built" 316 316 317 317 log_info "Running migrations..." 318 318 cargo install sqlx-cli --no-default-features --features postgres 319 - export DATABASE_URL="postgres://bspds:${DB_PASSWORD}@localhost:5432/pds" 319 + export DATABASE_URL="postgres://tranquil_pds:${DB_PASSWORD}@localhost:5432/pds" 320 320 "$HOME/.cargo/bin/sqlx" migrate run 321 321 log_success "Migrations complete" 322 322 323 323 log_info "Setting up mail trap..." 324 - mkdir -p /var/spool/bspds-mail 325 - chmod 1777 /var/spool/bspds-mail 324 + mkdir -p /var/spool/tranquil-pds-mail 325 + chmod 1777 /var/spool/tranquil-pds-mail 326 326 327 - cat > /usr/local/bin/bspds-sendmail << 'SENDMAIL_EOF' 327 + cat > /usr/local/bin/tranquil-pds-sendmail << 'SENDMAIL_EOF' 328 328 #!/bin/bash 329 - MAIL_DIR="/var/spool/bspds-mail" 329 + MAIL_DIR="/var/spool/tranquil-pds-mail" 330 330 TIMESTAMP=$(date +%Y%m%d-%H%M%S) 331 331 RANDOM_ID=$(head -c 4 /dev/urandom | xxd -p) 332 332 MAIL_FILE="${MAIL_DIR}/${TIMESTAMP}-${RANDOM_ID}.eml" 333 333 mkdir -p "$MAIL_DIR" 334 334 { 335 - echo "X-BSPDS-Received: $(date -Iseconds)" 336 - echo "X-BSPDS-Args: $*" 335 + echo "X-Tranquil-PDS-Received: $(date -Iseconds)" 336 + echo "X-Tranquil-PDS-Args: $*" 337 337 echo "" 338 338 cat 339 339 } > "$MAIL_FILE" 340 340 chmod 644 "$MAIL_FILE" 341 341 exit 0 342 342 SENDMAIL_EOF 343 - chmod +x /usr/local/bin/bspds-sendmail 343 + chmod +x /usr/local/bin/tranquil-pds-sendmail 344 344 345 - cat > /usr/local/bin/bspds-mailq << 'MAILQ_EOF' 345 + cat > /usr/local/bin/tranquil-pds-mailq << 'MAILQ_EOF' 346 346 #!/bin/bash 347 - MAIL_DIR="/var/spool/bspds-mail" 347 + MAIL_DIR="/var/spool/tranquil-pds-mail" 348 348 case "${1:-list}" in 349 349 list) 350 350 ls -lt "$MAIL_DIR"/*.eml 2>/dev/null | head -20 || echo "No emails" ··· 365 365 [[ -f "$f" ]] && cat "$f" || echo "Not found" 366 366 ;; 367 367 *) 368 - [[ -f "$MAIL_DIR/$1" ]] && cat "$MAIL_DIR/$1" || echo "Usage: bspds-mailq [list|latest|clear|count|N]" 368 + [[ -f "$MAIL_DIR/$1" ]] && cat "$MAIL_DIR/$1" || echo "Usage: tranquil-pds-mailq [list|latest|clear|count|N]" 369 369 ;; 370 370 esac 371 371 MAILQ_EOF 372 - chmod +x /usr/local/bin/bspds-mailq 372 + chmod +x /usr/local/bin/tranquil-pds-mailq 373 373 374 - log_info "Creating BSPDS configuration..." 375 - cat > /etc/bspds/bspds.env << EOF 374 + log_info "Creating Tranquil PDS configuration..." 375 + cat > /etc/tranquil-pds/tranquil-pds.env << EOF 376 376 SERVER_HOST=127.0.0.1 377 377 SERVER_PORT=3000 378 378 PDS_HOSTNAME=${PDS_DOMAIN} 379 - DATABASE_URL=postgres://bspds:${DB_PASSWORD}@localhost:5432/pds 379 + DATABASE_URL=postgres://tranquil_pds:${DB_PASSWORD}@localhost:5432/pds 380 380 DATABASE_MAX_CONNECTIONS=100 381 381 DATABASE_MIN_CONNECTIONS=10 382 382 S3_ENDPOINT=http://localhost:9000 ··· 392 392 CRAWLERS=https://bsky.network 393 393 AVAILABLE_USER_DOMAINS=${PDS_DOMAIN} 394 394 MAIL_FROM_ADDRESS=noreply@${PDS_DOMAIN} 395 - MAIL_FROM_NAME=BSPDS 396 - SENDMAIL_PATH=/usr/local/bin/bspds-sendmail 395 + MAIL_FROM_NAME=Tranquil PDS 396 + SENDMAIL_PATH=/usr/local/bin/tranquil-pds-sendmail 397 397 EOF 398 - chmod 600 /etc/bspds/bspds.env 398 + chmod 600 /etc/tranquil-pds/tranquil-pds.env 399 399 400 - log_info "Installing BSPDS..." 401 - id -u bspds &>/dev/null || useradd -r -s /sbin/nologin bspds 402 - cp /opt/bspds/target/release/bspds /usr/local/bin/ 403 - mkdir -p /var/lib/bspds 404 - cp -r /opt/bspds/frontend/dist /var/lib/bspds/frontend 405 - chown -R bspds:bspds /var/lib/bspds 400 + log_info "Installing Tranquil PDS..." 401 + id -u tranquil-pds &>/dev/null || useradd -r -s /sbin/nologin tranquil-pds 402 + cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/ 403 + mkdir -p /var/lib/tranquil-pds 404 + cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend 405 + chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds 406 406 407 - cat > /etc/systemd/system/bspds.service << 'EOF' 407 + cat > /etc/systemd/system/tranquil-pds.service << 'EOF' 408 408 [Unit] 409 - Description=BSPDS - AT Protocol PDS 409 + Description=Tranquil PDS - AT Protocol PDS 410 410 After=network.target postgresql.service minio.service 411 411 412 412 [Service] 413 413 Type=simple 414 - User=bspds 415 - Group=bspds 416 - EnvironmentFile=/etc/bspds/bspds.env 417 - Environment=FRONTEND_DIR=/var/lib/bspds/frontend 418 - ExecStart=/usr/local/bin/bspds 414 + User=tranquil-pds 415 + Group=tranquil-pds 416 + EnvironmentFile=/etc/tranquil-pds/tranquil-pds.env 417 + Environment=FRONTEND_DIR=/var/lib/tranquil-pds/frontend 418 + ExecStart=/usr/local/bin/tranquil-pds 419 419 Restart=always 420 420 RestartSec=5 421 421 ··· 424 424 EOF 425 425 426 426 systemctl daemon-reload 427 - systemctl enable bspds 428 - systemctl start bspds 429 - log_success "BSPDS service started" 427 + systemctl enable tranquil-pds 428 + systemctl start tranquil-pds 429 + log_success "Tranquil PDS service started" 430 430 431 431 log_info "Installing nginx..." 432 432 apt install -y nginx 433 - cat > /etc/nginx/sites-available/bspds << EOF 433 + cat > /etc/nginx/sites-available/tranquil-pds << EOF 434 434 server { 435 435 listen 80; 436 436 listen [::]:80; ··· 456 456 } 457 457 EOF 458 458 459 - ln -sf /etc/nginx/sites-available/bspds /etc/nginx/sites-enabled/ 459 + ln -sf /etc/nginx/sites-available/tranquil-pds /etc/nginx/sites-enabled/ 460 460 rm -f /etc/nginx/sites-enabled/default 461 461 nginx -t 462 462 systemctl reload nginx ··· 496 496 -d "${PDS_DOMAIN}" -d "*.${PDS_DOMAIN}" \ 497 497 --email "${CERTBOT_EMAIL}" --agree-tos; then 498 498 499 - cat > /etc/nginx/sites-available/bspds << EOF 499 + cat > /etc/nginx/sites-available/tranquil-pds << EOF 500 500 server { 501 501 listen 80; 502 502 listen [::]:80; ··· 564 564 log_info "Verifying installation..." 565 565 sleep 3 566 566 if curl -s "http://localhost:3000/xrpc/_health" | grep -q "version"; then 567 - log_success "BSPDS is responding" 567 + log_success "Tranquil PDS is responding" 568 568 else 569 - log_warn "BSPDS may still be starting. Check: journalctl -u bspds -f" 569 + log_warn "Tranquil PDS may still be starting. Check: journalctl -u tranquil-pds -f" 570 570 fi 571 571 572 572 echo "" ··· 574 574 echo "" 575 575 echo "PDS: https://${PDS_DOMAIN}" 576 576 echo "" 577 - echo "Credentials (also in /etc/bspds/.credentials):" 577 + echo "Credentials (also in /etc/tranquil-pds/.credentials):" 578 578 echo " DB password: ${DB_PASSWORD}" 579 579 echo " MinIO password: ${MINIO_PASSWORD}" 580 580 echo "" 581 581 echo "Commands:" 582 - echo " journalctl -u bspds -f # logs" 583 - echo " systemctl restart bspds # restart" 584 - echo " bspds-mailq # view trapped emails" 582 + echo " journalctl -u tranquil-pds -f # logs" 583 + echo " systemctl restart tranquil-pds # restart" 584 + echo " tranquil-pds-mailq # view trapped emails" 585 585 echo ""
+1 -1
scripts/run-tests.sh
··· 10 10 } 11 11 trap cleanup EXIT 12 12 "$INFRA_SCRIPT" start 13 - source "${TMPDIR:-/tmp}/bspds_test_infra.env" 13 + source "${TMPDIR:-/tmp}/tranquil_pds_test_infra.env" 14 14 echo "" 15 15 echo "Running database migrations..." 16 16 sqlx database create 2>/dev/null || true
+8 -8
scripts/test-infra.sh
··· 1 1 #!/usr/bin/env bash 2 2 set -euo pipefail 3 - INFRA_FILE="${TMPDIR:-/tmp}/bspds_test_infra.env" 4 - CONTAINER_PREFIX="bspds-test" 3 + INFRA_FILE="${TMPDIR:-/tmp}/tranquil_pds_test_infra.env" 4 + CONTAINER_PREFIX="tranquil-pds-test" 5 5 command_exists() { 6 6 command -v "$1" >/dev/null 2>&1 7 7 } ··· 40 40 -e POSTGRES_USER=postgres \ 41 41 -e POSTGRES_DB=postgres \ 42 42 -P \ 43 - --label bspds_test=true \ 43 + --label tranquil_pds_test=true \ 44 44 postgres:18-alpine >/dev/null 45 45 echo "Starting MinIO..." 46 46 $CONTAINER_CMD run -d \ ··· 48 48 -e MINIO_ROOT_USER=minioadmin \ 49 49 -e MINIO_ROOT_PASSWORD=minioadmin \ 50 50 -P \ 51 - --label bspds_test=true \ 51 + --label tranquil_pds_test=true \ 52 52 minio/minio:latest server /data >/dev/null 53 53 echo "Starting Valkey..." 54 54 $CONTAINER_CMD run -d \ 55 55 --name "${CONTAINER_PREFIX}-valkey" \ 56 56 -P \ 57 - --label bspds_test=true \ 57 + --label tranquil_pds_test=true \ 58 58 valkey/valkey:8-alpine >/dev/null 59 59 echo "Waiting for services to be ready..." 60 60 sleep 2 ··· 95 95 export AWS_SECRET_ACCESS_KEY="minioadmin" 96 96 export AWS_REGION="us-east-1" 97 97 export VALKEY_URL="redis://127.0.0.1:${VALKEY_PORT}" 98 - export BSPDS_TEST_INFRA_READY="1" 99 - export BSPDS_ALLOW_INSECURE_SECRETS="1" 98 + export TRANQUIL_PDS_TEST_INFRA_READY="1" 99 + export TRANQUIL_PDS_ALLOW_INSECURE_SECRETS="1" 100 100 export SKIP_IMPORT_VERIFICATION="true" 101 101 export DISABLE_RATE_LIMITING="1" 102 102 EOF ··· 125 125 fi 126 126 echo "" 127 127 echo "Containers:" 128 - $CONTAINER_CMD ps -a --filter "label=bspds_test=true" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo " (none)" 128 + $CONTAINER_CMD ps -a --filter "label=tranquil_pds_test=true" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo " (none)" 129 129 } 130 130 case "${1:-}" in 131 131 start)
+18 -2
src/api/server/account_status.rs
··· 157 157 .await; 158 158 match result { 159 159 Ok(_) => { 160 - if let Some(h) = handle { 160 + if let Some(ref h) = handle { 161 161 let _ = state.cache.delete(&format!("handle:{}", h)).await; 162 + } 163 + if let Err(e) = 164 + crate::api::repo::record::sequence_account_event(&state, &did, true, None).await 165 + { 166 + warn!("Failed to sequence account activation event: {}", e); 167 + } 168 + if let Err(e) = 169 + crate::api::repo::record::sequence_identity_event(&state, &did, handle.as_deref()) 170 + .await 171 + { 172 + warn!("Failed to sequence identity event for activation: {}", e); 162 173 } 163 174 (StatusCode::OK, Json(json!({}))).into_response() 164 175 } ··· 222 233 .await; 223 234 match result { 224 235 Ok(_) => { 225 - if let Some(h) = handle { 236 + if let Some(ref h) = handle { 226 237 let _ = state.cache.delete(&format!("handle:{}", h)).await; 238 + } 239 + if let Err(e) = 240 + crate::api::repo::record::sequence_account_event(&state, &did, false, Some("deactivated")).await 241 + { 242 + warn!("Failed to sequence account deactivation event: {}", e); 227 243 } 228 244 (StatusCode::OK, Json(json!({}))).into_response() 229 245 }
+102 -3
src/api/server/service_auth.rs
··· 10 10 use serde_json::json; 11 11 use tracing::error; 12 12 13 + const HOUR_SECS: i64 = 3600; 14 + const MINUTE_SECS: i64 = 60; 15 + 16 + const PROTECTED_METHODS: &[&str] = &[ 17 + "com.atproto.admin.sendEmail", 18 + "com.atproto.identity.requestPlcOperationSignature", 19 + "com.atproto.identity.signPlcOperation", 20 + "com.atproto.identity.updateHandle", 21 + "com.atproto.server.activateAccount", 22 + "com.atproto.server.confirmEmail", 23 + "com.atproto.server.createAppPassword", 24 + "com.atproto.server.deactivateAccount", 25 + "com.atproto.server.getAccountInviteCodes", 26 + "com.atproto.server.getSession", 27 + "com.atproto.server.listAppPasswords", 28 + "com.atproto.server.requestAccountDelete", 29 + "com.atproto.server.requestEmailConfirmation", 30 + "com.atproto.server.requestEmailUpdate", 31 + "com.atproto.server.revokeAppPassword", 32 + "com.atproto.server.updateEmail", 33 + ]; 34 + 13 35 #[derive(Deserialize)] 14 36 pub struct GetServiceAuthParams { 15 37 pub aud: String, ··· 33 55 Some(t) => t, 34 56 None => return ApiError::AuthenticationRequired.into_response(), 35 57 }; 36 - let auth_user = match crate::auth::validate_bearer_token(&state.db, &token).await { 58 + let auth_user = match crate::auth::validate_bearer_token_for_service_auth(&state.db, &token).await { 37 59 Ok(user) => user, 38 60 Err(e) => return ApiError::from(e).into_response(), 39 61 }; ··· 46 68 .into_response(); 47 69 } 48 70 }; 49 - let lxm = params.lxm.as_deref().unwrap_or("*"); 71 + 72 + let lxm = params.lxm.as_deref(); 73 + let lxm_for_token = lxm.unwrap_or("*"); 74 + 75 + let user_status = sqlx::query!( 76 + "SELECT takedown_ref FROM users WHERE did = $1", 77 + auth_user.did 78 + ) 79 + .fetch_optional(&state.db) 80 + .await; 81 + 82 + let is_takendown = match user_status { 83 + Ok(Some(row)) => row.takedown_ref.is_some(), 84 + _ => false, 85 + }; 86 + 87 + if is_takendown && lxm != Some("com.atproto.server.createAccount") { 88 + return ( 89 + StatusCode::BAD_REQUEST, 90 + Json(json!({ 91 + "error": "InvalidToken", 92 + "message": "Bad token scope" 93 + })), 94 + ) 95 + .into_response(); 96 + } 97 + 98 + if let Some(method) = lxm { 99 + if PROTECTED_METHODS.contains(&method) { 100 + return ( 101 + StatusCode::BAD_REQUEST, 102 + Json(json!({ 103 + "error": "InvalidRequest", 104 + "message": format!("cannot request a service auth token for the following protected method: {}", method) 105 + })), 106 + ) 107 + .into_response(); 108 + } 109 + } 110 + 111 + if let Some(exp) = params.exp { 112 + let now = chrono::Utc::now().timestamp(); 113 + let diff = exp - now; 114 + 115 + if diff < 0 { 116 + return ( 117 + StatusCode::BAD_REQUEST, 118 + Json(json!({ 119 + "error": "BadExpiration", 120 + "message": "expiration is in past" 121 + })), 122 + ) 123 + .into_response(); 124 + } 125 + 126 + if diff > HOUR_SECS { 127 + return ( 128 + StatusCode::BAD_REQUEST, 129 + Json(json!({ 130 + "error": "BadExpiration", 131 + "message": "cannot request a token with an expiration more than an hour in the future" 132 + })), 133 + ) 134 + .into_response(); 135 + } 136 + 137 + if lxm.is_none() && diff > MINUTE_SECS { 138 + return ( 139 + StatusCode::BAD_REQUEST, 140 + Json(json!({ 141 + "error": "BadExpiration", 142 + "message": "cannot request a method-less token with an expiration more than a minute in the future" 143 + })), 144 + ) 145 + .into_response(); 146 + } 147 + } 148 + 50 149 let service_token = 51 - match crate::auth::create_service_token(&auth_user.did, &params.aud, lxm, &key_bytes) { 150 + match crate::auth::create_service_token(&auth_user.did, &params.aud, lxm_for_token, &key_bytes) { 52 151 Ok(t) => t, 53 152 Err(e) => { 54 153 error!("Failed to create service token: {:?}", e);
+13 -5
src/auth/mod.rs
··· 59 59 db: &PgPool, 60 60 token: &str, 61 61 ) -> Result<AuthenticatedUser, TokenValidationError> { 62 - validate_bearer_token_with_options_internal(db, None, token, false).await 62 + validate_bearer_token_with_options_internal(db, None, token, false, false).await 63 63 } 64 64 65 65 pub async fn validate_bearer_token_allow_deactivated( 66 66 db: &PgPool, 67 67 token: &str, 68 68 ) -> Result<AuthenticatedUser, TokenValidationError> { 69 - validate_bearer_token_with_options_internal(db, None, token, true).await 69 + validate_bearer_token_with_options_internal(db, None, token, true, false).await 70 70 } 71 71 72 72 pub async fn validate_bearer_token_cached( ··· 74 74 cache: &Arc<dyn Cache>, 75 75 token: &str, 76 76 ) -> Result<AuthenticatedUser, TokenValidationError> { 77 - validate_bearer_token_with_options_internal(db, Some(cache), token, false).await 77 + validate_bearer_token_with_options_internal(db, Some(cache), token, false, false).await 78 78 } 79 79 80 80 pub async fn validate_bearer_token_cached_allow_deactivated( ··· 82 82 cache: &Arc<dyn Cache>, 83 83 token: &str, 84 84 ) -> Result<AuthenticatedUser, TokenValidationError> { 85 - validate_bearer_token_with_options_internal(db, Some(cache), token, true).await 85 + validate_bearer_token_with_options_internal(db, Some(cache), token, true, false).await 86 + } 87 + 88 + pub async fn validate_bearer_token_for_service_auth( 89 + db: &PgPool, 90 + token: &str, 91 + ) -> Result<AuthenticatedUser, TokenValidationError> { 92 + validate_bearer_token_with_options_internal(db, None, token, true, true).await 86 93 } 87 94 88 95 async fn validate_bearer_token_with_options_internal( ··· 90 97 cache: Option<&Arc<dyn Cache>>, 91 98 token: &str, 92 99 allow_deactivated: bool, 100 + allow_takendown: bool, 93 101 ) -> Result<AuthenticatedUser, TokenValidationError> { 94 102 let did_from_token = get_did_from_token(token).ok(); 95 103 ··· 155 163 return Err(TokenValidationError::AccountDeactivated); 156 164 } 157 165 158 - if takedown_ref.is_some() { 166 + if !allow_takendown && takedown_ref.is_some() { 159 167 return Err(TokenValidationError::AccountTakedown); 160 168 } 161 169
+2 -2
src/comms/sender.rs
··· 87 87 88 88 pub fn from_env() -> Option<Self> { 89 89 let from_address = std::env::var("MAIL_FROM_ADDRESS").ok()?; 90 - let from_name = std::env::var("MAIL_FROM_NAME").unwrap_or_else(|_| "BSPDS".to_string()); 90 + let from_name = std::env::var("MAIL_FROM_NAME").unwrap_or_else(|_| "Tranquil PDS".to_string()); 91 91 Some(Self::new(from_address, from_name)) 92 92 } 93 93 ··· 168 168 let content = format!("**{}**\n\n{}", subject, notification.body); 169 169 let payload = json!({ 170 170 "content": content, 171 - "username": "BSPDS" 171 + "username": "Tranquil PDS" 172 172 }); 173 173 let mut last_error = None; 174 174 for attempt in 0..MAX_RETRIES {
+10 -10
src/config.rs
··· 25 25 pub fn init() -> &'static Self { 26 26 CONFIG.get_or_init(|| { 27 27 let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| { 28 - if cfg!(test) || std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_ok() { 28 + if cfg!(test) || std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_ok() { 29 29 "test-jwt-secret-not-for-production".to_string() 30 30 } else { 31 31 panic!( 32 32 "JWT_SECRET environment variable must be set in production. \ 33 - Set BSPDS_ALLOW_INSECURE_SECRETS=1 for development/testing." 33 + Set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 for development/testing." 34 34 ); 35 35 } 36 36 }); 37 37 38 38 let dpop_secret = std::env::var("DPOP_SECRET").unwrap_or_else(|_| { 39 - if cfg!(test) || std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_ok() { 39 + if cfg!(test) || std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_ok() { 40 40 "test-dpop-secret-not-for-production".to_string() 41 41 } else { 42 42 panic!( 43 43 "DPOP_SECRET environment variable must be set in production. \ 44 - Set BSPDS_ALLOW_INSECURE_SECRETS=1 for development/testing." 44 + Set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 for development/testing." 45 45 ); 46 46 } 47 47 }); 48 48 49 - if jwt_secret.len() < 32 && std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_err() { 49 + if jwt_secret.len() < 32 && std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_err() { 50 50 panic!("JWT_SECRET must be at least 32 characters"); 51 51 } 52 52 53 - if dpop_secret.len() < 32 && std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_err() { 53 + if dpop_secret.len() < 32 && std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_err() { 54 54 panic!("DPOP_SECRET must be at least 32 characters"); 55 55 } 56 56 ··· 87 87 let signing_key_id = URL_SAFE_NO_PAD.encode(&kid_hash[..8]); 88 88 89 89 let master_key = std::env::var("MASTER_KEY").unwrap_or_else(|_| { 90 - if cfg!(test) || std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_ok() { 90 + if cfg!(test) || std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_ok() { 91 91 "test-master-key-not-for-production".to_string() 92 92 } else { 93 93 panic!( 94 94 "MASTER_KEY environment variable must be set in production. \ 95 - Set BSPDS_ALLOW_INSECURE_SECRETS=1 for development/testing." 95 + Set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 for development/testing." 96 96 ); 97 97 } 98 98 }); 99 99 100 - if master_key.len() < 32 && std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_err() { 100 + if master_key.len() < 32 && std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_err() { 101 101 panic!("MASTER_KEY must be at least 32 characters"); 102 102 } 103 103 104 104 let hk = Hkdf::<Sha256>::new(None, master_key.as_bytes()); 105 105 let mut key_encryption_key = [0u8; 32]; 106 - hk.expand(b"bspds-user-key-encryption", &mut key_encryption_key) 106 + hk.expand(b"tranquil-pds-user-key-encryption", &mut key_encryption_key) 107 107 .expect("HKDF expansion failed"); 108 108 109 109 AuthConfig {
+8 -8
src/lib.rs
··· 52 52 get(api::server::get_session), 53 53 ) 54 54 .route( 55 - "/xrpc/com.bspds.account.listSessions", 55 + "/xrpc/com.tranquil.account.listSessions", 56 56 get(api::server::list_sessions), 57 57 ) 58 58 .route( 59 - "/xrpc/com.bspds.account.revokeSession", 59 + "/xrpc/com.tranquil.account.revokeSession", 60 60 post(api::server::revoke_session), 61 61 ) 62 62 .route( ··· 199 199 post(api::server::reset_password), 200 200 ) 201 201 .route( 202 - "/xrpc/com.bspds.account.changePassword", 202 + "/xrpc/com.tranquil.account.changePassword", 203 203 post(api::server::change_password), 204 204 ) 205 205 .route( ··· 283 283 get(api::admin::get_invite_codes), 284 284 ) 285 285 .route( 286 - "/xrpc/com.bspds.admin.getServerStats", 286 + "/xrpc/com.tranquil.admin.getServerStats", 287 287 get(api::admin::get_server_stats), 288 288 ) 289 289 .route( ··· 370 370 get(api::temp::check_signup_queue), 371 371 ) 372 372 .route( 373 - "/xrpc/com.bspds.account.getNotificationPrefs", 373 + "/xrpc/com.tranquil.account.getNotificationPrefs", 374 374 get(api::notification_prefs::get_notification_prefs), 375 375 ) 376 376 .route( 377 - "/xrpc/com.bspds.account.updateNotificationPrefs", 377 + "/xrpc/com.tranquil.account.updateNotificationPrefs", 378 378 post(api::notification_prefs::update_notification_prefs), 379 379 ) 380 380 .route( 381 - "/xrpc/com.bspds.account.getNotificationHistory", 381 + "/xrpc/com.tranquil.account.getNotificationHistory", 382 382 get(api::notification_prefs::get_notification_history), 383 383 ) 384 384 .route( 385 - "/xrpc/com.bspds.account.confirmChannelVerification", 385 + "/xrpc/com.tranquil.account.confirmChannelVerification", 386 386 post(api::verification::confirm_channel_verification), 387 387 ) 388 388 .route("/xrpc/{*method}", any(api::proxy::proxy_handler))
+6 -6
src/main.rs
··· 1 - use bspds::comms::{CommsService, DiscordSender, EmailSender, SignalSender, TelegramSender}; 2 - use bspds::crawlers::{Crawlers, start_crawlers_service}; 3 - use bspds::state::AppState; 1 + use tranquil_pds::comms::{CommsService, DiscordSender, EmailSender, SignalSender, TelegramSender}; 2 + use tranquil_pds::crawlers::{Crawlers, start_crawlers_service}; 3 + use tranquil_pds::state::AppState; 4 4 use std::net::SocketAddr; 5 5 use std::process::ExitCode; 6 6 use std::sync::Arc; ··· 11 11 async fn main() -> ExitCode { 12 12 dotenvy::dotenv().ok(); 13 13 tracing_subscriber::fmt::init(); 14 - bspds::metrics::init_metrics(); 14 + tranquil_pds::metrics::init_metrics(); 15 15 16 16 match run().await { 17 17 Ok(()) => ExitCode::SUCCESS, ··· 62 62 .map_err(|e| format!("Failed to run migrations: {}", e))?; 63 63 64 64 let state = AppState::new(pool.clone()).await; 65 - bspds::sync::listener::start_sequencer_listener(state.clone()).await; 65 + tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await; 66 66 67 67 let (shutdown_tx, shutdown_rx) = watch::channel(false); 68 68 ··· 108 108 None 109 109 }; 110 110 111 - let app = bspds::app(state); 111 + let app = tranquil_pds::app(state); 112 112 let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); 113 113 info!("listening on {}", addr); 114 114
+25 -25
src/metrics.rs
··· 24 24 } 25 25 26 26 fn describe_metrics() { 27 - metrics::describe_counter!("bspds_http_requests_total", "Total number of HTTP requests"); 27 + metrics::describe_counter!("tranquil_pds_http_requests_total", "Total number of HTTP requests"); 28 28 metrics::describe_histogram!( 29 - "bspds_http_request_duration_seconds", 29 + "tranquil_pds_http_request_duration_seconds", 30 30 "HTTP request duration in seconds" 31 31 ); 32 32 metrics::describe_counter!( 33 - "bspds_auth_cache_hits_total", 33 + "tranquil_pds_auth_cache_hits_total", 34 34 "Total number of authentication cache hits" 35 35 ); 36 36 metrics::describe_counter!( 37 - "bspds_auth_cache_misses_total", 37 + "tranquil_pds_auth_cache_misses_total", 38 38 "Total number of authentication cache misses" 39 39 ); 40 40 metrics::describe_gauge!( 41 - "bspds_firehose_subscribers", 41 + "tranquil_pds_firehose_subscribers", 42 42 "Number of active firehose WebSocket subscribers" 43 43 ); 44 44 metrics::describe_counter!( 45 - "bspds_firehose_events_total", 45 + "tranquil_pds_firehose_events_total", 46 46 "Total number of firehose events published" 47 47 ); 48 48 metrics::describe_counter!( 49 - "bspds_block_operations_total", 49 + "tranquil_pds_block_operations_total", 50 50 "Total number of block store operations" 51 51 ); 52 52 metrics::describe_counter!( 53 - "bspds_s3_operations_total", 53 + "tranquil_pds_s3_operations_total", 54 54 "Total number of S3/blob storage operations" 55 55 ); 56 56 metrics::describe_gauge!( 57 - "bspds_comms_queue_size", 57 + "tranquil_pds_comms_queue_size", 58 58 "Current size of the comms queue" 59 59 ); 60 60 metrics::describe_counter!( 61 - "bspds_rate_limit_rejections_total", 61 + "tranquil_pds_rate_limit_rejections_total", 62 62 "Total number of rate limit rejections" 63 63 ); 64 - metrics::describe_counter!("bspds_db_queries_total", "Total number of database queries"); 64 + metrics::describe_counter!("tranquil_pds_db_queries_total", "Total number of database queries"); 65 65 metrics::describe_histogram!( 66 - "bspds_db_query_duration_seconds", 66 + "tranquil_pds_db_query_duration_seconds", 67 67 "Database query duration in seconds" 68 68 ); 69 69 } ··· 97 97 let status = response.status().as_u16().to_string(); 98 98 99 99 counter!( 100 - "bspds_http_requests_total", 100 + "tranquil_pds_http_requests_total", 101 101 "method" => method.clone(), 102 102 "path" => path.clone(), 103 103 "status" => status.clone() ··· 105 105 .increment(1); 106 106 107 107 histogram!( 108 - "bspds_http_request_duration_seconds", 108 + "tranquil_pds_http_request_duration_seconds", 109 109 "method" => method, 110 110 "path" => path 111 111 ) ··· 135 135 } 136 136 137 137 pub fn record_auth_cache_hit(cache_type: &str) { 138 - counter!("bspds_auth_cache_hits_total", "cache_type" => cache_type.to_string()).increment(1); 138 + counter!("tranquil_pds_auth_cache_hits_total", "cache_type" => cache_type.to_string()).increment(1); 139 139 } 140 140 141 141 pub fn record_auth_cache_miss(cache_type: &str) { 142 - counter!("bspds_auth_cache_misses_total", "cache_type" => cache_type.to_string()).increment(1); 142 + counter!("tranquil_pds_auth_cache_misses_total", "cache_type" => cache_type.to_string()).increment(1); 143 143 } 144 144 145 145 pub fn set_firehose_subscribers(count: usize) { 146 - gauge!("bspds_firehose_subscribers").set(count as f64); 146 + gauge!("tranquil_pds_firehose_subscribers").set(count as f64); 147 147 } 148 148 149 149 pub fn increment_firehose_subscribers() { 150 - counter!("bspds_firehose_events_total").increment(1); 150 + counter!("tranquil_pds_firehose_events_total").increment(1); 151 151 } 152 152 153 153 pub fn record_firehose_event() { 154 - counter!("bspds_firehose_events_total").increment(1); 154 + counter!("tranquil_pds_firehose_events_total").increment(1); 155 155 } 156 156 157 157 pub fn record_block_operation(op_type: &str) { 158 - counter!("bspds_block_operations_total", "op_type" => op_type.to_string()).increment(1); 158 + counter!("tranquil_pds_block_operations_total", "op_type" => op_type.to_string()).increment(1); 159 159 } 160 160 161 161 pub fn record_s3_operation(op_type: &str, status: &str) { 162 162 counter!( 163 - "bspds_s3_operations_total", 163 + "tranquil_pds_s3_operations_total", 164 164 "op_type" => op_type.to_string(), 165 165 "status" => status.to_string() 166 166 ) ··· 168 168 } 169 169 170 170 pub fn set_comms_queue_size(size: usize) { 171 - gauge!("bspds_comms_queue_size").set(size as f64); 171 + gauge!("tranquil_pds_comms_queue_size").set(size as f64); 172 172 } 173 173 174 174 pub fn record_rate_limit_rejection(limiter: &str) { 175 - counter!("bspds_rate_limit_rejections_total", "limiter" => limiter.to_string()).increment(1); 175 + counter!("tranquil_pds_rate_limit_rejections_total", "limiter" => limiter.to_string()).increment(1); 176 176 } 177 177 178 178 pub fn record_db_query(query_type: &str, duration_seconds: f64) { 179 - counter!("bspds_db_queries_total", "query_type" => query_type.to_string()).increment(1); 179 + counter!("tranquil_pds_db_queries_total", "query_type" => query_type.to_string()).increment(1); 180 180 histogram!( 181 - "bspds_db_query_duration_seconds", 181 + "tranquil_pds_db_query_duration_seconds", 182 182 "query_type" => query_type.to_string() 183 183 ) 184 184 .record(duration_seconds);
+11 -11
tests/account_notifications.rs
··· 1 1 mod common; 2 2 use common::{base_url, client, create_account_and_login, get_db_connection_string}; 3 - use bspds::comms::{NewComms, CommsType, enqueue_comms}; 3 + use tranquil_pds::comms::{NewComms, CommsType, enqueue_comms}; 4 4 use serde_json::{Value, json}; 5 5 use sqlx::PgPool; 6 6 ··· 37 37 } 38 38 39 39 let resp = client 40 - .get(format!("{}/xrpc/com.bspds.account.getNotificationHistory", base)) 40 + .get(format!("{}/xrpc/com.tranquil.account.getNotificationHistory", base)) 41 41 .header("Authorization", format!("Bearer {}", token)) 42 42 .send() 43 43 .await ··· 63 63 "discordId": "123456789" 64 64 }); 65 65 let resp = client 66 - .post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base)) 66 + .post(format!("{}/xrpc/com.tranquil.account.updateNotificationPrefs", base)) 67 67 .header("Authorization", format!("Bearer {}", token)) 68 68 .json(&prefs) 69 69 .send() ··· 92 92 "code": code 93 93 }); 94 94 let resp = client 95 - .post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base)) 95 + .post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base)) 96 96 .header("Authorization", format!("Bearer {}", token)) 97 97 .json(&input) 98 98 .send() ··· 101 101 assert_eq!(resp.status(), 200); 102 102 103 103 let resp = client 104 - .get(format!("{}/xrpc/com.bspds.account.getNotificationPrefs", base)) 104 + .get(format!("{}/xrpc/com.tranquil.account.getNotificationPrefs", base)) 105 105 .header("Authorization", format!("Bearer {}", token)) 106 106 .send() 107 107 .await ··· 121 121 "telegramUsername": "testuser" 122 122 }); 123 123 let resp = client 124 - .post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base)) 124 + .post(format!("{}/xrpc/com.tranquil.account.updateNotificationPrefs", base)) 125 125 .header("Authorization", format!("Bearer {}", token)) 126 126 .json(&prefs) 127 127 .send() ··· 134 134 "code": "000000" 135 135 }); 136 136 let resp = client 137 - .post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base)) 137 + .post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base)) 138 138 .header("Authorization", format!("Bearer {}", token)) 139 139 .json(&input) 140 140 .send() ··· 154 154 "code": "123456" 155 155 }); 156 156 let resp = client 157 - .post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base)) 157 + .post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base)) 158 158 .header("Authorization", format!("Bearer {}", token)) 159 159 .json(&input) 160 160 .send() ··· 175 175 "email": unique_email 176 176 }); 177 177 let resp = client 178 - .post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base)) 178 + .post(format!("{}/xrpc/com.tranquil.account.updateNotificationPrefs", base)) 179 179 .header("Authorization", format!("Bearer {}", token)) 180 180 .json(&prefs) 181 181 .send() ··· 203 203 "code": code 204 204 }); 205 205 let resp = client 206 - .post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base)) 206 + .post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base)) 207 207 .header("Authorization", format!("Bearer {}", token)) 208 208 .json(&input) 209 209 .send() ··· 212 212 assert_eq!(resp.status(), 200); 213 213 214 214 let resp = client 215 - .get(format!("{}/xrpc/com.bspds.account.getNotificationPrefs", base)) 215 + .get(format!("{}/xrpc/com.tranquil.account.getNotificationPrefs", base)) 216 216 .header("Authorization", format!("Bearer {}", token)) 217 217 .send() 218 218 .await
+2 -2
tests/admin_stats.rs
··· 11 11 let (_, _) = create_admin_account_and_login(&client).await; 12 12 13 13 let resp = client 14 - .get(format!("{}/xrpc/com.bspds.admin.getServerStats", base)) 14 + .get(format!("{}/xrpc/com.tranquil.admin.getServerStats", base)) 15 15 .header("Authorization", format!("Bearer {}", token1)) 16 16 .send() 17 17 .await ··· 33 33 let client = client(); 34 34 let base = base_url().await; 35 35 let resp = client 36 - .get(format!("{}/xrpc/com.bspds.admin.getServerStats", base)) 36 + .get(format!("{}/xrpc/com.tranquil.admin.getServerStats", base)) 37 37 .send() 38 38 .await 39 39 .unwrap();
+6 -6
tests/change_password.rs
··· 33 33 let jwt = verify_new_account(&client, did).await; 34 34 let change_res = client 35 35 .post(format!( 36 - "{}/xrpc/com.bspds.account.changePassword", 36 + "{}/xrpc/com.tranquil.account.changePassword", 37 37 base_url().await 38 38 )) 39 39 .bearer_auth(&jwt) ··· 79 79 let (_, jwt) = setup_new_user("change-pw-wrong").await; 80 80 let res = client 81 81 .post(format!( 82 - "{}/xrpc/com.bspds.account.changePassword", 82 + "{}/xrpc/com.tranquil.account.changePassword", 83 83 base_url().await 84 84 )) 85 85 .bearer_auth(&jwt) ··· 122 122 let jwt = verify_new_account(&client, did).await; 123 123 let res = client 124 124 .post(format!( 125 - "{}/xrpc/com.bspds.account.changePassword", 125 + "{}/xrpc/com.tranquil.account.changePassword", 126 126 base_url().await 127 127 )) 128 128 .bearer_auth(&jwt) ··· 144 144 let (_, jwt) = setup_new_user("change-pw-empty").await; 145 145 let res = client 146 146 .post(format!( 147 - "{}/xrpc/com.bspds.account.changePassword", 147 + "{}/xrpc/com.tranquil.account.changePassword", 148 148 base_url().await 149 149 )) 150 150 .bearer_auth(&jwt) ··· 164 164 let (_, jwt) = setup_new_user("change-pw-emptynew").await; 165 165 let res = client 166 166 .post(format!( 167 - "{}/xrpc/com.bspds.account.changePassword", 167 + "{}/xrpc/com.tranquil.account.changePassword", 168 168 base_url().await 169 169 )) 170 170 .bearer_auth(&jwt) ··· 183 183 let client = client(); 184 184 let res = client 185 185 .post(format!( 186 - "{}/xrpc/com.bspds.account.changePassword", 186 + "{}/xrpc/com.tranquil.account.changePassword", 187 187 base_url().await 188 188 )) 189 189 .json(&json!({
+10 -10
tests/common/mod.rs
··· 1 1 use aws_config::BehaviorVersion; 2 2 use aws_sdk_s3::Client as S3Client; 3 3 use aws_sdk_s3::config::Credentials; 4 - use bspds::state::AppState; 4 + use tranquil_pds::state::AppState; 5 5 use chrono::Utc; 6 6 use reqwest::{Client, StatusCode, header}; 7 7 use serde_json::{Value, json}; ··· 40 40 pub const TARGET_DID: &str = "did:plc:target"; 41 41 42 42 fn has_external_infra() -> bool { 43 - std::env::var("BSPDS_TEST_INFRA_READY").is_ok() 43 + std::env::var("TRANQUIL_PDS_TEST_INFRA_READY").is_ok() 44 44 || (std::env::var("DATABASE_URL").is_ok() && std::env::var("S3_ENDPOINT").is_ok()) 45 45 } 46 46 #[cfg(test)] ··· 51 51 } 52 52 if std::env::var("XDG_RUNTIME_DIR").is_ok() { 53 53 let _ = std::process::Command::new("podman") 54 - .args(&["rm", "-f", "--filter", "label=bspds_test=true"]) 54 + .args(&["rm", "-f", "--filter", "label=tranquil_pds_test=true"]) 55 55 .output(); 56 56 } 57 57 let _ = std::process::Command::new("docker") ··· 60 60 "prune", 61 61 "-f", 62 62 "--filter", 63 - "label=bspds_test=true", 63 + "label=tranquil_pds_test=true", 64 64 ]) 65 65 .output(); 66 66 } ··· 80 80 let (tx, rx) = std::sync::mpsc::channel(); 81 81 std::thread::spawn(move || { 82 82 unsafe { 83 - std::env::set_var("BSPDS_ALLOW_INSECURE_SECRETS", "1"); 83 + std::env::set_var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS", "1"); 84 84 } 85 85 if std::env::var("DOCKER_HOST").is_err() { 86 86 if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") { ··· 152 152 .with_env_var("MINIO_ROOT_USER", "minioadmin") 153 153 .with_env_var("MINIO_ROOT_PASSWORD", "minioadmin") 154 154 .with_cmd(vec!["server".to_string(), "/data".to_string()]) 155 - .with_label("bspds_test", "true") 155 + .with_label("tranquil_pds_test", "true") 156 156 .start() 157 157 .await 158 158 .expect("Failed to start MinIO"); ··· 195 195 S3_CONTAINER.set(s3_container).ok(); 196 196 let container = Postgres::default() 197 197 .with_tag("18-alpine") 198 - .with_label("bspds_test", "true") 198 + .with_label("tranquil_pds_test", "true") 199 199 .start() 200 200 .await 201 201 .expect("Failed to start Postgres"); ··· 236 236 } 237 237 238 238 async fn spawn_app(database_url: String) -> String { 239 - use bspds::rate_limit::RateLimiters; 239 + use tranquil_pds::rate_limit::RateLimiters; 240 240 let pool = PgPoolOptions::new() 241 241 .max_connections(50) 242 242 .connect(&database_url) ··· 260 260 .with_oauth_authorize_limit(10000) 261 261 .with_oauth_token_limit(10000); 262 262 let state = AppState::new(pool).await.with_rate_limiters(rate_limiters); 263 - bspds::sync::listener::start_sequencer_listener(state.clone()).await; 264 - let app = bspds::app(state); 263 + tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await; 264 + let app = tranquil_pds::app(state); 265 265 tokio::spawn(async move { 266 266 axum::serve(listener, app).await.unwrap(); 267 267 });
+1 -1
tests/image_processing.rs
··· 1 - use bspds::image::{ 1 + use tranquil_pds::image::{ 2 2 DEFAULT_MAX_FILE_SIZE, ImageError, ImageProcessor, OutputFormat, THUMB_SIZE_FEED, 3 3 THUMB_SIZE_FULL, 4 4 };
+1 -1
tests/import_with_verification.rs
··· 194 194 .fetch_optional(&pool) 195 195 .await 196 196 .ok()??; 197 - bspds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok() 197 + tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok() 198 198 } 199 199 #[tokio::test] 200 200 #[ignore = "requires exclusive env var access; run with: cargo test test_import_with_valid_signature_and_mock_plc -- --ignored --test-threads=1"]
+20 -4
tests/jwt_security.rs
··· 1 1 #![allow(unused_imports)] 2 2 mod common; 3 3 use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; 4 - use bspds::auth::{ 4 + use tranquil_pds::auth::{ 5 5 self, SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED, SCOPE_REFRESH, 6 6 TOKEN_TYPE_ACCESS, TOKEN_TYPE_REFRESH, TOKEN_TYPE_SERVICE, create_access_token, 7 7 create_refresh_token, create_service_token, get_did_from_token, get_jti_from_token, ··· 409 409 } 410 410 411 411 #[tokio::test] 412 - async fn test_deactivated_account_rejected() { 412 + async fn test_deactivated_account_behavior() { 413 413 let url = base_url().await; 414 414 let http_client = client(); 415 415 let (access_jwt, _did) = create_account_and_login(&http_client).await; ··· 423 423 let res = http_client.get(format!("{}/xrpc/com.atproto.server.getSession", url)) 424 424 .header("Authorization", format!("Bearer {}", access_jwt)) 425 425 .send().await.unwrap(); 426 - assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 426 + assert_eq!(res.status(), StatusCode::OK); 427 427 let body: Value = res.json().await.unwrap(); 428 - assert_eq!(body["error"], "AccountDeactivated"); 428 + assert_eq!(body["active"], false); 429 + 430 + let post_res = http_client.post(format!("{}/xrpc/com.atproto.repo.createRecord", url)) 431 + .header("Authorization", format!("Bearer {}", access_jwt)) 432 + .json(&json!({ 433 + "repo": _did, 434 + "collection": "app.bsky.feed.post", 435 + "record": { 436 + "$type": "app.bsky.feed.post", 437 + "text": "test", 438 + "createdAt": "2024-01-01T00:00:00Z" 439 + } 440 + })) 441 + .send().await.unwrap(); 442 + assert_eq!(post_res.status(), StatusCode::UNAUTHORIZED); 443 + let post_body: Value = post_res.json().await.unwrap(); 444 + assert_eq!(post_body["error"], "AccountDeactivated"); 429 445 } 430 446 431 447 #[tokio::test]
+1 -1
tests/notifications.rs
··· 1 1 mod common; 2 - use bspds::comms::{ 2 + use tranquil_pds::comms::{ 3 3 CommsChannel, CommsStatus, CommsType, NewComms, enqueue_comms, enqueue_welcome, 4 4 }; 5 5 use sqlx::PgPool;
+1 -1
tests/oauth_security.rs
··· 2 2 mod common; 3 3 mod helpers; 4 4 use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; 5 - use bspds::oauth::dpop::{DPoPJwk, DPoPVerifier, compute_jwk_thumbprint}; 5 + use tranquil_pds::oauth::dpop::{DPoPJwk, DPoPVerifier, compute_jwk_thumbprint}; 6 6 use chrono::Utc; 7 7 use common::{base_url, client}; 8 8 use helpers::verify_new_account;
+1 -1
tests/plc_migration.rs
··· 50 50 .fetch_optional(&pool) 51 51 .await 52 52 .ok()??; 53 - bspds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok() 53 + tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok() 54 54 } 55 55 56 56 async fn get_plc_token_from_db(did: &str) -> Option<String> {
+1 -1
tests/plc_validation.rs
··· 1 - use bspds::plc::{ 1 + use tranquil_pds::plc::{ 2 2 PlcError, PlcOperation, PlcService, PlcValidationContext, cid_for_cbor, sign_operation, 3 3 signing_key_to_did_key, validate_plc_operation, validate_plc_operation_for_submission, 4 4 verify_operation_signature,
+1 -1
tests/rate_limit.rs
··· 162 162 println!("VALKEY_URL not set, skipping distributed rate limiter test"); 163 163 return; 164 164 } 165 - use bspds::cache::{DistributedRateLimiter, RedisRateLimiter}; 165 + use tranquil_pds::cache::{DistributedRateLimiter, RedisRateLimiter}; 166 166 let valkey_url = std::env::var("VALKEY_URL").unwrap(); 167 167 let client = redis::Client::open(valkey_url.as_str()).expect("Failed to create Redis client"); 168 168 let conn = client
+1 -1
tests/record_validation.rs
··· 1 - use bspds::validation::{ 1 + use tranquil_pds::validation::{ 2 2 RecordValidator, ValidationError, ValidationStatus, validate_collection_nsid, 3 3 validate_record_key, 4 4 };
+3 -3
tests/security_fixes.rs
··· 1 1 mod common; 2 - use bspds::image::{ImageError, ImageProcessor}; 3 - use bspds::comms::{SendError, is_valid_phone_number, sanitize_header_value}; 4 - use bspds::oauth::templates::{error_page, login_page, success_page}; 2 + use tranquil_pds::image::{ImageError, ImageProcessor}; 3 + use tranquil_pds::comms::{SendError, is_valid_phone_number, sanitize_header_value}; 4 + use tranquil_pds::oauth::templates::{error_page, login_page, success_page}; 5 5 6 6 #[test] 7 7 fn test_header_injection_sanitization() {
+9 -9
tests/session_management.rs
··· 11 11 let (did, jwt) = setup_new_user("list-sessions").await; 12 12 let res = client 13 13 .get(format!( 14 - "{}/xrpc/com.bspds.account.listSessions", 14 + "{}/xrpc/com.tranquil.account.listSessions", 15 15 base_url().await 16 16 )) 17 17 .bearer_auth(&jwt) ··· 74 74 let jwt2 = login_body["accessJwt"].as_str().unwrap(); 75 75 let list_res = client 76 76 .get(format!( 77 - "{}/xrpc/com.bspds.account.listSessions", 77 + "{}/xrpc/com.tranquil.account.listSessions", 78 78 base_url().await 79 79 )) 80 80 .bearer_auth(jwt2) ··· 93 93 let client = client(); 94 94 let res = client 95 95 .get(format!( 96 - "{}/xrpc/com.bspds.account.listSessions", 96 + "{}/xrpc/com.tranquil.account.listSessions", 97 97 base_url().await 98 98 )) 99 99 .send() ··· 145 145 let jwt2 = login_body["accessJwt"].as_str().unwrap(); 146 146 let list_res = client 147 147 .get(format!( 148 - "{}/xrpc/com.bspds.account.listSessions", 148 + "{}/xrpc/com.tranquil.account.listSessions", 149 149 base_url().await 150 150 )) 151 151 .bearer_auth(jwt2) ··· 159 159 let session_id = other_session.unwrap()["id"].as_str().unwrap(); 160 160 let revoke_res = client 161 161 .post(format!( 162 - "{}/xrpc/com.bspds.account.revokeSession", 162 + "{}/xrpc/com.tranquil.account.revokeSession", 163 163 base_url().await 164 164 )) 165 165 .bearer_auth(jwt2) ··· 170 170 assert_eq!(revoke_res.status(), StatusCode::OK); 171 171 let list_after_res = client 172 172 .get(format!( 173 - "{}/xrpc/com.bspds.account.listSessions", 173 + "{}/xrpc/com.tranquil.account.listSessions", 174 174 base_url().await 175 175 )) 176 176 .bearer_auth(jwt2) ··· 190 190 let (_, jwt) = setup_new_user("revoke-invalid").await; 191 191 let res = client 192 192 .post(format!( 193 - "{}/xrpc/com.bspds.account.revokeSession", 193 + "{}/xrpc/com.tranquil.account.revokeSession", 194 194 base_url().await 195 195 )) 196 196 .bearer_auth(&jwt) ··· 207 207 let (_, jwt) = setup_new_user("revoke-notfound").await; 208 208 let res = client 209 209 .post(format!( 210 - "{}/xrpc/com.bspds.account.revokeSession", 210 + "{}/xrpc/com.tranquil.account.revokeSession", 211 211 base_url().await 212 212 )) 213 213 .bearer_auth(&jwt) ··· 223 223 let client = client(); 224 224 let res = client 225 225 .post(format!( 226 - "{}/xrpc/com.bspds.account.revokeSession", 226 + "{}/xrpc/com.tranquil.account.revokeSession", 227 227 base_url().await 228 228 )) 229 229 .json(&json!({"sessionId": "1"}))