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
/blobendpoint, 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#
-
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. -
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.
-
Return to the demo — After authorizing ALF, your PDS redirects to ALF's callback. If
OAUTH_SUCCESS_REDIRECTis 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. -
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
/blobendpoint, then sends an XRPCcreateRecordcall with thex-scheduled-atheader. -
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