update readme
MREADME.md
@@ -1,28 +1,29 @@
# Hearthforge 

A self-hosted git forge designed to host your own repositories, but allow others to interact with them via issues and patch proposals.
Frontend works without any JS at all enabled, just required for WebAuthn (with graceful fallback to password-only).
A self-hosted git forge designed to host your own repositories while allowing others to interact with them via issues and patch proposals.
The frontend works without JavaScript — JS is only required for WebAuthn, with a graceful fallback to password-only auth.
## Features
- **Repository browser** — file tree, directly edit single files, blob view, commit log, markdown rendering, media previews
- **Issues** — create, comment, react
- **Patches** — submit git .patch files for review & comments. Admin can merge applicable patches directly into the repository.
- **Templates** — issue/patch templates
- **Releases** — releases with source archives, extra uploaded assets, and optional tag creation
- **Repository browser** — integrated file tree, single-file editing, blob view, commit log, markdown rendering, media previews
- **Issues & patches** — create, comment, react with emoji; submit git `.patch` files for review and merge them directly from the UI
- **Labels** — per-repo labels with custom colors; optionally allow users to label their own issues and patches
- **Templates** — issue and patch templates per repository
- **Releases** — create releases with source archives (zip/tar.gz), uploaded assets, and optional tag creation
- **SSH push/pull** — built-in SSH server, no external git daemon needed
- **Auth** — password login or passkeys (WebAuthn/FIDO2)
- **Commit signing** — merged patches and filed edited through the UI are automatically signed and verification badges are shown in the commit list view
- **Optional registration** — others can create accounts to file issues and patches; can be disabled, or put in queue mode where the admin manually approves new accounts.
- **Commit signing** — patches merged and files edited through the UI are automatically signed; verification badges shown in the commit log
- **Registration control** — open registration, disabled, or queue mode where the admin manually approves new accounts
## Stack
- [Bun](https://bun.sh) — runtime and package manager
- [ElysiaJS](https://elysiajs.com) — HTTP framework
- SQLite — single-file database via Kysely
- Server-side JSX via @kitajs/html (no client-side framework)
- Shiki — syntax highlighting
- [SQLite](https://www.sqlite.org) — single-file database via [Kysely](https://kysely.dev)
- [@kitajs/html](https://github.com/kitajs/html) — server-side JSX (no client-side framework)
- [Shiki](https://shiki.style) — syntax highlighting
## Running
@@ -33,71 +34,77 @@ bun run start # http://localhost:3000, SSH on port 2222
```
Default admin credentials: `admin` / `changeme`
Set the `ADMIN_PASSWORD` environment variable **before** running `db:init` to choose your own.
## Manual repository interactions
### Docker / Podman
Existing Repositories can be copied manually to the `data/repos` directory.
Non-bare repos are automatically converted to bare repos on startup, discarding uncomitted changes and worktrees.
Pushing directly to the repositories (i.e. not through the bundled http/ssh endpoints) should generally works as well.
A `Containerfile` and `compose.yml` are provided:
### Docker / Podman
Container image and compose file are provided.
```bash
docker compose up # or
podman compose up
docker compose up # or podman compose up
```
### Configuration
The container stores all persistent data (repos, database, avatars, releases, SSH host key) under `/data` — mount a volume there to keep it across restarts.
All settings are environment variables:
### Manual repository import
Existing repositories can be copied into the `data/repos` directory.
Non-bare repos are automatically converted to bare repos on startup (uncommitted changes and worktrees are discarded).
Pushing directly to the on-disk repositories (bypassing the bundled HTTP/SSH endpoints) generally works as well.
| Variable | Default | Description |
|-------------------------|----------------------------------|--------------------------------------------------------------------------------|
| `PORT` | `3000` | HTTP port |
| `SSH_PORT` | `2222` | SSH port |
| `DATA_DIR` | `./data` | Repos, database, uploads |
| `ADMIN_PASSWORD` | `changeme` | Initial admin password |
| `OWNER_DISPLAY_NAME` | `Admin` | Display name for the owner |
| `BASE_URL` | `http://localhost:3000` | Used in clone URLs and links |
| `REGISTRATION_TYPE` | `enabled` | Registration mode: `enabled`, `disabled`, or `queue` (requires admin approval) |
| `REGISTER_QUESTION` | _(empty)_ | Question shown on the registration form when `REGISTRATION_TYPE=queue` |
| `MAX_UPLOAD_BYTES` | `10485760` | Max request body size (any uploads/requests) |
| `MAX_USER_UPLOAD_BYTES` | `2097152` | Max request body size (user uploads) |
| `INLINE_MAX_BYTES` | `524288` | Max file size to render inline in the file view |
| `SSH_DISABLED` | `0` | Disable the embedded SSH-server |
| `TRUSTED_PROXY` | `0` | Trust `X-Forwarded-For` |
| `RATE_LIMIT_DISABLED` | `0` | Set to `1` to disable rate limiting |
| `HIGHLIGHT_WORKERS` | `4` | Number of syntax highlighting workers* |
| `COMMITTER_NAME` | `$OWNER_DISPLAY_NAME` | Git committer name used when merging patches or editing files through the UI |
| `COMMITTER_EMAIL` | `$OWNER_DISPLAY_NAME@<hostname>` | same as above but for email |
| `MAX_TITLE_BYTES` | `500` | Max length for titles (issues, patches, releases) |
| `MAX_TEXT_BODY_BYTES` | `100000` | Max length for text bodies (issue/patch descriptions, comments, release notes) |
| `MAX_USERNAME_BYTES` | `64` | Max length for usernames at registration |
| `MAX_PASSWORD_BYTES` | `1024` | Max length for passwords at registration and password change |
\* More workers mean more CPU cores can be used to parallelize highlighting of files.
Because of the language grammars, which can't be shared across workers, the memory usage per worker is quite high, at about 200MB.
So be careful when increasing this.
### SSH access
Add your public key under Settings → SSH keys, then clone with:
Add your public key under **Settings → SSH keys**, then:
```
git clone ssh://git@localhost:{SSH_PORT}/{REPO_NAME}
git clone ssh://git@localhost:2222/REPO_NAME
```
Pushing is also supported for the admin.
Pushing is supported for the admin.
### Configuration
All settings are environment variables:
| Variable | Default | Description |
|------------------------------|----------------------------------|-------------------------------------------------------------|
| `PORT` | `3000` | HTTP port |
| `SSH_PORT` | `2222` | SSH port |
| `DATA_DIR` | `./data` | Repos, database, uploads |
| `ADMIN_PASSWORD` | `changeme` | Initial admin password (only used by `db:init`) |
| `OWNER_DISPLAY_NAME` | `Admin` | Display name for the owner |
| `BASE_URL` | `http://localhost:$PORT` | Used in clone URLs and links |
| `REGISTRATION_TYPE` | `enabled` | `enabled`, `disabled`, or `queue` (admin approval) |
| `REGISTER_QUESTION` | _(empty)_ | Question shown on the registration form in `queue` mode |
| `MAX_UPLOAD_BYTES` | `10485760` | Max request body size (10 MB) |
| `MAX_USER_UPLOAD_BYTES` | `2097152` | Max upload size for non-admin users (2 MB) |
| `INLINE_MAX_BYTES` | `524288` | Max file size rendered inline in the code view (512 KB) |
| `SSH_DISABLED` | `0` | Set to `1` to disable the embedded SSH server |
| `TRUSTED_PROXY` | `0` | Trust `X-Forwarded-For` headers |
| `RATE_LIMIT_DISABLED` | `0` | Set to `1` to disable rate limiting |
| `HIGHLIGHT_WORKERS` | `4` | Syntax highlighting worker threads \* |
| `COMMITTER_NAME` | `$OWNER_DISPLAY_NAME` | Git committer name for merges and UI edits |
| `COMMITTER_EMAIL` | `$OWNER_DISPLAY_NAME@<hostname>` | Git committer email for merges and UI edits |
| `SSH_HOST_KEY_PATH` | `$DATA_DIR/ssh_host_key` | Path to the SSH host key (auto-generated if missing) |
| `EXTRA_ALLOWED_SIGNERS_PATH` | _(empty)_ | Additional git allowed-signers file for commit verification |
| `MAX_TITLE_BYTES` | `500` | Max length for titles (issues, patches, releases) |
| `MAX_TEXT_BODY_BYTES` | `100000` | Max length for text bodies (descriptions, comments, notes) |
| `MAX_USERNAME_BYTES` | `64` | Max username length at registration |
| `MAX_PASSWORD_BYTES` | `1024` | Max password length |
\* Each highlighting worker loads its own copy of the language grammars and uses ~200 MB of memory. Increase with care.
## Development
```bash
bun run dev # watch mode
bun run lint # Biome lint
bun run format # Biome format
bun run test # Run E2E and unit tests (don't use bun test directly, it doesn't respect the timeout)
bun run dev # watch mode with auto-restart
bun run lint # lint with Biome
bun run format # format with Biome
bun run test # E2E and unit tests (uses Playwright; don't call bun test directly)
```
## Roadmap
- Use [git-bug](https://github.com/git-bug/git-bug) for issue tracking instead of custom implementation
- more repository manipulation through the UI, e.g. file/directory/branch creation, renaming and deletion
- remove test retry logic when bun doesn't randomly get stuck anymore
- More repository manipulation through the UI — file/directory/branch creation, renaming, and deletion
- Remove test retry logic once Bun no longer randomly stalls