Image board style fedi front end with a emphasis on gallery view
  • JavaScript 60.2%
  • CSS 36.7%
  • HTML 2.8%
  • Python 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
nak 9ba48881ac Render move and admin.sign_up notification verbs correctly
These two notification types fell through to the raw `type (you)` fallback.

- move: now reads "@old moved to @new" with the destination handle rendered
  as a profile link (hover-previewed and click-to-profile, like the actor),
  sourced from the notification's `target` account.
- admin.sign_up: now reads "signed up" instead of "admin.sign_up (you)".

Adds a generic inline "link" part type to the notification verb renderer
(used here for the move target), wired with mark-read/navigate on click.
2026-07-23 14:36:13 -07:00
public Render move and admin.sign_up notification verbs correctly 2026-07-23 14:36:13 -07:00
screenshots Add README screenshots 2026-07-12 20:31:08 -07:00
tests Add local hashtag catalog views 2026-07-12 03:18:14 -07:00
tools Add optional esbuild deployment workflow 2026-07-11 02:41:59 -07:00
.gitignore Stop tracking TODO.md (private scratch pad) 2026-07-21 20:36:17 -07:00
AGENTS.md Remove 'no build' step repetition in comments/docs 2026-07-11 14:21:45 -07:00
Caddyfile.example Prepare project for initial release 2026-07-12 02:25:19 -07:00
CHANGELOG.md Render move and admin.sign_up notification verbs correctly 2026-07-23 14:36:13 -07:00
DEV_NOTES.md Refresh README for boards and dev notes 2026-07-06 17:31:43 -07:00
LICENSE Prepare project for initial release 2026-07-12 02:25:19 -07:00
package-lock.json Make panel dialogs draggable and non-blocking 2026-07-22 02:34:58 -07:00
package.json Finalize v0.1.0 release metadata 2026-07-12 20:28:21 -07:00
README.md Update README with web_client_dir deploy notes 2026-07-22 01:29:38 -07:00

chan-fe

Imageboard-style frontend for Mitra and Mastodon-compatible fediverse APIs.

chan-fe renders timelines as board catalogs with thread views, reactions, polls, media galleries, and posting tools. The application is a set of static files and does not require a server-side runtime of its own.

Status and compatibility

chan-fe is experimental, but actively usable for daily browsing and posting on a Mitra instance.

Mitra is the primary target and the only server exercised against in earnest. Several features use Mitra extensions, including custom post titles, emoji reactions, conversation visibility, and group-backed boards.

The frontend otherwise uses Mastodon-compatible /api/* and /oauth/* endpoints. OAuth sign-in against Pleroma has worked, but broader Pleroma/Mastodon compatibility has not been tested extensively. Expect rough edges outside Mitra.

Highlights

  • 4chan-style board catalogs and chronological thread views
  • Mitra group-backed custom boards
  • OAuth sign-in, posting, replies, editing, moderation, polls, and media uploads
  • Likes, reposts, bookmarks, and Unicode/custom emoji reactions
  • Inline reply and profile previews, media galleries, and opt-in media embeds
  • Local hashtag catalogs linked from posts and federated search results
  • Thread watcher and paginated fediverse notifications
  • Configurable NSFW handling, activity previews, mention collapsing, and an Anonymous presentation mode
  • No required build step

See CHANGELOG.md for the detailed initial feature inventory and future release notes.

Installation

Clone the repository onto the machine that will serve the frontend:

git clone https://codeberg.org/nak/chan-fe.git
cd chan-fe

The simplest deployment is to let Mitra serve chan-fe itself by pointing its web_client_dir setting at this public/ directory — chan-fe then replaces mitra-web as the instance's main frontend. See Serve chan-fe from Mitra directly.

Alternatively, serve public/ from a separate HTTPS origin whose hostname is the default Mitra instance, and reverse-proxy /api/*, /oauth/*, and /media/* to Mitra. Either keeps normal API requests same-origin while still allowing users to sign in to other compatible instances directly.

No Node.js installation or build step is required when serving public/ directly.

Configuration

Frontend configuration lives in public/config.js. It does not contain generated credentials or secrets; OAuth applications are registered dynamically with the selected instance.

OAUTH_CONFIG.defaultInstance defaults to the hostname serving chan-fe. This is correct when that hostname reverse-proxies the Mitra endpoints described above. If the frontend uses a different hostname, either set the default in public/config.js or provide it to the optional production build:

npm run build -- --default-instance example.com

The configured instance is only the sign-in dialog's default. Users may still enter another compatible instance.

Board definitions, labels, and timeline API paths also live in public/config.js.

Deployment

Serve chan-fe from Mitra directly

The simplest way to run chan-fe as your instance's main frontend is to let Mitra serve it, replacing its default web client. Clone the repository onto the Mitra host and point Mitra's top-level web_client_dir setting at the public/ directory:

# Mitra config.yaml
web_client_dir: /srv/chan-fe/public

Restart Mitra. It now serves chan-fe at its own origin, so /api/*, /oauth/*, and /media/* are same-origin and OAUTH_CONFIG.defaultInstance — which defaults to the serving hostname — is correct with no extra configuration. No separate web server or reverse proxy is needed; sign-in to other compatible instances still works directly at their own origins.

For a minified bundle, run the production build and point web_client_dir at dist/ instead:

npm install
npm run build
# then set web_client_dir to the dist/ directory, e.g. /srv/chan-fe/dist

Serve the static files directly

Point the web root at public/. Route API requests before the static-file SPA fallback:

  1. Reverse-proxy /api/*, /oauth/*, and /media/* to Mitra.
  2. Serve existing static files.
  3. Fall back to index.html for client-side routes.

Caddyfile.example contains a reference configuration with the required route order and suggested security headers. Copy the relevant blocks into your existing Caddy configuration rather than replacing it wholesale.

If the static-file handler runs before the proxy rules, API requests may receive index.html instead of JSON.

Optional production build

The optional build bundles and minifies JavaScript into dist/app.js, writes an external source map, and copies the other static assets into dist/:

npm install
npm run build

Serve dist/ instead of public/ after building. To bake in an instance on a different hostname:

npm run build -- --default-instance example.com

Deployment helper

The deployment helper builds and uploads dist/ with rsync:

npm run deploy -- example.com user@frontend.example:/var/www/chan-fe/

It uses --delete and --delay-updates. Check the destination carefully: files at the destination that are absent from dist/ will be removed.

Screenshots

Catalog Thread view
Board catalog Thread view
Compose Emoji picker Media gallery
Compose modal Emoji picker Media gallery

Development

There is no required development build step. Application code lives in public/ and is plain JavaScript, HTML, and CSS.

Local static server

python3 tools/serve.py --port 8080
# open http://localhost:8080/

tools/serve.py sends Cache-Control: no-cache so browser refreshes reliably pick up JavaScript and CSS edits.

You can also use Python's standard static server:

python3 -m http.server 8080 --directory public

When developing on localhost, set the default instance in public/config.js or use the production build flag described above. The live instance must allow the required cross-origin API requests unless a local reverse proxy is used.

Project layout

public/
├── index.html          # app shell and modals
├── style.css           # application styles
├── config.js           # OAuth, board, and endpoint configuration
├── catalog.js          # catalog sorting and thread-root helpers
├── threading.js        # thread-root helpers for contexts and parent maps
├── media.js            # media size, dimension, URL, and format helpers
├── gallery.js          # multi-attachment gallery overlay
├── data.js             # API access and timeline hydration
├── app.js              # routing, state, rendering, compose, auth, and interactions
├── images/             # local presentation assets
├── css/                # vendored Font Awesome stylesheets
└── webfonts/           # vendored Font Awesome font files

tools/
├── build.mjs           # optional production bundle
├── deploy.sh           # build and rsync helper
└── serve.py            # local no-cache static server

tests/
├── catalog.test.js
├── data.test.js
├── security.test.js
└── threading.test.js

screenshots/            # README screenshots

Testing

The Node built-in test suite covers the core pure-logic modules and URL-scheme security checks:

npm test

Watch mode:

npm run test:watch

See DEV_NOTES.md for architecture notes and browser verification guidance.

Security

  • Fediverse post, profile, and instance HTML passes through a local tag, attribute, and URL-scheme allowlist before reaching the rendered DOM.
  • YouTube and PeerTube frames are created only after an explicit click. PeerTube URLs must be HTTPS canonical /w/<video-id> links, are converted to constrained embed URLs, and run in a sandbox without forms, popups, top-level navigation, or downloads.
  • OAuth bearer tokens are stored in localStorage. This keeps the static app simple, but means any same-origin script execution could read the token. Serve chan-fe over HTTPS, retain a restrictive CSP, do not add untrusted third-party scripts, and keep the frontend origin under your control.
  • The API wrapper attaches a bearer token only to the selected signed-in instance origin or the configured same-origin reverse proxy.

PeerTube embeds may originate on any HTTPS instance, so the example CSP uses frame-src https:. Tighten connect-src, img-src, media-src, and frame-src when deploying against a fixed set of instances.

Reference projects

  • bloat-fe — authentication flow inspiration
  • mitra-web — API behavior reference
  • FChannel — federated imageboard server with a different architecture

License

chan-fe is released under the MIT License.