Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/backfill-episodes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Backfill Episodes to ATProto

on:
workflow_dispatch:
inputs:
confirm:
description: 'Type "backfill" to confirm publishing all episodes to ATProto'
required: true
type: string

jobs:
backfill:
runs-on: ubuntu-latest
permissions:
contents: read
if: ${{ github.event.inputs.confirm == 'backfill' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: pnpm/action-setup@v4
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Backfill all episodes
run: pnpm tsx scripts/publish-episodes.ts --backfill
env:
ATPROTO_HANDLE: ${{ secrets.ATPROTO_HANDLE }}
ATPROTO_APP_PASSWORD: ${{ secrets.ATPROTO_APP_PASSWORD }}
STANDARD_SITE_URL: ${{ secrets.STANDARD_SITE_URL }}
STANDARD_SITE_PUBLICATION_RKEY: ${{ secrets.STANDARD_SITE_PUBLICATION_RKEY }}
42 changes: 42 additions & 0 deletions .github/workflows/publish-episodes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Publish Episodes to ATProto

on:
# Run after the daily site rebuild to catch new episodes
workflow_run:
workflows: ["Rebuild Astro Site"]
types: [completed]
# Allow manual trigger
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
# Only run if the triggering workflow succeeded (or manual dispatch)
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: pnpm/action-setup@v4
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Publish new episodes
run: pnpm tsx scripts/publish-episodes.ts
env:
ATPROTO_HANDLE: ${{ secrets.ATPROTO_HANDLE }}
ATPROTO_APP_PASSWORD: ${{ secrets.ATPROTO_APP_PASSWORD }}
STANDARD_SITE_URL: ${{ secrets.STANDARD_SITE_URL }}
STANDARD_SITE_PUBLICATION_RKEY: ${{ secrets.STANDARD_SITE_PUBLICATION_RKEY }}
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,13 @@ configured for Preact (`jsxImportSource: "preact"`).
- `ASTRO_DB_REMOTE_URL` — Turso/libSQL database URL (e.g.,
`libsql://your-db.turso.io`).
- `ASTRO_DB_APP_TOKEN` — Authentication token for Turso database.
- `STANDARD_SITE_DID` — Your ATProto DID for standard.site verification (e.g.,
`did:plc:abc123`). Find yours at https://bsky.app/settings.
- `STANDARD_SITE_PUBLICATION_RKEY` — The publication record key returned when
creating a publication via `scripts/create-publication.ts`.
- `ATPROTO_HANDLE` — Your Bluesky handle (e.g., `you.bsky.social`) for
publishing episodes to ATProto.
- `ATPROTO_APP_PASSWORD` — App password for ATProto API access. Create at
https://bsky.app/settings/app-passwords.
- `STANDARD_SITE_URL` — Your podcast website URL (e.g., `https://whiskey.fm`)
used as the publication site when publishing documents.
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,94 @@ environment variable it should work for you. Of course, feel free to customize
the code [here](./src/pages/api/contact.ts) to send the data elsewhere as you
see fit.

#### standard.site (ATProto Federation)

Starpod supports [standard.site](https://standard.site/) — a specification that
connects your podcast website to [ATProto](https://atproto.com/) (the protocol
behind Bluesky). Each episode is published as an individual document on the
federated web. Enabling this allows:

- **Verified ownership** — Cryptographically prove you own your content across
the federated web
- **Cross-platform discovery** — Your podcast appears on ATProto readers like
[Leaflet](https://leaflet.pub/) and [Pckt](https://pckt.blog)
- **Federated engagement** — Comments and interactions from Bluesky and other
ATProto apps can connect back to your site
- **Episode-level publishing** — Each episode is a standalone document in ATProto

This feature is entirely optional. The site works perfectly without it — the
verification endpoint simply returns a 404 when unconfigured. No changes to
`astro.config.mjs` are needed.

##### Initial Setup

1. Create an [app password](https://bsky.app/settings/app-passwords) on Bluesky
2. Create your publication record (run once):

```bash
ATPROTO_HANDLE=you.bsky.social \
ATPROTO_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx \
STANDARD_SITE_URL=https://your-podcast.com \
pnpm tsx scripts/create-publication.ts
```

3. Save the output values as environment variables

##### Environment Variables

Set these in your `.env` file for local development and as **GitHub Actions
secrets** for automated publishing:

| Variable | Description | Where to find it |
|----------|-------------|------------------|
| `STANDARD_SITE_DID` | Your ATProto DID (decentralized identifier) | [bsky.app/settings](https://bsky.app/settings) → scroll to "DID" |
| `STANDARD_SITE_PUBLICATION_RKEY` | Record key for your publication | Returned by `scripts/create-publication.ts` |
| `ATPROTO_HANDLE` | Your Bluesky handle (e.g., `you.bsky.social`) | Your Bluesky username |
| `ATPROTO_APP_PASSWORD` | App password for ATProto API access | [bsky.app/settings/app-passwords](https://bsky.app/settings/app-passwords) |
| `STANDARD_SITE_URL` | Your podcast website URL (e.g., `https://whiskey.fm`) | Your deployed site URL |

##### GitHub Actions Secrets

Add the following secrets to your repository at **Settings → Secrets and
variables → Actions → New repository secret**:

- `ATPROTO_HANDLE`
- `ATPROTO_APP_PASSWORD`
- `STANDARD_SITE_URL`
- `STANDARD_SITE_PUBLICATION_RKEY`
- `STANDARD_SITE_DID`

##### Publishing Episodes

Episodes are published to ATProto as individual documents automatically:

- **Automatic** — The `Publish Episodes to ATProto` workflow runs after each
daily site rebuild and publishes any new episodes
- **Manual** — Trigger the workflow manually from the Actions tab
- **Backfill** — Use the `Backfill Episodes to ATProto` workflow (Actions tab →
Run workflow → type "backfill") to publish all existing episodes

You can also publish locally:

```bash
# Publish only new episodes
pnpm publish:episodes

# Backfill all episodes
pnpm publish:episodes:backfill
```

##### Verification

After deploying, verify the well-known endpoint with:

```bash
curl https://your-site.com/.well-known/site.standard.publication
```

For full setup instructions (creating a publication, syncing posts, etc.), see
the [`@bryanguffey/astro-standard-site` README](https://github.com/musicjunkieg/astro-standard-site#readme).

#### Configuring guests

We use Turso and Astro DB to setup guests per episode. If you would also like to
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
"lint:fix": "eslint . --fix",
"preview": "astro preview",
"start": "astro dev",
"publish:episodes": "tsx scripts/publish-episodes.ts",
"publish:episodes:backfill": "tsx scripts/publish-episodes.ts --backfill",
"test": "concurrently \"pnpm:test:*(!fix)\" --names \"test:\"",
"test:e2e": "pnpm exec playwright test",
"test:unit": "vitest"
},
"dependencies": {
"@astrojs/preact": "^5.1.4",
"@astrojs/vercel": "^10.0.8",
"@bryanguffey/astro-standard-site": "^1.0.3",
"@libsql/client": "^0.17.3",
"@preact/signals": "^2.9.1",
"@vercel/analytics": "^1.6.1",
Expand Down
Loading