fix: clarify docker dashboard auth setup guidance

This commit is contained in:
Tam Nhu Tran
2026-04-15 21:15:41 -04:00
parent 1b43d76841
commit 8edb56331e
7 changed files with 77 additions and 49 deletions
+2
View File
@@ -62,6 +62,8 @@ environment:
CCS_DASHBOARD_PASSWORD_HASH: "<bcrypt-hash>"
```
Running `ccs config auth setup` on the outer host shell updates that machine's own `~/.ccs`, not the Docker volume mounted into `ccs-cliproxy`. For the integrated stack, configure auth inside the container or provide the auth env vars in Compose.
Generate a bcrypt hash:
```bash
+8
View File
@@ -12,6 +12,12 @@ Authentication is **disabled by default** for backward compatibility. Use the CL
CCS does **not** ship a default dashboard username or password. When someone opens the dashboard from a non-loopback/IP address before auth is enabled, the UI now shows a setup state instead of an ambiguous login form. The host owner must run `ccs config auth setup`, or the user should switch back to the localhost URL if they are on the same machine.
Docker note: the integrated `ccs docker` stack stores its config inside the running container volume, not in the outer shell's `~/.ccs`. For Docker deployments, run auth setup inside the container:
```bash
docker exec -it ccs-cliproxy ccs config auth setup
```
When auth stays disabled, CCS now applies a localhost-only fallback on sensitive management endpoints. Remote devices can still open the dashboard UI when you intentionally bind it beyond loopback, but write-capable routes such as AI Provider management and CLIProxy auth/status helpers reject non-loopback requests until you enable dashboard auth.
## Account Context Modes (Related Feature)
@@ -193,6 +199,8 @@ dashboard_auth:
Run `ccs config auth setup` to configure credentials.
If you are using the integrated Docker stack, run that command inside `ccs-cliproxy`. Running it on the outer host shell updates a different config directory and will not unlock the running dashboard.
### Forgot password
Run `ccs config auth setup` again to set a new password.
+1
View File
@@ -41,6 +41,7 @@ All major modularization work is complete. The codebase evolved from monolithic
### Recent Fixes
- **2026-04-15**: **#1010** Remote dashboard auth guidance now explains the Docker boundary explicitly. The readonly banner, remote login/setup card, and dashboard-auth docs now tell users that integrated Docker deployments keep config inside the running `ccs-cliproxy` container volume, so `ccs config auth setup` must run there rather than in the outer host shell.
- **2026-04-14**: **#991** CCS now auto-routes Claude-target settings profiles that use OpenAI-compatible endpoints through a local Anthropic-compatible proxy instead of sending raw Anthropic `/v1/messages` traffic directly to chat-completions backends. The `ccs proxy` command now supports `start`, `status`, `activate`, and `stop` with explicit host binding, shell-aware activation helpers, and a fuller local runtime env contract. The proxy surface now exposes `GET /`, `/health`, `/v1/models`, and `/v1/messages`, logs routing decisions into CCS structured logs, supports Anthropic image blocks plus request-time `profile:model` overrides, and adds config-driven scenario routing (`background`, `think`, `longContext`, `webSearch`) on top of the compatible-profile path. Coverage now includes request routing, rate-limit/timeout/empty-upstream failures, chunked tool-call streaming, and disconnect cleanup alongside the existing unit, integration, and e2e suites.
- **2026-04-10**: **#765** `/providers` now includes a first-class Hugging Face preset for API Profiles. CCS exposes Hugging Face Inference Providers through the existing OpenAI-compatible profile flow with the official router endpoint `https://router.huggingface.co/v1`, a short `hf` default profile name, and `hf` preset alias support for both the dashboard chooser and `ccs api create --preset hf`.
- **2026-04-10**: **#944** Image Analysis auth readiness no longer collapses to native Read when merged runtime-status dependency overrides include a missing initializer value. CCS now preserves default dependency functions when override entries are `undefined`, still reads token-backed auth status directly in the local readiness path, and includes regression coverage for the missing-initializer case that previously surfaced as `deps.initializeAccounts is not a function`.
+52 -45
View File
@@ -51,7 +51,7 @@ const resources = {
remoteGuardLabel: 'Remote access guard',
loading: 'Checking dashboard access…',
loginDescription:
'Use the username and password configured on the host to access the dashboard.',
'Use the username and password configured for this CCS instance to access the dashboard.',
username: 'Username',
password: 'Password',
usernamePlaceholder: 'Enter username',
@@ -63,21 +63,22 @@ const resources = {
darkMode: 'Dark',
noDefaultCredentials: 'No default credentials ship with CCS.',
credentialsHint:
'Credentials are created on the host with `ccs config auth setup`, then used here.',
remoteSetupTitle: 'Remote access needs host setup',
'Credentials are created with `ccs config auth setup` on the CCS host. Docker deployments must run that command inside the container.',
remoteSetupTitle: 'Remote access needs auth setup',
remoteSetupDescription:
'This dashboard was opened from a non-local address, but dashboard auth is not enabled on the host yet.',
'This dashboard was opened from a non-local address, but dashboard auth is not enabled for this CCS instance yet.',
incompleteSetupDescription:
'Dashboard auth is turned on, but the host setup is incomplete. Finish the host configuration before signing in.',
'Dashboard auth is turned on, but the setup is incomplete. Finish the configuration for this CCS instance before signing in.',
safetyNoteRemote:
'Remote management stays locked until the host owner enables dashboard auth.',
'Remote management stays locked until the CCS host owner enables dashboard auth.',
safetyNoteLocal:
'If you are on the same machine, the localhost URL remains the simplest path in.',
safetyNoteSession:
'Successful sign-ins create an HTTP-only session that stays scoped to this host.',
hostStepTitle: 'On the host machine',
'Successful sign-ins create an HTTP-only session that stays scoped to this dashboard host.',
hostStepTitle: 'On the CCS host',
hostStepDescription:
'Create or re-enable dashboard credentials, then reopen this page from the remote device.',
dockerStepDescription: 'Docker deployment? Run the setup inside the running container:',
localStepTitle: 'If this is your machine',
localStepDescription:
'Open the localhost URL printed by `ccs config` instead of the LAN or Tailscale address.',
@@ -1543,12 +1544,13 @@ const resources = {
},
localhostDisclaimer: {
remoteReadonlyAuthDisabledLong:
'Remote dashboard access is read-only because dashboard auth is currently disabled on the host. Re-enable dashboard auth on the host to unlock remote changes.',
'Remote dashboard access is read-only because dashboard auth is currently disabled for this CCS instance. Re-enable it on the CCS host. Docker deployments must do that inside the running container.',
remoteReadonlyAuthDisabledShort:
'Remote dashboard is read-only until dashboard auth is re-enabled on the host.',
'Remote dashboard is read-only until dashboard auth is re-enabled for this CCS instance.',
remoteReadonlySetupLong:
'Remote dashboard access is read-only until you run ccs config auth setup on the host.',
remoteReadonlySetupShort: 'Remote dashboard is read-only until host auth is configured.',
'Remote dashboard access is read-only until you run ccs config auth setup for this CCS instance. Docker deployments must run it inside the container.',
remoteReadonlySetupShort:
'Remote dashboard is read-only until auth is configured for this CCS instance.',
localLong: 'This dashboard runs locally. All data stays on your machine.',
localShort: 'Local dashboard - data stays on your device.',
dismiss: 'Dismiss disclaimer',
@@ -2530,16 +2532,18 @@ const resources = {
lightMode: '浅色',
darkMode: '深色',
noDefaultCredentials: 'CCS 不提供默认用户名或密码。',
credentialsHint: '凭据需要在主机上通过 `ccs config auth setup` 创建,然后在这里使用。',
remoteSetupTitle: '远程访问需要在主机上完成设置',
remoteSetupDescription: '当前通过非本机地址打开控制台,但主机尚未启用控制台认证。',
credentialsHint:
'凭据需要在 CCS 主机上通过 `ccs config auth setup` 创建;Docker 部署必须在容器内运行该命令。',
remoteSetupTitle: '远程访问需要完成认证设置',
remoteSetupDescription: '当前通过非本机地址打开控制台,但此 CCS 实例尚未启用控制台认证。',
incompleteSetupDescription:
'控制台认证已开启,但主机上的设置尚未完成。请先完成主机配置再登录。',
safetyNoteRemote: '在主机所有者启用控制台认证前,远程管理会保持锁定。',
'控制台认证已开启,但设置尚未完成。请先为此 CCS 实例完成配置再登录。',
safetyNoteRemote: '在 CCS 主机所有者启用控制台认证前,远程管理会保持锁定。',
safetyNoteLocal: '如果你就在这台机器上,使用 localhost 地址始终是最直接的方式。',
safetyNoteSession: '登录成功后会创建仅限此主机的 HTTP-only 会话。',
hostStepTitle: '在主机上操作',
safetyNoteSession: '登录成功后会创建仅限此控制台主机的 HTTP-only 会话。',
hostStepTitle: '在 CCS 主机上操作',
hostStepDescription: '创建或重新启用控制台凭据,然后再从远程设备重新打开此页面。',
dockerStepDescription: '如果是 Docker 部署,请在正在运行的容器内执行:',
localStepTitle: '如果这就是你的机器',
localStepDescription:
'请使用 `ccs config` 输出的 localhost 地址,而不是局域网或 Tailscale 地址。',
@@ -3930,10 +3934,11 @@ const resources = {
},
localhostDisclaimer: {
remoteReadonlyAuthDisabledLong:
'远程控制台当前为只读,因为主机未启用控制台认证。请在主机上重新启用控制台认证以解锁远程编辑。',
remoteReadonlyAuthDisabledShort: '远程控制台为只读,直到主机重新启用控制台认证。',
remoteReadonlySetupLong: '远程控制台为只读,直到在主机上运行 ccs config auth setup。',
remoteReadonlySetupShort: '远程控制台为只读,直到完成主机认证配置。',
'远程控制台当前为只读,因为此 CCS 实例未启用控制台认证。请在 CCS 主机上重新启用;Docker 部署需要在运行中的容器内完成。',
remoteReadonlyAuthDisabledShort: '远程控制台为只读,直到此 CCS 实例重新启用控制台认证。',
remoteReadonlySetupLong:
'远程控制台为只读,直到为此 CCS 实例运行 ccs config auth setup。Docker 部署必须在容器内运行该命令。',
remoteReadonlySetupShort: '远程控制台为只读,直到为此 CCS 实例完成认证配置。',
localLong: '本控制台在本地运行,所有数据保留在本机。',
localShort: '本地控制台 - 数据保留在本机。',
dismiss: '关闭提示',
@@ -4884,21 +4889,22 @@ const resources = {
darkMode: 'Tối',
noDefaultCredentials: 'CCS không có sẵn tài khoản hay mật khẩu mặc định.',
credentialsHint:
'Thông tin đăng nhập được tạo trên máy host bằng `ccs config auth setup`, rồi dùng tại đây.',
remoteSetupTitle: 'Truy cập từ xa cần được thiết lập trên máy host',
'Thông tin đăng nhập được tạo bằng `ccs config auth setup` trên máy CCS host. Với Docker, phải chạy lệnh đó bên trong container.',
remoteSetupTitle: 'Truy cập từ xa cần thiết lập xác thực',
remoteSetupDescription:
'Bảng điều khiển này đang được mở từ một địa chỉ không phải localhost, nhưng máy host chưa bật dashboard auth.',
'Bảng điều khiển này đang được mở từ một địa chỉ không phải localhost, nhưng phiên bản CCS này chưa bật dashboard auth.',
incompleteSetupDescription:
'Dashboard auth đã được bật nhưng cấu hình trên máy host vẫn chưa hoàn tất. Hãy hoàn thành cấu hình trước khi đăng nhập.',
'Dashboard auth đã được bật nhưng cấu hình vẫn chưa hoàn tất. Hãy hoàn thành cấu hình cho phiên bản CCS này trước khi đăng nhập.',
safetyNoteRemote:
'Quản trị từ xa sẽ tiếp tục bị khóa cho tới khi chủ máy host bật dashboard auth.',
'Quản trị từ xa sẽ tiếp tục bị khóa cho tới khi chủ máy CCS host bật dashboard auth.',
safetyNoteLocal:
'Nếu bạn đang ngồi ngay trên máy đó, đường localhost vẫn là cách đơn giản nhất để vào.',
safetyNoteSession:
'Sau khi đăng nhập thành công, phiên HTTP-only sẽ chỉ có hiệu lực trên đúng máy host này.',
hostStepTitle: 'Trên máy host',
'Sau khi đăng nhập thành công, phiên HTTP-only sẽ chỉ có hiệu lực trên đúng máy chủ dashboard này.',
hostStepTitle: 'Trên máy CCS host',
hostStepDescription:
'Tạo hoặc bật lại thông tin đăng nhập cho dashboard, rồi mở lại trang này từ thiết bị từ xa.',
dockerStepDescription: 'Nếu dùng Docker, hãy chạy lệnh này bên trong container đang chạy:',
localStepTitle: 'Nếu đây là máy của bạn',
localStepDescription:
'Hãy mở URL localhost mà `ccs config` in ra, thay vì địa chỉ LAN hoặc Tailscale.',
@@ -6380,13 +6386,13 @@ const resources = {
},
localhostDisclaimer: {
remoteReadonlyAuthDisabledLong:
'Dashboard từ xa ở chế độ chỉ đọc vì dashboard auth đang bị tắt trên máy host. Bật lại dashboard auth trên máy host để mở khóa thay đổi từ xa.',
'Dashboard từ xa đang ở chế độ chỉ đọc vì dashboard auth đang bị tắt cho phiên bản CCS này. Hãy bật lại trên máy CCS host; với Docker, việc đó phải được thực hiện bên trong container đang chạy.',
remoteReadonlyAuthDisabledShort:
'Dashboard từ xa chỉ đọc cho đến khi dashboard auth được bật lại trên máy host.',
'Dashboard từ xa chỉ đọc cho đến khi dashboard auth được bật lại cho phiên bản CCS này.',
remoteReadonlySetupLong:
'Dashboard từ xa chỉ đọc cho đến khi bạn chạy ccs config auth setup trên máy host.',
'Dashboard từ xa chỉ đọc cho đến khi bạn chạy ccs config auth setup cho phiên bản CCS này. Với Docker, hãy chạy lệnh đó bên trong container.',
remoteReadonlySetupShort:
'Dashboard từ xa chỉ đọc cho đến khi auth được cấu hình trên máy host.',
'Dashboard từ xa chỉ đọc cho đến khi auth được cấu hình cho phiên bản CCS này.',
localLong: 'Dashboard này chạy cục bộ. Toàn bộ dữ liệu nằm trên máy của bạn.',
localShort: 'Dashboard cục bộ - dữ liệu nằm trên thiết bị của bạn.',
dismiss: 'Bỏ qua thông báo',
@@ -7346,20 +7352,21 @@ const resources = {
darkMode: 'ダーク',
noDefaultCredentials: 'CCS にデフォルトの認証情報はありません。',
credentialsHint:
'認証情報はホストで `ccs config auth setup` を実行して作成し、ここで使用します。',
remoteSetupTitle: 'リモートアクセスにはホスト側の設定が必要です',
'認証情報は CCS ホストで `ccs config auth setup` を実行して作成します。Docker では、そのコマンドをコンテナ内で実行する必要があります。',
remoteSetupTitle: 'リモートアクセスには認証設定が必要です',
remoteSetupDescription:
'このダッシュボードはローカル以外のアドレスから開かれていますが、ホストでダッシュボード認証がまだ有効になっていません。',
'このダッシュボードはローカル以外のアドレスから開かれていますが、この CCS インスタンスではダッシュボード認証がまだ有効になっていません。',
incompleteSetupDescription:
'ダッシュボード認証は有効ですが、ホスト側の設定が未完了です。サインイン前に設定を完了してください。',
'ダッシュボード認証は有効ですが、設定が未完了です。この CCS インスタンスの設定を完了してからサインインしてください。',
safetyNoteRemote:
'ホスト管理者がダッシュボード認証を有効にするまで、リモート管理はロックされたままです。',
'CCS ホスト管理者がダッシュボード認証を有効にするまで、リモート管理はロックされたままです。',
safetyNoteLocal: '同じマシン上にいる場合は、localhost の URL を使うのが最も簡単です。',
safetyNoteSession:
'サインインに成功すると、このホストに限定された HTTP-only セッションが作成されます。',
hostStepTitle: 'ホスト側で行うこと',
'サインインに成功すると、このダッシュボードホストに限定された HTTP-only セッションが作成されます。',
hostStepTitle: 'CCS ホスト側で行うこと',
hostStepDescription:
'ダッシュボード認証情報を作成または再有効化してから、このページをリモート端末で開き直してください。',
dockerStepDescription: 'Docker の場合は、実行中のコンテナ内で次を実行してください:',
localStepTitle: 'もしこのマシンを使っているなら',
localStepDescription:
'`ccs config` が表示する localhost の URL を使い、LAN や Tailscale のアドレスは避けてください。',
@@ -9254,13 +9261,13 @@ const resources = {
},
localhostDisclaimer: {
remoteReadonlyAuthDisabledLong:
'ホストでダッシュボード認証が無効になっているため、リモートダッシュボードは読み取り専用です。リモートでの変更を有効にするには、ホスト側でダッシュボード認証を再度有効にしてください。',
'この CCS インスタンスでダッシュボード認証が無効になっているため、リモートダッシュボードは読み取り専用です。CCS ホスト側で再度有効にしてください。Docker では、実行中のコンテナ内で行う必要があります。',
remoteReadonlyAuthDisabledShort:
'ホストでダッシュボード認証が再有効化されるまで、リモートダッシュボードは読み取り専用です。',
'この CCS インスタンスでダッシュボード認証が再有効化されるまで、リモートダッシュボードは読み取り専用です。',
remoteReadonlySetupLong:
'ホストで ccs config auth setup を実行するまで、リモートダッシュボードは読み取り専用です。',
'この CCS インスタンスで ccs config auth setup を実行するまで、リモートダッシュボードは読み取り専用です。Docker では、そのコマンドをコンテナ内で実行してください。',
remoteReadonlySetupShort:
'ホストの認証が設定されるまで、リモートダッシュボードは読み取り専用です。',
'この CCS インスタンスの認証が設定されるまで、リモートダッシュボードは読み取り専用です。',
localLong:
'このダッシュボードはローカルで動作しています。データはすべてお使いのマシンに残ります。',
localShort: 'ローカルダッシュボード - データはお使いのデバイスに保存されます。',
+6
View File
@@ -207,6 +207,12 @@ export function LoginPage() {
<code className="mt-4 block rounded-xl border border-border/70 bg-muted/60 px-3 py-2 font-mono text-[13px] text-foreground">
ccs config auth setup
</code>
<p className="mt-3 text-xs leading-5 text-muted-foreground">
{t('auth.dockerStepDescription')}
</p>
<code className="mt-2 block rounded-xl border border-border/70 bg-muted/60 px-3 py-2 font-mono text-[13px] text-foreground">
docker exec -it ccs-cliproxy ccs config auth setup
</code>
</div>
<div className="rounded-2xl border border-border/70 bg-background/75 p-4">
@@ -42,7 +42,7 @@ describe('LocalhostDisclaimer', () => {
expect(
screen.getByText(
'Remote dashboard access is read-only until you run ccs config auth setup on the host.'
'Remote dashboard access is read-only until you run ccs config auth setup for this CCS instance. Docker deployments must run it inside the container.'
)
).toBeVisible();
expect(screen.queryByLabelText('Dismiss disclaimer')).toBeNull();
@@ -60,7 +60,7 @@ describe('LocalhostDisclaimer', () => {
expect(
screen.getByText(
'Remote dashboard access is read-only because dashboard auth is currently disabled on the host. Re-enable dashboard auth on the host to unlock remote changes.'
'Remote dashboard access is read-only because dashboard auth is currently disabled for this CCS instance. Re-enable it on the CCS host. Docker deployments must do that inside the running container.'
)
).toBeVisible();
expect(screen.queryByLabelText('Dismiss disclaimer')).toBeNull();
+6 -2
View File
@@ -59,7 +59,7 @@ describe('LoginPage', () => {
await waitFor(() => {
expect(navigateMock).toHaveBeenCalledWith('/settings', { replace: true });
});
expect(screen.queryByRole('heading', { name: 'Remote access needs host setup' })).toBeNull();
expect(screen.queryByRole('heading', { name: 'Remote access needs auth setup' })).toBeNull();
});
it('renders the incomplete setup copy when auth is enabled without credentials', () => {
@@ -80,7 +80,7 @@ describe('LoginPage', () => {
expect(
screen.getAllByText(
'Dashboard auth is turned on, but the host setup is incomplete. Finish the host configuration before signing in.'
'Dashboard auth is turned on, but the setup is incomplete. Finish the configuration for this CCS instance before signing in.'
)
).not.toHaveLength(0);
expect(
@@ -88,6 +88,10 @@ describe('LoginPage', () => {
'Create or re-enable dashboard credentials, then reopen this page from the remote device.'
)
).toBeVisible();
expect(
screen.getByText('Docker deployment? Run the setup inside the running container:')
).toBeVisible();
expect(screen.getByText('docker exec -it ccs-cliproxy ccs config auth setup')).toBeVisible();
expect(screen.queryByLabelText('Username')).not.toBeInTheDocument();
});