catence 0.2.1-beta.2 → 0.2.1-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/config.example.json +1 -0
- package/dist/contracts/release.d.ts +1 -1
- package/dist/contracts/release.js +1 -1
- package/dist/contracts/runtime.d.ts +4 -0
- package/dist/core/query/catalog.d.ts +1 -1
- package/dist/core/query/catalog.js +16 -10
- package/dist/core/query/repository.d.ts +1 -1
- package/dist/core/query/repository.js +31 -2
- package/dist/core/retrieval/index.js +23 -5
- package/dist/core/runtime/athlete-file.d.ts +49 -0
- package/dist/core/runtime/athlete-file.js +177 -0
- package/dist/core/runtime/configuration.js +2 -0
- package/dist/core/runtime/console-accounts.d.ts +52 -0
- package/dist/core/runtime/console-accounts.js +103 -0
- package/dist/core/runtime/mcp-keys.d.ts +65 -0
- package/dist/core/runtime/mcp-keys.js +171 -0
- package/dist/core/runtime/oauth/model.d.ts +44 -0
- package/dist/core/runtime/oauth/model.js +345 -0
- package/dist/core/runtime/oauth/public-client.d.ts +28 -0
- package/dist/core/runtime/oauth/public-client.js +35 -0
- package/dist/core/runtime/oauth/settings.d.ts +33 -0
- package/dist/core/runtime/oauth/settings.js +96 -0
- package/dist/core/runtime/secrets.d.ts +15 -0
- package/dist/core/runtime/secrets.js +48 -7
- package/dist/elt/application/autosync.d.ts +71 -0
- package/dist/elt/application/autosync.js +173 -0
- package/dist/elt/application/detached-sync.d.ts +7 -2
- package/dist/elt/application/detached-sync.js +10 -4
- package/dist/elt/application/management.d.ts +7 -0
- package/dist/elt/application/management.js +103 -7
- package/dist/elt/ingestion/importer.d.ts +11 -1
- package/dist/elt/ingestion/importer.js +63 -14
- package/dist/elt/ingestion/providers/intervals.d.ts +7 -0
- package/dist/elt/ingestion/providers/intervals.js +21 -2
- package/dist/elt/normalization/normalizers.js +122 -0
- package/dist/elt/storage/database.d.ts +6 -1
- package/dist/elt/storage/database.js +7 -1
- package/dist/elt/storage/migrations.js +45 -1
- package/dist/elt/storage/progress-sidecar.d.ts +9 -0
- package/dist/elt/storage/progress-sidecar.js +31 -16
- package/dist/interfaces/cli/commands.js +117 -2
- package/dist/interfaces/http/cli-options.d.ts +64 -1
- package/dist/interfaces/http/cli-options.js +214 -2
- package/dist/interfaces/http/oauth.d.ts +16 -0
- package/dist/interfaces/http/oauth.js +228 -0
- package/dist/interfaces/http/server.d.ts +60 -1
- package/dist/interfaces/http/server.js +810 -16
- package/dist/interfaces/mcp/main.js +35 -3
- package/dist/interfaces/mcp/server.d.ts +21 -1
- package/dist/interfaces/mcp/server.js +107 -21
- package/dist/runtime/index.d.ts +15 -3
- package/dist/runtime/index.js +9 -2
- package/docs/architecture.md +1 -1
- package/docs/configuration.md +76 -6
- package/docs/deployment/docker.md +173 -15
- package/docs/deployment/local-console.md +132 -21
- package/docs/deployment/local-mcp.md +190 -11
- package/docs/development.md +5 -3
- package/docs/llm-providers.md +4 -4
- package/package.json +11 -4
- package/pyproject.toml +1 -1
- package/python/catence/providers/garmin/cli.py +55 -7
- package/python/catence/providers/garmin/progress.py +71 -35
- package/python/catence/providers/garmin/staging.py +11 -4
- package/python/catence/providers/garmin/worker.py +420 -206
- package/uv.lock +3 -3
|
@@ -26,6 +26,8 @@ explained in the [beta release notes](../../README.md#beta-releases).
|
|
|
26
26
|
| Provider credentials | per-athlete secret file, mode 0600 | `catence-data secret set` |
|
|
27
27
|
| API keys | process environment only | `.env` |
|
|
28
28
|
| Chat history + preferences | data home | `console/chat-history.sqlite3` |
|
|
29
|
+
| Console logins + athlete grants | data home (mode 0600, bcrypt hashes) | `console/accounts.json` |
|
|
30
|
+
| Per-account MCP keys + OAuth state | data home (registry 0600; `oauth/` dir 0700) | `mcp-keys.json`, `oauth/` |
|
|
29
31
|
|
|
30
32
|
Credentials never land in `config.json`; profiles reference environment
|
|
31
33
|
variable *names* (`apiKeyEnv`, `apiBaseEnv`, `apiVersionEnv`). See
|
|
@@ -69,10 +71,16 @@ Script options (run `./deploy-console.sh --help` for the full list):
|
|
|
69
71
|
|
|
70
72
|
## 2. Fill in `catence-deploy/.env`
|
|
71
73
|
|
|
74
|
+
The Console always needs `CHAINLIT_AUTH_SECRET`, plus at least one login
|
|
75
|
+
source: the **environment break-glass account**
|
|
76
|
+
(`CATENCE_CONSOLE_USERNAME` + `CATENCE_CONSOLE_PASSWORD_HASH`, set together or
|
|
77
|
+
not at all) or a **stored Console account** in the data volume (see
|
|
78
|
+
[Console accounts](#console-accounts)). Add one model-provider key either way.
|
|
79
|
+
|
|
72
80
|
```sh
|
|
73
|
-
CATENCE_CONSOLE_USERNAME=coach
|
|
74
|
-
CATENCE_CONSOLE_PASSWORD_HASH='…' # generate: ./catence-deploy/hash-password.sh
|
|
75
81
|
CHAINLIT_AUTH_SECRET='…' # generate: openssl rand -hex 32
|
|
82
|
+
CATENCE_CONSOLE_USERNAME=coach # environment break-glass account …
|
|
83
|
+
CATENCE_CONSOLE_PASSWORD_HASH='…' # generate: ./catence-deploy/hash-password.sh
|
|
76
84
|
OPENAI_API_KEY='…' # or ANTHROPIC_API_KEY / OPENCODE_GO_API_KEY …
|
|
77
85
|
OPENAI_API_BASE='' # only for OpenAI-compatible endpoints
|
|
78
86
|
OPENCODE_GO_API_KEY='…' # the OpenCode Go base URLs are pre-filled below
|
|
@@ -80,9 +88,14 @@ OPENCODE_GO_API_BASE='https://opencode.ai/zen/go/v1'
|
|
|
80
88
|
OPENCODE_GO_MESSAGES_API_BASE='https://opencode.ai/zen/go'
|
|
81
89
|
```
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
Leave the username/hash pair empty to log in with stored accounts instead: the
|
|
92
|
+
script accepts an existing `console/accounts.json` in the data volume and
|
|
93
|
+
starts without the pair. Set both or neither — a lone username without a hash
|
|
94
|
+
fails the deploy with a clear error.
|
|
95
|
+
|
|
96
|
+
Generate the hash for the environment pair with the scaffold helper (it
|
|
97
|
+
prompts on the terminal, never echoes, and uses the compose project so it works
|
|
98
|
+
whatever image tag the script built):
|
|
86
99
|
|
|
87
100
|
```sh
|
|
88
101
|
./catence-deploy/hash-password.sh
|
|
@@ -102,6 +115,67 @@ Then start the stack (or re-run the script, which re-reads the saved `.env`):
|
|
|
102
115
|
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env up -d
|
|
103
116
|
```
|
|
104
117
|
|
|
118
|
+
### Console accounts
|
|
119
|
+
|
|
120
|
+
Stored accounts are the multi-user path: each account has a role and an
|
|
121
|
+
athlete grant list, and the Console enforces both server-side on logins,
|
|
122
|
+
roster/dashboard requests, and agent turns. Accounts live in
|
|
123
|
+
`console/accounts.json` in the data volume (mode 0600, bcrypt hashes only).
|
|
124
|
+
|
|
125
|
+
Manage them with the `users` command inside the container — via the generated
|
|
126
|
+
helper:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
./catence-deploy/users.sh add martina --athlete martina # member: one granted athlete
|
|
130
|
+
./catence-deploy/users.sh add coach --admin # admin: every athlete
|
|
131
|
+
./catence-deploy/users.sh list
|
|
132
|
+
./catence-deploy/users.sh grant martina sam
|
|
133
|
+
./catence-deploy/users.sh revoke martina sam
|
|
134
|
+
./catence-deploy/users.sh passwd martina
|
|
135
|
+
./catence-deploy/users.sh set-role martina admin
|
|
136
|
+
./catence-deploy/users.sh remove martina
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`users.sh` uses `docker compose exec`, so the stack must be running. The long
|
|
140
|
+
forms are:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
# Interactive: prompts for the password twice
|
|
144
|
+
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env exec console \
|
|
145
|
+
/opt/catence-console/bin/catence-console users add martina --athlete martina
|
|
146
|
+
|
|
147
|
+
# Non-interactive: read the password from an environment variable
|
|
148
|
+
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env run --rm -T \
|
|
149
|
+
-e CATENCE_CONSOLE_NEW_PASSWORD='…' \
|
|
150
|
+
--entrypoint /opt/catence-console/bin/catence-console console \
|
|
151
|
+
users add martina --athlete martina --password-env CATENCE_CONSOLE_NEW_PASSWORD
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Before the first start there is no running container **and** no login yet —
|
|
155
|
+
the Console refuses to start without one — so create the first account with
|
|
156
|
+
the one-off form (it prompts for the password):
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env run --rm \
|
|
160
|
+
--entrypoint /opt/catence-console/bin/catence-console console users add coach --admin
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Roles: `admin` sees every athlete; `member` sees only explicitly granted
|
|
164
|
+
athletes. A member with zero grants can sign in and browse, but has no athlete
|
|
165
|
+
to scope a chat to. The environment pair is a never-persisted break-glass
|
|
166
|
+
admin: it keeps working when the accounts file is lost or the last admin is
|
|
167
|
+
locked out, and unsetting the two variables revokes it.
|
|
168
|
+
|
|
169
|
+
Accounts gate the Console only. The MCP server has no authentication by
|
|
170
|
+
default — the caller's `athleteId` is trusted — so port 8787 still needs
|
|
171
|
+
loopback or network-layer restriction unless you switch on the bearer gate,
|
|
172
|
+
per-account keys, or OAuth (see
|
|
173
|
+
[Connecting to the Docker-based MCP from outside](#connecting-to-the-docker-based-mcp-from-outside)).
|
|
174
|
+
|
|
175
|
+
The accounts file is the only copy of your stored logins: keep it in backups
|
|
176
|
+
of the data volume (or bind-mounted home), along with `config.json` and the
|
|
177
|
+
athlete stores.
|
|
178
|
+
|
|
105
179
|
## 3. Initialize an athlete store
|
|
106
180
|
|
|
107
181
|
The data volume already contains a seeded `config.json`, so `setup` accepts
|
|
@@ -184,8 +258,9 @@ export OPENCODE_GO_API_KEY='…' # any non-empty value pass
|
|
|
184
258
|
## 7. Access the Console
|
|
185
259
|
|
|
186
260
|
Open `http://127.0.0.1:8000` (or the `--bind:--port` you configured) and log in
|
|
187
|
-
with
|
|
188
|
-
`
|
|
261
|
+
with a stored Console account (see [Console accounts](#console-accounts)) or
|
|
262
|
+
with the environment break-glass pair (`CATENCE_CONSOLE_USERNAME` and the
|
|
263
|
+
password whose hash is in `CATENCE_CONSOLE_PASSWORD_HASH`).
|
|
189
264
|
|
|
190
265
|
## 8. Verify with doctor
|
|
191
266
|
|
|
@@ -395,11 +470,12 @@ CATENCE_MCP_BIND=0.0.0.0 ./scripts/deploy-console.sh beta
|
|
|
395
470
|
After deployment, the MCP server is available at
|
|
396
471
|
`http://<tailscale-ip>:8787/mcp` from any device on your tailnet.
|
|
397
472
|
|
|
398
|
-
**Security note:**
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
473
|
+
**Security note:** Without the bearer gate, per-account keys, or OAuth, the MCP
|
|
474
|
+
server has **no authentication** — it trusts the caller's `athleteId`
|
|
475
|
+
parameter. When exposing on `0.0.0.0`, you **must** restrict access at the
|
|
476
|
+
network layer (Tailscale ACLs, firewall rules, etc.). The Console UI remains
|
|
477
|
+
on loopback (`127.0.0.1:8000`) and should still be fronted by Cloudflare
|
|
478
|
+
Tunnel or a reverse proxy.
|
|
403
479
|
|
|
404
480
|
**Examples: common MCP clients**
|
|
405
481
|
|
|
@@ -425,14 +501,90 @@ claude mcp add --transport http catence -- http://<tailscale-ip>:8787/mcp
|
|
|
425
501
|
|
|
426
502
|
### Why not expose port 8787 directly?
|
|
427
503
|
|
|
428
|
-
-
|
|
429
|
-
`athleteId` parameter.
|
|
504
|
+
- Without the bearer gate, per-account keys, or OAuth, the MCP server has
|
|
505
|
+
**no authentication** — it trusts the caller's `athleteId` parameter.
|
|
430
506
|
- Exposing it would allow any network caller to read all athlete data in the
|
|
431
507
|
catalog.
|
|
432
508
|
- The Console proxies dashboard requests through its **authenticated**
|
|
433
509
|
same-origin route.
|
|
434
510
|
- SSH port forwarding keeps the trust boundary at your SSH credentials.
|
|
435
511
|
|
|
512
|
+
### Bearer-token authentication for remote MCP clients
|
|
513
|
+
|
|
514
|
+
To serve the MCP endpoint to a remote MCP client (for example a Mistral Work
|
|
515
|
+
custom connector) over a Cloudflare Tunnel, switch on the native
|
|
516
|
+
bearer-token gate. The deploy scaffold arms it at `/data/mcp-token` (inside
|
|
517
|
+
the data volume, so it survives rebuilds); the file's presence is the switch
|
|
518
|
+
and Catence generates the token itself:
|
|
519
|
+
|
|
520
|
+
```sh
|
|
521
|
+
# enable from the host (or use the Console Settings once logged in):
|
|
522
|
+
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env \
|
|
523
|
+
exec console catence-data --home /data mcp enable
|
|
524
|
+
|
|
525
|
+
# print the token for the client's credential flow:
|
|
526
|
+
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env \
|
|
527
|
+
exec console catence-data --home /data mcp token
|
|
528
|
+
|
|
529
|
+
# disable again:
|
|
530
|
+
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env \
|
|
531
|
+
exec console catence-data --home /data mcp disable
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
With the gate enabled, every runtime endpoint except `/health` and
|
|
535
|
+
`/api/v1/health` answers `401` without an exact `Authorization: Bearer
|
|
536
|
+
<token>` header — including `/mcp` and the JSON APIs. Toggling takes effect
|
|
537
|
+
without a restart; the Console attaches the header automatically to its own
|
|
538
|
+
runtime calls.
|
|
539
|
+
|
|
540
|
+
Keep `CATENCE_MCP_BIND=127.0.0.1` (the default): the tunnel client on the
|
|
541
|
+
host forwards to the host-loopback mapping, so the port still never binds a
|
|
542
|
+
LAN interface. Point the tunnel ingress for the MCP hostname at
|
|
543
|
+
`http://127.0.0.1:8787` and complete the client's credential flow with the
|
|
544
|
+
token from `mcp token`. Rotate with `mcp disable && mcp enable` and
|
|
545
|
+
re-authenticate the client. The only additional setup the tunnel needs is
|
|
546
|
+
the ingress rule for the MCP hostname.
|
|
547
|
+
|
|
548
|
+
A custom host-side token file is still possible: set
|
|
549
|
+
`CATENCE_MCP_TOKEN_FILE=<host path>` in `.env`. The file is then bind-mounted
|
|
550
|
+
read-only at the same path, so it must already exist on the host — enabling
|
|
551
|
+
from the Console cannot create it.
|
|
552
|
+
|
|
553
|
+
#### Per-account MCP keys and OAuth 2.1 (Docker)
|
|
554
|
+
|
|
555
|
+
The runtime reads three more environment variables. The deploy script passes
|
|
556
|
+
them into the container, writes them into `catence-deploy/.env`, and validates
|
|
557
|
+
them before rendering the compose file:
|
|
558
|
+
|
|
559
|
+
| Variable | Default | Meaning |
|
|
560
|
+
| --- | --- | --- |
|
|
561
|
+
| `CATENCE_OAUTH_ENABLED` | empty (disabled) | `1` or `true` runs the OAuth 2.1 authorization server so ChatGPT/Codex connect with their OAuth flow. Startup default — the Console (Settings → MCP Exposure → OAuth 2.1 sign-in) overrides it at runtime. Requires `CATENCE_PUBLIC_URL` when enabled. |
|
|
562
|
+
| `CATENCE_PUBLIC_URL` | empty | Public `https` URL of this server (the tunnel URL; no query string or fragment). It becomes the advertised OAuth issuer. Startup default — overridable in the Console. |
|
|
563
|
+
| `CATENCE_MCP_KEYS_FILE` | `/data/mcp-keys.json` | Per-account MCP key registry, a container path — no host mount needed. Managed with `catence-data mcp key` or the Console (Settings → MCP Exposure → API keys). |
|
|
564
|
+
|
|
565
|
+
```sh
|
|
566
|
+
# Per-account key for a Console account (the raw token is printed exactly once):
|
|
567
|
+
docker compose -f catence-deploy/docker-compose.yml --env-file catence-deploy/.env \
|
|
568
|
+
exec console catence-data --home /data mcp key add --account coach
|
|
569
|
+
|
|
570
|
+
# OAuth for ChatGPT/Codex — preferred: enable in the Console
|
|
571
|
+
# (Settings → MCP Exposure → OAuth 2.1 sign-in; applies without a restart).
|
|
572
|
+
# Startup defaults, set in catence-deploy/.env and re-run the script:
|
|
573
|
+
# CATENCE_OAUTH_ENABLED=1
|
|
574
|
+
# CATENCE_PUBLIC_URL=https://catence-mcp.example.com
|
|
575
|
+
./scripts/deploy-console.sh beta
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
The script refuses to deploy when `CATENCE_OAUTH_ENABLED` is set without a
|
|
579
|
+
valid https `CATENCE_PUBLIC_URL`. Once a valid Console settings file exists
|
|
580
|
+
(`<catalog-home>/oauth/settings.json`, inside the data volume), it overrides
|
|
581
|
+
these startup values at runtime. The checked-in dev compose
|
|
582
|
+
(`docker-compose.beta.yml`) accepts `CATENCE_OAUTH_ENABLED` and
|
|
583
|
+
`CATENCE_PUBLIC_URL` the same way (empty default = disabled). See
|
|
584
|
+
[local-mcp.md](local-mcp.md#per-account-api-keys) and
|
|
585
|
+
[local-mcp.md](local-mcp.md#oauth-21-chatgptcodex) for the full semantics and
|
|
586
|
+
the client matrix.
|
|
587
|
+
|
|
436
588
|
## Exposing the Console safely
|
|
437
589
|
|
|
438
590
|
Keep the Console port bound to `127.0.0.1` (the default) and front it with a
|
|
@@ -472,11 +624,17 @@ docker run --rm -it -v catence-data:/data --entrypoint catence-data catence-cons
|
|
|
472
624
|
|
|
473
625
|
Use the same `--entrypoint catence-data` pattern for `secret set` and `sync`.
|
|
474
626
|
|
|
627
|
+
Console logins work the same way: keep the environment pair together (or
|
|
628
|
+
neither variable), or seed a stored account into the volume with the `users`
|
|
629
|
+
command (see [Console accounts](#console-accounts)).
|
|
630
|
+
|
|
475
631
|
## Updating
|
|
476
632
|
|
|
477
633
|
Docker deployments: re-run `deploy-console.sh beta` (or `stable`); it
|
|
478
634
|
re-resolves the newest versions from the registries and rebuilds. The data
|
|
479
|
-
volume
|
|
635
|
+
volume is preserved, so your `config.json`, the stored Console accounts
|
|
636
|
+
(`console/accounts.json`), chat history, and the athlete stores all survive
|
|
637
|
+
the rebuild.
|
|
480
638
|
|
|
481
639
|
```sh
|
|
482
640
|
curl -fsSL https://raw.githubusercontent.com/rifusaki/catence/beta/scripts/deploy-console.sh | bash -s beta
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Catence Console is a password-protected local web chat that starts a matching
|
|
4
4
|
Catence runtime on loopback. It uses Chainlit password login and fails closed
|
|
5
|
-
unless
|
|
5
|
+
unless `CHAINLIT_AUTH_SECRET` and at least one login source — a stored Console
|
|
6
|
+
account or the environment account pair — are configured.
|
|
6
7
|
|
|
7
8
|
This guide covers the **local** path: the npm package plus the Python Console
|
|
8
9
|
on your machine. The Docker path (one container built from the public
|
|
@@ -27,6 +28,7 @@ model.
|
|
|
27
28
|
| Provider credentials | per-athlete secret file, mode 0600 | `catence-data secret set` |
|
|
28
29
|
| API keys | process environment only | shell exports |
|
|
29
30
|
| Chat history + preferences | data home | `console/chat-history.sqlite3` |
|
|
31
|
+
| Console logins + athlete grants | data home (mode 0600, bcrypt hashes) | `console/accounts.json` |
|
|
30
32
|
|
|
31
33
|
Credentials never land in `config.json`; profiles reference environment
|
|
32
34
|
variable *names* (`apiKeyEnv`, `apiBaseEnv`, `apiVersionEnv`). See
|
|
@@ -75,6 +77,12 @@ printf %s 'strava-client-secret' | catence-data --athlete alex secret set --prov
|
|
|
75
77
|
Set only the providers you actually sync; `secret set` accepts each field
|
|
76
78
|
independently.
|
|
77
79
|
|
|
80
|
+
Administrators can also manage any athlete's credentials from **Settings →
|
|
81
|
+
Athletes → Credentials** in the Console. The dialog is write-only — it shows
|
|
82
|
+
each field as configured or not and lets you replace or remove values, but
|
|
83
|
+
values are never displayed. `catence-data --athlete <id> secret remove
|
|
84
|
+
--provider <provider> --field <field>` mirrors the dialog for scripted setups.
|
|
85
|
+
|
|
78
86
|
## 4. Sync data and build retrieval context
|
|
79
87
|
|
|
80
88
|
```sh
|
|
@@ -95,15 +103,28 @@ To add OpenCode Go models, run the
|
|
|
95
103
|
|
|
96
104
|
```sh
|
|
97
105
|
export OPENAI_API_KEY='…' # or ANTHROPIC_API_KEY / OPENCODE_GO_API_KEY …
|
|
98
|
-
export CATENCE_CONSOLE_USERNAME='coach'
|
|
99
|
-
export CATENCE_CONSOLE_PASSWORD_HASH="$(catence-console auth hash-password)"
|
|
100
106
|
export CHAINLIT_AUTH_SECRET="$(openssl rand -hex 32)"
|
|
101
107
|
```
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
`CHAINLIT_AUTH_SECRET` is always required: it signs Chainlit sessions. On top
|
|
110
|
+
of it, the Console needs at least one login source:
|
|
111
|
+
|
|
112
|
+
- **Stored accounts** (the multi-user path). Each account carries a role and
|
|
113
|
+
an athlete grant list and lives in `<home>/console/accounts.json`. Create
|
|
114
|
+
the first one with the users CLI, which prompts for the password twice:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
catence-console users add coach --admin
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- **The environment break-glass account**: set both
|
|
121
|
+
`CATENCE_CONSOLE_USERNAME` and `CATENCE_CONSOLE_PASSWORD_HASH` (the bcrypt
|
|
122
|
+
hash printed by `catence-console auth hash-password`). The pair is never
|
|
123
|
+
persisted and always acts as an admin — the way back in when the accounts
|
|
124
|
+
file is lost.
|
|
125
|
+
|
|
126
|
+
Roles, grants, and the rest of the CLI are described under
|
|
127
|
+
[Console accounts](#console-accounts).
|
|
107
128
|
|
|
108
129
|
## 7. Start the Console
|
|
109
130
|
|
|
@@ -121,7 +142,7 @@ catence-console doctor
|
|
|
121
142
|
|
|
122
143
|
### Manage models in the Console
|
|
123
144
|
|
|
124
|
-
The **Models**
|
|
145
|
+
The **Settings → Models** tab manages the model list without editing
|
|
125
146
|
`config.json` by hand:
|
|
126
147
|
|
|
127
148
|
- Enable/disable toggles hide models from the chat's Model dropdown. Disabled
|
|
@@ -137,11 +158,87 @@ The **Models** page (header button) manages the model list without editing
|
|
|
137
158
|
strict parser used at startup before it replaces the file, and secrets stay
|
|
138
159
|
forbidden (only environment-variable names are ever stored).
|
|
139
160
|
|
|
140
|
-
|
|
161
|
+
**Settings → Status** has a **Sync data** button that starts the same detached
|
|
141
162
|
sync as `catence-data sync --provider all` through the authenticated Console
|
|
142
163
|
origin, shows live progress while the run is active, and displays the last
|
|
143
|
-
completed sync afterwards
|
|
144
|
-
|
|
164
|
+
completed sync afterwards; the Dashboard keeps a read-only “Syncing…” pill and
|
|
165
|
+
links there when a write lock makes its data unavailable. Each manual sync also
|
|
166
|
+
refreshes OpenCode Go model profiles first; a discovery failure never blocks the
|
|
167
|
+
data sync.
|
|
168
|
+
|
|
169
|
+
### Add athletes in the Console
|
|
170
|
+
|
|
171
|
+
Admins can add a catalog athlete from **Settings → Athletes** (athlete id,
|
|
172
|
+
display name, and optionally “set as default”). The form calls the runtime's
|
|
173
|
+
`POST /api/v1/athletes` through the Console, which rejects members with
|
|
174
|
+
`403 admin_required` and otherwise performs the same catalog write as
|
|
175
|
+
`catence-data athlete add --id <id> --label <label>`. The id must match
|
|
176
|
+
`^[a-z][a-z0-9-]{0,62}$` and must not already exist (`409 athlete_exists`);
|
|
177
|
+
single-store runtimes without a catalog refuse with `400 catalog_required`.
|
|
178
|
+
The roster refreshes immediately, so a new athlete can be granted to members
|
|
179
|
+
and selected in chat without restarting the Console.
|
|
180
|
+
|
|
181
|
+
## Console accounts
|
|
182
|
+
|
|
183
|
+
Console accounts are Catence's multi-user login model, enforced entirely by
|
|
184
|
+
the Console: sign-in, the athlete roster, dashboard and athlete-file requests,
|
|
185
|
+
and the athlete scoping of every chat turn are all checked server-side against
|
|
186
|
+
the account's role and athlete grants. The MCP server itself is untouched — it
|
|
187
|
+
has no authentication and trusts the `athleteId` each call names (see
|
|
188
|
+
[local-mcp.md](local-mcp.md#streamable-http-server)); accounts only gate what
|
|
189
|
+
goes through the Console.
|
|
190
|
+
|
|
191
|
+
| Role | Athlete access |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| `admin` | Every athlete in the catalog (`athletes: "all"`) |
|
|
194
|
+
| `member` | Only the explicitly granted athletes; zero grants allowed |
|
|
195
|
+
|
|
196
|
+
Accounts live in `<home>/console/accounts.json` (mode 0600, bcrypt hashes
|
|
197
|
+
only), where `<home>` is `$CATENCE_HOME` or `~/.catence`. The Console re-reads
|
|
198
|
+
the file on every login, so new accounts, password resets, and grant changes
|
|
199
|
+
apply without a restart.
|
|
200
|
+
|
|
201
|
+
### Manage accounts with the users CLI
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
catence-console users add coach --admin # admin: all athletes
|
|
205
|
+
catence-console users add martina --athlete martina # member with one grant
|
|
206
|
+
catence-console users add sam # member, zero grants
|
|
207
|
+
catence-console users list
|
|
208
|
+
catence-console users passwd martina
|
|
209
|
+
catence-console users set-role sam member
|
|
210
|
+
catence-console users grant sam martina
|
|
211
|
+
catence-console users revoke sam martina
|
|
212
|
+
catence-console users remove sam
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- `add <username> (--admin | --athlete <id> [--athlete <id> …])` — members
|
|
216
|
+
default to zero grants; `--admin` cannot be combined with `--athlete` and
|
|
217
|
+
always stores access to every athlete.
|
|
218
|
+
- `add` and `passwd` prompt for the password twice (never echoed). For
|
|
219
|
+
automation, `--password-env VAR` reads the password from environment
|
|
220
|
+
variable `VAR` instead, so it never appears in the command line or shell
|
|
221
|
+
history.
|
|
222
|
+
- `grant`/`revoke` change a member's athlete list; revoking the last grant
|
|
223
|
+
leaves a zero-grant member. `set-role` promotes or demotes: promoting to
|
|
224
|
+
`admin` coerces the grants to `"all"`, demoting to `member` keeps them.
|
|
225
|
+
- `add`/`grant`/`revoke` validate athlete ids against `catalog.json` when it
|
|
226
|
+
exists, and `--home <dir>` points any subcommand at a non-default catalog.
|
|
227
|
+
|
|
228
|
+
A member with zero grants can sign in and browse the Console, but there is no
|
|
229
|
+
athlete to scope a chat to: the Athlete selector is disabled with *"You don't
|
|
230
|
+
have access to any athlete yet. Ask an administrator to grant you access."*
|
|
231
|
+
and every chat turn is answered with the same notice. A member that requests
|
|
232
|
+
an athlete outside their grants receives `403 athlete_forbidden`; the Console
|
|
233
|
+
never forwards the request to the runtime.
|
|
234
|
+
|
|
235
|
+
### The environment break-glass account
|
|
236
|
+
|
|
237
|
+
`CATENCE_CONSOLE_USERNAME` and `CATENCE_CONSOLE_PASSWORD_HASH` must be set
|
|
238
|
+
together. When they are, the pair always acts as an `admin` and is never
|
|
239
|
+
written to `accounts.json`: unsetting the two variables revokes it. Use it as
|
|
240
|
+
the recovery path when the accounts file is lost or a lockout leaves nobody
|
|
241
|
+
who can grant access — not as the everyday login.
|
|
145
242
|
|
|
146
243
|
## Console serve options
|
|
147
244
|
|
|
@@ -167,12 +264,18 @@ chat starts.
|
|
|
167
264
|
|
|
168
265
|
## How multi-athlete works in the Console
|
|
169
266
|
|
|
170
|
-
The Console shares the same per-athlete stores as MCP
|
|
171
|
-
|
|
267
|
+
The Console shares the same per-athlete stores as MCP. Access is layered:
|
|
268
|
+
per-account grants at the login, then server-owned scoping per chat.
|
|
172
269
|
|
|
270
|
+
- **Per-account grants.** Each stored account is an `admin` (every athlete) or
|
|
271
|
+
a `member` with an explicit grant list (see
|
|
272
|
+
[Console accounts](#console-accounts)). The **Athlete** selector and the
|
|
273
|
+
proxied roster are narrowed to the account's grants; requests that name an
|
|
274
|
+
ungranted athlete are refused with `403 athlete_forbidden` before they reach
|
|
275
|
+
the runtime, and a zero-grant member sees no athletes at all.
|
|
173
276
|
- The settings panel has an **Athlete** selector built from
|
|
174
277
|
`GET /api/v1/athletes` on the runtime. The default is the catalog's
|
|
175
|
-
`defaultAthleteId
|
|
278
|
+
`defaultAthleteId` (for a member, the first granted athlete).
|
|
176
279
|
- On every personal-data tool call, the Console **forces** the selected
|
|
177
280
|
athleteId onto the arguments — the model cannot name or switch athletes, and
|
|
178
281
|
a chat's system message states: *"This Console chat is scoped to athleteId X.
|
|
@@ -183,10 +286,14 @@ The Console shares the same per-athlete stores as MCP, but scoping is
|
|
|
183
286
|
announces *"This chat is scoped to athlete **<id>**."*
|
|
184
287
|
- `list_athletes` is exempt from forcing so the roster can be rendered.
|
|
185
288
|
|
|
186
|
-
|
|
187
|
-
middleware proxies
|
|
188
|
-
|
|
189
|
-
|
|
289
|
+
Console pages and chats reach the runtime through the authenticated Console
|
|
290
|
+
origin: Chainlit middleware proxies the Console's API surface — `GET
|
|
291
|
+
/api/v1/dashboard`, `GET`/`POST /api/v1/athletes` (POST is admin-only),
|
|
292
|
+
`GET`/`PUT /api/v1/athlete-secrets` and `POST /api/v1/athlete-secrets/remove`
|
|
293
|
+
(all admin-only), `GET /api/v1/threads/<id>/generation`, and the models,
|
|
294
|
+
accounts, tool-server, and sync routes — to the runtime only when the Console's
|
|
295
|
+
JWT cookie is valid (otherwise 401), so raw port 8787 does not need to be
|
|
296
|
+
exposed.
|
|
190
297
|
|
|
191
298
|
## Model discovery (OpenCode Go)
|
|
192
299
|
|
|
@@ -205,8 +312,8 @@ disappeared from the live catalog.
|
|
|
205
312
|
npm run discover:opencode-go -- --write ~/.catence/config.json
|
|
206
313
|
```
|
|
207
314
|
|
|
208
|
-
Inside
|
|
209
|
-
|
|
315
|
+
Inside **Settings → Models**, **Discover OpenCode Go models** runs the same
|
|
316
|
+
merge for the current home without touching athlete data (runtime route
|
|
210
317
|
`POST /api/v1/models/discover`, proxied behind the Console login). Models can
|
|
211
318
|
also be edited in place there — the update action rewrites the `console`
|
|
212
319
|
section atomically and never stores secret values.
|
|
@@ -260,4 +367,8 @@ uv tool install --upgrade catence-console
|
|
|
260
367
|
environment variables; set them in the shell and restart the Console.
|
|
261
368
|
- **Runtime not reachable** — `catence-console doctor --home "$HOME/.catence"
|
|
262
369
|
--mcp-url http://127.0.0.1:8787/mcp` checks the handshake; confirm the
|
|
263
|
-
runtime is running on that port and the protocol versions agree.
|
|
370
|
+
runtime is running on that port and the protocol versions agree.
|
|
371
|
+
- **Locked out of the Console** — sign in with the environment break-glass
|
|
372
|
+
account (`CATENCE_CONSOLE_USERNAME`/`CATENCE_CONSOLE_PASSWORD_HASH`) and
|
|
373
|
+
restore access with `catence-console users` (reset a password, re-grant
|
|
374
|
+
athletes, or add a new admin).
|