at://Press
TypeScript 55.8%
Astro 31.1%
CSS 9.8%
JavaScript 2.9%
Dockerfile 0.4%
43 1 1

Clone this repository

https://tangled.org/bkb.arcnode.xyz/at-press https://tangled.org/did:plc:k23ujfuppr3hr4pxvtaz7jro/at-press
git@tangled.org:bkb.arcnode.xyz/at-press git@tangled.org:did:plc:k23ujfuppr3hr4pxvtaz7jro/at-press

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

at://press#

Protocol-native REpo-Stored Stories — a blog engine where your AT Protocol PDS is your CMS.

Posts are stored as AT Protocol records on your personal data server. No database, no CMS, just protocol-native content that you own.

Features#

  • PDS as CMS — Published posts are AT Protocol records on your PDS
  • WhiteWind compatible — Uses com.whtwnd.blog.entry lexicon by default (configurable)
  • Private drafts — SQLite-backed drafts with publish/unpublish transitions
  • ATAuth login — Sign in with your AT Protocol identity to write and edit
  • Image uploads — Upload images directly to your PDS blob store
  • 5 color themes — Default, Parchment, Moss, Slate, Rose (+ E-ink)
  • RSS feed — Auto-generated at /rss.xml
  • Editable about section — Markdown bio stored on your PDS
  • Responsive layout — Sidebar profile, 3-column grid, mobile-friendly
  • Bluesky integration — Profile link in sidebar

Quick Start#

git clone https://github.com/bkb/at-press.git
cd at-press
npm install
npm run setup    # Interactive — validates PDS, resolves DID, tests auth, writes .env
npm run dev      # → http://localhost:4321

Or manually:

cp .env.example .env
# Fill in your PDS_URL, DID, HANDLE, PDS_APP_PASSWORD, and BLOG_URL
npm install
npm run dev

Docker#

One-liner#

docker run -d --name at-press \
  --env-file .env \
  -p 4000:4000 \
  ghcr.io/bkb/at-press:latest

Docker Compose#

services:
  blog:
    image: ghcr.io/bkb/at-press:latest
    container_name: at-press
    restart: unless-stopped
    ports:
      - "4000:4000"
    env_file: .env
    environment:
      - HOST=0.0.0.0
      - PORT=4000
      - DRAFTS_DB_PATH=/data/drafts.db
    volumes:
      - blog-data:/data

volumes:
  blog-data:

Build from source#

docker build -t at-press .
docker run -d --env-file .env -p 4000:4000 at-press

Configuration#

Variable Required Default Description
PDS_URL Yes Your PDS URL (e.g., https://pds.example.com)
DID Yes Your DID (e.g., did:plc:abc123)
HANDLE Yes Your handle (e.g., you.example.com)
PDS_APP_PASSWORD Yes App password from your PDS
BLOG_URL Yes http://localhost:4321 Public URL of your blog
ATAUTH_GATEWAY_URL No ATAuth gateway for authenticated editing
ATAUTH_PUBLIC_URL No ATAuth public URL for login redirects
BLOG_COLLECTION No com.whtwnd.blog.entry Lexicon for blog posts
ABOUT_COLLECTION No xyz.arcnode.blog.about Lexicon for about section
DRAFTS_DB_PATH No /data/drafts.db SQLite path for draft storage

Architecture#

┌──────────────┐     ┌─────────────────┐
│  Browser     │────▶│  at://press     │
│              │◀────│  (Astro SSR)    │
└──────────────┘     └────────┬────────┘
                              │
                    ┌─────────┴─────────┐
                    │                   │
              ┌─────▼─────┐     ┌──────▼──────┐
              │  Your PDS  │     │  SQLite     │
              │ (published)│     │ (drafts)    │
              └───────────┘     └─────────────┘
  • Published posts: Stored as AT Protocol records on your PDS
  • Drafts: Private, stored in local SQLite (persisted via Docker volume)
  • Auth: Optional ATAuth proxy for browser-based editing
  • Images: Uploaded to your PDS blob store

Lexicons#

By default, at://press uses the WhiteWind blog lexicon (com.whtwnd.blog.entry), making your posts readable on WhiteWind and any other WhiteWind-compatible reader.

You can switch to Leaflet (com.leaflet.blog.post) or any other blog lexicon by setting the BLOG_COLLECTION environment variable.

Stack#

  • Framework: Astro 5 (SSR, Node adapter)
  • Styling: Tailwind CSS 4
  • Markdown: marked + DOMPurify
  • Testing: Vitest (unit) + Playwright (E2E)
  • Container: Docker (multi-arch: amd64 + arm64)

Development#

npm run dev        # Dev server at localhost:4321
npm test           # Unit tests
npm run test:e2e   # E2E tests (start server first)
npm run build      # Production build
npm start          # Run production server on port 4000

Customization#

Replace the text logo in src/layouts/Base.astro with your own SVG or text:

<!-- Find this in Base.astro and replace with your logo -->
<a href="/" class="hover:opacity-80 transition-opacity font-mono text-sm font-bold text-accent">
  <span class="opacity-50">at://</span>press
</a>

Themes#

Themes are defined in src/styles/global.css. The active theme is stored in localStorage under "blog-theme".

Roadmap#

  • Full PDS + at://press installer — one-command self-sovereign publishing
  • Plugin system for custom lexicons
  • Comment system via AT Protocol

License#

MIT


Originally built by bkb for blog.arcnode.xyz. Powered by AT Protocol.