alf: the atproto Latency Fabric alf.fly.dev/
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Running the Demo App#

The demo/ directory contains a standalone web application that demonstrates scheduled posting with ALF. It shows the full integration: OAuth sign-in, authorizing ALF, creating scheduled posts with images, and managing drafts through ALF's XRPC endpoints.

What the demo shows#

  • Authenticating a Bluesky user via ATProto OAuth (loopback client pattern, all in the browser)
  • Authorizing ALF to post on the user's behalf via a second OAuth flow
  • Scheduling a Bluesky post with a custom or preset publish time
  • Attaching an image to a scheduled post (upload to ALF's /blob endpoint, referenced in the record)
  • Listing all drafts with live status updates (polling every 5 seconds)
  • Editing the text or schedule time of a pending draft
  • Publishing a draft immediately or deleting it

The demo is a two-panel web UI: the left panel is the interactive scheduling interface; the right panel shows an architecture overview explaining how the proxy works.

Architecture#

The demo is a minimal Express server (demo/server.ts) that serves static files and exposes a single /api/config endpoint. All OAuth logic and API calls run entirely in the browser (demo/client/index.ts) using @atproto/oauth-client-browser. The browser talks directly to ALF — the demo server itself never proxies API requests.

Browser (demo/client/index.ts)
  └─ OAuth login → your PDS
  └─ OAuth authorize ALF → your PDS → ALF callback
  └─ XRPC writes + reads → ALF (ALF_URL)
       └─ publishes to PDS at scheduled time

Demo server (demo/server.ts)
  └─ Serves static files
  └─ GET /api/config → { alfUrl }

Prerequisites#

  • Node.js 20 or later
  • ALF running locally (see below)

Step 1: Start ALF#

The demo is a client for ALF — ALF must be running before you open the demo in your browser.

See the Quick Start in the main README for full instructions. The short version:

# In the ALF root directory
cp .env.example .env
# Edit .env and set ENCRYPTION_KEY (generate one with the command in the file)

# Optional but recommended: set this so the demo's OAuth flow returns you
# to the demo automatically after you authorize ALF
echo 'OAUTH_SUCCESS_REDIRECT=http://localhost:1756' >> .env

npm install
npm run dev

ALF starts on port 1986 by default (configurable with ALF_PORT).

Step 2: Start the demo#

cd demo
npm install
cp .env.example .env
# Edit .env if needed (see env vars below)
npm run dev

The demo server starts on port 1756. Open http://localhost:1756 in your browser.

npm run dev builds the client bundle with esbuild and then starts the Express server with ts-node.

Environment variables#

Both files live in demo/.env (copied from demo/.env.example):

Variable Default Description
ALF_URL http://localhost:1986 URL of the running ALF instance
PORT 1756 Port the demo server listens on

The demo server exposes ALF_URL to the browser via /api/config, so the client-side code knows where to send XRPC requests.

Demo flow#

  1. Sign in — Enter your ATProto handle (e.g. alice.bsky.social) and click "Sign in". The browser starts an ATProto OAuth flow and redirects you to your PDS to approve.

  2. Authorize ALF — After sign-in, if ALF does not yet have a stored session for your account, you will see the "Grant access" screen. Click the button. Your browser is redirected to your PDS again, this time to authorize ALF to publish on your behalf.

  3. Return to the demo — After authorizing ALF, your PDS redirects to ALF's callback. If OAUTH_SUCCESS_REDIRECT is set in ALF's .env (recommended), ALF redirects you back to the demo automatically and a brief "Authorized" banner appears. Otherwise you need to navigate back manually.

  4. Schedule a post — Type your post text, optionally attach an image, pick a schedule time (preset buttons: +90s, +1 hr, Tomorrow 9am, Next week, or set a custom datetime), and click "Schedule Post". The browser uploads any image directly to ALF's /blob endpoint, then sends an XRPC createRecord call with the x-scheduled-at header.

  5. Manage drafts — The "Drafts" section updates every 5 seconds. Each draft shows its status (draft, scheduled, published, failed). You can edit the text or schedule time, publish immediately, or delete.

OAUTH_SUCCESS_REDIRECT#

When a user completes the ALF OAuth authorization flow, ALF's callback normally returns a JSON response. Setting OAUTH_SUCCESS_REDIRECT in ALF's .env causes ALF to redirect the browser to your app instead, with the authorized DID as a query parameter:

http://localhost:1756?did=did%3Aplc%3A...

This makes the flow seamless — the user lands back in the demo automatically. Without it, the user sees a JSON response from ALF and must navigate back by hand.

To enable:

# In ALF's .env
OAUTH_SUCCESS_REDIRECT=http://localhost:1756