# Fossil SCM Hub — /var/fossil

Self-hosted multi-repo Fossil SCM server for craftdesign.group.

## Architecture

```
https://fossil.craftdesign.group
    ↓ Cloudflare named tunnel (fossil / 45ea54d2-1b49-496b-bc35-c01c45d95297)
Caddy :8090
    ├── GET /  →  /var/fossil/www/index.html  (dark landing page)
    └── /*     →  proxy to Fossil :8081
Fossil :8081 (localhost only)
    └── serves /var/fossil/repos/*.fossil  (multi-repo directory mode)
```

## Directory Layout

```
/var/fossil/
├── repos/                   # all .fossil repo files (auto-served by fossil)
│   ├── edgesync.fossil
│   └── go-libfossil.fossil
├── skin/                    # shared skin — push to repos via script below
│   ├── header.txt           # TH1 template
│   ├── footer.txt
│   └── css.txt              # dark theme matching landing page
├── www/
│   └── index.html           # custom landing page (dark theme)
├── Caddyfile                # Caddy reverse proxy config
└── CLAUDE.md                # this file
```

## Systemd Services

All three are enabled and start on boot.

| Service | Command |
|---------|---------|
| `fossil.service` | `fossil server /var/fossil/repos --port 8081 --localhost --repolist --https --baseurl https://fossil.craftdesign.group --notfound /` |
| `caddy-fossil.service` | `caddy run --config /var/fossil/Caddyfile` |
| `cloudflared-fossil.service` | `cloudflared tunnel --config ~/.cloudflared/config.yml run` |

```bash
sudo systemctl status fossil caddy-fossil cloudflared-fossil
sudo systemctl restart fossil caddy-fossil cloudflared-fossil
sudo journalctl -u fossil -f
```

## Repositories

| Name | URL | Admin password |
|------|-----|----------------|
| edgesync | https://fossil.craftdesign.group/edgesync/ | set via /edgesync/setup_alist |
| go-libfossil | https://fossil.craftdesign.group/go-libfossil/ | set via /go-libfossil/setup_alist |

Admin user: `dmestas`

## Adding a Repository

```bash
# 1. Init on server
fossil init /var/fossil/repos/<name>.fossil

# 2. Apply the skin
python3 /var/fossil/skin/apply.py

# 3. Add a card to /var/fossil/www/index.html

# From local machine:
fossil clone https://dmestas@fossil.craftdesign.group/<name>/ <name>.fossil
# or push existing:
fossil remote add hub https://dmestas@fossil.craftdesign.group/<name>/
fossil push hub
```

## Skin

Dark theme matching the landing page. Design tokens:
- `--bg: #0f1117` / `--surface: #1a1d27` / `--border: #2a2d3e`
- `--accent: #6c8ebf` / `--text: #e2e8f0` / `--muted: #8892a4`
- Fonts: Inter (body), JetBrains Mono (code)

The skin lives in `/var/fossil/skin/` and must be written into each repo's
SQLite config table (keys: `css`, `header`, `footer`). After editing skin files, push with:

```bash
python3 << 'EOF'
import sqlite3, time, os, glob

REPOS = glob.glob("/var/fossil/repos/*.fossil")
SKIN_DIR = "/var/fossil/skin"
parts = {
    "css":    open(f"{SKIN_DIR}/css.txt").read(),
    "header": open(f"{SKIN_DIR}/header.txt").read(),
    "footer": open(f"{SKIN_DIR}/footer.txt").read(),
}
mtime = int(time.time())
for repo in REPOS:
    con = sqlite3.connect(repo)
    for name, value in parts.items():
        con.execute("INSERT OR REPLACE INTO config(name,value,mtime) VALUES(?,?,?)",
                    (name, value, mtime))
    con.commit()
    con.close()
    print(f"skinned {os.path.basename(repo)}")
EOF
```

## Cloudflare Tunnel

- Tunnel name: `fossil`
- Tunnel ID: `45ea54d2-1b49-496b-bc35-c01c45d95297`
- Config: `~/.cloudflared/config.yml`  (points to Caddy :8090)
- Credentials: `~/.cloudflared/45ea54d2-….json`
- DNS CNAME managed automatically by cloudflared

## Installed Versions

### fossil.craftdesign.group (hetzner-vps / 100.78.32.45)
- Fossil 2.28 — `/usr/local/bin/fossil` (built from source tarball)
- Caddy 2.9.1 — `/usr/local/bin/caddy`
- cloudflared 2026.3.0 — `/usr/local/bin/cloudflared`

### fossil-west.craftdesign.group (hetzner-west-us / 100.89.234.67)
- Fossil 2.23 — `/usr/local/bin/fossil` (built from source tarball)
- Caddy 2.11.2 — `/usr/local/bin/caddy`
- cloudflared 2026.3.0 — `/usr/local/bin/cloudflared`

## Known Gotchas

- Fossil skin keys are `css`/`header`/`footer` (no `skin:` prefix) — written directly to each repo's SQLite `config` table
- `--https` flag is required on `fossil server` when behind a reverse proxy — without it Fossil generates http:// redirect loops
- Skins are per-repo; adding a new repo requires running the apply script
- Port 8080 is taken by Coolify's Traefik proxy — Fossil runs on 8081
- The repo listing page (`/`) has no skin — it's replaced entirely by Caddy serving the static `index.html`
