Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d201abb
Add multi-user concurrent annotation support with JWT auth
claude May 30, 2026
d0aa89a
Ignore TypeScript incremental build cache files
claude May 30, 2026
b637aff
Add auth unit and integration tests; fix three real bugs found
claude May 30, 2026
35e0416
Add membership guards to all remaining unprotected endpoints
claude May 31, 2026
c67af3e
Fix model × auth interactions: internal token, prediction namespace, …
claude May 31, 2026
afda911
Add update user management & styling
samueljackson92 Jun 1, 2026
fb2aaba
Restore DB default path to user cache directory
claude Jun 6, 2026
628aacc
Fix test collection failures when optional deps are absent
claude Jun 6, 2026
70dc8e3
Fix ruff lint, ruff format, ESLint and Prettier CI failures
claude Jun 6, 2026
818c1d8
Remove db
samueljackson92 Jun 8, 2026
68acd06
Add dep
samueljackson92 Jun 8, 2026
54142e6
Fix CI test failures: auth bypass, stale model type refs, new behavio…
samueljackson92 Jun 8, 2026
19174fe
Fix E2E server startup and fork PR build failure
samueljackson92 Jun 8, 2026
051f2e8
Increase Pytest CI timeout from 15 to 30 minutes
samueljackson92 Jun 8, 2026
3a8e585
Fix E2E tests: always call /auth/me on mount to support auth-not-requ…
samueljackson92 Jun 8, 2026
f192dae
Fix E2E test: expect created_by="admin" after auth-passthrough save
samueljackson92 Jun 9, 2026
cb2edcd
Ruff linting
samueljackson92 Jun 18, 2026
6cb20a7
Fix CI: add testcontainers to dev deps, remove unused import
samueljackson92 Jun 18, 2026
c4e8007
Fix test_model_stop_training and test_model_delete_predictions
samueljackson92 Jun 18, 2026
d1bc044
Fix test_model_delete_predictions: correct model type and created_by …
samueljackson92 Jun 18, 2026
470cf58
Fix E2E test: remove model:: prefix from annotation created_by
samueljackson92 Jun 18, 2026
83438bb
Fix issues from review after merge
samueljackson92 Jun 22, 2026
eaeafd0
Update to fix UI issues
samueljackson92 Jun 22, 2026
172085c
Update docs, fix warnings
samueljackson92 Jun 22, 2026
d285b1b
Update to address PR comments
samueljackson92 Jun 23, 2026
e28c9c0
Address remaining PR #279 review comments
samueljackson92 Jun 23, 2026
83dbfb9
Restore settings fixture lost during rebase
samueljackson92 Jun 23, 2026
6486dc0
Fix test_models.py: use config.settings instead of os.environ
samueljackson92 Jun 23, 2026
dc8ea15
Fix e2e test: restore model:: prefix on predictions
samueljackson92 Jun 23, 2026
780dd8d
Fix merge regression: replace testcontainers with mongita in tests/co…
samueljackson92 Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
test_models_disabled:
name: Pytest (Models Disabled)
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 30

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume this should be increased in both the models_disabled and models_enabled CI jobs?


strategy:
matrix:
Expand Down Expand Up @@ -116,7 +116,7 @@ jobs:
test_models_enabled:
name: Pytest (Models Enabled)
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 30

strategy:
matrix:
Expand Down Expand Up @@ -144,6 +144,7 @@ jobs:
run: uv run --all-extras pytest tests/end_to_end

build:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- name: Checkout repo
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ package.json

data/jet_images/
toktagger/ui/.vite/
*.tsbuildinfo

# Ignore config file
toktagger.toml
# Local Mongita database directory (should live in user cache dir, not the repo)
toktagger_db/
49 changes: 47 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ It currently supports the following features:
- **Annotation Tools**: Apply consistent labels to signals and images using a customizable tagging system.
- **ML Models**: Train and infer from ML models within the UI.
- **Dataset Management**: Organize and manage annotations in a central repository.
- **Multi-User Support**: Role-based access control with per-project membership, suitable for team annotation workflows.
- **Extensible API**: A Python API for integrating with existing workflows and tools.


Expand Down Expand Up @@ -56,10 +57,54 @@ uv tool install --python 3.12.6 toktagger[models]
```

## Quick Start
To get started, run:

To start the application:

```sh
toktagger
```

This will start a local instance of the application running at `http://localhost:8002`.
This launches 4 Gunicorn workers and opens the UI at `http://localhost:8002`. On first launch an `admin` account is created automatically and the credentials are printed to the terminal.

### Options

| Flag | Default | Description |
|------|---------|-------------|
| `--workers N` | `4` | Number of Gunicorn worker processes |
| `--host HOST` | `0.0.0.0` | Host to bind to |
| `--port PORT` | `8002` | Port to listen on |
| `--no-browser` | off | Suppress automatic browser launch |
| `--reload` | off | Auto-reload on code changes (single-worker dev mode only) |

### Development Mode

For local development with automatic reload on code changes, use a single worker:

```sh
toktagger --workers 1 --reload
```

### Multi-User / Team Deployment

For server deployments, run with multiple workers and disable the automatic browser launch:

```sh
toktagger --workers 4 --host 0.0.0.0 --port 8002 --no-browser
```

Or directly via Gunicorn (use `python -m gunicorn` to ensure the correct virtual environment is used):

```sh
python -m gunicorn toktagger.api.asgi:app \
--worker-class uvicorn.workers.UvicornWorker \
--workers 4 \
--bind 0.0.0.0:8002
```

With Docker Compose, the production stack defaults to 4 workers. Override with the `WORKERS` environment variable:

```sh
WORKERS=8 docker compose up
```

See the [User Management](docs/user_management.md) guide for creating accounts, assigning roles, and managing project membership.
15 changes: 9 additions & 6 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@ services:
- ${CUSTOM_SCRIPT:-./toktagger/api/run.py}:/app/run.py
- ~/.sal/:/root/.sal
environment:
DATABASE_MONGO_URL: "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@mongo:27017"
MODELS_CACHE_DIR: "/app/data/models"
SERVER_HOST: api_app
SERVER_PORT: 8002
SERVER_RELOAD: "true"
CUSTOM_SCRIPT: ${CUSTOM_SCRIPT}
MONGO_URL: "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@mongo:27017"
UDA_HOST: "uda2.mast.l"
UDA_META_PLUGINNAME: "MASTU_DB"
UDA_METANEW_PLUGINNAME: "MAST_DB"
SAL_HOST: "https://sal.jetdata.eu"
MODEL_STORAGE: "/app/data/models"
API_URL: "http://api_app:8002"
RELOAD: "true"
WORKERS: "1"
working_dir: /app
command: ["python", "run.py"]
networks:
Expand Down
14 changes: 8 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ services:
- ${CUSTOM_SCRIPT:-./toktagger/api/run.py}:/app/run.py
- ~/.sal/:/root/.sal
environment:
DATABASE_MONGO_URL: "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@mongo:27017"
MODELS_CACHE_DIR: "/app/data/models"
SERVER_HOST: api_app
SERVER_PORT: 8002
SERVER_RELOAD: "false"
CUSTOM_SCRIPT: ${CUSTOM_SCRIPT}
MONGO_URL: "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@mongo:27017"
UDA_HOST: "uda2.mast.l"
UDA_META_PLUGINNAME: "MASTU_DB"
UDA_METANEW_PLUGINNAME: "MAST_DB"
MODEL_STORAGE: "/app/data/models"
SAL_HOST: "https://sal.jetdata.eu"
API_URL: "http://api_app:8002"
WORKERS: "${WORKERS:-4}"
working_dir: /app
command: ["python", "run.py"]
networks:
Expand Down
4 changes: 3 additions & 1 deletion docs/dev/developer-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ npm --prefix toktagger/ui install
5. Run the backend API service in development mode. The backend API will be accessible at `http://localhost:8002`.

```sh
API_URL=http://0.0.0.0:8002 uvicorn toktagger.api.cli:app --host 0.0.0.0 --port 8002 --reload
toktagger --workers 1 --reload --no-browser
```

This starts a single Uvicorn worker with auto-reload enabled. The `--no-browser` flag suppresses the automatic browser launch since the frontend dev server (step 6) is used instead.

6. Run the frontend UI service in development mode. The UI will be accessible at `http://localhost:5173`
```sh
npm --prefix toktagger/ui run dev
Expand Down
34 changes: 32 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ It currently supports the following features:
- **Annotation Tools**: Apply consistent labels to signals and images using a customizable tagging system.
- **ML Models**: Train and infer from ML models within the UI.
- **Dataset Management**: Organize and manage annotations in a central repository.
- **Multi-User Support**: Role-based access control with per-project membership, suitable for team annotation workflows.
- **Extensible API**: A Python API for integrating with existing workflows and tools.


Expand Down Expand Up @@ -49,13 +50,42 @@ uv tool install --python 3.12.6 toktagger[models]
```

## Quick Start
To get started, run:

To start a local single-user instance:

```sh
toktagger
```

This will start a local instance of the application running at `http://localhost:8002`.
This starts the application at `http://localhost:8002`. On first launch an `admin` account is created automatically and the credentials are printed to the terminal.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should warn here that they will not see this password again after this point

Should we have a way to either regenerate this password, or set it to a more memorable one (eg, password), and just on first login it forces you to change it before you can log in?


!!! warning
**Save the generated password immediately** — it is only printed once and cannot be recovered.

### Multi-User / Team Deployment

For concurrent multi-user access, run with multiple Gunicorn workers:

```sh
toktagger --workers 4 --host 0.0.0.0 --port 8002 --no-browser
```

Or directly via Gunicorn:

```sh
gunicorn toktagger.api.asgi:app \
--worker-class uvicorn.workers.UvicornWorker \
--workers 4 \
--bind 0.0.0.0:8002
```

With Docker Compose, the production stack defaults to 4 workers. Override with the `WORKERS` environment variable:

```sh
WORKERS=8 docker compose up
```

See [User Management](user_management.md) for creating accounts, assigning roles, and managing project membership.

## Configuration
There are a series of additional options which you can configure to customise the functionality of TokTagger - [find details about these here.](./configuration.md)
Expand Down
154 changes: 154 additions & 0 deletions docs/user_management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# User Management

TokTagger supports multiple concurrent users with role-based access control. An **admin** user manages accounts and project membership; regular **users** annotate within the projects they are assigned to.

---

## User Roles

TokTagger has two layers of roles:

### Global roles (account-level)

| Global Role | Permissions |
|---|---|
| `admin` | Full access: create/edit/delete any project, manage all user accounts, view all annotations |
| `user` | Access only to projects they are a member of |

### Project roles (per-project membership)

| Project Role | Permissions |
|---|---|
| `admin` | Manage project membership, delete samples and annotations |
| `annotator` | Submit and update annotations for samples |
| `viewer` | Read-only access to the project's samples and annotations |

A global `admin` automatically has unrestricted access to all projects regardless of project role.

---

## First-Run Setup

On first launch TokTagger automatically creates an `admin` account with a random password and prints the credentials to the terminal:

```
Admin user created — username: admin password: <generated>
```

!!! warning
Save this password immediately. You can change it afterwards from the **Profile** page, but it is only printed once.

---

## Signing In

Navigate to `http://<host>:<port>/ui/login` (or the root URL, which redirects there automatically). Enter your username and password to sign in.

---

## Admin Panel

The admin panel is accessible from the **Admin Panel** button on the Projects page (visible to admin users only).

### Viewing Users

The panel lists all registered accounts with their username, email, role, and active status.

### Creating a User

1. Click **Add User**.
2. Fill in **Username**, **Password**, and optionally **Email**.
3. Select a **Role** (`user` or `admin`).
4. Click **Create**.

### Changing a User's Role

1. Find the user in the table and click **Edit**.
2. Select the new **Global Role**.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, you could reduce all admins down to regular users - maybe it should be enforced that at least one admin must be active, and if you try to change the final admin down to a regular user it doesnt allow it?

3. Click **Save**.

!!! note
TokTagger prevents demoting or deactivating the last remaining active admin account to avoid an unrecoverable lockout.

### Deactivating / Reactivating a User

Click **Deactivate** (or **Activate**) next to the user. Deactivated accounts cannot sign in but their annotations are preserved. You cannot deactivate your own account.

### Deleting a User

Click **Delete** next to the user and confirm. This is permanent. You cannot delete your own account.

---

## Profile Page

Any signed-in user can update their own profile. Click **Profile** from the Projects page.

### Updating Email

Enter a new address in the **Email** field and click **Save Email**.

### Changing Password

1. Enter a new password in **New password** (minimum 8 characters).
2. Confirm it in **Confirm new password**.
3. Click **Change Password**.

---

## Project Membership

Access to a project is controlled per-project. From the project's Samples page, an admin can click **Members** to add or remove users.

Only members (and admins) can view samples and submit annotations for a given project.

---

## Scripted User & Project Setup

For automated deployments, the helper script `scripts/setup.py` can create projects and samples via the API using token-based auth:

```sh
python scripts/setup.py \
--url http://localhost:8002 \
--username admin \
--password <password>
```

The script authenticates, obtains a JWT token, and creates projects and sample sets using the REST API. You can adapt it to pre-create user accounts with the `POST /users` endpoint:

```python
import requests

token = get_token(base_url, "admin", admin_password)
requests.post(
f"{base_url}/users",
json={"username": "alice", "password": "s3cr3t", "global_role": "user"},
headers={"Authorization": f"Bearer {token}"},
)
```

---

## Multi-User Deployment

For team use, run the API under **Gunicorn** so multiple requests can be served concurrently:

```sh
# Command-line (installed package)
toktagger --workers 4 --host 0.0.0.0 --port 8002

# Direct Gunicorn invocation
gunicorn toktagger.api.asgi:app \
--worker-class uvicorn.workers.UvicornWorker \
--workers 4 \
--bind 0.0.0.0:8002
```

With Docker Compose the `WORKERS` variable controls the worker count (default 4 in production, 1 in dev):

```sh
WORKERS=8 docker compose up
```

A single Uvicorn worker (the default for `toktagger` without `--workers`) is sufficient for personal/local use but will serialise all requests, so concurrent annotators will experience latency under load.
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ classifiers = [

dependencies = [
"fastapi",
"uvicorn",
"uvicorn[standard]",
"gunicorn",
"fsspec",
"xarray",
"s3fs",
Expand All @@ -39,6 +40,8 @@ dependencies = [
"bump-my-version>=1.2.7",
"platformdirs>=4.4.0",
"pydantic-settings>=2.11.0",
"itsdangerous>=2.0.0",
"python-multipart>=0.0.7",
]
[project.optional-dependencies]
models = [
Expand All @@ -47,6 +50,9 @@ models = [
]


[tool.pytest.ini_options]
asyncio_mode = "auto"

[tool.setuptools.packages.find]
where = ["."]
include = ["toktagger.api*"]
Expand Down
Loading