kryten-moderator 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.
@@ -0,0 +1,212 @@
1
+ # Installation Guide
2
+
3
+ ## Prerequisites
4
+
5
+ - Python 3.10 or higher
6
+ - Poetry (Python package manager)
7
+ - NATS server (running and accessible)
8
+ - Git
9
+
10
+ ## Quick Start
11
+
12
+ ### 1. Clone the Repository
13
+
14
+ ```bash
15
+ git clone https://github.com/grobertson/kryten-moderator.git
16
+ cd kryten-moderator
17
+ ```
18
+
19
+ ### 2. Install Dependencies
20
+
21
+ ```bash
22
+ poetry install
23
+ ```
24
+
25
+ ### 3. Configure the Service
26
+
27
+ Copy the example configuration:
28
+
29
+ ```bash
30
+ cp config.example.json config.json
31
+ ```
32
+
33
+ Edit `config.json` with your settings:
34
+
35
+ ```json
36
+ {
37
+ "nats_url": "nats://localhost:4222",
38
+ "nats_subject_prefix": "cytube",
39
+ "service_name": "kryten-moderator"
40
+ }
41
+ ```
42
+
43
+ ### 4. Run the Service
44
+
45
+ **Development mode:**
46
+
47
+ ```bash
48
+ poetry run kryten-moderator --config config.json --log-level DEBUG
49
+ ```
50
+
51
+ **Using startup script (PowerShell):**
52
+
53
+ ```powershell
54
+ .\start-moderator.ps1
55
+ ```
56
+
57
+ **Using startup script (Bash):**
58
+
59
+ ```bash
60
+ ./start-moderator.sh
61
+ ```
62
+
63
+ ## Production Installation
64
+
65
+ ### System User Setup
66
+
67
+ Create a dedicated system user:
68
+
69
+ ```bash
70
+ sudo useradd -r -s /bin/false -d /opt/kryten-moderator kryten
71
+ ```
72
+
73
+ ### Installation Directory
74
+
75
+ ```bash
76
+ sudo mkdir -p /opt/kryten-moderator
77
+ sudo chown kryten:kryten /opt/kryten-moderator
78
+ ```
79
+
80
+ ### Configuration Directory
81
+
82
+ ```bash
83
+ sudo mkdir -p /etc/kryten/moderator
84
+ sudo chown kryten:kryten /etc/kryten/moderator
85
+ sudo cp config.example.json /etc/kryten/moderator/config.json
86
+ sudo chown kryten:kryten /etc/kryten/moderator/config.json
87
+ sudo chmod 600 /etc/kryten/moderator/config.json
88
+ ```
89
+
90
+ Edit the configuration:
91
+
92
+ ```bash
93
+ sudo nano /etc/kryten/moderator/config.json
94
+ ```
95
+
96
+ ### Log Directory
97
+
98
+ ```bash
99
+ sudo mkdir -p /var/log/kryten-moderator
100
+ sudo chown kryten:kryten /var/log/kryten-moderator
101
+ ```
102
+
103
+ ### Install Application
104
+
105
+ ```bash
106
+ cd /opt/kryten-moderator
107
+ sudo -u kryten git clone https://github.com/grobertson/kryten-moderator.git .
108
+ sudo -u kryten poetry install --no-dev
109
+ ```
110
+
111
+ ### Systemd Service
112
+
113
+ Install the systemd service:
114
+
115
+ ```bash
116
+ sudo cp systemd/kryten-moderator.service /etc/systemd/system/
117
+ sudo systemctl daemon-reload
118
+ sudo systemctl enable kryten-moderator
119
+ sudo systemctl start kryten-moderator
120
+ ```
121
+
122
+ Check service status:
123
+
124
+ ```bash
125
+ sudo systemctl status kryten-moderator
126
+ sudo journalctl -u kryten-moderator -f
127
+ ```
128
+
129
+ ## Updating
130
+
131
+ ### Development
132
+
133
+ ```bash
134
+ git pull
135
+ poetry install
136
+ ```
137
+
138
+ ### Production
139
+
140
+ ```bash
141
+ cd /opt/kryten-moderator
142
+ sudo systemctl stop kryten-moderator
143
+ sudo -u kryten git pull
144
+ sudo -u kryten poetry install --no-dev
145
+ sudo systemctl start kryten-moderator
146
+ ```
147
+
148
+ ## Troubleshooting
149
+
150
+ ### Check NATS Connection
151
+
152
+ Ensure NATS server is running:
153
+
154
+ ```bash
155
+ # Check if NATS is listening
156
+ netstat -tuln | grep 4222
157
+ ```
158
+
159
+ ### View Logs
160
+
161
+ ```bash
162
+ # Systemd service logs
163
+ sudo journalctl -u kryten-moderator -f
164
+
165
+ # Check for errors
166
+ sudo journalctl -u kryten-moderator --since "1 hour ago" | grep -i error
167
+ ```
168
+
169
+ ### Test Configuration
170
+
171
+ ```bash
172
+ # Validate JSON syntax
173
+ python -m json.tool config.json
174
+
175
+ # Test connection manually
176
+ poetry run kryten-moderator --config config.json --log-level DEBUG
177
+ ```
178
+
179
+ ### Permissions Issues
180
+
181
+ Ensure correct ownership:
182
+
183
+ ```bash
184
+ sudo chown -R kryten:kryten /opt/kryten-moderator
185
+ sudo chown -R kryten:kryten /etc/kryten/moderator
186
+ sudo chown -R kryten:kryten /var/log/kryten-moderator
187
+ ```
188
+
189
+ ## Uninstalling
190
+
191
+ ### Stop and Disable Service
192
+
193
+ ```bash
194
+ sudo systemctl stop kryten-moderator
195
+ sudo systemctl disable kryten-moderator
196
+ sudo rm /etc/systemd/system/kryten-moderator.service
197
+ sudo systemctl daemon-reload
198
+ ```
199
+
200
+ ### Remove Files
201
+
202
+ ```bash
203
+ sudo rm -rf /opt/kryten-moderator
204
+ sudo rm -rf /etc/kryten/moderator
205
+ sudo rm -rf /var/log/kryten-moderator
206
+ ```
207
+
208
+ ### Remove User
209
+
210
+ ```bash
211
+ sudo userdel kryten
212
+ ```
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Kryten Robot Team
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.
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: kryten-moderator
3
+ Version: 0.1.0
4
+ Summary: Kryten moderation service - handles chat moderation and filtering
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: cytube,moderation,chat,nats,microservices
8
+ Author: Kryten Robot Team
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Communications :: Chat
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Dist: kryten-py (>=0.6.0,<0.7.0)
22
+ Project-URL: Documentation, https://github.com/grobertson/kryten-moderator/blob/main/README.md
23
+ Project-URL: Homepage, https://github.com/grobertson/kryten-moderator
24
+ Project-URL: Repository, https://github.com/grobertson/kryten-moderator
25
+ Description-Content-Type: text/markdown
26
+
27
+ "# Kryten Moderator
28
+
29
+ Kryten moderation service - handles chat moderation and filtering for CyTube.
30
+
31
+ ## Features
32
+
33
+ - Real-time chat message monitoring
34
+ - User tracking and management
35
+ - Event-driven architecture using NATS
36
+ - Extensible moderation rules
37
+
38
+ ## Installation
39
+
40
+ ### Prerequisites
41
+
42
+ - Python 3.10 or higher
43
+ - Poetry
44
+ - NATS server running
45
+ - kryten-py library
46
+
47
+ ### Setup
48
+
49
+ 1. Install dependencies:
50
+ ```bash
51
+ poetry install
52
+ ```
53
+
54
+ 2. Copy the example configuration:
55
+ ```bash
56
+ cp config.example.json config.json
57
+ ```
58
+
59
+ 3. Edit `config.json` with your settings:
60
+ ```json
61
+ {
62
+ "nats_url": "nats://localhost:4222",
63
+ "nats_subject_prefix": "cytube",
64
+ "service_name": "kryten-moderator"
65
+ }
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ ### Running the Service
71
+
72
+ Using Poetry:
73
+ ```bash
74
+ poetry run kryten-moderator --config config.json
75
+ ```
76
+
77
+ Using the startup script (PowerShell):
78
+ ```powershell
79
+ .\start-moderator.ps1
80
+ ```
81
+
82
+ Using the startup script (Bash):
83
+ ```bash
84
+ ./start-moderator.sh
85
+ ```
86
+
87
+ ### Command Line Options
88
+
89
+ - `--config PATH`: Path to configuration file (default: `/etc/kryten/moderator/config.json`)
90
+ - `--log-level LEVEL`: Set logging level (DEBUG, INFO, WARNING, ERROR)
91
+
92
+ ## Event Handling
93
+
94
+ The service currently listens for:
95
+
96
+ - **chatMsg**: Chat messages from users
97
+ - **addUser**: User join events
98
+
99
+ ## Development
100
+
101
+ ### Running Tests
102
+
103
+ ```bash
104
+ poetry run pytest
105
+ ```
106
+
107
+ ### Linting
108
+
109
+ ```bash
110
+ poetry run ruff check .
111
+ ```
112
+
113
+ ### Formatting
114
+
115
+ ```bash
116
+ poetry run black .
117
+ ```
118
+
119
+ ## License
120
+
121
+ MIT License - see LICENSE file for details
122
+ "
123
+
@@ -0,0 +1,96 @@
1
+ "# Kryten Moderator
2
+
3
+ Kryten moderation service - handles chat moderation and filtering for CyTube.
4
+
5
+ ## Features
6
+
7
+ - Real-time chat message monitoring
8
+ - User tracking and management
9
+ - Event-driven architecture using NATS
10
+ - Extensible moderation rules
11
+
12
+ ## Installation
13
+
14
+ ### Prerequisites
15
+
16
+ - Python 3.10 or higher
17
+ - Poetry
18
+ - NATS server running
19
+ - kryten-py library
20
+
21
+ ### Setup
22
+
23
+ 1. Install dependencies:
24
+ ```bash
25
+ poetry install
26
+ ```
27
+
28
+ 2. Copy the example configuration:
29
+ ```bash
30
+ cp config.example.json config.json
31
+ ```
32
+
33
+ 3. Edit `config.json` with your settings:
34
+ ```json
35
+ {
36
+ "nats_url": "nats://localhost:4222",
37
+ "nats_subject_prefix": "cytube",
38
+ "service_name": "kryten-moderator"
39
+ }
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ### Running the Service
45
+
46
+ Using Poetry:
47
+ ```bash
48
+ poetry run kryten-moderator --config config.json
49
+ ```
50
+
51
+ Using the startup script (PowerShell):
52
+ ```powershell
53
+ .\start-moderator.ps1
54
+ ```
55
+
56
+ Using the startup script (Bash):
57
+ ```bash
58
+ ./start-moderator.sh
59
+ ```
60
+
61
+ ### Command Line Options
62
+
63
+ - `--config PATH`: Path to configuration file (default: `/etc/kryten/moderator/config.json`)
64
+ - `--log-level LEVEL`: Set logging level (DEBUG, INFO, WARNING, ERROR)
65
+
66
+ ## Event Handling
67
+
68
+ The service currently listens for:
69
+
70
+ - **chatMsg**: Chat messages from users
71
+ - **addUser**: User join events
72
+
73
+ ## Development
74
+
75
+ ### Running Tests
76
+
77
+ ```bash
78
+ poetry run pytest
79
+ ```
80
+
81
+ ### Linting
82
+
83
+ ```bash
84
+ poetry run ruff check .
85
+ ```
86
+
87
+ ### Formatting
88
+
89
+ ```bash
90
+ poetry run black .
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT License - see LICENSE file for details
96
+ "
@@ -0,0 +1,22 @@
1
+ """Kryten Moderator Service - Chat moderation and filtering."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+
7
+ def _read_version() -> str:
8
+ """Read version from VERSION file."""
9
+ # Try package root first
10
+ version_file = Path(__file__).parent / "VERSION"
11
+ if version_file.exists():
12
+ return version_file.read_text().strip()
13
+
14
+ # Try repository root
15
+ version_file = Path(__file__).parent.parent / "VERSION"
16
+ if version_file.exists():
17
+ return version_file.read_text().strip()
18
+
19
+ return "0.0.0"
20
+
21
+
22
+ __version__ = _read_version()
@@ -0,0 +1,81 @@
1
+ """Main entry point for kryten-moderator service."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ import logging
6
+ import signal
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from kryten_moderator.service import ModeratorService
11
+
12
+
13
+ def setup_logging(level: str = "INFO") -> None:
14
+ """Configure logging for the service."""
15
+ logging.basicConfig(
16
+ level=getattr(logging, level.upper()),
17
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
18
+ handlers=[logging.StreamHandler(sys.stdout)],
19
+ )
20
+
21
+
22
+ def parse_args() -> argparse.Namespace:
23
+ """Parse command line arguments."""
24
+ parser = argparse.ArgumentParser(description="Kryten Moderator Service")
25
+ parser.add_argument(
26
+ "--config",
27
+ type=Path,
28
+ default=Path("/etc/kryten/moderator/config.json"),
29
+ help="Path to configuration file (default: /etc/kryten/moderator/config.json)",
30
+ )
31
+ parser.add_argument(
32
+ "--log-level",
33
+ choices=["DEBUG", "INFO", "WARNING", "ERROR"],
34
+ default="INFO",
35
+ help="Logging level (default: INFO)",
36
+ )
37
+ return parser.parse_args()
38
+
39
+
40
+ async def main_async() -> None:
41
+ """Main async entry point."""
42
+ args = parse_args()
43
+ setup_logging(args.log_level)
44
+
45
+ logger = logging.getLogger(__name__)
46
+ logger.info("Starting Kryten Moderator Service")
47
+
48
+ service = ModeratorService(config_path=args.config)
49
+
50
+ # Setup signal handlers
51
+ loop = asyncio.get_event_loop()
52
+
53
+ def signal_handler(sig: int) -> None:
54
+ logger.info(f"Received signal {sig}, shutting down...")
55
+ asyncio.create_task(service.stop())
56
+
57
+ for sig in (signal.SIGTERM, signal.SIGINT):
58
+ loop.add_signal_handler(sig, lambda s=sig: signal_handler(s))
59
+
60
+ try:
61
+ await service.start()
62
+ await service.wait_for_shutdown()
63
+ except KeyboardInterrupt:
64
+ logger.info("Keyboard interrupt received")
65
+ except Exception as e:
66
+ logger.error(f"Service error: {e}", exc_info=True)
67
+ sys.exit(1)
68
+ finally:
69
+ await service.stop()
70
+
71
+
72
+ def main() -> None:
73
+ """Main entry point."""
74
+ try:
75
+ asyncio.run(main_async())
76
+ except KeyboardInterrupt:
77
+ pass
78
+
79
+
80
+ if __name__ == "__main__":
81
+ main()
@@ -0,0 +1,42 @@
1
+ """Configuration management for kryten-moderator."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+
8
+ class Config:
9
+ """Service configuration."""
10
+
11
+ def __init__(self, config_path: Path):
12
+ """Initialize configuration from file."""
13
+ self.config_path = config_path
14
+ self._config: dict[str, Any] = {}
15
+ self.load()
16
+
17
+ def load(self) -> None:
18
+ """Load configuration from file."""
19
+ if not self.config_path.exists():
20
+ raise FileNotFoundError(f"Configuration file not found: {self.config_path}")
21
+
22
+ with open(self.config_path, encoding="utf-8") as f:
23
+ self._config = json.load(f)
24
+
25
+ def get(self, key: str, default: Any = None) -> Any:
26
+ """Get configuration value."""
27
+ return self._config.get(key, default)
28
+
29
+ @property
30
+ def nats_url(self) -> str:
31
+ """Get NATS server URL."""
32
+ return self.get("nats_url", "nats://localhost:4222")
33
+
34
+ @property
35
+ def nats_subject_prefix(self) -> str:
36
+ """Get NATS subject prefix."""
37
+ return self.get("nats_subject_prefix", "cytube")
38
+
39
+ @property
40
+ def service_name(self) -> str:
41
+ """Get service name."""
42
+ return self.get("service_name", "kryten-moderator")
@@ -0,0 +1,74 @@
1
+ """Main service class for kryten-moderator."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from pathlib import Path
6
+
7
+ from kryten import KrytenClient
8
+
9
+ from kryten_moderator.config import Config
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ModeratorService:
15
+ """Kryten Moderator Service."""
16
+
17
+ def __init__(self, config_path: Path):
18
+ """Initialize the service."""
19
+ self.config = Config(config_path)
20
+ self.client = KrytenClient(
21
+ nats_url=self.config.nats_url,
22
+ subject_prefix=self.config.nats_subject_prefix,
23
+ service_name=self.config.service_name,
24
+ )
25
+ self._shutdown_event = asyncio.Event()
26
+
27
+ async def start(self) -> None:
28
+ """Start the service."""
29
+ logger.info("Starting moderator service")
30
+
31
+ # Connect to NATS
32
+ await self.client.connect()
33
+
34
+ # Subscribe to events
35
+ await self.client.subscribe("chatMsg", self._handle_chat_message)
36
+ await self.client.subscribe("addUser", self._handle_add_user)
37
+
38
+ logger.info("Moderator service started")
39
+
40
+ async def stop(self) -> None:
41
+ """Stop the service."""
42
+ logger.info("Stopping moderator service")
43
+ self._shutdown_event.set()
44
+
45
+ # Disconnect from NATS
46
+ await self.client.disconnect()
47
+
48
+ logger.info("Moderator service stopped")
49
+
50
+ async def wait_for_shutdown(self) -> None:
51
+ """Wait for shutdown signal."""
52
+ await self._shutdown_event.wait()
53
+
54
+ async def _handle_chat_message(self, subject: str, data: dict) -> None:
55
+ """Handle chatMsg events."""
56
+ username = data.get("username", "unknown")
57
+ msg = data.get("msg", "")
58
+ logger.info(f"Chat message from {username}: {msg}")
59
+
60
+ # TODO: Add moderation logic here
61
+ # - Check for spam
62
+ # - Check for banned words
63
+ # - Check for excessive caps
64
+ # - etc.
65
+
66
+ async def _handle_add_user(self, subject: str, data: dict) -> None:
67
+ """Handle addUser events."""
68
+ name = data.get("name", "unknown")
69
+ logger.info(f"User added: {name}")
70
+
71
+ # TODO: Add user tracking logic here
72
+ # - Track user joins
73
+ # - Check against ban lists
74
+ # - etc.
@@ -0,0 +1,73 @@
1
+ [tool.poetry]
2
+ name = "kryten-moderator"
3
+ version = "0.1.0"
4
+ description = "Kryten moderation service - handles chat moderation and filtering"
5
+ authors = ["Kryten Robot Team"]
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ homepage = "https://github.com/grobertson/kryten-moderator"
9
+ repository = "https://github.com/grobertson/kryten-moderator"
10
+ documentation = "https://github.com/grobertson/kryten-moderator/blob/main/README.md"
11
+ keywords = ["cytube", "moderation", "chat", "nats", "microservices"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Communications :: Chat",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ ]
23
+ packages = [{include = "kryten_moderator"}]
24
+ include = ["systemd/*.service", "systemd/README.md", "INSTALL.md"]
25
+ exclude = ["config.json", "config-*.json", "logs/*", "*.log"]
26
+
27
+ [tool.poetry.dependencies]
28
+ python = "^3.10"
29
+ kryten-py = "^0.6.0"
30
+
31
+ [tool.poetry.group.dev.dependencies]
32
+ pytest = "^8.0.0"
33
+ pytest-asyncio = "^0.24.0"
34
+ pytest-mock = "^3.14.0"
35
+ black = "^23.0.0"
36
+ ruff = "^0.1.0"
37
+ mypy = "^1.7.0"
38
+
39
+ [tool.poetry.scripts]
40
+ kryten-moderator = "kryten_moderator.__main__:main"
41
+
42
+ [build-system]
43
+ requires = ["poetry-core"]
44
+ build-backend = "poetry.core.masonry.api"
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+ asyncio_mode = "auto"
49
+ addopts = "-v"
50
+ python_files = ["test_*.py"]
51
+ python_classes = ["Test*"]
52
+ python_functions = ["test_*"]
53
+
54
+ [tool.black]
55
+ line-length = 100
56
+ target-version = ['py310']
57
+
58
+ [tool.ruff]
59
+ line-length = 100
60
+ target-version = "py310"
61
+
62
+ [tool.ruff.lint]
63
+ select = ["E", "F", "W", "I", "N"]
64
+ ignore = ["E501"]
65
+
66
+ [tool.ruff.lint.per-file-ignores]
67
+ "__init__.py" = ["E402", "F401"]
68
+
69
+ [tool.mypy]
70
+ python_version = "3.10"
71
+ warn_return_any = true
72
+ warn_unused_configs = true
73
+ disallow_untyped_defs = false
@@ -0,0 +1,57 @@
1
+ # Systemd Service Files
2
+
3
+ This directory contains systemd service unit files for running kryten-moderator as a system service.
4
+
5
+ ## Installation
6
+
7
+ 1. Copy the service file to systemd directory:
8
+ ```bash
9
+ sudo cp systemd/kryten-moderator.service /etc/systemd/system/
10
+ ```
11
+
12
+ 2. Reload systemd daemon:
13
+ ```bash
14
+ sudo systemctl daemon-reload
15
+ ```
16
+
17
+ 3. Enable the service to start on boot:
18
+ ```bash
19
+ sudo systemctl enable kryten-moderator
20
+ ```
21
+
22
+ 4. Start the service:
23
+ ```bash
24
+ sudo systemctl start kryten-moderator
25
+ ```
26
+
27
+ ## Service Management
28
+
29
+ Check service status:
30
+ ```bash
31
+ sudo systemctl status kryten-moderator
32
+ ```
33
+
34
+ View logs:
35
+ ```bash
36
+ sudo journalctl -u kryten-moderator -f
37
+ ```
38
+
39
+ Stop the service:
40
+ ```bash
41
+ sudo systemctl stop kryten-moderator
42
+ ```
43
+
44
+ Restart the service:
45
+ ```bash
46
+ sudo systemctl restart kryten-moderator
47
+ ```
48
+
49
+ ## Configuration
50
+
51
+ The service expects:
52
+ - Installation directory: `/opt/kryten-moderator`
53
+ - Configuration file: `/etc/kryten/moderator/config.json`
54
+ - User/Group: `kryten`
55
+ - Log directory: `/var/log/kryten-moderator`
56
+
57
+ Make sure to create these directories and the kryten user before starting the service.
@@ -0,0 +1,28 @@
1
+ [Unit]
2
+ Description=Kryten Moderator Service
3
+ After=network.target nats.service
4
+ Wants=nats.service
5
+
6
+ [Service]
7
+ Type=simple
8
+ User=kryten
9
+ Group=kryten
10
+ WorkingDirectory=/opt/kryten-moderator
11
+ Environment="PATH=/opt/kryten-moderator/.venv/bin:/usr/local/bin:/usr/bin:/bin"
12
+ ExecStart=/opt/kryten-moderator/.venv/bin/kryten-moderator --config /etc/kryten/moderator/config.json
13
+ Restart=on-failure
14
+ RestartSec=5
15
+ StandardOutput=journal
16
+ StandardError=journal
17
+ SyslogIdentifier=kryten-moderator
18
+
19
+ # Security hardening
20
+ NoNewPrivileges=true
21
+ PrivateTmp=true
22
+ ProtectSystem=strict
23
+ ProtectHome=true
24
+ ReadWritePaths=/var/log/kryten-moderator
25
+ ReadOnlyPaths=/etc/kryten/moderator
26
+
27
+ [Install]
28
+ WantedBy=multi-user.target