roomz 0.1.0__tar.gz

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.
Files changed (102) hide show
  1. roomz-0.1.0/.claude/settings.local.json +22 -0
  2. roomz-0.1.0/.github/workflows/ci.yml +112 -0
  3. roomz-0.1.0/.github/workflows/publish.yml +53 -0
  4. roomz-0.1.0/.gitignore +53 -0
  5. roomz-0.1.0/.readthedocs.yaml +20 -0
  6. roomz-0.1.0/LICENSE +21 -0
  7. roomz-0.1.0/Makefile +68 -0
  8. roomz-0.1.0/PKG-INFO +353 -0
  9. roomz-0.1.0/README.md +305 -0
  10. roomz-0.1.0/REQUIREMENTS.md +157 -0
  11. roomz-0.1.0/TODO.md +280 -0
  12. roomz-0.1.0/analysis/2026-05-14-architecture-review-I1-001.md +654 -0
  13. roomz-0.1.0/analysis/api-auth-I2-001.md +891 -0
  14. roomz-0.1.0/analysis/api-broadcast.md +585 -0
  15. roomz-0.1.0/analysis/api-email-integration.md +655 -0
  16. roomz-0.1.0/analysis/api-jwt-sessions.md +777 -0
  17. roomz-0.1.0/analysis/api-private-channels.md +802 -0
  18. roomz-0.1.0/analysis/api-python-client.md +869 -0
  19. roomz-0.1.0/analysis/functional.md +681 -0
  20. roomz-0.1.0/analysis/jwt-channels-architecture.md +234 -0
  21. roomz-0.1.0/analysis/security-auth-I2-001.md +1476 -0
  22. roomz-0.1.0/analysis/security-email-integration.md +745 -0
  23. roomz-0.1.0/analysis/security-jwt-sessions.md +1590 -0
  24. roomz-0.1.0/analysis/security-private-channels.md +1003 -0
  25. roomz-0.1.0/analysis/ux-auth-I2-001.md +954 -0
  26. roomz-0.1.0/analysis/ux-chat-ui.md +560 -0
  27. roomz-0.1.0/app/__init__.py +521 -0
  28. roomz-0.1.0/app/auth.py +574 -0
  29. roomz-0.1.0/app/components/__init__.py +2 -0
  30. roomz-0.1.0/app/components/auth/__init__.py +16 -0
  31. roomz-0.1.0/app/components/auth/auth.js +249 -0
  32. roomz-0.1.0/app/email/__init__.py +70 -0
  33. roomz-0.1.0/app/email/console.py +60 -0
  34. roomz-0.1.0/app/email/protocol.py +38 -0
  35. roomz-0.1.0/app/email/resend.py +203 -0
  36. roomz-0.1.0/app/models.py +118 -0
  37. roomz-0.1.0/app/pages/__init__.py +8 -0
  38. roomz-0.1.0/app/pages/chat/__init__.py +16 -0
  39. roomz-0.1.0/app/pages/chat/chat.js +247 -0
  40. roomz-0.1.0/app/static/css/roomz.css +11 -0
  41. roomz-0.1.0/docs/Makefile +19 -0
  42. roomz-0.1.0/docs/api.md +441 -0
  43. roomz-0.1.0/docs/changelog.md +40 -0
  44. roomz-0.1.0/docs/conf.py +26 -0
  45. roomz-0.1.0/docs/configuration.md +397 -0
  46. roomz-0.1.0/docs/contributing.md +108 -0
  47. roomz-0.1.0/docs/index.md +155 -0
  48. roomz-0.1.0/docs/installation.md +75 -0
  49. roomz-0.1.0/docs/quickstart.md +191 -0
  50. roomz-0.1.0/docs/server.md +337 -0
  51. roomz-0.1.0/media/chat.png +0 -0
  52. roomz-0.1.0/media/cli.png +0 -0
  53. roomz-0.1.0/media/login.png +0 -0
  54. roomz-0.1.0/media/magic-link.png +0 -0
  55. roomz-0.1.0/memory/2026-05-14-agile-testing-philosophy.md +214 -0
  56. roomz-0.1.0/memory/2026-05-14-uv-primary-tool.md +99 -0
  57. roomz-0.1.0/memory/websocket-testing.md +76 -0
  58. roomz-0.1.0/pyproject.toml +129 -0
  59. roomz-0.1.0/reporting/I1-001-minimal-chat-broadcast/consensus.md +208 -0
  60. roomz-0.1.0/reporting/I1-001-minimal-chat-broadcast/summary.md +270 -0
  61. roomz-0.1.0/reporting/I1-001-minimal-chat-broadcast/ux-ui-review.md +457 -0
  62. roomz-0.1.0/reporting/I2-001/consensus.md +810 -0
  63. roomz-0.1.0/reporting/I2-001/development-summary.md +203 -0
  64. roomz-0.1.0/reporting/I2-001/summary.md +149 -0
  65. roomz-0.1.0/reporting/I2-001/ux-ui-review.md +349 -0
  66. roomz-0.1.0/reporting/I3-001-python-client/summary.md +178 -0
  67. roomz-0.1.0/reporting/I4-001-jwt-session-tokens/functional-review.md +461 -0
  68. roomz-0.1.0/reporting/I4-001-jwt-sessions/consensus.md +169 -0
  69. roomz-0.1.0/reporting/I4-001-jwt-sessions/summary.md +105 -0
  70. roomz-0.1.0/reporting/I4-002-private-channels/consensus.md +102 -0
  71. roomz-0.1.0/reporting/I4-002-private-channels/development-summary.md +130 -0
  72. roomz-0.1.0/reporting/I4-002-private-channels/functional-review.md +227 -0
  73. roomz-0.1.0/reporting/I4-002-private-channels/summary.md +60 -0
  74. roomz-0.1.0/reporting/I5-001-email-integration/consensus.md +99 -0
  75. roomz-0.1.0/reporting/I5-001-email-integration/functional-review.md +196 -0
  76. roomz-0.1.0/src/roomz/__init__.py +25 -0
  77. roomz-0.1.0/src/roomz/cli/__init__.py +26 -0
  78. roomz-0.1.0/src/roomz/cli/__main__.py +27 -0
  79. roomz-0.1.0/src/roomz/cli/app_tui.py +365 -0
  80. roomz-0.1.0/src/roomz/cli/styles/chat.tcss +40 -0
  81. roomz-0.1.0/src/roomz/client/__init__.py +25 -0
  82. roomz-0.1.0/src/roomz/client/async_client.py +453 -0
  83. roomz-0.1.0/src/roomz/client/events.py +73 -0
  84. roomz-0.1.0/src/roomz/client/exceptions.py +23 -0
  85. roomz-0.1.0/src/roomz/client/state.py +23 -0
  86. roomz-0.1.0/src/roomz/client/sync_client.py +179 -0
  87. roomz-0.1.0/tests/WEBSOCKET_TESTING.md +195 -0
  88. roomz-0.1.0/tests/__init__.py +6 -0
  89. roomz-0.1.0/tests/conftest.py +300 -0
  90. roomz-0.1.0/tests/test_app_helpers.py +142 -0
  91. roomz-0.1.0/tests/test_app_structure.py +378 -0
  92. roomz-0.1.0/tests/test_auth.py +807 -0
  93. roomz-0.1.0/tests/test_chat_page.py +146 -0
  94. roomz-0.1.0/tests/test_email_integration.py +1003 -0
  95. roomz-0.1.0/tests/test_jwt_integration.py +976 -0
  96. roomz-0.1.0/tests/test_jwt_security.py +1313 -0
  97. roomz-0.1.0/tests/test_jwt_unit.py +779 -0
  98. roomz-0.1.0/tests/test_private_channels.py +470 -0
  99. roomz-0.1.0/tests/test_python_client.py +748 -0
  100. roomz-0.1.0/tests/test_session.py +122 -0
  101. roomz-0.1.0/tests/test_socketio_broadcast.py +526 -0
  102. roomz-0.1.0/tests/test_websocket.py +762 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Skill(c3:commit)",
5
+ "Skill(c3:python)",
6
+ "Bash(uv run *)",
7
+ "Bash(uv sync *)",
8
+ "Bash(uv pip *)",
9
+ "Skill(c3:readme)",
10
+ "Bash(uv add *)",
11
+ "WebSearch",
12
+ "WebFetch(domain:python-socketio.readthedocs.io)",
13
+ "Skill(c3:textual)",
14
+ "Bash(cat)",
15
+ "Bash(python3 /tmp/fix_websocket.py)",
16
+ "Bash(git restore *)",
17
+ "Bash(make html *)",
18
+ "Bash(make typecheck *)",
19
+ "Bash(make build *)"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1,112 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ jobs:
10
+ lint:
11
+ name: Lint
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.10"
25
+
26
+ - name: Install dependencies
27
+ run: uv sync --extra dev
28
+
29
+ - name: Run ruff linting
30
+ run: uv run ruff check app/ tests/
31
+
32
+ test:
33
+ name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }})
34
+ runs-on: ${{ matrix.os }}
35
+ strategy:
36
+ fail-fast: false
37
+ matrix:
38
+ os: [ubuntu-latest, macos-latest, windows-latest]
39
+ python-version: ["3.10", "3.11", "3.12"]
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+
43
+ - name: Install uv
44
+ uses: astral-sh/setup-uv@v5
45
+ with:
46
+ enable-cache: true
47
+
48
+ - name: Set up Python ${{ matrix.python-version }}
49
+ uses: actions/setup-python@v5
50
+ with:
51
+ python-version: ${{ matrix.python-version }}
52
+
53
+ - name: Install dependencies
54
+ run: uv sync --extra dev
55
+
56
+ - name: Run tests with coverage
57
+ run: uv run pytest -v --cov=app --cov=src/roomz --cov-report=xml --cov-report=term
58
+
59
+ - name: Upload coverage to Coveralls
60
+ if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
61
+ uses: coverallsapp/github-action@v2
62
+ with:
63
+ github-token: ${{ secrets.GITHUB_TOKEN }}
64
+
65
+ typecheck:
66
+ name: Type Check
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+
71
+ - name: Install uv
72
+ uses: astral-sh/setup-uv@v5
73
+ with:
74
+ enable-cache: true
75
+
76
+ - name: Set up Python
77
+ uses: actions/setup-python@v5
78
+ with:
79
+ python-version: "3.10"
80
+
81
+ - name: Install dependencies
82
+ run: uv sync --extra dev
83
+
84
+ - name: Run mypy
85
+ run: uv run mypy app/
86
+
87
+ build:
88
+ name: Build Package
89
+ runs-on: ubuntu-latest
90
+ needs: [lint, test, typecheck]
91
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
92
+ steps:
93
+ - uses: actions/checkout@v4
94
+
95
+ - name: Install uv
96
+ uses: astral-sh/setup-uv@v5
97
+ with:
98
+ enable-cache: true
99
+
100
+ - name: Set up Python
101
+ uses: actions/setup-python@v5
102
+ with:
103
+ python-version: "3.10"
104
+
105
+ - name: Build package
106
+ run: uv build
107
+
108
+ - name: Upload artifacts
109
+ uses: actions/upload-artifact@v4
110
+ with:
111
+ name: dist
112
+ path: dist/
@@ -0,0 +1,53 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ target:
9
+ description: 'Target repository'
10
+ required: true
11
+ default: 'testpypi'
12
+ type: choice
13
+ options:
14
+ - testpypi
15
+ - pypi
16
+
17
+ jobs:
18
+ publish:
19
+ name: Build and Publish
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ id-token: write # Required for trusted publishing
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v5
28
+ with:
29
+ enable-cache: true
30
+
31
+ - name: Set up Python
32
+ uses: actions/setup-python@v5
33
+ with:
34
+ python-version: "3.12"
35
+
36
+ - name: Install dependencies
37
+ run: uv sync --extra dev
38
+
39
+ - name: Run tests
40
+ run: uv run pytest -v
41
+
42
+ - name: Build package
43
+ run: uv build
44
+
45
+ - name: Publish to TestPyPI
46
+ if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
47
+ uses: pypa/gh-action-pypi-publish@release/v1
48
+ with:
49
+ repository-url: https://test.pypi.org/legacy/
50
+
51
+ - name: Publish to PyPI
52
+ if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
53
+ uses: pypa/gh-action-pypi-publish@release/v1
roomz-0.1.0/.gitignore ADDED
@@ -0,0 +1,53 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+
28
+ # IDE
29
+ .vscode/
30
+ .idea/
31
+ *.swp
32
+ *.swo
33
+ *~
34
+
35
+ # Testing
36
+ .pytest_cache/
37
+ .coverage
38
+ htmlcov/
39
+ coverage.xml
40
+ .mypy_cache/
41
+ .ruff_cache/
42
+ .tox/
43
+
44
+ # Documentation
45
+ docs/_build/
46
+
47
+ # Project specific
48
+ *.log
49
+
50
+ # Distribution
51
+ uv.lock
52
+ .env.local
53
+ resend_email_test.py
@@ -0,0 +1,20 @@
1
+ # .readthedocs.yaml
2
+ # Read the Docs configuration file
3
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4
+
5
+ version: 2
6
+
7
+ build:
8
+ os: ubuntu-22.04
9
+ tools:
10
+ python: "3.12"
11
+
12
+ sphinx:
13
+ configuration: docs/conf.py
14
+
15
+ python:
16
+ install:
17
+ - method: pip
18
+ path: .
19
+ extra_requirements:
20
+ - docs
roomz-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christophe VG
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
roomz-0.1.0/Makefile ADDED
@@ -0,0 +1,68 @@
1
+ # Makefile for Roomz
2
+ # Utility targets for common development actions
3
+
4
+ -include ~/.claude/Makefile
5
+
6
+ .PHONY: run-env dev-env test test-all lint format typecheck build publish clean all help run
7
+
8
+ ## run-env: Install production dependencies
9
+ run-env:
10
+ uv sync
11
+
12
+ ## dev-env: Install with all dependencies (dev, extras)
13
+ dev-env:
14
+ uv sync --extra dev
15
+
16
+ ## test: Run tests with pytest
17
+ test: dev-env
18
+ uv run pytest -v
19
+
20
+ ## test-all: Run tests with tox (all Python versions)
21
+ test-all: dev-env
22
+ uv run tox
23
+
24
+ ## test-cov: Run tests with coverage report
25
+ test-cov: dev-env
26
+ uv run pytest --cov=app --cov-report=term-missing
27
+
28
+ ## lint: Run ruff linting
29
+ lint: dev-env
30
+ uv run ruff check app/ tests/ src/
31
+
32
+ ## format: Format code with ruff
33
+ format: dev-env
34
+ uv run ruff check --fix app/ tests/
35
+
36
+ ## typecheck: Run mypy type checking
37
+ typecheck: dev-env
38
+ uv run mypy app/ src/
39
+
40
+ ## build: Build distribution packages
41
+ build: dev-env
42
+ uv build
43
+
44
+ ## publish: Publish to PyPI (requires credentials)
45
+ publish: build
46
+ uv run twine upload dist/*
47
+
48
+ ## publish-test: Publish to TestPyPI (requires credentials)
49
+ publish-test: build
50
+ uv run twine upload --repository testpypi dist/*
51
+
52
+ ## clean: Clean build artifacts
53
+ clean:
54
+ rm -rf dist/ build/ *.egg-info .pytest_cache .coverage .mypy_cache .ruff_cache .tox
55
+ find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
56
+
57
+ ## check: Run format, lint, test, and typecheck
58
+ check: dev-env format lint test typecheck
59
+
60
+ ## run: Run the development server
61
+ run: run-env
62
+ uv run gunicorn -k uvicorn.workers.UvicornWorker app:asgi_app --reload
63
+
64
+ ## help: Show this help message
65
+ help:
66
+ @echo "Available targets:"
67
+ @echo ""
68
+ @sed -n 's/^## //p' $(MAKEFILE_LIST) | column -t -s ':'
roomz-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,353 @@
1
+ Metadata-Version: 2.4
2
+ Name: roomz
3
+ Version: 0.1.0
4
+ Summary: Real-time chatroom web service with magic link authentication
5
+ Project-URL: Homepage, https://github.com/christophevg/roomz
6
+ Project-URL: Documentation, https://github.com/christophevg/roomz#readme
7
+ Project-URL: Repository, https://github.com/christophevg/roomz
8
+ Project-URL: Issues, https://github.com/christophevg/roomz/issues
9
+ Author-email: Christophe VG <contact@christophe.vg>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: authentication,chat,quart,real-time,socketio,websocket
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: aiohttp>=3.9.0
25
+ Requires-Dist: baseweb>=0.5.2
26
+ Requires-Dist: gunicorn>=21.0.0
27
+ Requires-Dist: pyjwt>=2.8.0
28
+ Requires-Dist: python-dotenv>=1.2.2
29
+ Requires-Dist: python-socketio[asyncio]>=5.10.0
30
+ Requires-Dist: resend>=2.30.1
31
+ Requires-Dist: rich>=13.0.0
32
+ Requires-Dist: textual>=0.47.0
33
+ Requires-Dist: uvicorn[standard]>=0.24.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: mypy>=1.9.0; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
37
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
38
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
39
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
40
+ Requires-Dist: tomli>=2.0.0; extra == 'dev'
41
+ Requires-Dist: tox>=4.0.0; extra == 'dev'
42
+ Requires-Dist: twine>=6.0.0; extra == 'dev'
43
+ Provides-Extra: docs
44
+ Requires-Dist: myst-parser>=2.0.0; extra == 'docs'
45
+ Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == 'docs'
46
+ Requires-Dist: sphinx>=7.0.0; extra == 'docs'
47
+ Description-Content-Type: text/markdown
48
+
49
+ # Roomz
50
+
51
+ [![PyPI version](https://img.shields.io/pypi/v/roomz.svg)](https://pypi.org/project/roomz/)
52
+ [![PyPI downloads](https://img.shields.io/pypi/dm/roomz.svg)](https://pypistats.org/packages/roomz)
53
+ [![Python versions](https://img.shields.io/pypi/pyversions/roomz.svg)](https://pypi.org/project/roomz/)
54
+ [![License](https://img.shields.io/github/license/christophevg/roomz.svg)](https://github.com/christophevg/roomz/blob/main/LICENSE)
55
+ [![CI](https://github.com/christophevg/roomz/actions/workflows/ci.yml/badge.svg)](https://github.com/christophevg/roomz/actions/workflows/ci.yml)
56
+ [![Coverage](https://coveralls.io/repos/github/christophevg/roomz/badge.svg)](https://coveralls.io/github/christophevg/roomz)
57
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-blue.svg)](https://github.com/astral-sh/ruff)
58
+ [![Type checked: mypy](https://img.shields.io/badge/type%20checked-mypy-blue.svg)](https://mypy.readthedocs.io/)
59
+ [![Agentic](https://img.shields.io/badge/workflow-agentic-blueviolet?style=flat-square)](https://christophe.vg/about/Coding-Agent)
60
+
61
+ A real-time chatroom web service with magic link authentication.
62
+
63
+ ## What is Roomz?
64
+
65
+ Roomz is a real-time chat application with secure magic link authentication. Built with modern async technology (Quart + SocketIO), it provides seamless real-time messaging with passwordless login.
66
+
67
+ ## Screenshots
68
+
69
+ | Login | Magic Link | Chat | CLI |
70
+ |-------|-------------|------|-----|
71
+ | ![Login](media/login.png) | ![Magic Link](media/magic-link.png) | ![Chat](media/chat.png) | ![CLI](media/cli.png) |
72
+
73
+ ## Features
74
+
75
+ - **Magic Link Authentication**: Passwordless login via email
76
+ - **Instant Messaging**: Messages appear instantly across all connected users
77
+ - **Real-time Updates**: See when users join or leave
78
+ - **Responsive Design**: Works on desktop, tablet, and mobile
79
+ - **Connection Status**: Visual indicator shows when disconnected
80
+ - **Accessibility**: Keyboard navigation and screen reader support
81
+
82
+ ## Quick Start
83
+
84
+ ### Prerequisites
85
+
86
+ - Python 3.10 or higher
87
+ - [uv](https://docs.astral.sh/uv/) package manager
88
+
89
+ ### Installation
90
+
91
+ ```bash
92
+ # Clone or navigate to the project
93
+ cd /path/to/roomz
94
+
95
+ # Install dependencies
96
+ uv sync
97
+
98
+ # Install dev dependencies (for testing)
99
+ uv sync --extra dev
100
+ ```
101
+
102
+ ### Running the Application
103
+
104
+ ```bash
105
+ # Start the chat server
106
+ uv run gunicorn -k uvicorn.workers.UvicornWorker app:asgi_app
107
+
108
+ # Or for development with auto-reload:
109
+ uv run uvicorn app:asgi_app --reload --host 0.0.0.0 --port 8000
110
+ ```
111
+
112
+ Open [http://localhost:8000](http://localhost:8000) in your browser.
113
+
114
+ ### Configuration
115
+
116
+ Roomz uses environment variables for configuration. Create a `.env` file in the project root:
117
+
118
+ ```bash
119
+ # Required: JWT secret key (minimum 32 characters)
120
+ # Generate with: python -c "import secrets; print(secrets.token_urlsafe(32))"
121
+ JWT_SECRET_KEY=your-256-bit-secret-key-here
122
+
123
+ # Required: Comma-separated list of allowed email addresses
124
+ ALLOWED_EMAILS=user@example.com,other@example.com
125
+
126
+ # Email Configuration (choose one)
127
+ # Development: Log magic links to console (default)
128
+ EMAIL_SENDER=console
129
+
130
+ # Production: Send emails via Resend
131
+ # EMAIL_SENDER=resend
132
+ # RESEND_API_KEY=re_your_api_key_here
133
+ # EMAIL_FROM=noreply@yourdomain.com # Optional, defaults to no-reply@example.com
134
+
135
+ # Optional: JWT token expiration in days (default: 30)
136
+ JWT_EXPIRY_DAYS=30
137
+
138
+ # Optional: Magic link expiration in minutes (default: 15)
139
+ MAGIC_LINK_EXPIRY_MINUTES=15
140
+
141
+ # Optional: Rate limit for magic link requests per email per hour (default: 5)
142
+ MAGIC_LINK_RATE_LIMIT=5
143
+ ```
144
+
145
+ **Security Notes:**
146
+ - `JWT_SECRET_KEY` must be at least 32 characters (256 bits)
147
+ - `ALLOWED_EMAILS` controls who can authenticate
148
+ - Removing an email from `ALLOWED_EMAILS` immediately revokes their access
149
+ - Session cookies are httpOnly and SameSite=Strict
150
+
151
+ **Email Setup:**
152
+ - **Development**: Set `EMAIL_SENDER=console` (or leave unset). Magic links are logged to the server console.
153
+ - **Production**: Set `EMAIL_SENDER=resend` and provide `RESEND_API_KEY`. Get your API key from [resend.com](https://resend.com).
154
+
155
+ ### Testing
156
+
157
+ ```bash
158
+ # Run all tests
159
+ uv run pytest tests/ -v
160
+
161
+ # Run tests with coverage
162
+ uv run pytest --cov=app --cov-report=term-missing
163
+
164
+ # Run tests across Python versions
165
+ uv run tox
166
+ ```
167
+
168
+ ## How to Use
169
+
170
+ ### Authentication
171
+
172
+ 1. Open the application in your browser
173
+ 2. Enter your email address
174
+ 3. Click "Send Magic Link"
175
+ 4. Check the server console for the magic link (development mode)
176
+ 5. Click the magic link to authenticate
177
+ 6. You're now in the chat!
178
+
179
+ ### Chatting
180
+
181
+ 1. After authentication, you see the chat interface
182
+ 2. Type a message in the input field at the bottom
183
+ 3. Press **Enter** or click the **Send** button
184
+ 4. Your message appears instantly to all connected users
185
+
186
+ ### Multiple Users
187
+
188
+ 1. Open the application in multiple browser tabs or windows
189
+ 2. Authenticate in each tab (can use same or different email)
190
+ 3. Type messages in any tab
191
+ 4. All tabs see the messages instantly
192
+ 5. System messages show when users join or leave
193
+
194
+ ## Python Client Library
195
+
196
+ Roomz includes a Python client library for programmatic access to the chat service.
197
+
198
+ ### Using the CLI
199
+
200
+ The easiest way to use Roomz from the command line:
201
+
202
+ ```bash
203
+ # Start the CLI
204
+ uv run roomz-cli
205
+
206
+ # Or with a custom server
207
+ uv run roomz-cli --server http://your-server:8000
208
+ ```
209
+
210
+ **Commands:**
211
+ - `/login <email>` - Request a magic link
212
+ - `/token <token>` - Connect with magic link token
213
+ - `/logout` - Disconnect and clear session
214
+ - `/quit` - Exit the CLI
215
+
216
+ **Features:**
217
+ - Session caching (auto-reconnect on restart)
218
+ - Split-screen TUI with message history
219
+ - Color-coded messages (your messages in green)
220
+ - Multiline support (Enter to send, Ctrl+Enter for new line)
221
+
222
+ ### Using the AsyncClient
223
+
224
+ For programmatic access in your Python applications:
225
+
226
+ ```python
227
+ from roomz.client import AsyncClient
228
+
229
+ # Create client with session caching
230
+ client = AsyncClient(
231
+ server_url="http://localhost:8000",
232
+ session_cache_file="~/.roomz/session.json"
233
+ )
234
+
235
+ # Register event handlers
236
+ client.on("message", lambda data: print(f"{data['user']['email']}: {data['content']}"))
237
+ client.on("user_joined", lambda data: print(f"{data['user']['email']} joined"))
238
+
239
+ # Request magic link
240
+ await client.login("user@example.com")
241
+
242
+ # Connect with magic link token
243
+ await client.connect(token="magic-link-token")
244
+
245
+ # Or reconnect with cached session
246
+ await client.connect()
247
+
248
+ # Send message
249
+ result = await client.send("Hello, world!")
250
+ if "error" in result:
251
+ print(f"Failed: {result['error']}")
252
+
253
+ # Disconnect
254
+ await client.disconnect()
255
+ client.clear_cached_session()
256
+ ```
257
+
258
+ ### Using the SyncClient
259
+
260
+ For synchronous applications:
261
+
262
+ ```python
263
+ from roomz.client import SyncClient
264
+
265
+ with SyncClient(server_url="http://localhost:8000", session_token="token") as client:
266
+ client.on("message", lambda data: print(data['content']))
267
+ result = client.send("Hello!")
268
+ ```
269
+
270
+ ### Session Caching
271
+
272
+ The client can cache session cookies for automatic reconnection:
273
+
274
+ ```python
275
+ # Enable caching (recommended for CLI apps)
276
+ client = AsyncClient(
277
+ server_url="http://localhost:8000",
278
+ session_cache_file=Path.home() / ".roomz" / "session.json"
279
+ )
280
+
281
+ # Disable caching (default)
282
+ client = AsyncClient(server_url="http://localhost:8000")
283
+ ```
284
+
285
+ ## Technology Stack
286
+
287
+ | Layer | Technology |
288
+ |-------|------------|
289
+ | Backend | Quart (async Flask), SocketIO |
290
+ | Frontend | Vue 3, Vuetify 4 |
291
+ | Framework | Baseweb |
292
+ | Runtime | Python 3.10+ |
293
+ | Server | Gunicorn + Uvicorn |
294
+ | Auth | Magic links with httpOnly cookies |
295
+
296
+ ## Architecture
297
+
298
+ ```
299
+ Browser (Vue 3 + Vuetify 4)
300
+ ↓ HTTP POST /auth/request-magic-link
301
+ Magic Link Email (or console in dev)
302
+ ↓ HTTP GET /auth/verify?token=...
303
+ JWT Cookie Set (httpOnly, SameSite=Strict)
304
+ ↓ WebSocket with JWT cookie auth
305
+ Quart Server + SocketIO
306
+ ↓ JWT Validation + ALLOWED_EMAILS check
307
+ Connected Users
308
+ ```
309
+
310
+ **Stateless Authentication**: Sessions use JWT tokens, enabling server restarts without losing sessions. Access is controlled by the `ALLOWED_EMAILS` environment variable.
311
+
312
+ ## Project Structure
313
+
314
+ ```
315
+ roomz/
316
+ ├── app/
317
+ │ ├── __init__.py # Quart app + SocketIO + Auth endpoints
318
+ │ ├── auth.py # Magic link and session management
319
+ │ ├── models.py # Session and magic link models
320
+ │ ├── components/auth/ # AuthDialog Vue component
321
+ │ └── pages/chat/ # Chat page Vue component
322
+ ├── src/roomz/
323
+ │ ├── client/ # Python client library
324
+ │ │ ├── async_client.py # AsyncClient implementation
325
+ │ │ ├── sync_client.py # SyncClient wrapper
326
+ │ │ ├── events.py # Event emitter
327
+ │ │ └── exceptions.py # Client exceptions
328
+ │ └── cli/ # Command-line interface
329
+ │ ├── app_tui.py # Textual TUI application
330
+ │ └── styles/ # TUI stylesheets
331
+ ├── tests/ # Test suite
332
+ ├── analysis/ # Design documents
333
+ ├── reporting/ # Task reports
334
+ ├── pyproject.toml # Project configuration
335
+ └── README.md # This file
336
+ ```
337
+
338
+ ## Development
339
+
340
+ See [TODO.md](TODO.md) for planned features and [REQUIREMENTS.md](REQUIREMENTS.md) for full requirements list.
341
+
342
+ ## License
343
+
344
+ MIT License - See [LICENSE](LICENSE) for details.
345
+
346
+ ## Credits
347
+
348
+ Built with:
349
+ - [Baseweb](https://github.com/christophevg/baseweb) — Web framework
350
+ - [Quart](https://pgjones.gitlab.io/quart/) — Async Flask
351
+ - [Socket.IO](https://python-socketio.readthedocs.io/) — Real-time communication
352
+ - [Vue 3](https://vuejs.org/) — Frontend framework
353
+ - [Vuetify 4](https://vuetifyjs.com/) — Material Design components