mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-02-08 13:20:41 +00:00
Rename to tranquil PDS, sounds better than bullshit PDS
This commit is contained in:
@@ -30,7 +30,7 @@ AWS_SECRET_ACCESS_KEY=minioadmin
|
||||
# Security Secrets
|
||||
# =============================================================================
|
||||
# These MUST be set in production (minimum 32 characters each)
|
||||
# In development, set BSPDS_ALLOW_INSECURE_SECRETS=1 to use defaults
|
||||
# In development, set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 to use defaults
|
||||
# Server-wide secret for OAuth token signing (HS256)
|
||||
# JWT_SECRET=your-secure-random-string-at-least-32-chars
|
||||
# Secret for DPoP proof validation
|
||||
@@ -38,7 +38,7 @@ AWS_SECRET_ACCESS_KEY=minioadmin
|
||||
# Key for encrypting user signing keys at rest (AES-256-GCM)
|
||||
# MASTER_KEY=your-secure-random-string-at-least-32-chars
|
||||
# Set this ONLY in development to allow default/weak secrets
|
||||
# BSPDS_ALLOW_INSECURE_SECRETS=1
|
||||
# TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1
|
||||
# =============================================================================
|
||||
# PLC Directory
|
||||
# =============================================================================
|
||||
|
||||
22
.sqlx/query-1add22e111d5eff8beadbd832b4b8146d95da0a0ce8ce31dc9a2f930a26cc9ce.json
generated
Normal file
22
.sqlx/query-1add22e111d5eff8beadbd832b4b8146d95da0a0ce8ce31dc9a2f930a26cc9ce.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT takedown_ref FROM users WHERE did = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "takedown_ref",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1add22e111d5eff8beadbd832b4b8146d95da0a0ce8ce31dc9a2f930a26cc9ce"
|
||||
}
|
||||
122
Cargo.lock
generated
122
Cargo.lock
generated
@@ -929,67 +929,6 @@ dependencies = [
|
||||
"cfg_aliases",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bspds"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"aws-config",
|
||||
"aws-sdk-s3",
|
||||
"axum",
|
||||
"base32",
|
||||
"base64 0.22.1",
|
||||
"bcrypt",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"cid",
|
||||
"ctor",
|
||||
"dotenvy",
|
||||
"ed25519-dalek",
|
||||
"futures",
|
||||
"governor",
|
||||
"hickory-resolver",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"image",
|
||||
"ipld-core",
|
||||
"iroh-car",
|
||||
"jacquard",
|
||||
"jacquard-axum",
|
||||
"jacquard-repo",
|
||||
"jsonwebtoken",
|
||||
"k256",
|
||||
"metrics",
|
||||
"metrics-exporter-prometheus",
|
||||
"multibase",
|
||||
"multihash",
|
||||
"p256 0.13.2",
|
||||
"p384",
|
||||
"rand 0.8.5",
|
||||
"redis",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_ipld_dagcbor",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sqlx",
|
||||
"subtle",
|
||||
"testcontainers",
|
||||
"testcontainers-modules",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"wiremock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "btree-range-map"
|
||||
version = "0.7.2"
|
||||
@@ -6223,6 +6162,67 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-pds"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"aws-config",
|
||||
"aws-sdk-s3",
|
||||
"axum",
|
||||
"base32",
|
||||
"base64 0.22.1",
|
||||
"bcrypt",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"cid",
|
||||
"ctor",
|
||||
"dotenvy",
|
||||
"ed25519-dalek",
|
||||
"futures",
|
||||
"governor",
|
||||
"hickory-resolver",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"image",
|
||||
"ipld-core",
|
||||
"iroh-car",
|
||||
"jacquard",
|
||||
"jacquard-axum",
|
||||
"jacquard-repo",
|
||||
"jsonwebtoken",
|
||||
"k256",
|
||||
"metrics",
|
||||
"metrics-exporter-prometheus",
|
||||
"multibase",
|
||||
"multihash",
|
||||
"p256 0.13.2",
|
||||
"p384",
|
||||
"rand 0.8.5",
|
||||
"redis",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_ipld_dagcbor",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sqlx",
|
||||
"subtle",
|
||||
"testcontainers",
|
||||
"testcontainers-modules",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"wiremock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.15"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "bspds"
|
||||
name = "tranquil-pds"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
[dependencies]
|
||||
|
||||
@@ -16,7 +16,7 @@ COPY .sqlx ./.sqlx
|
||||
RUN touch src/main.rs && cargo build --release
|
||||
# Stage 3: Final image
|
||||
FROM alpine:3.23
|
||||
COPY --from=builder /app/target/release/bspds /usr/local/bin/bspds
|
||||
COPY --from=builder /app/target/release/tranquil-pds /usr/local/bin/tranquil-pds
|
||||
COPY --from=builder /app/migrations /app/migrations
|
||||
COPY --from=frontend-builder /frontend/dist /app/frontend/dist
|
||||
WORKDIR /app
|
||||
@@ -24,4 +24,4 @@ ENV SERVER_HOST=0.0.0.0
|
||||
ENV SERVER_PORT=3000
|
||||
ENV FRONTEND_DIR=/app/frontend/dist
|
||||
EXPOSE 3000
|
||||
CMD ["bspds"]
|
||||
CMD ["tranquil-pds"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# BSPDS
|
||||
# Tranquil PDS
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
[Unit]
|
||||
Description=BSPDS postgres database
|
||||
[Container]
|
||||
ContainerName=bspds-db
|
||||
Image=docker.io/library/postgres:18-alpine
|
||||
Pod=bspds.pod
|
||||
Environment=POSTGRES_USER=bspds
|
||||
Environment=POSTGRES_DB=pds
|
||||
Secret=bspds-db-password,type=env,target=POSTGRES_PASSWORD
|
||||
Volume=/srv/bspds/postgres:/var/lib/postgresql/data:Z
|
||||
HealthCmd=pg_isready -U bspds -d pds
|
||||
HealthInterval=10s
|
||||
HealthTimeout=5s
|
||||
HealthRetries=5
|
||||
HealthStartPeriod=10s
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,15 +0,0 @@
|
||||
[Unit]
|
||||
Description=BSPDS nginx reverse proxy
|
||||
After=bspds-app.service
|
||||
[Container]
|
||||
ContainerName=bspds-nginx
|
||||
Image=docker.io/library/nginx:1.28-alpine
|
||||
Pod=bspds.pod
|
||||
Volume=/srv/bspds/config/nginx.conf:/etc/nginx/nginx.conf:ro,Z
|
||||
Volume=/srv/bspds/certs:/etc/nginx/certs:ro,Z
|
||||
Volume=/srv/bspds/acme:/var/www/acme:ro,Z
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,11 +1,11 @@
|
||||
[Unit]
|
||||
Description=BSPDS AT Protocol PDS
|
||||
After=bspds-db.service bspds-minio.service bspds-valkey.service
|
||||
Description=Tranquil PDS AT Protocol PDS
|
||||
After=tranquil-pds-db.service tranquil-pds-minio.service tranquil-pds-valkey.service
|
||||
[Container]
|
||||
ContainerName=bspds-app
|
||||
Image=localhost/bspds:latest
|
||||
Pod=bspds.pod
|
||||
EnvironmentFile=/srv/bspds/config/bspds.env
|
||||
ContainerName=tranquil-pds-app
|
||||
Image=localhost/tranquil-pds:latest
|
||||
Pod=tranquil-pds.pod
|
||||
EnvironmentFile=/srv/tranquil-pds/config/tranquil-pds.env
|
||||
Environment=SERVER_HOST=0.0.0.0
|
||||
Environment=SERVER_PORT=3000
|
||||
Environment=S3_ENDPOINT=http://localhost:9000
|
||||
20
deploy/quadlets/tranquil-pds-db.container
Normal file
20
deploy/quadlets/tranquil-pds-db.container
Normal file
@@ -0,0 +1,20 @@
|
||||
[Unit]
|
||||
Description=Tranquil PDS postgres database
|
||||
[Container]
|
||||
ContainerName=tranquil-pds-db
|
||||
Image=docker.io/library/postgres:18-alpine
|
||||
Pod=tranquil-pds.pod
|
||||
Environment=POSTGRES_USER=tranquil_pds
|
||||
Environment=POSTGRES_DB=pds
|
||||
Secret=tranquil-pds-db-password,type=env,target=POSTGRES_PASSWORD
|
||||
Volume=/srv/tranquil-pds/postgres:/var/lib/postgresql/data:Z
|
||||
HealthCmd=pg_isready -U tranquil_pds -d pds
|
||||
HealthInterval=10s
|
||||
HealthTimeout=5s
|
||||
HealthRetries=5
|
||||
HealthStartPeriod=10s
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,12 +1,12 @@
|
||||
[Unit]
|
||||
Description=BSPDS minio object storage
|
||||
Description=Tranquil PDS minio object storage
|
||||
[Container]
|
||||
ContainerName=bspds-minio
|
||||
ContainerName=tranquil-pds-minio
|
||||
Image=docker.io/minio/minio:RELEASE.2025-10-15T17-29-55Z
|
||||
Pod=bspds.pod
|
||||
Pod=tranquil-pds.pod
|
||||
Environment=MINIO_ROOT_USER=minioadmin
|
||||
Secret=bspds-minio-password,type=env,target=MINIO_ROOT_PASSWORD
|
||||
Volume=/srv/bspds/minio:/data:Z
|
||||
Secret=tranquil-pds-minio-password,type=env,target=MINIO_ROOT_PASSWORD
|
||||
Volume=/srv/tranquil-pds/minio:/data:Z
|
||||
Exec=server /data --console-address :9001
|
||||
HealthCmd=curl -f http://localhost:9000/minio/health/live || exit 1
|
||||
HealthInterval=30s
|
||||
15
deploy/quadlets/tranquil-pds-nginx.container
Normal file
15
deploy/quadlets/tranquil-pds-nginx.container
Normal file
@@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Tranquil PDS nginx reverse proxy
|
||||
After=tranquil-pds-app.service
|
||||
[Container]
|
||||
ContainerName=tranquil-pds-nginx
|
||||
Image=docker.io/library/nginx:1.28-alpine
|
||||
Pod=tranquil-pds.pod
|
||||
Volume=/srv/tranquil-pds/config/nginx.conf:/etc/nginx/nginx.conf:ro,Z
|
||||
Volume=/srv/tranquil-pds/certs:/etc/nginx/certs:ro,Z
|
||||
Volume=/srv/tranquil-pds/acme:/var/www/acme:ro,Z
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,10 +1,10 @@
|
||||
[Unit]
|
||||
Description=BSPDS valkey cache
|
||||
Description=Tranquil PDS valkey cache
|
||||
[Container]
|
||||
ContainerName=bspds-valkey
|
||||
ContainerName=tranquil-pds-valkey
|
||||
Image=docker.io/valkey/valkey:9-alpine
|
||||
Pod=bspds.pod
|
||||
Volume=/srv/bspds/valkey:/data:Z
|
||||
Pod=tranquil-pds.pod
|
||||
Volume=/srv/tranquil-pds/valkey:/data:Z
|
||||
Exec=valkey-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
HealthCmd=valkey-cli ping
|
||||
HealthInterval=10s
|
||||
@@ -1,5 +1,5 @@
|
||||
[Pod]
|
||||
PodName=bspds
|
||||
PodName=tranquil-pds
|
||||
PublishPort=80:80
|
||||
PublishPort=443:443
|
||||
[Install]
|
||||
@@ -1,9 +1,9 @@
|
||||
services:
|
||||
bspds:
|
||||
tranquil-pds:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: bspds:latest
|
||||
image: tranquil-pds:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
SERVER_HOST: "0.0.0.0"
|
||||
SERVER_PORT: "3000"
|
||||
PDS_HOSTNAME: "${PDS_HOSTNAME:?PDS_HOSTNAME is required}"
|
||||
DATABASE_URL: "postgres://bspds:${DB_PASSWORD:?DB_PASSWORD is required}@db:5432/pds"
|
||||
DATABASE_URL: "postgres://tranquil_pds:${DB_PASSWORD:?DB_PASSWORD is required}@db:5432/pds"
|
||||
S3_ENDPOINT: "http://minio:9000"
|
||||
AWS_REGION: "us-east-1"
|
||||
S3_BUCKET: "pds-blobs"
|
||||
@@ -46,13 +46,13 @@ services:
|
||||
image: postgres:18-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: bspds
|
||||
POSTGRES_USER: tranquil_pds
|
||||
POSTGRES_PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required}"
|
||||
POSTGRES_DB: pds
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U bspds -d pds"]
|
||||
test: ["CMD-SHELL", "pg_isready -U tranquil_pds -d pds"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
@@ -128,7 +128,7 @@ services:
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
- acme_challenge:/var/www/acme:ro
|
||||
depends_on:
|
||||
- bspds
|
||||
- tranquil-pds
|
||||
healthcheck:
|
||||
test: ["CMD", "nginx", "-t"]
|
||||
interval: 30s
|
||||
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: bspds
|
||||
image: tranquil-pds
|
||||
ports:
|
||||
- "3000:3000"
|
||||
env_file:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# BSPDS Production Installation on Alpine Linux
|
||||
# Tranquil PDS Production Installation on Alpine Linux
|
||||
> **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.
|
||||
|
||||
This guide covers installing BSPDS on Alpine Linux 3.23 (current stable as of December 2025).
|
||||
This guide covers installing Tranquil PDS on Alpine Linux 3.23.
|
||||
|
||||
## Prerequisites
|
||||
- A VPS with at least 2GB RAM and 20GB disk
|
||||
@@ -20,17 +20,16 @@ rustup-init -y
|
||||
source ~/.cargo/env
|
||||
rustup default stable
|
||||
```
|
||||
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.
|
||||
This installs the latest stable Rust. Alpine also ships Rust via `apk add rust cargo` if you prefer system packages.
|
||||
## 3. Install postgres
|
||||
Alpine 3.23 includes PostgreSQL 18:
|
||||
```sh
|
||||
apk add postgresql postgresql-contrib
|
||||
rc-update add postgresql
|
||||
/etc/init.d/postgresql setup
|
||||
rc-service postgresql start
|
||||
psql -U postgres -c "CREATE USER bspds WITH PASSWORD 'your-secure-password';"
|
||||
psql -U postgres -c "CREATE DATABASE pds OWNER bspds;"
|
||||
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;"
|
||||
psql -U postgres -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';"
|
||||
psql -U postgres -c "CREATE DATABASE pds OWNER tranquil_pds;"
|
||||
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
|
||||
```
|
||||
## 4. Install minio
|
||||
```sh
|
||||
@@ -78,7 +77,6 @@ mc alias set local http://localhost:9000 minioadmin your-minio-password
|
||||
mc mb local/pds-blobs
|
||||
```
|
||||
## 5. Install valkey
|
||||
Alpine 3.23 includes Valkey 9:
|
||||
```sh
|
||||
apk add valkey
|
||||
rc-update add valkey
|
||||
@@ -90,11 +88,11 @@ curl -fsSL https://deno.land/install.sh | sh
|
||||
export PATH="$HOME/.deno/bin:$PATH"
|
||||
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile
|
||||
```
|
||||
## 7. Clone and Build BSPDS
|
||||
## 7. Clone and Build Tranquil PDS
|
||||
```sh
|
||||
mkdir -p /opt && cd /opt
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox bspds
|
||||
cd bspds
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
|
||||
cd tranquil-pds
|
||||
cd frontend
|
||||
deno task build
|
||||
cd ..
|
||||
@@ -103,56 +101,55 @@ cargo build --release
|
||||
## 8. Install sqlx-cli and Run Migrations
|
||||
```sh
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
export DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds"
|
||||
export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds"
|
||||
sqlx migrate run
|
||||
```
|
||||
## 9. Configure BSPDS
|
||||
## 9. Configure Tranquil PDS
|
||||
```sh
|
||||
mkdir -p /etc/bspds
|
||||
cp /opt/bspds/.env.example /etc/bspds/bspds.env
|
||||
chmod 600 /etc/bspds/bspds.env
|
||||
mkdir -p /etc/tranquil-pds
|
||||
cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.env
|
||||
chmod 600 /etc/tranquil-pds/tranquil-pds.env
|
||||
```
|
||||
Edit `/etc/bspds/bspds.env` and fill in your values. Generate secrets with:
|
||||
Edit `/etc/tranquil-pds/tranquil-pds.env` and fill in your values. Generate secrets with:
|
||||
```sh
|
||||
openssl rand -base64 48
|
||||
```
|
||||
## 10. Create OpenRC Service
|
||||
```sh
|
||||
adduser -D -H -s /sbin/nologin bspds
|
||||
cp /opt/bspds/target/release/bspds /usr/local/bin/
|
||||
mkdir -p /var/lib/bspds
|
||||
cp -r /opt/bspds/frontend/dist /var/lib/bspds/frontend
|
||||
chown -R bspds:bspds /var/lib/bspds
|
||||
cat > /etc/init.d/bspds << 'EOF'
|
||||
adduser -D -H -s /sbin/nologin tranquil-pds
|
||||
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
|
||||
mkdir -p /var/lib/tranquil-pds
|
||||
cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend
|
||||
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds
|
||||
cat > /etc/init.d/tranquil-pds << 'EOF'
|
||||
#!/sbin/openrc-run
|
||||
name="bspds"
|
||||
description="BSPDS - AT Protocol PDS"
|
||||
command="/usr/local/bin/bspds"
|
||||
command_user="bspds"
|
||||
name="tranquil-pds"
|
||||
description="Tranquil PDS - AT Protocol PDS"
|
||||
command="/usr/local/bin/tranquil-pds"
|
||||
command_user="tranquil-pds"
|
||||
command_background=true
|
||||
pidfile="/run/${RC_SVCNAME}.pid"
|
||||
output_log="/var/log/bspds.log"
|
||||
error_log="/var/log/bspds.log"
|
||||
output_log="/var/log/tranquil-pds.log"
|
||||
error_log="/var/log/tranquil-pds.log"
|
||||
depend() {
|
||||
need net postgresql minio
|
||||
}
|
||||
start_pre() {
|
||||
export FRONTEND_DIR=/var/lib/bspds/frontend
|
||||
. /etc/bspds/bspds.env
|
||||
export FRONTEND_DIR=/var/lib/tranquil-pds/frontend
|
||||
. /etc/tranquil-pds/tranquil-pds.env
|
||||
export SERVER_HOST SERVER_PORT PDS_HOSTNAME DATABASE_URL
|
||||
export S3_ENDPOINT AWS_REGION S3_BUCKET AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
|
||||
export VALKEY_URL JWT_SECRET DPOP_SECRET MASTER_KEY CRAWLERS
|
||||
}
|
||||
EOF
|
||||
chmod +x /etc/init.d/bspds
|
||||
rc-update add bspds
|
||||
rc-service bspds start
|
||||
chmod +x /etc/init.d/tranquil-pds
|
||||
rc-update add tranquil-pds
|
||||
rc-service tranquil-pds start
|
||||
```
|
||||
## 11. Install and Configure nginx
|
||||
Alpine 3.23 includes nginx 1.28:
|
||||
```sh
|
||||
apk add nginx certbot certbot-nginx
|
||||
cat > /etc/nginx/http.d/bspds.conf << 'EOF'
|
||||
cat > /etc/nginx/http.d/tranquil-pds.conf << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
@@ -217,26 +214,26 @@ rc-update add ip6tables
|
||||
```
|
||||
## 14. Verify Installation
|
||||
```sh
|
||||
rc-service bspds status
|
||||
rc-service tranquil-pds status
|
||||
curl -s https://pds.example.com/xrpc/_health
|
||||
curl -s https://pds.example.com/.well-known/atproto-did
|
||||
```
|
||||
## Maintenance
|
||||
View logs:
|
||||
```sh
|
||||
tail -f /var/log/bspds.log
|
||||
tail -f /var/log/tranquil-pds.log
|
||||
```
|
||||
Update BSPDS:
|
||||
Update Tranquil PDS:
|
||||
```sh
|
||||
cd /opt/bspds
|
||||
cd /opt/tranquil-pds
|
||||
git pull
|
||||
cd frontend && deno task build && cd ..
|
||||
cargo build --release
|
||||
rc-service bspds stop
|
||||
cp target/release/bspds /usr/local/bin/
|
||||
cp -r frontend/dist /var/lib/bspds/frontend
|
||||
DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" sqlx migrate run
|
||||
rc-service bspds start
|
||||
rc-service tranquil-pds stop
|
||||
cp target/release/tranquil-pds /usr/local/bin/
|
||||
cp -r frontend/dist /var/lib/tranquil-pds/frontend
|
||||
DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run
|
||||
rc-service tranquil-pds start
|
||||
```
|
||||
Backup database:
|
||||
```sh
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# BSPDS Containerized Production Deployment
|
||||
# Tranquil PDS Containerized Production Deployment
|
||||
> **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.
|
||||
This guide covers deploying BSPDS using containers with podman.
|
||||
This guide covers deploying Tranquil PDS using containers with podman.
|
||||
- **Debian 13+**: Uses systemd quadlets (modern, declarative container management)
|
||||
- **Alpine 3.23+**: Uses OpenRC service script with podman-compose
|
||||
## Prerequisites
|
||||
@@ -39,14 +39,14 @@ apt install -y podman
|
||||
## 2. Create Directory Structure
|
||||
```bash
|
||||
mkdir -p /etc/containers/systemd
|
||||
mkdir -p /srv/bspds/{postgres,minio,valkey,certs,acme,config}
|
||||
mkdir -p /srv/tranquil-pds/{postgres,minio,valkey,certs,acme,config}
|
||||
```
|
||||
## 3. Create Environment File
|
||||
```bash
|
||||
cp /opt/bspds/.env.example /srv/bspds/config/bspds.env
|
||||
chmod 600 /srv/bspds/config/bspds.env
|
||||
cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env
|
||||
chmod 600 /srv/tranquil-pds/config/tranquil-pds.env
|
||||
```
|
||||
Edit `/srv/bspds/config/bspds.env` and fill in your values. Generate secrets with:
|
||||
Edit `/srv/tranquil-pds/config/tranquil-pds.env` and fill in your values. Generate secrets with:
|
||||
```bash
|
||||
openssl rand -base64 48
|
||||
```
|
||||
@@ -54,37 +54,37 @@ For quadlets, also add `DATABASE_URL` with the full connection string (systemd d
|
||||
## 4. Install Quadlet Definitions
|
||||
Copy the quadlet files from the repository:
|
||||
```bash
|
||||
cp /opt/bspds/deploy/quadlets/*.pod /etc/containers/systemd/
|
||||
cp /opt/bspds/deploy/quadlets/*.container /etc/containers/systemd/
|
||||
cp /opt/tranquil-pds/deploy/quadlets/*.pod /etc/containers/systemd/
|
||||
cp /opt/tranquil-pds/deploy/quadlets/*.container /etc/containers/systemd/
|
||||
```
|
||||
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.
|
||||
## 5. Create nginx Configuration
|
||||
```bash
|
||||
cp /opt/bspds/deploy/nginx/nginx-quadlet.conf /srv/bspds/config/nginx.conf
|
||||
cp /opt/tranquil-pds/deploy/nginx/nginx-quadlet.conf /srv/tranquil-pds/config/nginx.conf
|
||||
```
|
||||
## 6. Build BSPDS Image
|
||||
## 6. Build Tranquil PDS Image
|
||||
```bash
|
||||
cd /opt
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox bspds
|
||||
cd bspds
|
||||
podman build -t bspds:latest .
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
|
||||
cd tranquil-pds
|
||||
podman build -t tranquil-pds:latest .
|
||||
```
|
||||
## 7. Create Podman Secrets
|
||||
```bash
|
||||
source /srv/bspds/config/bspds.env
|
||||
echo "$DB_PASSWORD" | podman secret create bspds-db-password -
|
||||
echo "$MINIO_ROOT_PASSWORD" | podman secret create bspds-minio-password -
|
||||
source /srv/tranquil-pds/config/tranquil-pds.env
|
||||
echo "$DB_PASSWORD" | podman secret create tranquil-pds-db-password -
|
||||
echo "$MINIO_ROOT_PASSWORD" | podman secret create tranquil-pds-minio-password -
|
||||
```
|
||||
## 8. Start Services and Initialize
|
||||
```bash
|
||||
systemctl daemon-reload
|
||||
systemctl start bspds-db bspds-minio bspds-valkey
|
||||
systemctl start tranquil-pds-db tranquil-pds-minio tranquil-pds-valkey
|
||||
sleep 10
|
||||
```
|
||||
|
||||
Create the minio bucket:
|
||||
```bash
|
||||
podman run --rm --pod bspds \
|
||||
podman run --rm --pod tranquil-pds \
|
||||
-e MINIO_ROOT_USER=minioadmin \
|
||||
-e MINIO_ROOT_PASSWORD=your-minio-password \
|
||||
docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \
|
||||
@@ -94,7 +94,7 @@ podman run --rm --pod bspds \
|
||||
Run migrations:
|
||||
```bash
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
DATABASE_URL="postgres://bspds:your-db-password@localhost:5432/pds" sqlx migrate run --source /opt/bspds/migrations
|
||||
DATABASE_URL="postgres://tranquil_pds:your-db-password@localhost:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations
|
||||
```
|
||||
## 9. Obtain Wildcard SSL Certificate
|
||||
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,16 +102,16 @@ User handles are served as subdomains (e.g., `alice.pds.example.com`), so you ne
|
||||
Create temporary self-signed cert to start services:
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
|
||||
-keyout /srv/bspds/certs/privkey.pem \
|
||||
-out /srv/bspds/certs/fullchain.pem \
|
||||
-keyout /srv/tranquil-pds/certs/privkey.pem \
|
||||
-out /srv/tranquil-pds/certs/fullchain.pem \
|
||||
-subj "/CN=pds.example.com"
|
||||
systemctl start bspds-app bspds-nginx
|
||||
systemctl start tranquil-pds-app tranquil-pds-nginx
|
||||
```
|
||||
|
||||
Get a wildcard certificate using DNS validation:
|
||||
```bash
|
||||
podman run --rm -it \
|
||||
-v /srv/bspds/certs:/etc/letsencrypt:Z \
|
||||
-v /srv/tranquil-pds/certs:/etc/letsencrypt:Z \
|
||||
docker.io/certbot/certbot:v5.2.2 certonly \
|
||||
--manual --preferred-challenges dns \
|
||||
-d pds.example.com -d '*.pds.example.com' \
|
||||
@@ -123,13 +123,13 @@ For automated renewal, use a DNS provider plugin (e.g., cloudflare, route53).
|
||||
|
||||
Link certificates and restart:
|
||||
```bash
|
||||
ln -sf /srv/bspds/certs/live/pds.example.com/fullchain.pem /srv/bspds/certs/fullchain.pem
|
||||
ln -sf /srv/bspds/certs/live/pds.example.com/privkey.pem /srv/bspds/certs/privkey.pem
|
||||
systemctl restart bspds-nginx
|
||||
ln -sf /srv/tranquil-pds/certs/live/pds.example.com/fullchain.pem /srv/tranquil-pds/certs/fullchain.pem
|
||||
ln -sf /srv/tranquil-pds/certs/live/pds.example.com/privkey.pem /srv/tranquil-pds/certs/privkey.pem
|
||||
systemctl restart tranquil-pds-nginx
|
||||
```
|
||||
## 10. Enable All Services
|
||||
```bash
|
||||
systemctl enable bspds-db bspds-minio bspds-valkey bspds-app bspds-nginx
|
||||
systemctl enable tranquil-pds-db tranquil-pds-minio tranquil-pds-valkey tranquil-pds-app tranquil-pds-nginx
|
||||
```
|
||||
## 11. Configure Firewall
|
||||
```bash
|
||||
@@ -142,7 +142,7 @@ ufw enable
|
||||
## 12. Certificate Renewal
|
||||
Add to root's crontab (`crontab -e`):
|
||||
```
|
||||
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
|
||||
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
|
||||
```
|
||||
---
|
||||
# Alpine 3.23+ with OpenRC
|
||||
@@ -161,79 +161,79 @@ rc-service podman start
|
||||
```
|
||||
## 2. Create Directory Structure
|
||||
```sh
|
||||
mkdir -p /srv/bspds/{data,config}
|
||||
mkdir -p /srv/bspds/data/{postgres,minio,valkey,certs,acme}
|
||||
mkdir -p /srv/tranquil-pds/{data,config}
|
||||
mkdir -p /srv/tranquil-pds/data/{postgres,minio,valkey,certs,acme}
|
||||
```
|
||||
## 3. Clone Repository and Build
|
||||
```sh
|
||||
cd /opt
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox bspds
|
||||
cd bspds
|
||||
podman build -t bspds:latest .
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
|
||||
cd tranquil-pds
|
||||
podman build -t tranquil-pds:latest .
|
||||
```
|
||||
## 4. Create Environment File
|
||||
```sh
|
||||
cp /opt/bspds/.env.example /srv/bspds/config/bspds.env
|
||||
chmod 600 /srv/bspds/config/bspds.env
|
||||
cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env
|
||||
chmod 600 /srv/tranquil-pds/config/tranquil-pds.env
|
||||
```
|
||||
Edit `/srv/bspds/config/bspds.env` and fill in your values. Generate secrets with:
|
||||
Edit `/srv/tranquil-pds/config/tranquil-pds.env` and fill in your values. Generate secrets with:
|
||||
```sh
|
||||
openssl rand -base64 48
|
||||
```
|
||||
## 5. Set Up Compose and nginx
|
||||
Copy the production compose and nginx configs:
|
||||
```sh
|
||||
cp /opt/bspds/docker-compose.prod.yml /srv/bspds/docker-compose.yml
|
||||
cp /opt/bspds/nginx.prod.conf /srv/bspds/config/nginx.conf
|
||||
cp /opt/tranquil-pds/docker-compose.prod.yml /srv/tranquil-pds/docker-compose.yml
|
||||
cp /opt/tranquil-pds/nginx.prod.conf /srv/tranquil-pds/config/nginx.conf
|
||||
```
|
||||
Edit `/srv/bspds/docker-compose.yml` to adjust paths if needed:
|
||||
- Update volume mounts to use `/srv/bspds/data/` paths
|
||||
- Update nginx cert paths to match `/srv/bspds/data/certs/`
|
||||
Edit `/srv/bspds/config/nginx.conf` to update cert paths:
|
||||
Edit `/srv/tranquil-pds/docker-compose.yml` to adjust paths if needed:
|
||||
- Update volume mounts to use `/srv/tranquil-pds/data/` paths
|
||||
- Update nginx cert paths to match `/srv/tranquil-pds/data/certs/`
|
||||
Edit `/srv/tranquil-pds/config/nginx.conf` to update cert paths:
|
||||
- Change `/etc/nginx/certs/live/${PDS_HOSTNAME}/` to `/etc/nginx/certs/`
|
||||
## 6. Create OpenRC Service
|
||||
```sh
|
||||
cat > /etc/init.d/bspds << 'EOF'
|
||||
cat > /etc/init.d/tranquil-pds << 'EOF'
|
||||
#!/sbin/openrc-run
|
||||
name="bspds"
|
||||
description="BSPDS AT Protocol PDS (containerized)"
|
||||
name="tranquil-pds"
|
||||
description="Tranquil PDS AT Protocol PDS (containerized)"
|
||||
command="/usr/bin/podman-compose"
|
||||
command_args="-f /srv/bspds/docker-compose.yml up"
|
||||
command_args="-f /srv/tranquil-pds/docker-compose.yml up"
|
||||
command_background=true
|
||||
pidfile="/run/${RC_SVCNAME}.pid"
|
||||
directory="/srv/bspds"
|
||||
directory="/srv/tranquil-pds"
|
||||
depend() {
|
||||
need net podman
|
||||
after firewall
|
||||
}
|
||||
start_pre() {
|
||||
set -a
|
||||
. /srv/bspds/config/bspds.env
|
||||
. /srv/tranquil-pds/config/tranquil-pds.env
|
||||
set +a
|
||||
}
|
||||
stop() {
|
||||
ebegin "Stopping ${name}"
|
||||
cd /srv/bspds
|
||||
cd /srv/tranquil-pds
|
||||
set -a
|
||||
. /srv/bspds/config/bspds.env
|
||||
. /srv/tranquil-pds/config/tranquil-pds.env
|
||||
set +a
|
||||
podman-compose -f /srv/bspds/docker-compose.yml down
|
||||
podman-compose -f /srv/tranquil-pds/docker-compose.yml down
|
||||
eend $?
|
||||
}
|
||||
EOF
|
||||
chmod +x /etc/init.d/bspds
|
||||
chmod +x /etc/init.d/tranquil-pds
|
||||
```
|
||||
## 7. Initialize Services
|
||||
Start services:
|
||||
```sh
|
||||
rc-service bspds start
|
||||
rc-service tranquil-pds start
|
||||
sleep 15
|
||||
```
|
||||
|
||||
Create the minio bucket:
|
||||
```sh
|
||||
source /srv/bspds/config/bspds.env
|
||||
podman run --rm --network bspds_default \
|
||||
source /srv/tranquil-pds/config/tranquil-pds.env
|
||||
podman run --rm --network tranquil-pds_default \
|
||||
-e MINIO_ROOT_USER="$MINIO_ROOT_USER" \
|
||||
-e MINIO_ROOT_PASSWORD="$MINIO_ROOT_PASSWORD" \
|
||||
docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \
|
||||
@@ -246,8 +246,8 @@ apk add rustup
|
||||
rustup-init -y
|
||||
source ~/.cargo/env
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
DB_IP=$(podman inspect bspds-db-1 --format '{{.NetworkSettings.Networks.bspds_default.IPAddress}}')
|
||||
DATABASE_URL="postgres://bspds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/bspds/migrations
|
||||
DB_IP=$(podman inspect tranquil-pds-db-1 --format '{{.NetworkSettings.Networks.tranquil-pds_default.IPAddress}}')
|
||||
DATABASE_URL="postgres://tranquil_pds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations
|
||||
```
|
||||
## 8. Obtain Wildcard SSL Certificate
|
||||
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,16 +255,16 @@ User handles are served as subdomains (e.g., `alice.pds.example.com`), so you ne
|
||||
Create temporary self-signed cert to start services:
|
||||
```sh
|
||||
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
|
||||
-keyout /srv/bspds/data/certs/privkey.pem \
|
||||
-out /srv/bspds/data/certs/fullchain.pem \
|
||||
-keyout /srv/tranquil-pds/data/certs/privkey.pem \
|
||||
-out /srv/tranquil-pds/data/certs/fullchain.pem \
|
||||
-subj "/CN=pds.example.com"
|
||||
rc-service bspds restart
|
||||
rc-service tranquil-pds restart
|
||||
```
|
||||
|
||||
Get a wildcard certificate using DNS validation:
|
||||
```sh
|
||||
podman run --rm -it \
|
||||
-v /srv/bspds/data/certs:/etc/letsencrypt \
|
||||
-v /srv/tranquil-pds/data/certs:/etc/letsencrypt \
|
||||
docker.io/certbot/certbot:v5.2.2 certonly \
|
||||
--manual --preferred-challenges dns \
|
||||
-d pds.example.com -d '*.pds.example.com' \
|
||||
@@ -274,13 +274,13 @@ Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't aut
|
||||
|
||||
Link certificates and restart:
|
||||
```sh
|
||||
ln -sf /srv/bspds/data/certs/live/pds.example.com/fullchain.pem /srv/bspds/data/certs/fullchain.pem
|
||||
ln -sf /srv/bspds/data/certs/live/pds.example.com/privkey.pem /srv/bspds/data/certs/privkey.pem
|
||||
rc-service bspds restart
|
||||
ln -sf /srv/tranquil-pds/data/certs/live/pds.example.com/fullchain.pem /srv/tranquil-pds/data/certs/fullchain.pem
|
||||
ln -sf /srv/tranquil-pds/data/certs/live/pds.example.com/privkey.pem /srv/tranquil-pds/data/certs/privkey.pem
|
||||
rc-service tranquil-pds restart
|
||||
```
|
||||
## 9. Enable Service at Boot
|
||||
```sh
|
||||
rc-update add bspds
|
||||
rc-update add tranquil-pds
|
||||
```
|
||||
## 10. Configure Firewall
|
||||
```sh
|
||||
@@ -305,7 +305,7 @@ rc-update add ip6tables
|
||||
## 11. Certificate Renewal
|
||||
Add to root's crontab (`crontab -e`):
|
||||
```
|
||||
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
|
||||
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
|
||||
```
|
||||
---
|
||||
# Verification and Maintenance
|
||||
@@ -317,36 +317,36 @@ curl -s https://pds.example.com/.well-known/atproto-did
|
||||
## View Logs
|
||||
**Debian:**
|
||||
```bash
|
||||
journalctl -u bspds-app -f
|
||||
podman logs -f bspds-app
|
||||
journalctl -u tranquil-pds-app -f
|
||||
podman logs -f tranquil-pds-app
|
||||
```
|
||||
**Alpine:**
|
||||
```sh
|
||||
podman-compose -f /srv/bspds/docker-compose.yml logs -f
|
||||
podman logs -f bspds-bspds-1
|
||||
podman-compose -f /srv/tranquil-pds/docker-compose.yml logs -f
|
||||
podman logs -f tranquil-pds-tranquil-pds-1
|
||||
```
|
||||
## Update BSPDS
|
||||
## Update Tranquil PDS
|
||||
```sh
|
||||
cd /opt/bspds
|
||||
cd /opt/tranquil-pds
|
||||
git pull
|
||||
podman build -t bspds:latest .
|
||||
podman build -t tranquil-pds:latest .
|
||||
```
|
||||
|
||||
Debian:
|
||||
```bash
|
||||
systemctl restart bspds-app
|
||||
systemctl restart tranquil-pds-app
|
||||
```
|
||||
|
||||
Alpine:
|
||||
```sh
|
||||
rc-service bspds restart
|
||||
rc-service tranquil-pds restart
|
||||
```
|
||||
## Backup Database
|
||||
**Debian:**
|
||||
```bash
|
||||
podman exec bspds-db pg_dump -U bspds pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
podman exec tranquil-pds-db pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
```
|
||||
**Alpine:**
|
||||
```sh
|
||||
podman exec bspds-db-1 pg_dump -U bspds pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
podman exec tranquil-pds-db-1 pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# BSPDS Production Installation on Debian
|
||||
# Tranquil PDS Production Installation on Debian
|
||||
> **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.
|
||||
|
||||
This guide covers installing BSPDS on Debian 13 "Trixie" (current stable as of December 2025).
|
||||
This guide covers installing Tranquil PDS on Debian 13 "Trixie".
|
||||
|
||||
## Prerequisites
|
||||
- A VPS with at least 2GB RAM and 20GB disk
|
||||
@@ -19,16 +19,15 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
source ~/.cargo/env
|
||||
rustup default stable
|
||||
```
|
||||
This installs the latest stable Rust (1.92+ as of December 2025).
|
||||
This installs the latest stable Rust.
|
||||
## 3. Install postgres
|
||||
Debian 13 includes PostgreSQL 17:
|
||||
```bash
|
||||
apt install -y postgresql postgresql-contrib
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql
|
||||
sudo -u postgres psql -c "CREATE USER bspds WITH PASSWORD 'your-secure-password';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE pds OWNER bspds;"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;"
|
||||
sudo -u postgres psql -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE pds OWNER tranquil_pds;"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
|
||||
```
|
||||
## 4. Install minio
|
||||
```bash
|
||||
@@ -71,7 +70,6 @@ mc alias set local http://localhost:9000 minioadmin your-minio-password
|
||||
mc mb local/pds-blobs
|
||||
```
|
||||
## 5. Install valkey
|
||||
Debian 13 includes Valkey 8:
|
||||
```bash
|
||||
apt install -y valkey
|
||||
systemctl enable valkey-server
|
||||
@@ -83,11 +81,11 @@ curl -fsSL https://deno.land/install.sh | sh
|
||||
export PATH="$HOME/.deno/bin:$PATH"
|
||||
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
|
||||
```
|
||||
## 7. Clone and Build BSPDS
|
||||
## 7. Clone and Build Tranquil PDS
|
||||
```bash
|
||||
cd /opt
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox bspds
|
||||
cd bspds
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
|
||||
cd tranquil-pds
|
||||
cd frontend
|
||||
deno task build
|
||||
cd ..
|
||||
@@ -96,51 +94,50 @@ cargo build --release
|
||||
## 8. Install sqlx-cli and Run Migrations
|
||||
```bash
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
export DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds"
|
||||
export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds"
|
||||
sqlx migrate run
|
||||
```
|
||||
## 9. Configure BSPDS
|
||||
## 9. Configure Tranquil PDS
|
||||
```bash
|
||||
mkdir -p /etc/bspds
|
||||
cp /opt/bspds/.env.example /etc/bspds/bspds.env
|
||||
chmod 600 /etc/bspds/bspds.env
|
||||
mkdir -p /etc/tranquil-pds
|
||||
cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.env
|
||||
chmod 600 /etc/tranquil-pds/tranquil-pds.env
|
||||
```
|
||||
Edit `/etc/bspds/bspds.env` and fill in your values. Generate secrets with:
|
||||
Edit `/etc/tranquil-pds/tranquil-pds.env` and fill in your values. Generate secrets with:
|
||||
```bash
|
||||
openssl rand -base64 48
|
||||
```
|
||||
## 10. Create Systemd Service
|
||||
```bash
|
||||
useradd -r -s /sbin/nologin bspds
|
||||
cp /opt/bspds/target/release/bspds /usr/local/bin/
|
||||
mkdir -p /var/lib/bspds
|
||||
cp -r /opt/bspds/frontend/dist /var/lib/bspds/frontend
|
||||
chown -R bspds:bspds /var/lib/bspds
|
||||
cat > /etc/systemd/system/bspds.service << 'EOF'
|
||||
useradd -r -s /sbin/nologin tranquil-pds
|
||||
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
|
||||
mkdir -p /var/lib/tranquil-pds
|
||||
cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend
|
||||
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds
|
||||
cat > /etc/systemd/system/tranquil-pds.service << 'EOF'
|
||||
[Unit]
|
||||
Description=BSPDS - AT Protocol PDS
|
||||
Description=Tranquil PDS - AT Protocol PDS
|
||||
After=network.target postgresql.service minio.service
|
||||
[Service]
|
||||
Type=simple
|
||||
User=bspds
|
||||
Group=bspds
|
||||
EnvironmentFile=/etc/bspds/bspds.env
|
||||
Environment=FRONTEND_DIR=/var/lib/bspds/frontend
|
||||
ExecStart=/usr/local/bin/bspds
|
||||
User=tranquil-pds
|
||||
Group=tranquil-pds
|
||||
EnvironmentFile=/etc/tranquil-pds/tranquil-pds.env
|
||||
Environment=FRONTEND_DIR=/var/lib/tranquil-pds/frontend
|
||||
ExecStart=/usr/local/bin/tranquil-pds
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable bspds
|
||||
systemctl start bspds
|
||||
systemctl enable tranquil-pds
|
||||
systemctl start tranquil-pds
|
||||
```
|
||||
## 11. Install and Configure nginx
|
||||
Debian 13 includes nginx 1.26:
|
||||
```bash
|
||||
apt install -y nginx certbot python3-certbot-nginx
|
||||
cat > /etc/nginx/sites-available/bspds << 'EOF'
|
||||
cat > /etc/nginx/sites-available/tranquil-pds << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
@@ -158,7 +155,7 @@ server {
|
||||
}
|
||||
}
|
||||
EOF
|
||||
ln -s /etc/nginx/sites-available/bspds /etc/nginx/sites-enabled/
|
||||
ln -s /etc/nginx/sites-available/tranquil-pds /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
@@ -192,26 +189,26 @@ ufw enable
|
||||
```
|
||||
## 14. Verify Installation
|
||||
```bash
|
||||
systemctl status bspds
|
||||
systemctl status tranquil-pds
|
||||
curl -s https://pds.example.com/xrpc/_health | jq
|
||||
curl -s https://pds.example.com/.well-known/atproto-did
|
||||
```
|
||||
## Maintenance
|
||||
View logs:
|
||||
```bash
|
||||
journalctl -u bspds -f
|
||||
journalctl -u tranquil-pds -f
|
||||
```
|
||||
Update BSPDS:
|
||||
Update Tranquil PDS:
|
||||
```bash
|
||||
cd /opt/bspds
|
||||
cd /opt/tranquil-pds
|
||||
git pull
|
||||
cd frontend && deno task build && cd ..
|
||||
cargo build --release
|
||||
systemctl stop bspds
|
||||
cp target/release/bspds /usr/local/bin/
|
||||
cp -r frontend/dist /var/lib/bspds/frontend
|
||||
DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" sqlx migrate run
|
||||
systemctl start bspds
|
||||
systemctl stop tranquil-pds
|
||||
cp target/release/tranquil-pds /usr/local/bin/
|
||||
cp -r frontend/dist /var/lib/tranquil-pds/frontend
|
||||
DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run
|
||||
systemctl start tranquil-pds
|
||||
```
|
||||
Backup database:
|
||||
```bash
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# BSPDS on Kubernetes
|
||||
# Tranquil PDS on Kubernetes
|
||||
|
||||
If you're reaching for kubernetes for this app, you're experienced enough to know how to spin up:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# BSPDS Production Installation on OpenBSD
|
||||
# Tranquil PDS Production Installation on OpenBSD
|
||||
> **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.
|
||||
This guide covers installing BSPDS on OpenBSD 7.8 (current release as of December 2025).
|
||||
This guide covers installing Tranquil PDS on OpenBSD 7.8.
|
||||
## Prerequisites
|
||||
- A VPS with at least 2GB RAM and 20GB disk
|
||||
- A domain name pointing to your server's IP
|
||||
@@ -16,7 +16,7 @@ pkg_add curl git
|
||||
```sh
|
||||
pkg_add rust
|
||||
```
|
||||
OpenBSD 7.8 ships Rust 1.82+. For the latest stable (1.92+), use rustup:
|
||||
OpenBSD ships Rust in ports. For the latest stable, use rustup:
|
||||
```sh
|
||||
pkg_add rustup
|
||||
rustup-init -y
|
||||
@@ -24,7 +24,6 @@ source ~/.cargo/env
|
||||
rustup default stable
|
||||
```
|
||||
## 3. Install postgres
|
||||
OpenBSD 7.8 includes PostgreSQL 17 (PostgreSQL 18 may not yet be in ports):
|
||||
```sh
|
||||
pkg_add postgresql-server postgresql-client
|
||||
mkdir -p /var/postgresql/data
|
||||
@@ -32,9 +31,9 @@ chown _postgresql:_postgresql /var/postgresql/data
|
||||
su - _postgresql -c "initdb -D /var/postgresql/data -U postgres -A scram-sha-256"
|
||||
rcctl enable postgresql
|
||||
rcctl start postgresql
|
||||
psql -U postgres -c "CREATE USER bspds WITH PASSWORD 'your-secure-password';"
|
||||
psql -U postgres -c "CREATE DATABASE pds OWNER bspds;"
|
||||
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;"
|
||||
psql -U postgres -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';"
|
||||
psql -U postgres -c "CREATE DATABASE pds OWNER tranquil_pds;"
|
||||
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
|
||||
```
|
||||
## 4. Install minio
|
||||
OpenBSD doesn't have a minio package. Options:
|
||||
@@ -93,11 +92,11 @@ curl -fsSL https://deno.land/install.sh | sh
|
||||
export PATH="$HOME/.deno/bin:$PATH"
|
||||
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile
|
||||
```
|
||||
## 7. Clone and Build BSPDS
|
||||
## 7. Clone and Build Tranquil PDS
|
||||
```sh
|
||||
mkdir -p /opt && cd /opt
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox bspds
|
||||
cd bspds
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
|
||||
cd tranquil-pds
|
||||
cd frontend
|
||||
deno task build
|
||||
cd ..
|
||||
@@ -106,46 +105,46 @@ cargo build --release
|
||||
## 8. Install sqlx-cli and Run Migrations
|
||||
```sh
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
export DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds"
|
||||
export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds"
|
||||
sqlx migrate run
|
||||
```
|
||||
## 9. Configure BSPDS
|
||||
## 9. Configure Tranquil PDS
|
||||
```sh
|
||||
mkdir -p /etc/bspds
|
||||
cp /opt/bspds/.env.example /etc/bspds/bspds.conf
|
||||
chmod 600 /etc/bspds/bspds.conf
|
||||
mkdir -p /etc/tranquil-pds
|
||||
cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.conf
|
||||
chmod 600 /etc/tranquil-pds/tranquil-pds.conf
|
||||
```
|
||||
Edit `/etc/bspds/bspds.conf` and fill in your values. Generate secrets with:
|
||||
Edit `/etc/tranquil-pds/tranquil-pds.conf` and fill in your values. Generate secrets with:
|
||||
```sh
|
||||
openssl rand -base64 48
|
||||
```
|
||||
## 10. Create rc.d Service
|
||||
```sh
|
||||
useradd -d /var/empty -s /sbin/nologin _bspds
|
||||
cp /opt/bspds/target/release/bspds /usr/local/bin/
|
||||
mkdir -p /var/bspds
|
||||
cp -r /opt/bspds/frontend/dist /var/bspds/frontend
|
||||
chown -R _bspds:_bspds /var/bspds
|
||||
cat > /etc/rc.d/bspds << 'EOF'
|
||||
useradd -d /var/empty -s /sbin/nologin _tranquil_pds
|
||||
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
|
||||
mkdir -p /var/tranquil-pds
|
||||
cp -r /opt/tranquil-pds/frontend/dist /var/tranquil-pds/frontend
|
||||
chown -R _tranquil_pds:_tranquil_pds /var/tranquil-pds
|
||||
cat > /etc/rc.d/tranquil_pds << 'EOF'
|
||||
#!/bin/ksh
|
||||
daemon="/usr/local/bin/bspds"
|
||||
daemon_user="_bspds"
|
||||
daemon="/usr/local/bin/tranquil-pds"
|
||||
daemon_user="_tranquil_pds"
|
||||
daemon_logger="daemon.info"
|
||||
. /etc/rc.d/rc.subr
|
||||
rc_pre() {
|
||||
export FRONTEND_DIR=/var/bspds/frontend
|
||||
export FRONTEND_DIR=/var/tranquil-pds/frontend
|
||||
while IFS='=' read -r key value; do
|
||||
case "$key" in
|
||||
\#*|"") continue ;;
|
||||
esac
|
||||
export "$key=$value"
|
||||
done < /etc/bspds/bspds.conf
|
||||
done < /etc/tranquil-pds/tranquil-pds.conf
|
||||
}
|
||||
rc_cmd $1
|
||||
EOF
|
||||
chmod +x /etc/rc.d/bspds
|
||||
rcctl enable bspds
|
||||
rcctl start bspds
|
||||
chmod +x /etc/rc.d/tranquil_pds
|
||||
rcctl enable tranquil_pds
|
||||
rcctl start tranquil_pds
|
||||
```
|
||||
## 11. Install and Configure nginx
|
||||
```sh
|
||||
@@ -227,7 +226,7 @@ pfctl -f /etc/pf.conf
|
||||
```
|
||||
## 14. Verify Installation
|
||||
```sh
|
||||
rcctl check bspds
|
||||
rcctl check tranquil_pds
|
||||
ftp -o - https://pds.example.com/xrpc/_health
|
||||
ftp -o - https://pds.example.com/.well-known/atproto-did
|
||||
```
|
||||
@@ -236,17 +235,17 @@ View logs:
|
||||
```sh
|
||||
tail -f /var/log/daemon
|
||||
```
|
||||
Update BSPDS:
|
||||
Update Tranquil PDS:
|
||||
```sh
|
||||
cd /opt/bspds
|
||||
cd /opt/tranquil-pds
|
||||
git pull
|
||||
cd frontend && deno task build && cd ..
|
||||
cargo build --release
|
||||
rcctl stop bspds
|
||||
cp target/release/bspds /usr/local/bin/
|
||||
cp -r frontend/dist /var/bspds/frontend
|
||||
DATABASE_URL="postgres://bspds:your-secure-password@localhost:5432/pds" sqlx migrate run
|
||||
rcctl start bspds
|
||||
rcctl stop tranquil_pds
|
||||
cp target/release/tranquil-pds /usr/local/bin/
|
||||
cp -r frontend/dist /var/tranquil-pds/frontend
|
||||
DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run
|
||||
rcctl start tranquil_pds
|
||||
```
|
||||
Backup database:
|
||||
```sh
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BSPDS</title>
|
||||
<title>Tranquil PDS</title>
|
||||
<style>
|
||||
html { background: #fafafa; }
|
||||
@media (prefers-color-scheme: dark) { html { background: #1a1a1a; } }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "bspds-frontend",
|
||||
"name": "tranquil-pds-frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
|
||||
@@ -255,7 +255,7 @@ export const api = {
|
||||
signalNumber: string | null
|
||||
signalVerified: boolean
|
||||
}> {
|
||||
return xrpc('com.bspds.account.getNotificationPrefs', { token })
|
||||
return xrpc('com.tranquil.account.getNotificationPrefs', { token })
|
||||
},
|
||||
|
||||
async updateNotificationPrefs(token: string, prefs: {
|
||||
@@ -264,7 +264,7 @@ export const api = {
|
||||
telegramUsername?: string
|
||||
signalNumber?: string
|
||||
}): Promise<{ success: boolean }> {
|
||||
return xrpc('com.bspds.account.updateNotificationPrefs', {
|
||||
return xrpc('com.tranquil.account.updateNotificationPrefs', {
|
||||
method: 'POST',
|
||||
token,
|
||||
body: prefs,
|
||||
@@ -272,7 +272,7 @@ export const api = {
|
||||
},
|
||||
|
||||
async confirmChannelVerification(token: string, channel: string, code: string): Promise<{ success: boolean }> {
|
||||
return xrpc('com.bspds.account.confirmChannelVerification', {
|
||||
return xrpc('com.tranquil.account.confirmChannelVerification', {
|
||||
method: 'POST',
|
||||
token,
|
||||
body: { channel, code },
|
||||
@@ -289,7 +289,7 @@ export const api = {
|
||||
body: string
|
||||
}>
|
||||
}> {
|
||||
return xrpc('com.bspds.account.getNotificationHistory', { token })
|
||||
return xrpc('com.tranquil.account.getNotificationHistory', { token })
|
||||
},
|
||||
|
||||
async getServerStats(token: string): Promise<{
|
||||
@@ -298,11 +298,11 @@ export const api = {
|
||||
recordCount: number
|
||||
blobStorageBytes: number
|
||||
}> {
|
||||
return xrpc('com.bspds.admin.getServerStats', { token })
|
||||
return xrpc('com.tranquil.admin.getServerStats', { token })
|
||||
},
|
||||
|
||||
async changePassword(token: string, currentPassword: string, newPassword: string): Promise<void> {
|
||||
await xrpc('com.bspds.account.changePassword', {
|
||||
await xrpc('com.tranquil.account.changePassword', {
|
||||
method: 'POST',
|
||||
token,
|
||||
body: { currentPassword, newPassword },
|
||||
@@ -317,11 +317,11 @@ export const api = {
|
||||
isCurrent: boolean
|
||||
}>
|
||||
}> {
|
||||
return xrpc('com.bspds.account.listSessions', { token })
|
||||
return xrpc('com.tranquil.account.listSessions', { token })
|
||||
},
|
||||
|
||||
async revokeSession(token: string, sessionId: string): Promise<void> {
|
||||
await xrpc('com.bspds.account.revokeSession', {
|
||||
await xrpc('com.tranquil.account.revokeSession', {
|
||||
method: 'POST',
|
||||
token,
|
||||
body: { sessionId },
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { api, type Session, type CreateAccountParams, type CreateAccountResult, ApiError } from './api'
|
||||
import { startOAuthLogin, handleOAuthCallback, checkForOAuthCallback, clearOAuthCallbackParams, refreshOAuthToken } from './oauth'
|
||||
|
||||
const STORAGE_KEY = 'bspds_session'
|
||||
const ACCOUNTS_KEY = 'bspds_accounts'
|
||||
const STORAGE_KEY = 'tranquil_pds_session'
|
||||
const ACCOUNTS_KEY = 'tranquil_pds_accounts'
|
||||
|
||||
export interface SavedAccount {
|
||||
did: string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const OAUTH_STATE_KEY = 'bspds_oauth_state'
|
||||
const OAUTH_VERIFIER_KEY = 'bspds_oauth_verifier'
|
||||
const OAUTH_STATE_KEY = 'tranquil_pds_oauth_state'
|
||||
const OAUTH_VERIFIER_KEY = 'tranquil_pds_oauth_verifier'
|
||||
|
||||
interface OAuthState {
|
||||
state: string
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { navigate } from '../lib/router.svelte'
|
||||
import { api, ApiError, type VerificationChannel } from '../lib/api'
|
||||
|
||||
const STORAGE_KEY = 'bspds_pending_verification'
|
||||
const STORAGE_KEY = 'tranquil_pds_pending_verification'
|
||||
|
||||
let handle = $state('')
|
||||
let email = $state('')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { confirmSignup, resendVerification, getAuthState } from '../lib/auth.svelte'
|
||||
import { navigate } from '../lib/router.svelte'
|
||||
|
||||
const STORAGE_KEY = 'bspds_pending_verification'
|
||||
const STORAGE_KEY = 'tranquil_pds_pending_verification'
|
||||
|
||||
interface PendingVerification {
|
||||
did: string
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
setupAuthenticatedUser,
|
||||
setupUnauthenticatedUser,
|
||||
} from './mocks'
|
||||
const STORAGE_KEY = 'bspds_session'
|
||||
const STORAGE_KEY = 'tranquil_pds_session'
|
||||
describe('Dashboard', () => {
|
||||
beforeEach(() => {
|
||||
clearMocks()
|
||||
@@ -38,8 +38,8 @@ describe('Dashboard', () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('heading', { name: /dashboard/i })).toBeInTheDocument()
|
||||
expect(screen.getByRole('heading', { name: /account overview/i })).toBeInTheDocument()
|
||||
expect(screen.getByText(/@testuser\.test\.bspds\.dev/)).toBeInTheDocument()
|
||||
expect(screen.getByText(/did:web:test\.bspds\.dev:u:testuser/)).toBeInTheDocument()
|
||||
expect(screen.getByText(/@testuser\.test\.tranquil\.dev/)).toBeInTheDocument()
|
||||
expect(screen.getByText(/did:web:test\.tranquil\.dev:u:testuser/)).toBeInTheDocument()
|
||||
expect(screen.getByText('test@example.com')).toBeInTheDocument()
|
||||
expect(screen.getByText('Verified')).toBeInTheDocument()
|
||||
expect(screen.getByText('Verified')).toHaveClass('badge', 'success')
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('Login', () => {
|
||||
json: async () => ({
|
||||
error: 'AccountNotVerified',
|
||||
message: 'Account not verified',
|
||||
did: 'did:web:test.bspds.dev:u:testuser',
|
||||
did: 'did:web:test.tranquil.dev:u:testuser',
|
||||
}),
|
||||
}))
|
||||
render(Login)
|
||||
@@ -116,7 +116,7 @@ describe('Login', () => {
|
||||
json: async () => ({
|
||||
error: 'AccountNotVerified',
|
||||
message: 'Account not verified',
|
||||
did: 'did:web:test.bspds.dev:u:testuser',
|
||||
did: 'did:web:test.tranquil.dev:u:testuser',
|
||||
}),
|
||||
}))
|
||||
render(Login)
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('Notifications', () => {
|
||||
describe('page structure', () => {
|
||||
beforeEach(() => {
|
||||
setupAuthenticatedUser()
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
})
|
||||
@@ -48,7 +48,7 @@ describe('Notifications', () => {
|
||||
setupAuthenticatedUser()
|
||||
})
|
||||
it('shows loading text while fetching preferences', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', async () => {
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
return jsonResponse(mockData.notificationPrefs())
|
||||
})
|
||||
@@ -61,7 +61,7 @@ describe('Notifications', () => {
|
||||
setupAuthenticatedUser()
|
||||
})
|
||||
it('displays all four channel options', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -73,7 +73,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('email channel is always selectable', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -83,7 +83,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('discord channel is disabled when not configured', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs({ discordId: null }))
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -93,7 +93,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('discord channel is enabled when configured', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs({ discordId: '123456789' }))
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -103,7 +103,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('shows hint for disabled channels', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -112,7 +112,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('selects current preferred channel', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs({ preferredChannel: 'email' }))
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -127,7 +127,7 @@ describe('Notifications', () => {
|
||||
setupAuthenticatedUser()
|
||||
})
|
||||
it('displays email as readonly with current value', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -138,7 +138,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('displays all channel inputs with current values', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs({
|
||||
discordId: '123456789',
|
||||
telegramUsername: 'testuser',
|
||||
@@ -158,7 +158,7 @@ describe('Notifications', () => {
|
||||
setupAuthenticatedUser()
|
||||
})
|
||||
it('shows Primary badge for email', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -167,7 +167,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('shows Verified badge for verified discord', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs({
|
||||
discordId: '123456789',
|
||||
discordVerified: true,
|
||||
@@ -180,7 +180,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('shows Not verified badge for unverified discord', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs({
|
||||
discordId: '123456789',
|
||||
discordVerified: false,
|
||||
@@ -192,7 +192,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('does not show badge when channel not configured', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -208,10 +208,10 @@ describe('Notifications', () => {
|
||||
})
|
||||
it('calls updateNotificationPrefs with correct data', async () => {
|
||||
let capturedBody: Record<string, unknown> | null = null
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
mockEndpoint('com.bspds.account.updateNotificationPrefs', (_url, options) => {
|
||||
mockEndpoint('com.tranquil.account.updateNotificationPrefs', (_url, options) => {
|
||||
capturedBody = JSON.parse((options?.body as string) || '{}')
|
||||
return jsonResponse({ success: true })
|
||||
})
|
||||
@@ -228,10 +228,10 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('shows loading state while saving', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
mockEndpoint('com.bspds.account.updateNotificationPrefs', async () => {
|
||||
mockEndpoint('com.tranquil.account.updateNotificationPrefs', async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
return jsonResponse({ success: true })
|
||||
})
|
||||
@@ -244,10 +244,10 @@ describe('Notifications', () => {
|
||||
expect(screen.getByRole('button', { name: /saving/i })).toBeDisabled()
|
||||
})
|
||||
it('shows success message after saving', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
mockEndpoint('com.bspds.account.updateNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.updateNotificationPrefs', () =>
|
||||
jsonResponse({ success: true })
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -260,10 +260,10 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('shows error when save fails', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
mockEndpoint('com.bspds.account.updateNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.updateNotificationPrefs', () =>
|
||||
errorResponse('InvalidRequest', 'Invalid channel configuration', 400)
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -278,11 +278,11 @@ describe('Notifications', () => {
|
||||
})
|
||||
it('reloads preferences after successful save', async () => {
|
||||
let loadCount = 0
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () => {
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () => {
|
||||
loadCount++
|
||||
return jsonResponse(mockData.notificationPrefs())
|
||||
})
|
||||
mockEndpoint('com.bspds.account.updateNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.updateNotificationPrefs', () =>
|
||||
jsonResponse({ success: true })
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -301,7 +301,7 @@ describe('Notifications', () => {
|
||||
setupAuthenticatedUser()
|
||||
})
|
||||
it('enables discord channel after entering discord ID', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
render(Notifications)
|
||||
@@ -314,7 +314,7 @@ describe('Notifications', () => {
|
||||
})
|
||||
})
|
||||
it('allows selecting a configured channel', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs({
|
||||
discordId: '123456789',
|
||||
discordVerified: true,
|
||||
@@ -334,7 +334,7 @@ describe('Notifications', () => {
|
||||
setupAuthenticatedUser()
|
||||
})
|
||||
it('shows error when loading preferences fails', async () => {
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
errorResponse('InternalError', 'Database connection failed', 500)
|
||||
)
|
||||
render(Notifications)
|
||||
|
||||
@@ -176,7 +176,7 @@ describe('Settings', () => {
|
||||
it('displays current handle', async () => {
|
||||
render(Settings)
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/current: @testuser\.test\.bspds\.dev/i)).toBeInTheDocument()
|
||||
expect(screen.getByText(/current: @testuser\.test\.tranquil\.dev/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
it('calls updateHandle with new handle', async () => {
|
||||
@@ -314,7 +314,7 @@ describe('Settings', () => {
|
||||
await waitFor(() => {
|
||||
expect(capturedBody?.token).toBe('DEL123')
|
||||
expect(capturedBody?.password).toBe('mypassword')
|
||||
expect(capturedBody?.did).toBe('did:web:test.bspds.dev:u:testuser')
|
||||
expect(capturedBody?.did).toBe('did:web:test.tranquil.dev:u:testuser')
|
||||
})
|
||||
})
|
||||
it('navigates to login after successful deletion', async () => {
|
||||
|
||||
@@ -85,8 +85,8 @@ export function errorResponse(error: string, message: string, status = 400): Moc
|
||||
}
|
||||
export const mockData = {
|
||||
session: (overrides?: Partial<Session>): Session => ({
|
||||
did: 'did:web:test.bspds.dev:u:testuser',
|
||||
handle: 'testuser.test.bspds.dev',
|
||||
did: 'did:web:test.tranquil.dev:u:testuser',
|
||||
handle: 'testuser.test.tranquil.dev',
|
||||
email: 'test@example.com',
|
||||
emailConfirmed: true,
|
||||
accessJwt: 'mock-access-jwt-token',
|
||||
@@ -102,8 +102,8 @@ export const mockData = {
|
||||
code: 'test-invite-123',
|
||||
available: 1,
|
||||
disabled: false,
|
||||
forAccount: 'did:web:test.bspds.dev:u:testuser',
|
||||
createdBy: 'did:web:test.bspds.dev:u:testuser',
|
||||
forAccount: 'did:web:test.tranquil.dev:u:testuser',
|
||||
createdBy: 'did:web:test.tranquil.dev:u:testuser',
|
||||
createdAt: new Date().toISOString(),
|
||||
uses: [],
|
||||
...overrides,
|
||||
@@ -120,7 +120,7 @@ export const mockData = {
|
||||
...overrides,
|
||||
}),
|
||||
describeServer: () => ({
|
||||
availableUserDomains: ['test.bspds.dev'],
|
||||
availableUserDomains: ['test.tranquil.dev'],
|
||||
inviteCodeRequired: false,
|
||||
links: {
|
||||
privacyPolicy: 'https://example.com/privacy',
|
||||
@@ -128,7 +128,7 @@ export const mockData = {
|
||||
},
|
||||
}),
|
||||
describeRepo: (did: string) => ({
|
||||
handle: 'testuser.test.bspds.dev',
|
||||
handle: 'testuser.test.tranquil.dev',
|
||||
did,
|
||||
didDoc: {},
|
||||
collections: ['app.bsky.feed.post', 'app.bsky.feed.like', 'app.bsky.graph.follow'],
|
||||
@@ -173,10 +173,10 @@ export function setupDefaultMocks(): void {
|
||||
mockEndpoint('com.atproto.server.createInviteCode', () =>
|
||||
jsonResponse({ code: 'new-invite-' + Date.now() })
|
||||
)
|
||||
mockEndpoint('com.bspds.account.getNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.getNotificationPrefs', () =>
|
||||
jsonResponse(mockData.notificationPrefs())
|
||||
)
|
||||
mockEndpoint('com.bspds.account.updateNotificationPrefs', () =>
|
||||
mockEndpoint('com.tranquil.account.updateNotificationPrefs', () =>
|
||||
jsonResponse({ success: true })
|
||||
)
|
||||
mockEndpoint('com.atproto.server.requestEmailUpdate', () =>
|
||||
|
||||
2
justfile
2
justfile
@@ -25,7 +25,7 @@ test-file file:
|
||||
./scripts/run-tests.sh --test {{file}}
|
||||
# Run tests with testcontainers (slower, no shared infra)
|
||||
test-standalone:
|
||||
BSPDS_ALLOW_INSECURE_SECRETS=1 cargo test
|
||||
TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 cargo test
|
||||
# Manually manage test infrastructure (for debugging)
|
||||
test-infra-start:
|
||||
./scripts/test-infra.sh start
|
||||
|
||||
@@ -34,8 +34,8 @@ http {
|
||||
ssl_session_tickets off;
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
upstream bspds {
|
||||
server bspds:3000;
|
||||
upstream tranquil-pds {
|
||||
server tranquil-pds:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
server {
|
||||
@@ -57,7 +57,7 @@ http {
|
||||
ssl_certificate_key /etc/nginx/certs/live/${PDS_HOSTNAME}/privkey.pem;
|
||||
client_max_body_size 100M;
|
||||
location / {
|
||||
proxy_pass http://bspds;
|
||||
proxy_pass http://tranquil-pds;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
@@ -71,7 +71,7 @@ http {
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
location /xrpc/com.atproto.sync.subscribeRepos {
|
||||
proxy_pass http://bspds;
|
||||
proxy_pass http://tranquil-pds;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
@@ -7,7 +7,7 @@ scrape_configs:
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
- job_name: 'bspds'
|
||||
- job_name: 'tranquil-pds'
|
||||
static_configs:
|
||||
- targets: ['app:3000']
|
||||
metrics_path: /metrics
|
||||
|
||||
@@ -24,25 +24,25 @@ fi
|
||||
nuke_installation() {
|
||||
log_warn "NUKING EXISTING INSTALLATION"
|
||||
log_info "Stopping services..."
|
||||
systemctl stop bspds 2>/dev/null || true
|
||||
systemctl disable bspds 2>/dev/null || true
|
||||
systemctl stop tranquil-pds 2>/dev/null || true
|
||||
systemctl disable tranquil-pds 2>/dev/null || true
|
||||
|
||||
log_info "Removing BSPDS files..."
|
||||
rm -rf /opt/bspds
|
||||
rm -rf /var/lib/bspds
|
||||
rm -f /usr/local/bin/bspds
|
||||
rm -f /usr/local/bin/bspds-sendmail
|
||||
rm -f /usr/local/bin/bspds-mailq
|
||||
rm -rf /var/spool/bspds-mail
|
||||
rm -f /etc/systemd/system/bspds.service
|
||||
log_info "Removing Tranquil PDS files..."
|
||||
rm -rf /opt/tranquil-pds
|
||||
rm -rf /var/lib/tranquil-pds
|
||||
rm -f /usr/local/bin/tranquil-pds
|
||||
rm -f /usr/local/bin/tranquil-pds-sendmail
|
||||
rm -f /usr/local/bin/tranquil-pds-mailq
|
||||
rm -rf /var/spool/tranquil-pds-mail
|
||||
rm -f /etc/systemd/system/tranquil-pds.service
|
||||
systemctl daemon-reload
|
||||
|
||||
log_info "Removing BSPDS configuration..."
|
||||
rm -rf /etc/bspds
|
||||
log_info "Removing Tranquil PDS configuration..."
|
||||
rm -rf /etc/tranquil-pds
|
||||
|
||||
log_info "Dropping postgres database and user..."
|
||||
sudo -u postgres psql -c "DROP DATABASE IF EXISTS pds;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "DROP USER IF EXISTS bspds;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "DROP USER IF EXISTS tranquil_pds;" 2>/dev/null || true
|
||||
|
||||
log_info "Removing minio bucket..."
|
||||
if command -v mc &>/dev/null; then
|
||||
@@ -54,14 +54,14 @@ nuke_installation() {
|
||||
rm -f /etc/default/minio 2>/dev/null || true
|
||||
|
||||
log_info "Removing nginx config..."
|
||||
rm -f /etc/nginx/sites-enabled/bspds
|
||||
rm -f /etc/nginx/sites-available/bspds
|
||||
rm -f /etc/nginx/sites-enabled/tranquil-pds
|
||||
rm -f /etc/nginx/sites-available/tranquil-pds
|
||||
systemctl reload nginx 2>/dev/null || true
|
||||
|
||||
log_success "Previous installation nuked"
|
||||
}
|
||||
|
||||
if [[ -f /etc/bspds/bspds.env ]] || [[ -d /opt/bspds ]] || [[ -f /usr/local/bin/bspds ]]; then
|
||||
if [[ -f /etc/tranquil-pds/tranquil-pds.env ]] || [[ -d /opt/tranquil-pds ]] || [[ -f /usr/local/bin/tranquil-pds ]]; then
|
||||
log_warn "Existing installation detected"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
@@ -76,8 +76,8 @@ if [[ -f /etc/bspds/bspds.env ]] || [[ -d /opt/bspds ]] || [[ -f /usr/local/bin/
|
||||
echo ""
|
||||
log_warn "This will DELETE:"
|
||||
echo " - PostgreSQL database 'pds' and all data"
|
||||
echo " - All BSPDS configuration and credentials"
|
||||
echo " - All source code in /opt/bspds"
|
||||
echo " - All Tranquil PDS configuration and credentials"
|
||||
echo " - All source code in /opt/tranquil-pds"
|
||||
echo " - MinIO bucket 'pds-blobs' and all blobs"
|
||||
echo ""
|
||||
read -p "Type 'NUKE' to confirm: " CONFIRM_NUKE
|
||||
@@ -102,7 +102,7 @@ if [[ -f /etc/bspds/bspds.env ]] || [[ -d /opt/bspds ]] || [[ -f /usr/local/bin/
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "BSPDS Installation Script for Debian"
|
||||
log_info "Tranquil PDS Installation Script for Debian"
|
||||
echo ""
|
||||
|
||||
get_public_ips() {
|
||||
@@ -142,7 +142,7 @@ if [[ ! "$DNS_CONFIRMED" =~ ^[Yy]$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CREDENTIALS_FILE="/etc/bspds/.credentials"
|
||||
CREDENTIALS_FILE="/etc/tranquil-pds/.credentials"
|
||||
if [[ -f "$CREDENTIALS_FILE" ]]; then
|
||||
log_info "Loading existing credentials..."
|
||||
source "$CREDENTIALS_FILE"
|
||||
@@ -154,7 +154,7 @@ else
|
||||
DB_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
|
||||
MINIO_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
|
||||
|
||||
mkdir -p /etc/bspds
|
||||
mkdir -p /etc/tranquil-pds
|
||||
cat > "$CREDENTIALS_FILE" << EOF
|
||||
JWT_SECRET="$JWT_SECRET"
|
||||
DPOP_SECRET="$DPOP_SECRET"
|
||||
@@ -196,10 +196,10 @@ log_info "Installing postgres..."
|
||||
apt install -y postgresql postgresql-contrib
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql
|
||||
sudo -u postgres psql -c "CREATE USER bspds WITH PASSWORD '${DB_PASSWORD}';" 2>/dev/null || \
|
||||
sudo -u postgres psql -c "ALTER USER bspds WITH PASSWORD '${DB_PASSWORD}';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE pds OWNER bspds;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;"
|
||||
sudo -u postgres psql -c "CREATE USER tranquil_pds WITH PASSWORD '${DB_PASSWORD}';" 2>/dev/null || \
|
||||
sudo -u postgres psql -c "ALTER USER tranquil_pds WITH PASSWORD '${DB_PASSWORD}';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE pds OWNER tranquil_pds;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
|
||||
log_success "postgres configured"
|
||||
|
||||
log_info "Installing valkey..."
|
||||
@@ -292,19 +292,19 @@ if ! command -v deno &>/dev/null && [[ ! -f "$HOME/.deno/bin/deno" ]]; then
|
||||
grep -q 'deno/bin' ~/.bashrc 2>/dev/null || echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
|
||||
fi
|
||||
|
||||
log_info "Cloning BSPDS..."
|
||||
if [[ ! -d /opt/bspds ]]; then
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox /opt/bspds
|
||||
log_info "Cloning Tranquil PDS..."
|
||||
if [[ ! -d /opt/tranquil-pds ]]; then
|
||||
git clone https://tangled.org/lewis.moe/bspds-sandbox /opt/tranquil-pds
|
||||
else
|
||||
cd /opt/bspds && git pull
|
||||
cd /opt/tranquil-pds && git pull
|
||||
fi
|
||||
cd /opt/bspds
|
||||
cd /opt/tranquil-pds
|
||||
|
||||
log_info "Building frontend..."
|
||||
"$HOME/.deno/bin/deno" task build --filter=frontend
|
||||
log_success "Frontend built"
|
||||
|
||||
log_info "Building BSPDS (this takes a while)..."
|
||||
log_info "Building Tranquil PDS (this takes a while)..."
|
||||
source "$HOME/.cargo/env"
|
||||
if [[ $TOTAL_MEM_KB -lt 4000000 ]]; then
|
||||
log_info "Low memory - limiting parallel jobs"
|
||||
@@ -312,39 +312,39 @@ if [[ $TOTAL_MEM_KB -lt 4000000 ]]; then
|
||||
else
|
||||
cargo build --release
|
||||
fi
|
||||
log_success "BSPDS built"
|
||||
log_success "Tranquil PDS built"
|
||||
|
||||
log_info "Running migrations..."
|
||||
cargo install sqlx-cli --no-default-features --features postgres
|
||||
export DATABASE_URL="postgres://bspds:${DB_PASSWORD}@localhost:5432/pds"
|
||||
export DATABASE_URL="postgres://tranquil_pds:${DB_PASSWORD}@localhost:5432/pds"
|
||||
"$HOME/.cargo/bin/sqlx" migrate run
|
||||
log_success "Migrations complete"
|
||||
|
||||
log_info "Setting up mail trap..."
|
||||
mkdir -p /var/spool/bspds-mail
|
||||
chmod 1777 /var/spool/bspds-mail
|
||||
mkdir -p /var/spool/tranquil-pds-mail
|
||||
chmod 1777 /var/spool/tranquil-pds-mail
|
||||
|
||||
cat > /usr/local/bin/bspds-sendmail << 'SENDMAIL_EOF'
|
||||
cat > /usr/local/bin/tranquil-pds-sendmail << 'SENDMAIL_EOF'
|
||||
#!/bin/bash
|
||||
MAIL_DIR="/var/spool/bspds-mail"
|
||||
MAIL_DIR="/var/spool/tranquil-pds-mail"
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||
RANDOM_ID=$(head -c 4 /dev/urandom | xxd -p)
|
||||
MAIL_FILE="${MAIL_DIR}/${TIMESTAMP}-${RANDOM_ID}.eml"
|
||||
mkdir -p "$MAIL_DIR"
|
||||
{
|
||||
echo "X-BSPDS-Received: $(date -Iseconds)"
|
||||
echo "X-BSPDS-Args: $*"
|
||||
echo "X-Tranquil-PDS-Received: $(date -Iseconds)"
|
||||
echo "X-Tranquil-PDS-Args: $*"
|
||||
echo ""
|
||||
cat
|
||||
} > "$MAIL_FILE"
|
||||
chmod 644 "$MAIL_FILE"
|
||||
exit 0
|
||||
SENDMAIL_EOF
|
||||
chmod +x /usr/local/bin/bspds-sendmail
|
||||
chmod +x /usr/local/bin/tranquil-pds-sendmail
|
||||
|
||||
cat > /usr/local/bin/bspds-mailq << 'MAILQ_EOF'
|
||||
cat > /usr/local/bin/tranquil-pds-mailq << 'MAILQ_EOF'
|
||||
#!/bin/bash
|
||||
MAIL_DIR="/var/spool/bspds-mail"
|
||||
MAIL_DIR="/var/spool/tranquil-pds-mail"
|
||||
case "${1:-list}" in
|
||||
list)
|
||||
ls -lt "$MAIL_DIR"/*.eml 2>/dev/null | head -20 || echo "No emails"
|
||||
@@ -365,18 +365,18 @@ case "${1:-list}" in
|
||||
[[ -f "$f" ]] && cat "$f" || echo "Not found"
|
||||
;;
|
||||
*)
|
||||
[[ -f "$MAIL_DIR/$1" ]] && cat "$MAIL_DIR/$1" || echo "Usage: bspds-mailq [list|latest|clear|count|N]"
|
||||
[[ -f "$MAIL_DIR/$1" ]] && cat "$MAIL_DIR/$1" || echo "Usage: tranquil-pds-mailq [list|latest|clear|count|N]"
|
||||
;;
|
||||
esac
|
||||
MAILQ_EOF
|
||||
chmod +x /usr/local/bin/bspds-mailq
|
||||
chmod +x /usr/local/bin/tranquil-pds-mailq
|
||||
|
||||
log_info "Creating BSPDS configuration..."
|
||||
cat > /etc/bspds/bspds.env << EOF
|
||||
log_info "Creating Tranquil PDS configuration..."
|
||||
cat > /etc/tranquil-pds/tranquil-pds.env << EOF
|
||||
SERVER_HOST=127.0.0.1
|
||||
SERVER_PORT=3000
|
||||
PDS_HOSTNAME=${PDS_DOMAIN}
|
||||
DATABASE_URL=postgres://bspds:${DB_PASSWORD}@localhost:5432/pds
|
||||
DATABASE_URL=postgres://tranquil_pds:${DB_PASSWORD}@localhost:5432/pds
|
||||
DATABASE_MAX_CONNECTIONS=100
|
||||
DATABASE_MIN_CONNECTIONS=10
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
@@ -392,30 +392,30 @@ PLC_DIRECTORY_URL=https://plc.directory
|
||||
CRAWLERS=https://bsky.network
|
||||
AVAILABLE_USER_DOMAINS=${PDS_DOMAIN}
|
||||
MAIL_FROM_ADDRESS=noreply@${PDS_DOMAIN}
|
||||
MAIL_FROM_NAME=BSPDS
|
||||
SENDMAIL_PATH=/usr/local/bin/bspds-sendmail
|
||||
MAIL_FROM_NAME=Tranquil PDS
|
||||
SENDMAIL_PATH=/usr/local/bin/tranquil-pds-sendmail
|
||||
EOF
|
||||
chmod 600 /etc/bspds/bspds.env
|
||||
chmod 600 /etc/tranquil-pds/tranquil-pds.env
|
||||
|
||||
log_info "Installing BSPDS..."
|
||||
id -u bspds &>/dev/null || useradd -r -s /sbin/nologin bspds
|
||||
cp /opt/bspds/target/release/bspds /usr/local/bin/
|
||||
mkdir -p /var/lib/bspds
|
||||
cp -r /opt/bspds/frontend/dist /var/lib/bspds/frontend
|
||||
chown -R bspds:bspds /var/lib/bspds
|
||||
log_info "Installing Tranquil PDS..."
|
||||
id -u tranquil-pds &>/dev/null || useradd -r -s /sbin/nologin tranquil-pds
|
||||
cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
|
||||
mkdir -p /var/lib/tranquil-pds
|
||||
cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend
|
||||
chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds
|
||||
|
||||
cat > /etc/systemd/system/bspds.service << 'EOF'
|
||||
cat > /etc/systemd/system/tranquil-pds.service << 'EOF'
|
||||
[Unit]
|
||||
Description=BSPDS - AT Protocol PDS
|
||||
Description=Tranquil PDS - AT Protocol PDS
|
||||
After=network.target postgresql.service minio.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=bspds
|
||||
Group=bspds
|
||||
EnvironmentFile=/etc/bspds/bspds.env
|
||||
Environment=FRONTEND_DIR=/var/lib/bspds/frontend
|
||||
ExecStart=/usr/local/bin/bspds
|
||||
User=tranquil-pds
|
||||
Group=tranquil-pds
|
||||
EnvironmentFile=/etc/tranquil-pds/tranquil-pds.env
|
||||
Environment=FRONTEND_DIR=/var/lib/tranquil-pds/frontend
|
||||
ExecStart=/usr/local/bin/tranquil-pds
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
@@ -424,13 +424,13 @@ WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable bspds
|
||||
systemctl start bspds
|
||||
log_success "BSPDS service started"
|
||||
systemctl enable tranquil-pds
|
||||
systemctl start tranquil-pds
|
||||
log_success "Tranquil PDS service started"
|
||||
|
||||
log_info "Installing nginx..."
|
||||
apt install -y nginx
|
||||
cat > /etc/nginx/sites-available/bspds << EOF
|
||||
cat > /etc/nginx/sites-available/tranquil-pds << EOF
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
@@ -456,7 +456,7 @@ server {
|
||||
}
|
||||
EOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/bspds /etc/nginx/sites-enabled/
|
||||
ln -sf /etc/nginx/sites-available/tranquil-pds /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
@@ -496,7 +496,7 @@ if [[ "$CERT_READY" =~ ^[Yy]$ ]]; then
|
||||
-d "${PDS_DOMAIN}" -d "*.${PDS_DOMAIN}" \
|
||||
--email "${CERTBOT_EMAIL}" --agree-tos; then
|
||||
|
||||
cat > /etc/nginx/sites-available/bspds << EOF
|
||||
cat > /etc/nginx/sites-available/tranquil-pds << EOF
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
@@ -564,9 +564,9 @@ fi
|
||||
log_info "Verifying installation..."
|
||||
sleep 3
|
||||
if curl -s "http://localhost:3000/xrpc/_health" | grep -q "version"; then
|
||||
log_success "BSPDS is responding"
|
||||
log_success "Tranquil PDS is responding"
|
||||
else
|
||||
log_warn "BSPDS may still be starting. Check: journalctl -u bspds -f"
|
||||
log_warn "Tranquil PDS may still be starting. Check: journalctl -u tranquil-pds -f"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -574,12 +574,12 @@ log_success "Installation complete"
|
||||
echo ""
|
||||
echo "PDS: https://${PDS_DOMAIN}"
|
||||
echo ""
|
||||
echo "Credentials (also in /etc/bspds/.credentials):"
|
||||
echo "Credentials (also in /etc/tranquil-pds/.credentials):"
|
||||
echo " DB password: ${DB_PASSWORD}"
|
||||
echo " MinIO password: ${MINIO_PASSWORD}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " journalctl -u bspds -f # logs"
|
||||
echo " systemctl restart bspds # restart"
|
||||
echo " bspds-mailq # view trapped emails"
|
||||
echo " journalctl -u tranquil-pds -f # logs"
|
||||
echo " systemctl restart tranquil-pds # restart"
|
||||
echo " tranquil-pds-mailq # view trapped emails"
|
||||
echo ""
|
||||
|
||||
@@ -10,7 +10,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
"$INFRA_SCRIPT" start
|
||||
source "${TMPDIR:-/tmp}/bspds_test_infra.env"
|
||||
source "${TMPDIR:-/tmp}/tranquil_pds_test_infra.env"
|
||||
echo ""
|
||||
echo "Running database migrations..."
|
||||
sqlx database create 2>/dev/null || true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
INFRA_FILE="${TMPDIR:-/tmp}/bspds_test_infra.env"
|
||||
CONTAINER_PREFIX="bspds-test"
|
||||
INFRA_FILE="${TMPDIR:-/tmp}/tranquil_pds_test_infra.env"
|
||||
CONTAINER_PREFIX="tranquil-pds-test"
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
@@ -40,7 +40,7 @@ start_infra() {
|
||||
-e POSTGRES_USER=postgres \
|
||||
-e POSTGRES_DB=postgres \
|
||||
-P \
|
||||
--label bspds_test=true \
|
||||
--label tranquil_pds_test=true \
|
||||
postgres:18-alpine >/dev/null
|
||||
echo "Starting MinIO..."
|
||||
$CONTAINER_CMD run -d \
|
||||
@@ -48,13 +48,13 @@ start_infra() {
|
||||
-e MINIO_ROOT_USER=minioadmin \
|
||||
-e MINIO_ROOT_PASSWORD=minioadmin \
|
||||
-P \
|
||||
--label bspds_test=true \
|
||||
--label tranquil_pds_test=true \
|
||||
minio/minio:latest server /data >/dev/null
|
||||
echo "Starting Valkey..."
|
||||
$CONTAINER_CMD run -d \
|
||||
--name "${CONTAINER_PREFIX}-valkey" \
|
||||
-P \
|
||||
--label bspds_test=true \
|
||||
--label tranquil_pds_test=true \
|
||||
valkey/valkey:8-alpine >/dev/null
|
||||
echo "Waiting for services to be ready..."
|
||||
sleep 2
|
||||
@@ -95,8 +95,8 @@ export AWS_ACCESS_KEY_ID="minioadmin"
|
||||
export AWS_SECRET_ACCESS_KEY="minioadmin"
|
||||
export AWS_REGION="us-east-1"
|
||||
export VALKEY_URL="redis://127.0.0.1:${VALKEY_PORT}"
|
||||
export BSPDS_TEST_INFRA_READY="1"
|
||||
export BSPDS_ALLOW_INSECURE_SECRETS="1"
|
||||
export TRANQUIL_PDS_TEST_INFRA_READY="1"
|
||||
export TRANQUIL_PDS_ALLOW_INSECURE_SECRETS="1"
|
||||
export SKIP_IMPORT_VERIFICATION="true"
|
||||
export DISABLE_RATE_LIMITING="1"
|
||||
EOF
|
||||
@@ -125,7 +125,7 @@ status_infra() {
|
||||
fi
|
||||
echo ""
|
||||
echo "Containers:"
|
||||
$CONTAINER_CMD ps -a --filter "label=bspds_test=true" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo " (none)"
|
||||
$CONTAINER_CMD ps -a --filter "label=tranquil_pds_test=true" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo " (none)"
|
||||
}
|
||||
case "${1:-}" in
|
||||
start)
|
||||
|
||||
@@ -157,9 +157,20 @@ pub async fn activate_account(
|
||||
.await;
|
||||
match result {
|
||||
Ok(_) => {
|
||||
if let Some(h) = handle {
|
||||
if let Some(ref h) = handle {
|
||||
let _ = state.cache.delete(&format!("handle:{}", h)).await;
|
||||
}
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_account_event(&state, &did, true, None).await
|
||||
{
|
||||
warn!("Failed to sequence account activation event: {}", e);
|
||||
}
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_identity_event(&state, &did, handle.as_deref())
|
||||
.await
|
||||
{
|
||||
warn!("Failed to sequence identity event for activation: {}", e);
|
||||
}
|
||||
(StatusCode::OK, Json(json!({}))).into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -222,9 +233,14 @@ pub async fn deactivate_account(
|
||||
.await;
|
||||
match result {
|
||||
Ok(_) => {
|
||||
if let Some(h) = handle {
|
||||
if let Some(ref h) = handle {
|
||||
let _ = state.cache.delete(&format!("handle:{}", h)).await;
|
||||
}
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_account_event(&state, &did, false, Some("deactivated")).await
|
||||
{
|
||||
warn!("Failed to sequence account deactivation event: {}", e);
|
||||
}
|
||||
(StatusCode::OK, Json(json!({}))).into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
@@ -10,6 +10,28 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tracing::error;
|
||||
|
||||
const HOUR_SECS: i64 = 3600;
|
||||
const MINUTE_SECS: i64 = 60;
|
||||
|
||||
const PROTECTED_METHODS: &[&str] = &[
|
||||
"com.atproto.admin.sendEmail",
|
||||
"com.atproto.identity.requestPlcOperationSignature",
|
||||
"com.atproto.identity.signPlcOperation",
|
||||
"com.atproto.identity.updateHandle",
|
||||
"com.atproto.server.activateAccount",
|
||||
"com.atproto.server.confirmEmail",
|
||||
"com.atproto.server.createAppPassword",
|
||||
"com.atproto.server.deactivateAccount",
|
||||
"com.atproto.server.getAccountInviteCodes",
|
||||
"com.atproto.server.getSession",
|
||||
"com.atproto.server.listAppPasswords",
|
||||
"com.atproto.server.requestAccountDelete",
|
||||
"com.atproto.server.requestEmailConfirmation",
|
||||
"com.atproto.server.requestEmailUpdate",
|
||||
"com.atproto.server.revokeAppPassword",
|
||||
"com.atproto.server.updateEmail",
|
||||
];
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetServiceAuthParams {
|
||||
pub aud: String,
|
||||
@@ -33,7 +55,7 @@ pub async fn get_service_auth(
|
||||
Some(t) => t,
|
||||
None => return ApiError::AuthenticationRequired.into_response(),
|
||||
};
|
||||
let auth_user = match crate::auth::validate_bearer_token(&state.db, &token).await {
|
||||
let auth_user = match crate::auth::validate_bearer_token_for_service_auth(&state.db, &token).await {
|
||||
Ok(user) => user,
|
||||
Err(e) => return ApiError::from(e).into_response(),
|
||||
};
|
||||
@@ -46,9 +68,86 @@ pub async fn get_service_auth(
|
||||
.into_response();
|
||||
}
|
||||
};
|
||||
let lxm = params.lxm.as_deref().unwrap_or("*");
|
||||
|
||||
let lxm = params.lxm.as_deref();
|
||||
let lxm_for_token = lxm.unwrap_or("*");
|
||||
|
||||
let user_status = sqlx::query!(
|
||||
"SELECT takedown_ref FROM users WHERE did = $1",
|
||||
auth_user.did
|
||||
)
|
||||
.fetch_optional(&state.db)
|
||||
.await;
|
||||
|
||||
let is_takendown = match user_status {
|
||||
Ok(Some(row)) => row.takedown_ref.is_some(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_takendown && lxm != Some("com.atproto.server.createAccount") {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"error": "InvalidToken",
|
||||
"message": "Bad token scope"
|
||||
})),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
if let Some(method) = lxm {
|
||||
if PROTECTED_METHODS.contains(&method) {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"error": "InvalidRequest",
|
||||
"message": format!("cannot request a service auth token for the following protected method: {}", method)
|
||||
})),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(exp) = params.exp {
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let diff = exp - now;
|
||||
|
||||
if diff < 0 {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"error": "BadExpiration",
|
||||
"message": "expiration is in past"
|
||||
})),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
if diff > HOUR_SECS {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"error": "BadExpiration",
|
||||
"message": "cannot request a token with an expiration more than an hour in the future"
|
||||
})),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
if lxm.is_none() && diff > MINUTE_SECS {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"error": "BadExpiration",
|
||||
"message": "cannot request a method-less token with an expiration more than a minute in the future"
|
||||
})),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
|
||||
let service_token =
|
||||
match crate::auth::create_service_token(&auth_user.did, ¶ms.aud, lxm, &key_bytes) {
|
||||
match crate::auth::create_service_token(&auth_user.did, ¶ms.aud, lxm_for_token, &key_bytes) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
error!("Failed to create service token: {:?}", e);
|
||||
|
||||
@@ -59,14 +59,14 @@ pub async fn validate_bearer_token(
|
||||
db: &PgPool,
|
||||
token: &str,
|
||||
) -> Result<AuthenticatedUser, TokenValidationError> {
|
||||
validate_bearer_token_with_options_internal(db, None, token, false).await
|
||||
validate_bearer_token_with_options_internal(db, None, token, false, false).await
|
||||
}
|
||||
|
||||
pub async fn validate_bearer_token_allow_deactivated(
|
||||
db: &PgPool,
|
||||
token: &str,
|
||||
) -> Result<AuthenticatedUser, TokenValidationError> {
|
||||
validate_bearer_token_with_options_internal(db, None, token, true).await
|
||||
validate_bearer_token_with_options_internal(db, None, token, true, false).await
|
||||
}
|
||||
|
||||
pub async fn validate_bearer_token_cached(
|
||||
@@ -74,7 +74,7 @@ pub async fn validate_bearer_token_cached(
|
||||
cache: &Arc<dyn Cache>,
|
||||
token: &str,
|
||||
) -> Result<AuthenticatedUser, TokenValidationError> {
|
||||
validate_bearer_token_with_options_internal(db, Some(cache), token, false).await
|
||||
validate_bearer_token_with_options_internal(db, Some(cache), token, false, false).await
|
||||
}
|
||||
|
||||
pub async fn validate_bearer_token_cached_allow_deactivated(
|
||||
@@ -82,7 +82,14 @@ pub async fn validate_bearer_token_cached_allow_deactivated(
|
||||
cache: &Arc<dyn Cache>,
|
||||
token: &str,
|
||||
) -> Result<AuthenticatedUser, TokenValidationError> {
|
||||
validate_bearer_token_with_options_internal(db, Some(cache), token, true).await
|
||||
validate_bearer_token_with_options_internal(db, Some(cache), token, true, false).await
|
||||
}
|
||||
|
||||
pub async fn validate_bearer_token_for_service_auth(
|
||||
db: &PgPool,
|
||||
token: &str,
|
||||
) -> Result<AuthenticatedUser, TokenValidationError> {
|
||||
validate_bearer_token_with_options_internal(db, None, token, true, true).await
|
||||
}
|
||||
|
||||
async fn validate_bearer_token_with_options_internal(
|
||||
@@ -90,6 +97,7 @@ async fn validate_bearer_token_with_options_internal(
|
||||
cache: Option<&Arc<dyn Cache>>,
|
||||
token: &str,
|
||||
allow_deactivated: bool,
|
||||
allow_takendown: bool,
|
||||
) -> Result<AuthenticatedUser, TokenValidationError> {
|
||||
let did_from_token = get_did_from_token(token).ok();
|
||||
|
||||
@@ -155,7 +163,7 @@ async fn validate_bearer_token_with_options_internal(
|
||||
return Err(TokenValidationError::AccountDeactivated);
|
||||
}
|
||||
|
||||
if takedown_ref.is_some() {
|
||||
if !allow_takendown && takedown_ref.is_some() {
|
||||
return Err(TokenValidationError::AccountTakedown);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ impl EmailSender {
|
||||
|
||||
pub fn from_env() -> Option<Self> {
|
||||
let from_address = std::env::var("MAIL_FROM_ADDRESS").ok()?;
|
||||
let from_name = std::env::var("MAIL_FROM_NAME").unwrap_or_else(|_| "BSPDS".to_string());
|
||||
let from_name = std::env::var("MAIL_FROM_NAME").unwrap_or_else(|_| "Tranquil PDS".to_string());
|
||||
Some(Self::new(from_address, from_name))
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ impl CommsSender for DiscordSender {
|
||||
let content = format!("**{}**\n\n{}", subject, notification.body);
|
||||
let payload = json!({
|
||||
"content": content,
|
||||
"username": "BSPDS"
|
||||
"username": "Tranquil PDS"
|
||||
});
|
||||
let mut last_error = None;
|
||||
for attempt in 0..MAX_RETRIES {
|
||||
|
||||
@@ -25,32 +25,32 @@ impl AuthConfig {
|
||||
pub fn init() -> &'static Self {
|
||||
CONFIG.get_or_init(|| {
|
||||
let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| {
|
||||
if cfg!(test) || std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_ok() {
|
||||
if cfg!(test) || std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_ok() {
|
||||
"test-jwt-secret-not-for-production".to_string()
|
||||
} else {
|
||||
panic!(
|
||||
"JWT_SECRET environment variable must be set in production. \
|
||||
Set BSPDS_ALLOW_INSECURE_SECRETS=1 for development/testing."
|
||||
Set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 for development/testing."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let dpop_secret = std::env::var("DPOP_SECRET").unwrap_or_else(|_| {
|
||||
if cfg!(test) || std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_ok() {
|
||||
if cfg!(test) || std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_ok() {
|
||||
"test-dpop-secret-not-for-production".to_string()
|
||||
} else {
|
||||
panic!(
|
||||
"DPOP_SECRET environment variable must be set in production. \
|
||||
Set BSPDS_ALLOW_INSECURE_SECRETS=1 for development/testing."
|
||||
Set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 for development/testing."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if jwt_secret.len() < 32 && std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_err() {
|
||||
if jwt_secret.len() < 32 && std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_err() {
|
||||
panic!("JWT_SECRET must be at least 32 characters");
|
||||
}
|
||||
|
||||
if dpop_secret.len() < 32 && std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_err() {
|
||||
if dpop_secret.len() < 32 && std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_err() {
|
||||
panic!("DPOP_SECRET must be at least 32 characters");
|
||||
}
|
||||
|
||||
@@ -87,23 +87,23 @@ impl AuthConfig {
|
||||
let signing_key_id = URL_SAFE_NO_PAD.encode(&kid_hash[..8]);
|
||||
|
||||
let master_key = std::env::var("MASTER_KEY").unwrap_or_else(|_| {
|
||||
if cfg!(test) || std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_ok() {
|
||||
if cfg!(test) || std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_ok() {
|
||||
"test-master-key-not-for-production".to_string()
|
||||
} else {
|
||||
panic!(
|
||||
"MASTER_KEY environment variable must be set in production. \
|
||||
Set BSPDS_ALLOW_INSECURE_SECRETS=1 for development/testing."
|
||||
Set TRANQUIL_PDS_ALLOW_INSECURE_SECRETS=1 for development/testing."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if master_key.len() < 32 && std::env::var("BSPDS_ALLOW_INSECURE_SECRETS").is_err() {
|
||||
if master_key.len() < 32 && std::env::var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS").is_err() {
|
||||
panic!("MASTER_KEY must be at least 32 characters");
|
||||
}
|
||||
|
||||
let hk = Hkdf::<Sha256>::new(None, master_key.as_bytes());
|
||||
let mut key_encryption_key = [0u8; 32];
|
||||
hk.expand(b"bspds-user-key-encryption", &mut key_encryption_key)
|
||||
hk.expand(b"tranquil-pds-user-key-encryption", &mut key_encryption_key)
|
||||
.expect("HKDF expansion failed");
|
||||
|
||||
AuthConfig {
|
||||
|
||||
16
src/lib.rs
16
src/lib.rs
@@ -52,11 +52,11 @@ pub fn app(state: AppState) -> Router {
|
||||
get(api::server::get_session),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.account.listSessions",
|
||||
"/xrpc/com.tranquil.account.listSessions",
|
||||
get(api::server::list_sessions),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.account.revokeSession",
|
||||
"/xrpc/com.tranquil.account.revokeSession",
|
||||
post(api::server::revoke_session),
|
||||
)
|
||||
.route(
|
||||
@@ -199,7 +199,7 @@ pub fn app(state: AppState) -> Router {
|
||||
post(api::server::reset_password),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.account.changePassword",
|
||||
"/xrpc/com.tranquil.account.changePassword",
|
||||
post(api::server::change_password),
|
||||
)
|
||||
.route(
|
||||
@@ -283,7 +283,7 @@ pub fn app(state: AppState) -> Router {
|
||||
get(api::admin::get_invite_codes),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.admin.getServerStats",
|
||||
"/xrpc/com.tranquil.admin.getServerStats",
|
||||
get(api::admin::get_server_stats),
|
||||
)
|
||||
.route(
|
||||
@@ -370,19 +370,19 @@ pub fn app(state: AppState) -> Router {
|
||||
get(api::temp::check_signup_queue),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.account.getNotificationPrefs",
|
||||
"/xrpc/com.tranquil.account.getNotificationPrefs",
|
||||
get(api::notification_prefs::get_notification_prefs),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.account.updateNotificationPrefs",
|
||||
"/xrpc/com.tranquil.account.updateNotificationPrefs",
|
||||
post(api::notification_prefs::update_notification_prefs),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.account.getNotificationHistory",
|
||||
"/xrpc/com.tranquil.account.getNotificationHistory",
|
||||
get(api::notification_prefs::get_notification_history),
|
||||
)
|
||||
.route(
|
||||
"/xrpc/com.bspds.account.confirmChannelVerification",
|
||||
"/xrpc/com.tranquil.account.confirmChannelVerification",
|
||||
post(api::verification::confirm_channel_verification),
|
||||
)
|
||||
.route("/xrpc/{*method}", any(api::proxy::proxy_handler))
|
||||
|
||||
12
src/main.rs
12
src/main.rs
@@ -1,6 +1,6 @@
|
||||
use bspds::comms::{CommsService, DiscordSender, EmailSender, SignalSender, TelegramSender};
|
||||
use bspds::crawlers::{Crawlers, start_crawlers_service};
|
||||
use bspds::state::AppState;
|
||||
use tranquil_pds::comms::{CommsService, DiscordSender, EmailSender, SignalSender, TelegramSender};
|
||||
use tranquil_pds::crawlers::{Crawlers, start_crawlers_service};
|
||||
use tranquil_pds::state::AppState;
|
||||
use std::net::SocketAddr;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::Arc;
|
||||
@@ -11,7 +11,7 @@ use tracing::{error, info, warn};
|
||||
async fn main() -> ExitCode {
|
||||
dotenvy::dotenv().ok();
|
||||
tracing_subscriber::fmt::init();
|
||||
bspds::metrics::init_metrics();
|
||||
tranquil_pds::metrics::init_metrics();
|
||||
|
||||
match run().await {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
@@ -62,7 +62,7 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.map_err(|e| format!("Failed to run migrations: {}", e))?;
|
||||
|
||||
let state = AppState::new(pool.clone()).await;
|
||||
bspds::sync::listener::start_sequencer_listener(state.clone()).await;
|
||||
tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await;
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = watch::channel(false);
|
||||
|
||||
@@ -108,7 +108,7 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
None
|
||||
};
|
||||
|
||||
let app = bspds::app(state);
|
||||
let app = tranquil_pds::app(state);
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
info!("listening on {}", addr);
|
||||
|
||||
|
||||
@@ -24,46 +24,46 @@ pub fn init_metrics() -> PrometheusHandle {
|
||||
}
|
||||
|
||||
fn describe_metrics() {
|
||||
metrics::describe_counter!("bspds_http_requests_total", "Total number of HTTP requests");
|
||||
metrics::describe_counter!("tranquil_pds_http_requests_total", "Total number of HTTP requests");
|
||||
metrics::describe_histogram!(
|
||||
"bspds_http_request_duration_seconds",
|
||||
"tranquil_pds_http_request_duration_seconds",
|
||||
"HTTP request duration in seconds"
|
||||
);
|
||||
metrics::describe_counter!(
|
||||
"bspds_auth_cache_hits_total",
|
||||
"tranquil_pds_auth_cache_hits_total",
|
||||
"Total number of authentication cache hits"
|
||||
);
|
||||
metrics::describe_counter!(
|
||||
"bspds_auth_cache_misses_total",
|
||||
"tranquil_pds_auth_cache_misses_total",
|
||||
"Total number of authentication cache misses"
|
||||
);
|
||||
metrics::describe_gauge!(
|
||||
"bspds_firehose_subscribers",
|
||||
"tranquil_pds_firehose_subscribers",
|
||||
"Number of active firehose WebSocket subscribers"
|
||||
);
|
||||
metrics::describe_counter!(
|
||||
"bspds_firehose_events_total",
|
||||
"tranquil_pds_firehose_events_total",
|
||||
"Total number of firehose events published"
|
||||
);
|
||||
metrics::describe_counter!(
|
||||
"bspds_block_operations_total",
|
||||
"tranquil_pds_block_operations_total",
|
||||
"Total number of block store operations"
|
||||
);
|
||||
metrics::describe_counter!(
|
||||
"bspds_s3_operations_total",
|
||||
"tranquil_pds_s3_operations_total",
|
||||
"Total number of S3/blob storage operations"
|
||||
);
|
||||
metrics::describe_gauge!(
|
||||
"bspds_comms_queue_size",
|
||||
"tranquil_pds_comms_queue_size",
|
||||
"Current size of the comms queue"
|
||||
);
|
||||
metrics::describe_counter!(
|
||||
"bspds_rate_limit_rejections_total",
|
||||
"tranquil_pds_rate_limit_rejections_total",
|
||||
"Total number of rate limit rejections"
|
||||
);
|
||||
metrics::describe_counter!("bspds_db_queries_total", "Total number of database queries");
|
||||
metrics::describe_counter!("tranquil_pds_db_queries_total", "Total number of database queries");
|
||||
metrics::describe_histogram!(
|
||||
"bspds_db_query_duration_seconds",
|
||||
"tranquil_pds_db_query_duration_seconds",
|
||||
"Database query duration in seconds"
|
||||
);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ pub async fn metrics_middleware(request: Request<Body>, next: Next) -> Response
|
||||
let status = response.status().as_u16().to_string();
|
||||
|
||||
counter!(
|
||||
"bspds_http_requests_total",
|
||||
"tranquil_pds_http_requests_total",
|
||||
"method" => method.clone(),
|
||||
"path" => path.clone(),
|
||||
"status" => status.clone()
|
||||
@@ -105,7 +105,7 @@ pub async fn metrics_middleware(request: Request<Body>, next: Next) -> Response
|
||||
.increment(1);
|
||||
|
||||
histogram!(
|
||||
"bspds_http_request_duration_seconds",
|
||||
"tranquil_pds_http_request_duration_seconds",
|
||||
"method" => method,
|
||||
"path" => path
|
||||
)
|
||||
@@ -135,32 +135,32 @@ fn normalize_path(path: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn record_auth_cache_hit(cache_type: &str) {
|
||||
counter!("bspds_auth_cache_hits_total", "cache_type" => cache_type.to_string()).increment(1);
|
||||
counter!("tranquil_pds_auth_cache_hits_total", "cache_type" => cache_type.to_string()).increment(1);
|
||||
}
|
||||
|
||||
pub fn record_auth_cache_miss(cache_type: &str) {
|
||||
counter!("bspds_auth_cache_misses_total", "cache_type" => cache_type.to_string()).increment(1);
|
||||
counter!("tranquil_pds_auth_cache_misses_total", "cache_type" => cache_type.to_string()).increment(1);
|
||||
}
|
||||
|
||||
pub fn set_firehose_subscribers(count: usize) {
|
||||
gauge!("bspds_firehose_subscribers").set(count as f64);
|
||||
gauge!("tranquil_pds_firehose_subscribers").set(count as f64);
|
||||
}
|
||||
|
||||
pub fn increment_firehose_subscribers() {
|
||||
counter!("bspds_firehose_events_total").increment(1);
|
||||
counter!("tranquil_pds_firehose_events_total").increment(1);
|
||||
}
|
||||
|
||||
pub fn record_firehose_event() {
|
||||
counter!("bspds_firehose_events_total").increment(1);
|
||||
counter!("tranquil_pds_firehose_events_total").increment(1);
|
||||
}
|
||||
|
||||
pub fn record_block_operation(op_type: &str) {
|
||||
counter!("bspds_block_operations_total", "op_type" => op_type.to_string()).increment(1);
|
||||
counter!("tranquil_pds_block_operations_total", "op_type" => op_type.to_string()).increment(1);
|
||||
}
|
||||
|
||||
pub fn record_s3_operation(op_type: &str, status: &str) {
|
||||
counter!(
|
||||
"bspds_s3_operations_total",
|
||||
"tranquil_pds_s3_operations_total",
|
||||
"op_type" => op_type.to_string(),
|
||||
"status" => status.to_string()
|
||||
)
|
||||
@@ -168,17 +168,17 @@ pub fn record_s3_operation(op_type: &str, status: &str) {
|
||||
}
|
||||
|
||||
pub fn set_comms_queue_size(size: usize) {
|
||||
gauge!("bspds_comms_queue_size").set(size as f64);
|
||||
gauge!("tranquil_pds_comms_queue_size").set(size as f64);
|
||||
}
|
||||
|
||||
pub fn record_rate_limit_rejection(limiter: &str) {
|
||||
counter!("bspds_rate_limit_rejections_total", "limiter" => limiter.to_string()).increment(1);
|
||||
counter!("tranquil_pds_rate_limit_rejections_total", "limiter" => limiter.to_string()).increment(1);
|
||||
}
|
||||
|
||||
pub fn record_db_query(query_type: &str, duration_seconds: f64) {
|
||||
counter!("bspds_db_queries_total", "query_type" => query_type.to_string()).increment(1);
|
||||
counter!("tranquil_pds_db_queries_total", "query_type" => query_type.to_string()).increment(1);
|
||||
histogram!(
|
||||
"bspds_db_query_duration_seconds",
|
||||
"tranquil_pds_db_query_duration_seconds",
|
||||
"query_type" => query_type.to_string()
|
||||
)
|
||||
.record(duration_seconds);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod common;
|
||||
use common::{base_url, client, create_account_and_login, get_db_connection_string};
|
||||
use bspds::comms::{NewComms, CommsType, enqueue_comms};
|
||||
use tranquil_pds::comms::{NewComms, CommsType, enqueue_comms};
|
||||
use serde_json::{Value, json};
|
||||
use sqlx::PgPool;
|
||||
|
||||
@@ -37,7 +37,7 @@ async fn test_get_notification_history() {
|
||||
}
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.account.getNotificationHistory", base))
|
||||
.get(format!("{}/xrpc/com.tranquil.account.getNotificationHistory", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await
|
||||
@@ -63,7 +63,7 @@ async fn test_verify_channel_discord() {
|
||||
"discordId": "123456789"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base))
|
||||
.post(format!("{}/xrpc/com.tranquil.account.updateNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&prefs)
|
||||
.send()
|
||||
@@ -92,7 +92,7 @@ async fn test_verify_channel_discord() {
|
||||
"code": code
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
@@ -101,7 +101,7 @@ async fn test_verify_channel_discord() {
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.account.getNotificationPrefs", base))
|
||||
.get(format!("{}/xrpc/com.tranquil.account.getNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await
|
||||
@@ -121,7 +121,7 @@ async fn test_verify_channel_invalid_code() {
|
||||
"telegramUsername": "testuser"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base))
|
||||
.post(format!("{}/xrpc/com.tranquil.account.updateNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&prefs)
|
||||
.send()
|
||||
@@ -134,7 +134,7 @@ async fn test_verify_channel_invalid_code() {
|
||||
"code": "000000"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
@@ -154,7 +154,7 @@ async fn test_verify_channel_not_set() {
|
||||
"code": "123456"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
@@ -175,7 +175,7 @@ async fn test_update_email_via_notification_prefs() {
|
||||
"email": unique_email
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base))
|
||||
.post(format!("{}/xrpc/com.tranquil.account.updateNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&prefs)
|
||||
.send()
|
||||
@@ -203,7 +203,7 @@ async fn test_update_email_via_notification_prefs() {
|
||||
"code": code
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.post(format!("{}/xrpc/com.tranquil.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
@@ -212,7 +212,7 @@ async fn test_update_email_via_notification_prefs() {
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.account.getNotificationPrefs", base))
|
||||
.get(format!("{}/xrpc/com.tranquil.account.getNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await
|
||||
|
||||
@@ -11,7 +11,7 @@ async fn test_get_server_stats() {
|
||||
let (_, _) = create_admin_account_and_login(&client).await;
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.admin.getServerStats", base))
|
||||
.get(format!("{}/xrpc/com.tranquil.admin.getServerStats", base))
|
||||
.header("Authorization", format!("Bearer {}", token1))
|
||||
.send()
|
||||
.await
|
||||
@@ -33,7 +33,7 @@ async fn test_get_server_stats_no_auth() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.admin.getServerStats", base))
|
||||
.get(format!("{}/xrpc/com.tranquil.admin.getServerStats", base))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -33,7 +33,7 @@ async fn test_change_password_success() {
|
||||
let jwt = verify_new_account(&client, did).await;
|
||||
let change_res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.changePassword",
|
||||
"{}/xrpc/com.tranquil.account.changePassword",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -79,7 +79,7 @@ async fn test_change_password_wrong_current() {
|
||||
let (_, jwt) = setup_new_user("change-pw-wrong").await;
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.changePassword",
|
||||
"{}/xrpc/com.tranquil.account.changePassword",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -122,7 +122,7 @@ async fn test_change_password_too_short() {
|
||||
let jwt = verify_new_account(&client, did).await;
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.changePassword",
|
||||
"{}/xrpc/com.tranquil.account.changePassword",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -144,7 +144,7 @@ async fn test_change_password_empty_current() {
|
||||
let (_, jwt) = setup_new_user("change-pw-empty").await;
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.changePassword",
|
||||
"{}/xrpc/com.tranquil.account.changePassword",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -164,7 +164,7 @@ async fn test_change_password_empty_new() {
|
||||
let (_, jwt) = setup_new_user("change-pw-emptynew").await;
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.changePassword",
|
||||
"{}/xrpc/com.tranquil.account.changePassword",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -183,7 +183,7 @@ async fn test_change_password_requires_auth() {
|
||||
let client = client();
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.changePassword",
|
||||
"{}/xrpc/com.tranquil.account.changePassword",
|
||||
base_url().await
|
||||
))
|
||||
.json(&json!({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use aws_config::BehaviorVersion;
|
||||
use aws_sdk_s3::Client as S3Client;
|
||||
use aws_sdk_s3::config::Credentials;
|
||||
use bspds::state::AppState;
|
||||
use tranquil_pds::state::AppState;
|
||||
use chrono::Utc;
|
||||
use reqwest::{Client, StatusCode, header};
|
||||
use serde_json::{Value, json};
|
||||
@@ -40,7 +40,7 @@ pub const AUTH_DID: &str = "did:plc:fake";
|
||||
pub const TARGET_DID: &str = "did:plc:target";
|
||||
|
||||
fn has_external_infra() -> bool {
|
||||
std::env::var("BSPDS_TEST_INFRA_READY").is_ok()
|
||||
std::env::var("TRANQUIL_PDS_TEST_INFRA_READY").is_ok()
|
||||
|| (std::env::var("DATABASE_URL").is_ok() && std::env::var("S3_ENDPOINT").is_ok())
|
||||
}
|
||||
#[cfg(test)]
|
||||
@@ -51,7 +51,7 @@ fn cleanup() {
|
||||
}
|
||||
if std::env::var("XDG_RUNTIME_DIR").is_ok() {
|
||||
let _ = std::process::Command::new("podman")
|
||||
.args(&["rm", "-f", "--filter", "label=bspds_test=true"])
|
||||
.args(&["rm", "-f", "--filter", "label=tranquil_pds_test=true"])
|
||||
.output();
|
||||
}
|
||||
let _ = std::process::Command::new("docker")
|
||||
@@ -60,7 +60,7 @@ fn cleanup() {
|
||||
"prune",
|
||||
"-f",
|
||||
"--filter",
|
||||
"label=bspds_test=true",
|
||||
"label=tranquil_pds_test=true",
|
||||
])
|
||||
.output();
|
||||
}
|
||||
@@ -80,7 +80,7 @@ pub async fn base_url() -> &'static str {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
unsafe {
|
||||
std::env::set_var("BSPDS_ALLOW_INSECURE_SECRETS", "1");
|
||||
std::env::set_var("TRANQUIL_PDS_ALLOW_INSECURE_SECRETS", "1");
|
||||
}
|
||||
if std::env::var("DOCKER_HOST").is_err() {
|
||||
if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") {
|
||||
@@ -152,7 +152,7 @@ async fn setup_with_testcontainers() -> String {
|
||||
.with_env_var("MINIO_ROOT_USER", "minioadmin")
|
||||
.with_env_var("MINIO_ROOT_PASSWORD", "minioadmin")
|
||||
.with_cmd(vec!["server".to_string(), "/data".to_string()])
|
||||
.with_label("bspds_test", "true")
|
||||
.with_label("tranquil_pds_test", "true")
|
||||
.start()
|
||||
.await
|
||||
.expect("Failed to start MinIO");
|
||||
@@ -195,7 +195,7 @@ async fn setup_with_testcontainers() -> String {
|
||||
S3_CONTAINER.set(s3_container).ok();
|
||||
let container = Postgres::default()
|
||||
.with_tag("18-alpine")
|
||||
.with_label("bspds_test", "true")
|
||||
.with_label("tranquil_pds_test", "true")
|
||||
.start()
|
||||
.await
|
||||
.expect("Failed to start Postgres");
|
||||
@@ -236,7 +236,7 @@ async fn setup_mock_appview(_mock_server: &MockServer) {
|
||||
}
|
||||
|
||||
async fn spawn_app(database_url: String) -> String {
|
||||
use bspds::rate_limit::RateLimiters;
|
||||
use tranquil_pds::rate_limit::RateLimiters;
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(50)
|
||||
.connect(&database_url)
|
||||
@@ -260,8 +260,8 @@ async fn spawn_app(database_url: String) -> String {
|
||||
.with_oauth_authorize_limit(10000)
|
||||
.with_oauth_token_limit(10000);
|
||||
let state = AppState::new(pool).await.with_rate_limiters(rate_limiters);
|
||||
bspds::sync::listener::start_sequencer_listener(state.clone()).await;
|
||||
let app = bspds::app(state);
|
||||
tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await;
|
||||
let app = tranquil_pds::app(state);
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use bspds::image::{
|
||||
use tranquil_pds::image::{
|
||||
DEFAULT_MAX_FILE_SIZE, ImageError, ImageProcessor, OutputFormat, THUMB_SIZE_FEED,
|
||||
THUMB_SIZE_FULL,
|
||||
};
|
||||
|
||||
@@ -194,7 +194,7 @@ async fn get_user_signing_key(did: &str) -> Option<Vec<u8>> {
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.ok()??;
|
||||
bspds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok()
|
||||
tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok()
|
||||
}
|
||||
#[tokio::test]
|
||||
#[ignore = "requires exclusive env var access; run with: cargo test test_import_with_valid_signature_and_mock_plc -- --ignored --test-threads=1"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(unused_imports)]
|
||||
mod common;
|
||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use bspds::auth::{
|
||||
use tranquil_pds::auth::{
|
||||
self, SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED, SCOPE_REFRESH,
|
||||
TOKEN_TYPE_ACCESS, TOKEN_TYPE_REFRESH, TOKEN_TYPE_SERVICE, create_access_token,
|
||||
create_refresh_token, create_service_token, get_did_from_token, get_jti_from_token,
|
||||
@@ -409,7 +409,7 @@ async fn test_session_lifecycle_security() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_deactivated_account_rejected() {
|
||||
async fn test_deactivated_account_behavior() {
|
||||
let url = base_url().await;
|
||||
let http_client = client();
|
||||
let (access_jwt, _did) = create_account_and_login(&http_client).await;
|
||||
@@ -423,9 +423,25 @@ async fn test_deactivated_account_rejected() {
|
||||
let res = http_client.get(format!("{}/xrpc/com.atproto.server.getSession", url))
|
||||
.header("Authorization", format!("Bearer {}", access_jwt))
|
||||
.send().await.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let body: Value = res.json().await.unwrap();
|
||||
assert_eq!(body["error"], "AccountDeactivated");
|
||||
assert_eq!(body["active"], false);
|
||||
|
||||
let post_res = http_client.post(format!("{}/xrpc/com.atproto.repo.createRecord", url))
|
||||
.header("Authorization", format!("Bearer {}", access_jwt))
|
||||
.json(&json!({
|
||||
"repo": _did,
|
||||
"collection": "app.bsky.feed.post",
|
||||
"record": {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"text": "test",
|
||||
"createdAt": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
}))
|
||||
.send().await.unwrap();
|
||||
assert_eq!(post_res.status(), StatusCode::UNAUTHORIZED);
|
||||
let post_body: Value = post_res.json().await.unwrap();
|
||||
assert_eq!(post_body["error"], "AccountDeactivated");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mod common;
|
||||
use bspds::comms::{
|
||||
use tranquil_pds::comms::{
|
||||
CommsChannel, CommsStatus, CommsType, NewComms, enqueue_comms, enqueue_welcome,
|
||||
};
|
||||
use sqlx::PgPool;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
mod common;
|
||||
mod helpers;
|
||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use bspds::oauth::dpop::{DPoPJwk, DPoPVerifier, compute_jwk_thumbprint};
|
||||
use tranquil_pds::oauth::dpop::{DPoPJwk, DPoPVerifier, compute_jwk_thumbprint};
|
||||
use chrono::Utc;
|
||||
use common::{base_url, client};
|
||||
use helpers::verify_new_account;
|
||||
|
||||
@@ -50,7 +50,7 @@ async fn get_user_signing_key(did: &str) -> Option<Vec<u8>> {
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.ok()??;
|
||||
bspds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok()
|
||||
tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version).ok()
|
||||
}
|
||||
|
||||
async fn get_plc_token_from_db(did: &str) -> Option<String> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use bspds::plc::{
|
||||
use tranquil_pds::plc::{
|
||||
PlcError, PlcOperation, PlcService, PlcValidationContext, cid_for_cbor, sign_operation,
|
||||
signing_key_to_did_key, validate_plc_operation, validate_plc_operation_for_submission,
|
||||
verify_operation_signature,
|
||||
|
||||
@@ -162,7 +162,7 @@ async fn test_distributed_rate_limiter_directly() {
|
||||
println!("VALKEY_URL not set, skipping distributed rate limiter test");
|
||||
return;
|
||||
}
|
||||
use bspds::cache::{DistributedRateLimiter, RedisRateLimiter};
|
||||
use tranquil_pds::cache::{DistributedRateLimiter, RedisRateLimiter};
|
||||
let valkey_url = std::env::var("VALKEY_URL").unwrap();
|
||||
let client = redis::Client::open(valkey_url.as_str()).expect("Failed to create Redis client");
|
||||
let conn = client
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use bspds::validation::{
|
||||
use tranquil_pds::validation::{
|
||||
RecordValidator, ValidationError, ValidationStatus, validate_collection_nsid,
|
||||
validate_record_key,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod common;
|
||||
use bspds::image::{ImageError, ImageProcessor};
|
||||
use bspds::comms::{SendError, is_valid_phone_number, sanitize_header_value};
|
||||
use bspds::oauth::templates::{error_page, login_page, success_page};
|
||||
use tranquil_pds::image::{ImageError, ImageProcessor};
|
||||
use tranquil_pds::comms::{SendError, is_valid_phone_number, sanitize_header_value};
|
||||
use tranquil_pds::oauth::templates::{error_page, login_page, success_page};
|
||||
|
||||
#[test]
|
||||
fn test_header_injection_sanitization() {
|
||||
|
||||
@@ -11,7 +11,7 @@ async fn test_list_sessions_returns_current_session() {
|
||||
let (did, jwt) = setup_new_user("list-sessions").await;
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/com.bspds.account.listSessions",
|
||||
"{}/xrpc/com.tranquil.account.listSessions",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -74,7 +74,7 @@ async fn test_list_sessions_multiple_sessions() {
|
||||
let jwt2 = login_body["accessJwt"].as_str().unwrap();
|
||||
let list_res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/com.bspds.account.listSessions",
|
||||
"{}/xrpc/com.tranquil.account.listSessions",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(jwt2)
|
||||
@@ -93,7 +93,7 @@ async fn test_list_sessions_requires_auth() {
|
||||
let client = client();
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/com.bspds.account.listSessions",
|
||||
"{}/xrpc/com.tranquil.account.listSessions",
|
||||
base_url().await
|
||||
))
|
||||
.send()
|
||||
@@ -145,7 +145,7 @@ async fn test_revoke_session_success() {
|
||||
let jwt2 = login_body["accessJwt"].as_str().unwrap();
|
||||
let list_res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/com.bspds.account.listSessions",
|
||||
"{}/xrpc/com.tranquil.account.listSessions",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(jwt2)
|
||||
@@ -159,7 +159,7 @@ async fn test_revoke_session_success() {
|
||||
let session_id = other_session.unwrap()["id"].as_str().unwrap();
|
||||
let revoke_res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.revokeSession",
|
||||
"{}/xrpc/com.tranquil.account.revokeSession",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(jwt2)
|
||||
@@ -170,7 +170,7 @@ async fn test_revoke_session_success() {
|
||||
assert_eq!(revoke_res.status(), StatusCode::OK);
|
||||
let list_after_res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/com.bspds.account.listSessions",
|
||||
"{}/xrpc/com.tranquil.account.listSessions",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(jwt2)
|
||||
@@ -190,7 +190,7 @@ async fn test_revoke_session_invalid_id() {
|
||||
let (_, jwt) = setup_new_user("revoke-invalid").await;
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.revokeSession",
|
||||
"{}/xrpc/com.tranquil.account.revokeSession",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -207,7 +207,7 @@ async fn test_revoke_session_not_found() {
|
||||
let (_, jwt) = setup_new_user("revoke-notfound").await;
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.revokeSession",
|
||||
"{}/xrpc/com.tranquil.account.revokeSession",
|
||||
base_url().await
|
||||
))
|
||||
.bearer_auth(&jwt)
|
||||
@@ -223,7 +223,7 @@ async fn test_revoke_session_requires_auth() {
|
||||
let client = client();
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.bspds.account.revokeSession",
|
||||
"{}/xrpc/com.tranquil.account.revokeSession",
|
||||
base_url().await
|
||||
))
|
||||
.json(&json!({"sessionId": "1"}))
|
||||
|
||||
Reference in New Issue
Block a user