···140# =============================================================================
141# If configured, moderation reports will be proxied to this service
142# instead of being stored locally. The service should implement the
143-# com.atproto.moderation.createReport endpoint (e.g., Bluesky's Ozone).
144# Both URL and DID must be set for proxying to be enabled.
145# REPORT_SERVICE_URL=https://mod.bsky.app
146# REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac
···148# Age Assurance Override
149# =============================================================================
150# Enable this if you have separately assured the ages of your users
151-# (e.g., through your own age verification process). When enabled, the PDS
152# will return "assured" status for age assurance checks instead of proxying
153# to the appview. This helps migrated users avoid the age assurance
154# catch-22 on bsky.app.
···158# =============================================================================
159# Allow HTTP for proxy requests (development only)
160# ALLOW_HTTP_PROXY=1
161-# Custom frontend directory (defaults to ./frontend/dist)
162-# FRONTEND_DIR=/path/to/frontend/dist
163# =============================================================================
164# SSO / Social Login
165# =============================================================================
···140# =============================================================================
141# If configured, moderation reports will be proxied to this service
142# instead of being stored locally. The service should implement the
143+# com.atproto.moderation.createReport endpoint (eg., Bluesky's Ozone).
144# Both URL and DID must be set for proxying to be enabled.
145# REPORT_SERVICE_URL=https://mod.bsky.app
146# REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac
···148# Age Assurance Override
149# =============================================================================
150# Enable this if you have separately assured the ages of your users
151+# (eg., through your own age verification process). When enabled, the PDS
152# will return "assured" status for age assurance checks instead of proxying
153# to the appview. This helps migrated users avoid the age assurance
154# catch-22 on bsky.app.
···158# =============================================================================
159# Allow HTTP for proxy requests (development only)
160# ALLOW_HTTP_PROXY=1
00161# =============================================================================
162# SSO / Social Login
163# =============================================================================
···1213## What's different about Tranquil PDS
1415-It is a superset of the reference PDS, including: passkeys and 2FA (WebAuthn/FIDO2, TOTP, backup codes, trusted devices), did:web support (PDS-hosted subdomains or bring-your-own), multi-channel communication (email, discord, telegram, signal) for verification and alerts, granular OAuth scopes with a consent UI showing human-readable descriptions, app passwords with granular permissions (read-only, post-only, or custom scopes), account delegation (letting others manage an account with configurable permission levels), automatic backups to s3-compatible object storage (configurable retention and frequency, one-click restore), and a built-in web UI for account management, OAuth consent, repo browsing, and admin.
1617The PDS itself is a single small binary with no node/npm runtime. It does require postgres, valkey, and s3-compatible storage, which makes setup heavier than the reference PDS's sqlite. The tradeoff is that these are battle-tested pieces of infra that we already know how to scale, back up, and monitor.
18···4546```bash
47cp .env.prod.example .env.prod
48-podman-compose -f docker-compose.prod.yml up -d
49```
5051### Installation Guides
···1213## What's different about Tranquil PDS
1415+It is a superset of the reference PDS, including: passkeys and 2FA (WebAuthn/FIDO2, TOTP, backup codes, trusted devices), SSO login and signup, did:web support (PDS-hosted subdomains or bring-your-own), multi-channel communication (email, discord, telegram, signal) for verification and alerts, granular OAuth scopes with a consent UI showing human-readable descriptions, app passwords with granular permissions (read-only, post-only, or custom scopes), account delegation (letting others manage an account with configurable permission levels), automatic backups to s3-compatible object storage (configurable retention and frequency, one-click restore), and a built-in web UI for account management, OAuth consent, repo browsing, and admin.
1617The PDS itself is a single small binary with no node/npm runtime. It does require postgres, valkey, and s3-compatible storage, which makes setup heavier than the reference PDS's sqlite. The tradeoff is that these are battle-tested pieces of infra that we already know how to scale, back up, and monitor.
18···4546```bash
47cp .env.prod.example .env.prod
48+podman-compose -f docker-compose.prod.yaml up -d
49```
5051### Installation Guides
···440 <a href="/app/register" class="btn primary" id="heroPrimary"
441 >Join This Server</a>
442 <a
443- href="https://tangled.org/lewis.moe/bspds-sandbox"
444 class="btn secondary"
445 id="heroSecondary"
446 target="_blank"
···461 <div class="feature">
462 <h3>Real security</h3>
463 <p>
464- Sign in with passkeys, add two-factor authentication, set up
465- backup codes, and mark devices you trust. Your account stays
466- yours.
467 </p>
468 </div>
469···546 <a href="/app/register" class="btn primary" id="footerPrimary"
547 >Join This Server</a>
548 <a
549- href="https://tangled.org/lewis.moe/bspds-sandbox"
550 class="btn secondary"
551 target="_blank"
552 rel="noopener"
···440 <a href="/app/register" class="btn primary" id="heroPrimary"
441 >Join This Server</a>
442 <a
443+ href="https://tangled.org/tranquil.farm/tranquil-pds"
444 class="btn secondary"
445 id="heroSecondary"
446 target="_blank"
···461 <div class="feature">
462 <h3>Real security</h3>
463 <p>
464+ Sign in with passkeys or SSO, add two-factor authentication,
465+ set up backup codes, and mark devices you trust. Your account
466+ stays yours.
467 </p>
468 </div>
469···546 <a href="/app/register" class="btn primary" id="footerPrimary"
547 >Join This Server</a>
548 <a
549+ href="https://tangled.org/tranquil.farm/tranquil-pds"
550 class="btn secondary"
551 target="_blank"
552 rel="noopener"
+8-8
frontend/src/locales/en.json
···160 "signal": "Signal",
161 "signalNumber": "Signal Phone Number",
162 "signalNumberPlaceholder": "+1234567890",
163- "signalNumberHint": "Include country code (e.g., +1 for US)",
164 "notConfigured": "not configured",
165 "inviteCode": "Invite Code",
166 "inviteCodePlaceholder": "Enter your invite code",
···263 "saveFailed": "Failed to save DID document",
264 "loadFailed": "Failed to load DID document",
265 "invalidMultibase": "Public key must be a valid multibase string starting with 'z'",
266- "invalidHandle": "Handle must be an at:// URI (e.g., at://handle.example.com)",
267 "helpTitle": "What is this?",
268 "helpText": "When you migrate to another PDS, that PDS generates new signing keys. Update your DID document here so it points to your new keys and location. This enables multi-hop migrations (PDS 1 → PDS 2 → PDS 3)."
269 },
···385 "title": "App Passwords",
386 "description": "App passwords let you sign in to third-party apps without giving them your main password. Each app password can be revoked individually.",
387 "createNew": "Create New App Password",
388- "appNamePlaceholder": "App name (e.g., Graysky, Skeets)",
389 "created": "App Password Created",
390 "createdMessage": "Copy this password now. You won't be able to see it again.",
391 "yourPasswords": "Your App Passwords",
···452 "adding": "Adding...",
453 "noPasskeys": "No passkeys registered",
454 "passkeyName": "Passkey name",
455- "passkeyNamePlaceholder": "e.g., MacBook Pro, iPhone",
456 "register": "Register",
457 "registering": "Registering...",
458 "rename": "Rename",
···1007 "infoAppAccess": "Using third-party apps",
1008 "infoAppAccessDesc": "After creating your account, you will receive an app password. Use this to sign in to Bluesky apps and other AT Protocol clients.",
1009 "passkeyNameLabel": "Passkey Name (optional)",
1010- "passkeyNamePlaceholder": "e.g., MacBook Touch ID",
1011 "passkeyNameHint": "A friendly name to identify this passkey",
1012 "passkeyPrompt": "Click the button below to create your passkey. You'll be prompted to use:",
1013 "passkeyPromptBullet1": "Touch ID or Face ID",
···1279 "checkingAvailability": "Checking availability...",
1280 "handleAvailable": "Handle is available!",
1281 "handleTaken": "Handle is already taken",
1282- "handleHint": "You can also use your own domain by entering the full handle (e.g., alice.mydomain.com)",
1283 "email": "Email Address",
1284 "authMethod": "Authentication Method",
1285 "authPassword": "Password",
···1320 "title": "Set Up Your Passkey",
1321 "desc": "Your email has been verified. Now set up your passkey for secure, passwordless login.",
1322 "nameLabel": "Passkey Name (optional)",
1323- "namePlaceholder": "e.g., MacBook Pro, iPhone",
1324 "nameHint": "A friendly name to identify this passkey",
1325 "instructions": "Click the button below to register your passkey. Your device will prompt you to use biometrics (fingerprint, Face ID) or a security key.",
1326 "register": "Register Passkey",
···1418 "title": "Enter Your DID",
1419 "desc": "Enter the DID of the account you want to restore.",
1420 "label": "Your DID",
1421- "hint": "Your decentralized identifier (e.g., did:plc:abc123...)"
1422 },
1423 "uploadCar": {
1424 "title": "Upload Repository Backup",
···160 "signal": "Signal",
161 "signalNumber": "Signal Phone Number",
162 "signalNumberPlaceholder": "+1234567890",
163+ "signalNumberHint": "Include country code (eg., +1 for US)",
164 "notConfigured": "not configured",
165 "inviteCode": "Invite Code",
166 "inviteCodePlaceholder": "Enter your invite code",
···263 "saveFailed": "Failed to save DID document",
264 "loadFailed": "Failed to load DID document",
265 "invalidMultibase": "Public key must be a valid multibase string starting with 'z'",
266+ "invalidHandle": "Handle must be an at:// URI (eg., at://handle.example.com)",
267 "helpTitle": "What is this?",
268 "helpText": "When you migrate to another PDS, that PDS generates new signing keys. Update your DID document here so it points to your new keys and location. This enables multi-hop migrations (PDS 1 → PDS 2 → PDS 3)."
269 },
···385 "title": "App Passwords",
386 "description": "App passwords let you sign in to third-party apps without giving them your main password. Each app password can be revoked individually.",
387 "createNew": "Create New App Password",
388+ "appNamePlaceholder": "App name (eg., Graysky, Skeets)",
389 "created": "App Password Created",
390 "createdMessage": "Copy this password now. You won't be able to see it again.",
391 "yourPasswords": "Your App Passwords",
···452 "adding": "Adding...",
453 "noPasskeys": "No passkeys registered",
454 "passkeyName": "Passkey name",
455+ "passkeyNamePlaceholder": "eg., MacBook Pro, iPhone",
456 "register": "Register",
457 "registering": "Registering...",
458 "rename": "Rename",
···1007 "infoAppAccess": "Using third-party apps",
1008 "infoAppAccessDesc": "After creating your account, you will receive an app password. Use this to sign in to Bluesky apps and other AT Protocol clients.",
1009 "passkeyNameLabel": "Passkey Name (optional)",
1010+ "passkeyNamePlaceholder": "eg., MacBook Touch ID",
1011 "passkeyNameHint": "A friendly name to identify this passkey",
1012 "passkeyPrompt": "Click the button below to create your passkey. You'll be prompted to use:",
1013 "passkeyPromptBullet1": "Touch ID or Face ID",
···1279 "checkingAvailability": "Checking availability...",
1280 "handleAvailable": "Handle is available!",
1281 "handleTaken": "Handle is already taken",
1282+ "handleHint": "You can also use your own domain by entering the full handle (eg., alice.mydomain.com)",
1283 "email": "Email Address",
1284 "authMethod": "Authentication Method",
1285 "authPassword": "Password",
···1320 "title": "Set Up Your Passkey",
1321 "desc": "Your email has been verified. Now set up your passkey for secure, passwordless login.",
1322 "nameLabel": "Passkey Name (optional)",
1323+ "namePlaceholder": "eg., MacBook Pro, iPhone",
1324 "nameHint": "A friendly name to identify this passkey",
1325 "instructions": "Click the button below to register your passkey. Your device will prompt you to use biometrics (fingerprint, Face ID) or a security key.",
1326 "register": "Register Passkey",
···1418 "title": "Enter Your DID",
1419 "desc": "Enter the DID of the account you want to restore.",
1420 "label": "Your DID",
1421+ "hint": "Your decentralized identifier (eg., did:plc:abc123...)"
1422 },
1423 "uploadCar": {
1424 "title": "Upload Repository Backup",
+3-2
justfile
···81 podman compose down
82podman-logs:
83 podman compose logs -f
84-podman-build:
85- podman compose build
08687frontend-dev:
88 . ~/.deno/env && cd frontend && deno task dev