A Bsky-like frontend using the atprotocol natively.
foxsky README.md
5.6 kB
Markdown
at main

Foxsky #

A Bluesky/AT Protocol web client built with React, TypeScript, and Vite.

Features #

  • OAuth 2.0 authentication — Uses @atproto/oauth-client-browser for secure, password-less login via your PDS (DPoP-bound tokens, PKCE)
  • Custom PDS support — Works with any AT Protocol PDS, not just bsky.social
  • Timeline with predictive prefetch — IntersectionObserver-based infinite scroll with velocity-aware preloading
  • Post interactions — Like, repost, quote, reply, and edit your own posts
  • Threaded conversations — Reddit-style nesting or flat chronological layout
  • Image gallery & lightbox — Multi-image grids with full-screen viewer, keyboard navigation
  • Video playback — HLS streaming via hls.js with custom controls and volume normalization
  • Profile views — Full profile pages, side panel, and hover cards with follow/following state
  • Notifications — Grouped notification display with post previews and avatar stacks
  • Search — Actor autocomplete and post search
  • Settings — Post view mode (page/modal/side panel), reply style, theme, video autoplay
  • Dark mode — System-preference-aware with manual override
  • CDN bypass — Blob URLs rewritten to serve directly from the author's PDS
  • Multi-account — Switch between accounts without re-authenticating

Prerequisites #

  • Node.js 18+
  • npm 9+

Getting Started #

Install dependencies #

npm install

Development #

The dev server binds to 127.0.0.1 (required for AT Protocol loopback OAuth — localhost will not work).

npm run dev

Open http://127.0.0.1:5173 in your browser.

Production Build #

For local development (loopback OAuth) #

npm run build

This produces a dist/ folder configured for loopback OAuth on 127.0.0.1.

For deployment (HTTPS with a real domain) #

Set the OAUTH_DOMAIN environment variable to your domain before building:

PowerShell (Windows):

$env:OAUTH_DOMAIN="foxsky.example.com"; npm run build

bash / zsh (Linux/macOS):

OAUTH_DOMAIN=foxsky.example.com npm run build

cmd (Windows):

set OAUTH_DOMAIN=foxsky.example.com && npm run build

This does two things:

  1. Replaces all __DOMAIN__ placeholders in dist/client-metadata.json with your domain (so the AT Protocol OAuth flow can discover your app)
  2. Injects the domain into the JS bundle so the OAuth client uses HTTPS mode instead of loopback mode

Your server must serve the app over HTTPS at the domain you specified, and client-metadata.json must be accessible at https://<your-domain>/client-metadata.json.

Deployment example (nginx) #

Since Foxsky is a single-page app with client-side routing, your web server must redirect all requests to index.html so React Router can handle them. Here's a minimal nginx config:

server {
    listen 443 ssl;
    server_name foxsky.example.com;

    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    root /var/www/foxsky;
    index index.html;

    # SPA fallback — serve static files first, then index.html for all routes
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache static assets (Vite hashes filenames, so they're immutable)
    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}
  1. Build with OAUTH_DOMAIN=foxsky.example.com npm run build
  2. Copy the contents of dist/ to /var/www/foxsky/
  3. Make sure client-metadata.json is at /var/www/foxsky/client-metadata.json (it's already in dist/ after build)

Preview the production build #

npm run preview

Lint #

npm run lint

Project Structure #

src/
├── api/            # AT Protocol client, blob rewriting, PDS resolution
├── auth/           # OAuth session management, multi-account support
├── components/     # React UI components
├── hooks/          # Reusable hooks (feed, scroll velocity, saved feeds)
├── labels/         # Label resolution and caching
├── plugins/        # Vite build plugins (domain replacement)
├── prefetch/       # Predictive data prefetching engine
├── settings/       # Settings persistence and context providers
├── utils/          # Post URL routing utilities
├── App.tsx         # Root component with providers and routes
├── icons.ts        # Font Awesome icon registration
├── index.css       # Global styles and theme variables
└── main.tsx        # Entry point

Tech Stack #

  • React 19 + TypeScript 6
  • Vite 8 with @vitejs/plugin-react
  • React Router 7 for client-side routing
  • @atproto/oauth-client-browser for AT Protocol OAuth 2.0
  • @atproto/api as a secondary SDK dependency
  • hls.js for HLS video playback
  • Font Awesome for icons

License #

This project is licensed under Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0).

  • ✅ You may view, use, and modify this software for personal/non-commercial purposes
  • ✅ You must give appropriate credit and indicate if changes were made
  • ❌ You may not use this software for commercial purposes without written permission
  • ❌ You may not claim this work as your own

See LICENSE for the full license text or visit creativecommons.org/licenses/by-nc/4.0.

To inquire about commercial licensing, please contact the author.