Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Every stack starts from the same [standard setup](documentation/standard-setup.m
| **BookLore** | Book manager and browser reader, with Kobo and KOReader sync. | [Details](services/booklore) |
| **Configarr** | Tool that applies YAML settings and TRaSH Guides profiles to Radarr and Sonarr, then exits. | [Details](services/configarr) |
| **FreshRSS** | Feed reader for RSS and Atom, with themes, extensions, and no separate database server. | [Details](services/freshrss) |
| **Fusion** | Lightweight feed reader for RSS, Atom, and JSON feeds, with a Fever API for mobile clients. | [Details](services/fusion) |
| **Hytale** | Hytale game server in a community image, for players on your Tailnet. | [Details](services/hytale) |
| **Immich** | Photo and video library that backs up mobile media, with search and sharing. | [Details](services/immich) |
| **Jellyfin** | Media server that organizes and streams your movies, series, and music. | [Details](services/jellyfin) |
Expand Down
33 changes: 33 additions & 0 deletions services/fusion/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#URL=https://github.com/tailscale-dev/ScaleTail
# To run a second copy, copy the folder under another name and change SERVICE.

# Service Configuration
# Service name (e.g., adguard). Used as hostname in Tailscale and for container naming.
SERVICE=fusion
# Docker image URL from container registry (e.g., adguard/adguardhome:latest).
IMAGE_URL=ghcr.io/0x2e/fusion:latest

# Network Configuration
# Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable.
SERVICEPORT=8080
# Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable.
DNS_SERVER=9.9.9.9

# Tailscale Configuration
# Auth key from https://console.tailscale.com/admin/settings/keys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions.
TS_AUTHKEY=

# Time Zone setting for containers. See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=Europe/Amsterdam

# Any Container environment variables are declared below. See https://docs.docker.com/compose/how-tos/environment-variables/

# Required: your Tailnet name, the part between the service name and ".ts.net" in https://fusion.<tailnet name>.ts.net, for example tail1234.
TAILNET_NAME=
# Required: password of the web interface. Generate it with: openssl rand -hex 32
FUSION_PASSWORD=

# Optional single sign-on. Set these three values and uncomment the FUSION_OIDC lines in compose.yaml.
#FUSION_OIDC_ISSUER=
#FUSION_OIDC_CLIENT_ID=
#FUSION_OIDC_CLIENT_SECRET=
42 changes: 42 additions & 0 deletions services/fusion/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Fusion

[Fusion](https://github.com/0x2E/fusion) is a lightweight feed reader for RSS, Atom, and JSON feeds. It keeps your feeds, articles, and read state in a single SQLite file, and serves third-party clients such as Reeder and Unread through its Fever API.

This stack runs Fusion with a Tailscale sidecar, as described in [the standard setup](../../documentation/standard-setup.md).

## At a glance

| Item | Value |
| ------------- | ---------------------------------- |
| Web interface | `https://fusion.<tailnet>.ts.net` |
| Service port | `8080` |
| Image | `ghcr.io/0x2e/fusion` |
| Data | `./fusion-data/` (SQLite database) |

## Before you start

Set these values in `.env`. Compose stops with an error if one of them is empty.

- **`TAILNET_NAME`.** Your Tailnet name, the part between the service name and `.ts.net`, for example `tail1234`. `compose.yaml` builds the allowed browser origin `https://<SERVICE>.<TAILNET_NAME>.ts.net` from it.
- **`FUSION_PASSWORD`.** The password of the web interface. Generate one with `openssl rand -hex 32`. Fusion reads it at every start and never stores it, so you change the password by changing this value and running `docker compose up -d --force-recreate`. That also ends every open session, because Fusion keeps sessions in memory.

## Deviations from the standard setup

- **Allowed browser origin.** `FUSION_CORS_ALLOWED_ORIGINS` in `compose.yaml` holds the Tailnet address of the service, built from `SERVICE` and `TAILNET_NAME`. Fusion answers `403 Forbidden` to any request that carries another `Origin` header, so a browser can only use the service at that address. An empty value would accept every origin instead. For the same reason the optional `ports` block alone does not give access from your local network: the login request from a page on the address of the Docker host is rejected. To use that address as well, add it to the comma-separated list.

## First run

Open the web interface and log in with the password from `FUSION_PASSWORD`. Fusion has no user accounts, so the login form asks for the password only. A session lasts 30 days.

Add your first feed with the Add button. Fusion discovers the feed of a site when you give it the address of the page itself, and it pulls every feed every 30 minutes.

## Configuration

- **Single sign-on.** Fusion can add a login through an OIDC provider. Set `FUSION_OIDC_ISSUER`, `FUSION_OIDC_CLIENT_ID`, and `FUSION_OIDC_CLIENT_SECRET` in `.env`, then uncomment the four `FUSION_OIDC` lines in `compose.yaml`. Register `https://fusion.<tailnet>.ts.net/api/oidc/callback` as the redirect URI with your provider, because it must match the value that `compose.yaml` sends. Your provider must be reachable from the Fusion container, and from the browser you log in with. The password login stays available next to it, and `FUSION_PASSWORD` stays required: Fusion refuses to start with an empty password unless you also set `FUSION_ALLOW_EMPTY_PASSWORD=true`.
- **Fever API clients.** Reeder, Unread, and other Fever clients use `https://fusion.<tailnet>.ts.net` as the server address, with the account type Fever, the username `fusion`, and your password. A client that asks for the endpoint itself takes `https://fusion.<tailnet>.ts.net/fever`. The device must be connected to your Tailnet. Fusion derives the Fever API key from the username and `FUSION_PASSWORD`, so a new password also means a new key in every client.
- **Feed pulling.** `FUSION_PULL_INTERVAL`, `FUSION_PULL_TIMEOUT`, and `FUSION_PULL_CONCURRENCY` change how often and how fast Fusion pulls feeds. The [variable reference](https://github.com/0x2E/fusion/blob/main/.env.example) lists the defaults.

## Links

- [Fusion documentation and source code](https://github.com/0x2E/fusion)
- [Fever API guide](https://github.com/0x2E/fusion/blob/main/docs/fever-api.md)
72 changes: 72 additions & 0 deletions services/fusion/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
configs:
ts-serve:
content: |
{"TCP":{"443":{"HTTPS":true}},
"Web":{"$${TS_CERT_DOMAIN}:443":
{"Handlers":{"/":
{"Proxy":"http://127.0.0.1:8080"}}}},
"AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}}

services:
# Make sure you have updated/checked the .env file with the correct variables.
# Every variable used in this file must be defined there, unless it has a default such as ${VAR:-value}.
# Tailscale Sidecar Configuration
tailscale:
image: tailscale/tailscale:latest # Image to be used
container_name: tailscale-${SERVICE} # Name for local container management
hostname: ${SERVICE} # Name used within your Tailscale environment
environment:
- TS_AUTHKEY=${TS_AUTHKEY}
- TS_STATE_DIR=/var/lib/tailscale
- TS_SERVE_CONFIG=/config/serve.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required
- TS_USERSPACE=false
- TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz"
- TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The <addr>:<port> for the healthz endpoint
#- TS_ACCEPT_DNS=true # Uncomment only if the service must resolve MagicDNS names - this replaces Docker DNS, so Compose service names no longer resolve
- TS_AUTH_ONCE=true
configs:
- source: ts-serve
target: /config/serve.json
volumes:
- ./config:/config # Config folder used to store Tailscale files - you may need to change the path
- ./ts/state:/var/lib/tailscale # Tailscale requirement - you may need to change the path
devices:
- /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work
cap_add:
- net_admin # Tailscale requirement
#ports:
# - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding the service port to the local network - may be removed if only exposure to your Tailnet is required
# If any DNS issues arise, use your preferred DNS provider by uncommenting the config below
#dns:
# - ${DNS_SERVER}
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational
interval: 1m # How often to perform the check
timeout: 10s # Time to wait for the check to succeed
retries: 3 # Number of retries before marking as unhealthy
start_period: 10s # Time to wait before starting health checks
restart: always

# Application
application:
image: ${IMAGE_URL} # Image to be used
network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale
container_name: app-${SERVICE} # Name for local container management
environment: # Variables are declared in .env file.
- TZ=${TZ}
- FUSION_DB_PATH=/data/fusion.db # SQLite database, kept in the mounted data folder
- FUSION_PASSWORD=${FUSION_PASSWORD:?Set FUSION_PASSWORD in .env}
- FUSION_CORS_ALLOWED_ORIGINS=https://${SERVICE}.${TAILNET_NAME:?Set TAILNET_NAME in .env}.ts.net # Only the Tailnet address may call the API from a browser; an empty value would allow any origin
- FUSION_TRUSTED_PROXIES=127.0.0.1 # Tailscale Serve reaches Fusion over the shared loopback, so trust its X-Forwarded-For for the login rate limit
# Optional single sign-on. Uncomment all four lines and set the three values in .env - see "Configuration" in the README.
#- FUSION_OIDC_ISSUER=${FUSION_OIDC_ISSUER}
#- FUSION_OIDC_CLIENT_ID=${FUSION_OIDC_CLIENT_ID}
#- FUSION_OIDC_CLIENT_SECRET=${FUSION_OIDC_CLIENT_SECRET}
#- FUSION_OIDC_REDIRECT_URI=https://${SERVICE}.${TAILNET_NAME}.ts.net/api/oidc/callback
volumes:
- ./${SERVICE}-data:/data # SQLite database with the feeds, articles, and settings
depends_on:
tailscale:
condition: service_healthy
# Healthcheck: defined by the image (wget against /api/oidc/enabled), so this file does not override it.
restart: always
Loading