Skip to content

Commit 7ccf8c5

Browse files
author
decolua
committed
# v0.4.36 (2026-05-13)
## Features - Add MiniMax TTS provider support (#1043) - Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry ## Improvements - Replace browser confirm dialogs with custom ConfirmModal (#1060) ## Fixes - Fix Docker `Cannot find module 'next'` error in standalone build - Restore /app/server.js in Docker standalone build (#1064, #1067) - Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B) - Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (#1080) - Fix zoom controls contrast in topology view (#1066)
1 parent 692d1fd commit 7ccf8c5

14 files changed

Lines changed: 589 additions & 287 deletions

File tree

.github/workflows/docker-publish.yml

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ on:
77
workflow_dispatch:
88

99
env:
10-
REGISTRY: ghcr.io
11-
IMAGE_NAME: ${{ github.repository }}
10+
GHCR_IMAGE: ghcr.io/${{ github.repository }}
11+
DOCKERHUB_IMAGE: decolua/9router
1212

1313
jobs:
1414
build-and-push:
@@ -22,18 +22,26 @@ jobs:
2222

2323
- uses: docker/setup-buildx-action@v3
2424

25-
- name: Log in to Container Registry
25+
- name: Log in to GHCR
2626
uses: docker/login-action@v3
2727
with:
28-
registry: ${{ env.REGISTRY }}
28+
registry: ghcr.io
2929
username: ${{ github.actor }}
3030
password: ${{ secrets.GITHUB_TOKEN }}
3131

32+
- name: Log in to Docker Hub
33+
uses: docker/login-action@v3
34+
with:
35+
username: ${{ secrets.DOCKERHUB_USERNAME }}
36+
password: ${{ secrets.DOCKERHUB_TOKEN }}
37+
3238
- name: Extract metadata
3339
id: meta
3440
uses: docker/metadata-action@v5
3541
with:
36-
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42+
images: |
43+
${{ env.GHCR_IMAGE }}
44+
${{ env.DOCKERHUB_IMAGE }}
3745
tags: |
3846
type=sha,prefix=
3947
type=semver,pattern={{version}}
@@ -47,8 +55,8 @@ jobs:
4755
push: true
4856
tags: ${{ steps.meta.outputs.tags }}
4957
labels: ${{ steps.meta.outputs.labels }}
50-
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
51-
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
58+
cache-from: type=registry,ref=${{ env.GHCR_IMAGE }}:buildcache
59+
cache-to: type=registry,ref=${{ env.GHCR_IMAGE }}:buildcache,mode=max
5260
platforms: linux/amd64,linux/arm64
5361
provenance: false
5462
sbom: false

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ source/*
5050
docs/*
5151
!docs/ARCHITECTURE.md
5252
test/*
53-
bin/*
5453
open-sse/test/*
5554
RM.vn.md
5655
RM.md

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
# v0.4.36 (2026-05-13)
2+
3+
## Features
4+
- Add MiniMax TTS provider support (#1043)
5+
- Docker images now published on both Docker Hub (`decolua/9router`) and GHCR — pull from your preferred registry
6+
7+
## Improvements
8+
- Replace browser confirm dialogs with custom ConfirmModal (#1060)
9+
10+
## Fixes
11+
- Fix Docker `Cannot find module 'next'` error in standalone build
12+
- Restore /app/server.js in Docker standalone build (#1064, #1067)
13+
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
14+
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (#1080)
15+
- Fix zoom controls contrast in topology view (#1066)
16+
117
# v0.4.33 (2026-05-12)
218

319
## Improvements

DOCKER.md

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,113 @@
11
# Docker
22

3-
This project ships with a `Dockerfile` for building and running 9Router in a container.
3+
Run 9Router in a container. Published image: [`decolua/9router`](https://hub.docker.com/r/decolua/9router) — multi-platform `linux/amd64` + `linux/arm64`.
44

5-
## Build image
5+
---
66

7-
```bash
8-
docker build -t 9router .
9-
```
7+
# 👤 For Users
108

11-
## Start container
9+
## Quick start
1210

1311
```bash
14-
docker run --rm \
12+
docker run -d \
1513
-p 20128:20128 \
1614
-v "$HOME/.9router:/app/data" \
1715
-e DATA_DIR=/app/data \
1816
--name 9router \
19-
9router
17+
decolua/9router:latest
2018
```
2119

22-
The app listens on port `20128` in the container.
20+
App listens on port `20128`. Open: http://localhost:20128
2321

24-
## What the volume does
22+
## Manage container
2523

2624
```bash
27-
-v "$HOME/.9router:/app/data" \
28-
-e DATA_DIR=/app/data
25+
docker logs -f 9router # view logs
26+
docker stop 9router # stop
27+
docker start 9router # start again
28+
docker rm -f 9router # remove
2929
```
3030

31-
`9router` stores its database at `path.join(DATA_DIR, "db.json")`.
32-
Without `DATA_DIR`, the app falls back to the current user's home directory (for example `~/.9router/db.json` on macOS/Linux). In the container, set `DATA_DIR=/app/data` so the bind mount is actually used.
31+
## Data persistence
3332

34-
With the example above, the database file is:
35-
36-
```text
37-
/app/data/db.json
33+
```bash
34+
-v "$HOME/.9router:/app/data" \
35+
-e DATA_DIR=/app/data
3836
```
3937

40-
and it is persisted on the host at:
38+
Without `DATA_DIR`, the app falls back to `~/.9router/` (macOS/Linux) or `%APPDATA%\9router\` (Windows). In the container, `DATA_DIR=/app/data` makes the bind mount work.
39+
40+
Data layout under `$DATA_DIR/`:
4141

4242
```text
43-
$HOME/.9router/db.json
43+
$DATA_DIR/
44+
├── db/
45+
│ ├── data.sqlite # main SQLite database
46+
│ └── backups/ # auto backups
47+
└── ... # certs, logs, runtime configs
4448
```
4549

46-
## Stop container
50+
Host path: `$HOME/.9router/db/data.sqlite`
51+
Container path: `/app/data/db/data.sqlite`
4752

48-
```bash
49-
docker stop 9router
50-
```
51-
52-
## Run in background
53+
## Optional env vars
5354

5455
```bash
5556
docker run -d \
5657
-p 20128:20128 \
5758
-v "$HOME/.9router:/app/data" \
5859
-e DATA_DIR=/app/data \
60+
-e PORT=20128 \
61+
-e HOSTNAME=0.0.0.0 \
62+
-e DEBUG=true \
5963
--name 9router \
60-
9router
64+
decolua/9router:latest
6165
```
6266

63-
## View logs
67+
## Update to latest
6468

6569
```bash
66-
docker logs -f 9router
70+
docker pull decolua/9router:latest
71+
docker rm -f 9router
72+
# re-run the quick start command
6773
```
6874

69-
## Optional environment variables
75+
---
7076

71-
You can override runtime env vars with `-e`.
77+
# 🛠 For Developers
7278

73-
Example:
79+
## Build image locally
7480

7581
```bash
76-
docker run --rm \
77-
-p 20128:20128 \
82+
# from repo root
83+
npm run docker:build
84+
```
85+
86+
Or directly:
87+
```bash
88+
cd app && docker build -t 9router .
89+
```
90+
91+
Run local build:
92+
```bash
93+
docker run --rm -p 20128:20128 \
7894
-v "$HOME/.9router:/app/data" \
7995
-e DATA_DIR=/app/data \
80-
-e PORT=20128 \
81-
-e HOSTNAME=0.0.0.0 \
82-
-e DEBUG=true \
83-
--name 9router \
8496
9router
8597
```
8698

87-
## Rebuild after code changes
99+
## Publish to Docker Hub (multi-platform)
100+
101+
Triggered automatically by `npm run cli:publish`. Manual:
88102

89103
```bash
90-
docker build -t 9router .
104+
# 1. Login once
105+
docker login
106+
107+
# 2. Build amd64 + arm64 + push (tag from app/cli/package.json version)
108+
npm run docker:publish
91109
```
92110

93-
Then restart the container.
111+
Tags pushed:
112+
- `decolua/9router:v{version}`
113+
- `decolua/9router:latest`

README.md

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
[![npm](https://img.shields.io/npm/v/9router.svg)](https://www.npmjs.com/package/9router)
1111
[![Downloads](https://img.shields.io/npm/dm/9router.svg)](https://www.npmjs.com/package/9router)
12+
[![Docker Hub](https://img.shields.io/docker/pulls/decolua/9router.svg?logo=docker&label=Docker%20Hub)](https://hub.docker.com/r/decolua/9router)
13+
[![GHCR](https://img.shields.io/badge/GHCR-decolua%2F9router-blue?logo=github)](https://github.com/decolua/9router/pkgs/container/9router)
1214
[![License](https://img.shields.io/npm/l/9router.svg)](https://github.com/decolua/9router/blob/main/LICENSE)
1315

1416
<a href="https://trendshift.io/repositories/22628" target="_blank"><img src="https://trendshift.io/api/badge/repositories/22628" alt="decolua%2F9router | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
@@ -1048,51 +1050,55 @@ pm2 startup
10481050

10491051
### Docker
10501052

1051-
```bash
1052-
# Build image (from repository root)
1053-
docker build -t 9router .
1053+
Published images (multi-platform `linux/amd64` + `linux/arm64`):
1054+
- Docker Hub: [`decolua/9router`](https://hub.docker.com/r/decolua/9router)
1055+
- GHCR: [`ghcr.io/decolua/9router`](https://github.com/decolua/9router/pkgs/container/9router)
1056+
1057+
**Quick start (use published image):**
10541058

1055-
# Run container (command used in current setup)
1059+
```bash
10561060
docker run -d \
10571061
--name 9router \
10581062
-p 20128:20128 \
1059-
--env-file /root/dev/9router/.env \
1060-
-v 9router-data:/app/data \
1061-
-v 9router-usage:/root/.9router \
1062-
9router
1063+
-v "$HOME/.9router:/app/data" \
1064+
-e DATA_DIR=/app/data \
1065+
decolua/9router:latest
10631066
```
10641067

1065-
Portable command (if you are already at repository root):
1068+
→ Open http://localhost:20128
1069+
1070+
**Build from source (dev):**
10661071

10671072
```bash
1068-
docker run -d \
1069-
--name 9router \
1070-
-p 20128:20128 \
1071-
--env-file ./.env \
1072-
-v 9router-data:/app/data \
1073-
-v 9router-usage:/root/.9router \
1074-
9router
1073+
git clone https://github.com/decolua/9router.git
1074+
cd 9router/app
1075+
docker build -t 9router .
1076+
docker run -d --name 9router -p 20128:20128 \
1077+
-v "$HOME/.9router:/app/data" -e DATA_DIR=/app/data 9router
10751078
```
10761079

1077-
Container defaults:
1080+
**Container defaults:**
10781081
- `PORT=20128`
10791082
- `HOSTNAME=0.0.0.0`
10801083

1081-
Useful commands:
1084+
**Useful commands:**
10821085

10831086
```bash
10841087
docker logs -f 9router
10851088
docker restart 9router
10861089
docker stop 9router && docker rm 9router
1090+
docker pull decolua/9router:latest # update to latest
10871091
```
10881092

1093+
**Data persistence:** `$HOME/.9router/db/data.sqlite` on host ↔ `/app/data/db/data.sqlite` in container.
1094+
10891095
### Environment Variables
10901096

10911097
| Variable | Default | Description |
10921098
|----------|---------|-------------|
10931099
| `JWT_SECRET` | `9router-default-secret-change-me` | JWT signing secret for dashboard auth cookie (**change in production**) |
10941100
| `INITIAL_PASSWORD` | `123456` | First login password when no saved hash exists |
1095-
| `DATA_DIR` | `~/.9router` | Main app database location (`db.json`) |
1101+
| `DATA_DIR` | `~/.9router` | Main app data location (SQLite at `$DATA_DIR/db/data.sqlite`) |
10961102
| `PORT` | framework default | Service port (`20128` in examples) |
10971103
| `HOSTNAME` | framework default | Bind host (Docker defaults to `0.0.0.0`) |
10981104
| `NODE_ENV` | runtime default | Set `production` for deploy |
@@ -1115,9 +1121,9 @@ Notes:
11151121

11161122
### Runtime Files and Storage
11171123

1118-
- Main app state: `${DATA_DIR}/db.json` (providers, combos, aliases, keys, settings), managed by `src/lib/localDb.js`.
1119-
- Usage history and logs: `${DATA_DIR}/usage.json` and `${DATA_DIR}/log.txt`, managed by `src/lib/usageDb.js`.
1120-
- Optional request/translator logs: `<repo>/logs/...` when `ENABLE_REQUEST_LOGS=true`.
1124+
- Main app state: `${DATA_DIR}/db/data.sqlite` (SQLite — providers, combos, aliases, keys, settings, usage history)
1125+
- Auto backups: `${DATA_DIR}/db/backups/`
1126+
- Optional request/translator logs: `<repo>/logs/...` when `ENABLE_REQUEST_LOGS=true`
11211127
- Both `${DATA_DIR}` and `~/.9router` resolve to the same location in a Docker container — the symlink `/root/.9router -> /app/data` is created at build time.
11221128

11231129
</details>
@@ -1228,7 +1234,7 @@ Notes:
12281234
- **Runtime**: Node.js 20+
12291235
- **Framework**: Next.js 16
12301236
- **UI**: React 19 + Tailwind CSS 4
1231-
- **Database**: LowDB (JSON file-based)
1237+
- **Database**: SQLite (better-sqlite3 / node:sqlite / sql.js fallback)
12321238
- **Streaming**: Server-Sent Events (SSE)
12331239
- **Auth**: OAuth 2.0 (PKCE) + JWT + API Keys
12341240

cli/README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
[![npm](https://img.shields.io/npm/v/9router.svg)](https://www.npmjs.com/package/9router)
88
[![Downloads](https://img.shields.io/npm/dm/9router.svg)](https://www.npmjs.com/package/9router)
9+
[![Docker Hub](https://img.shields.io/docker/pulls/decolua/9router.svg?logo=docker&label=Docker%20Hub)](https://hub.docker.com/r/decolua/9router)
10+
[![GHCR](https://img.shields.io/badge/GHCR-decolua%2F9router-blue?logo=github)](https://github.com/decolua/9router/pkgs/container/9router)
911
[![License](https://img.shields.io/npm/l/9router.svg)](https://github.com/decolua/9router/blob/main/LICENSE)
1012

1113
<a href="https://trendshift.io/repositories/22628" target="_blank"><img src="https://trendshift.io/api/badge/repositories/22628" alt="decolua%2F9router | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
@@ -35,7 +37,7 @@
3537

3638
## ⚡ Quick Start
3739

38-
**1. Install & run:**
40+
**Option 1 — npm (recommended for desktop):**
3941

4042
```bash
4143
npm install -g 9router
@@ -45,6 +47,16 @@ npm install -g 9router
4547
npx 9router
4648
```
4749

50+
**Option 2 — Docker (server/VPS):**
51+
52+
```bash
53+
docker run -d --name 9router -p 20128:20128 \
54+
-v "$HOME/.9router:/app/data" -e DATA_DIR=/app/data \
55+
decolua/9router:latest
56+
```
57+
58+
Published images: [Docker Hub](https://hub.docker.com/r/decolua/9router)[GHCR](https://github.com/decolua/9router/pkgs/container/9router) (multi-platform amd64/arm64).
59+
4860
🎉 Dashboard opens at `http://localhost:20128`
4961

5062
**2. Connect a FREE provider (no signup needed):**
@@ -88,8 +100,9 @@ Any tool supporting OpenAI/Claude-compatible API works.
88100

89101
## 💾 Data Location
90102

91-
- **macOS/Linux**: `~/.9router/db.json`
92-
- **Windows**: `%APPDATA%/9router/db.json`
103+
- **macOS/Linux**: `~/.9router/db/data.sqlite`
104+
- **Windows**: `%APPDATA%/9router/db/data.sqlite`
105+
- **Docker**: `/app/data/db/data.sqlite` (mount `$HOME/.9router` to persist)
93106

94107
---
95108

0 commit comments

Comments
 (0)