prep for self-hosting - updated readme, env example, dockerfile, compose, dockerignore

This commit is contained in:
2026-03-21 19:29:46 +02:00
parent d52088a88b
commit 010c811a48
5 changed files with 131 additions and 46 deletions

18
.dockerignore Normal file
View File

@@ -0,0 +1,18 @@
node_modules
dist
build
.env
.env.local
.git
.gitignore
*.md
!README.md
data
uploads
plugins-installed
plugins/*.zip
trash
.claude
.vscode
.idea
coverage

View File

@@ -1,24 +1,23 @@
# Database # Database (POSTGRES_PASSWORD is shared between the app and the postgres container)
DB_PASSWORD=change-me-to-a-random-string POSTGRES_PASSWORD=change-me-to-a-strong-random-string
DATABASE_URL=postgresql://echoboard:change-me-to-a-random-string@db:5432/echoboard
# Encryption (generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") # Encryption keys (generate each with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
APP_MASTER_KEY= APP_MASTER_KEY=
APP_BLIND_INDEX_KEY= APP_BLIND_INDEX_KEY=
# Auth secrets (generate each the same way as above) # Auth secrets (generate each the same way)
TOKEN_SECRET= TOKEN_SECRET=
JWT_SECRET= JWT_SECRET=
# ALTCHA spam protection # ALTCHA spam protection (generate the same way)
ALTCHA_HMAC_KEY= ALTCHA_HMAC_KEY=
# WebAuthn / Passkey # WebAuthn / Passkey (set these to your actual domain)
WEBAUTHN_RP_NAME=Echoboard WEBAUTHN_RP_NAME=Echoboard
WEBAUTHN_RP_ID=localhost WEBAUTHN_RP_ID=example.com
WEBAUTHN_ORIGIN=http://localhost:3000 WEBAUTHN_ORIGIN=https://example.com
# Web Push (generate with: npx web-push generate-vapid-keys) # Web push notifications (generate with: npx web-push generate-vapid-keys)
VAPID_PUBLIC_KEY= VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY= VAPID_PRIVATE_KEY=
VAPID_CONTACT=mailto:admin@example.com VAPID_CONTACT=mailto:admin@example.com

View File

@@ -31,7 +31,8 @@ COPY --from=builder /app/node_modules node_modules/
COPY --from=builder /app/echoboard.plugins.ts ./ COPY --from=builder /app/echoboard.plugins.ts ./
RUN addgroup -g 1001 echoboard && adduser -u 1001 -G echoboard -D echoboard && \ RUN addgroup -g 1001 echoboard && adduser -u 1001 -G echoboard -D echoboard && \
mkdir -p /app/packages/api/uploads && chown -R echoboard:echoboard /app mkdir -p /app/packages/api/uploads /app/packages/api/plugins-installed && \
chown -R echoboard:echoboard /app
ENV NODE_ENV=production ENV NODE_ENV=production
EXPOSE 3000 EXPOSE 3000

136
README.md
View File

@@ -1,56 +1,122 @@
# Echoboard # Echoboard
A self-hosted feedback board where users submit feature requests and bug reports without creating an account. Anonymous by default, with optional passkey registration for persistence across devices. Self-hosted feedback board. Users submit feature requests and bug reports without creating an account - anonymous by default, with optional passkey registration for persistence across devices. No email required for anything.
## Quick start ## Self-hosting with Docker
```bash ```bash
git clone <repo-url> git clone https://git.lashman.live/lashman/echoboard.git
cd echoboard cd echoboard
cp .env.example .env cp .env.example .env
# Generate secrets
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Paste output into APP_MASTER_KEY, APP_BLIND_INDEX_KEY, TOKEN_SECRET, JWT_SECRET, ALTCHA_HMAC_KEY
# Generate VAPID keys for push notifications
npx web-push generate-vapid-keys
# Paste into VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY
# Set WEBAUTHN_RP_ID and WEBAUTHN_ORIGIN to your domain
docker compose up -d
docker compose exec app npx echoboard create-admin --email you@example.com
``` ```
Edit `.env` and fill in:
1. Set `POSTGRES_PASSWORD` to something random
2. Generate five secrets (one for each of `APP_MASTER_KEY`, `APP_BLIND_INDEX_KEY`, `TOKEN_SECRET`, `JWT_SECRET`, `ALTCHA_HMAC_KEY`):
```bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
3. Generate VAPID keys for push notifications:
```bash
npx web-push generate-vapid-keys
```
4. Set `WEBAUTHN_RP_ID` to your domain (e.g. `feedback.example.com`)
5. Set `WEBAUTHN_ORIGIN` to your full URL (e.g. `https://feedback.example.com`)
Then start it:
```bash
docker compose up -d
```
Create the initial admin account:
```bash
docker compose exec app npx tsx packages/api/src/cli/create-admin.ts
```
The app is at `http://localhost:3000` (or whatever port you set). Put a reverse proxy in front for HTTPS.
## What's included
- Boards for organizing feedback by project or topic
- Feature requests and bug reports with voting and vote budgets
- Comments with markdown, @mentions, reactions, and file attachments
- Status tracking with custom statuses per board
- Roadmap and changelog pages (with scheduled publishing)
- Full-text search with similar post detection
- Push notifications and board-level subscriptions
- RSS feeds per board
- Post view counts
- Admin dashboard with post management, merge, bulk actions, edit rollback
- Team system with invites - super admin, admin, and moderator roles
- Granular locking - lock post edits, comment edits, threads, or voting independently
- Recovery codes for cookie-based users who can't use passkeys
- Plugin system - upload zip plugins through the admin dashboard, no restart needed
- Embed widget for external sites
- Dark and light themes
- i18n ready
- ALTCHA proof-of-work spam protection (no captchas)
## Reverse proxy
Echoboard needs HTTPS for passkeys to work. Example nginx config:
```nginx
server {
listen 443 ssl;
server_name feedback.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 50m;
}
}
```
## Updating
```bash
git pull
docker compose up -d --build
```
Migrations run automatically on startup.
## Data
All persistent data lives next to the compose file:
- `./data/postgres/` - database
- `./uploads/` - avatars and attachments
- `./plugins-installed/` - uploaded plugins
## Plugins
Plugins are zip files uploaded through the admin dashboard. A plugin contains a `manifest.json` and a JS entry point. Plugins can add API routes, respond to events (post created, status changed, etc.), and store their own data. They run with full server access so only install plugins you trust.
A Gitea sync plugin is included in `plugins/gitea-sync/` as an example.
## Development ## Development
```bash ```bash
npm install npm install
cp .env.example .env cp .env.example .env
# Fill in .env with dev values (WEBAUTHN_RP_ID=localhost, WEBAUTHN_ORIGIN=http://localhost:3000) # set WEBAUTHN_RP_ID=localhost, WEBAUTHN_ORIGIN=http://localhost:5173
# point DATABASE_URL at a local postgres
npm run dev npm run dev
``` ```
This starts the API on port 3000 and the Vite dev server on port 5173 (with API proxy). API on port 3001, Vite dev server on port 5173.
## Architecture
- **packages/api** - Fastify + Prisma backend
- **packages/web** - React + Vite frontend
- **plugins/** - Optional integrations (Gitea, GitHub, etc.)
## Identity model
Two tiers of user identity:
1. **Anonymous cookie** (default) - zero friction, browser-generated token, single device only
2. **Passkey** (optional upgrade) - username + WebAuthn biometric, works across devices, no email needed
## Plugin system
Plugins live in `plugins/` and are registered in `echoboard.plugins.ts`. Each plugin is self-contained with its own routes, database tables, and UI components. Removing a plugin leaves zero trace in the core app.
## License ## License

View File

@@ -15,6 +15,7 @@ services:
- "${PORT:-3000}:${PORT:-3000}" - "${PORT:-3000}:${PORT:-3000}"
volumes: volumes:
- ./uploads:/app/packages/api/uploads - ./uploads:/app/packages/api/uploads
- ./plugins-installed:/app/packages/api/plugins-installed
db: db:
image: postgres:16-alpine image: postgres:16-alpine