typefaster-cli 0.1.2__tar.gz → 0.1.4__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.
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/PKG-INFO +13 -1
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/README.md +12 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/__init__.py +1 -1
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/net/token_store.py +1 -1
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/online_race.py +4 -7
- typefaster_cli-0.1.4/docs/INSTALL.md +64 -0
- typefaster_cli-0.1.4/docs/deploy-oracle.md +89 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/pyproject.toml +1 -1
- typefaster_cli-0.1.4/scripts/deploy-oracle.sh +76 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.dockerignore +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.env.example +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/dependabot.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/workflows/ci.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/workflows/codeql.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.github/workflows/release.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/.gitignore +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/CONTRIBUTING.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/LICENSE +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/Makefile +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/SECURITY.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/Dockerfile +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/__main__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/assets/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/assets/quotes.json +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/cli.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/domain/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/domain/anti_cheat.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/domain/calculators.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/domain/errors.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/domain/ghost.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/domain/models.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/domain/typing_engine.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/clock.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/config.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/db.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/migrations.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/paths.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/quote_loader.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/replay_store.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/repository.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/infra/sqlite_repository.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/net/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/net/api.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/net/commands.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/services/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/services/container.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/services/daily_service.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/services/ghost_service.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/services/profile_service.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/services/race_service.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/services/stats_service.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/app.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/online_app.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/_base.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/daily.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/help.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/history.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/leaderboard.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/main_menu.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/practice.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/profile.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/race.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/results.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/settings.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/screens/stats.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/theme.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/widgets/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/widgets/bigtext.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/widgets/live_stats.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/widgets/progress_bars.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/client/typefaster/ui/widgets/typing_field.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docker-compose.yml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/RELEASING.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/api-spec.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/architecture.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/deploy-fly.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/deployment.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/online-setup.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/redis-schema.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/roadmap.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/sqlite-schema.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/ui-design.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/docs/websocket-protocol.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/fly.toml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/infra/README.md +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/infra/nginx/nginx.conf +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/infra/redis/redis.conf +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/packaging/homebrew/typefaster.rb +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/scripts/seed_quotes.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/Dockerfile +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/config.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/deps.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/logging_config.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/main.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/quotes.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/redis_keys.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/repositories.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/routers/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/routers/auth.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/routers/health.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/routers/leaderboards.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/routers/lobbies.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/routers/oauth.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/security.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/ws/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/app/ws/manager.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/pyproject.toml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/conftest.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/test_auth.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/test_health_leaderboards.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/test_lobbies.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/test_oauth.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/test_scoring_anticheat.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/server/tests/test_ws_race.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/shared/pyproject.toml +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/shared/typefaster_shared/__init__.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/shared/typefaster_shared/anti_cheat.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/shared/typefaster_shared/dto.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/shared/typefaster_shared/events.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/shared/typefaster_shared/scoring.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/conftest.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/fixtures/.gitkeep +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/integration/test_container_and_cli.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/integration/test_migrations.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/integration/test_profile_stats.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/integration/test_race_service.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/integration/test_race_timing.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/integration/test_sqlite_repository.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/integration/test_ui_smoke.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/unit/test_anti_cheat.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/unit/test_calculators.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/unit/test_config.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/unit/test_ghost.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/unit/test_net.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/unit/test_quote_loader.py +0 -0
- {typefaster_cli-0.1.2 → typefaster_cli-0.1.4}/tests/unit/test_typing_engine.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: typefaster-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A terminal-first typing game inspired by MonkeyType and TypeRacer.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Anoshor/typefaster-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/Anoshor/typefaster-cli
|
|
@@ -47,6 +47,18 @@ typefaster
|
|
|
47
47
|
|
|
48
48
|
…and you're racing within seconds. No login, no server, no Docker, no internet required.
|
|
49
49
|
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Homebrew
|
|
54
|
+
brew install Anoshor/typefaster/typefaster
|
|
55
|
+
|
|
56
|
+
# or pipx (any OS)
|
|
57
|
+
pipx install typefaster-cli
|
|
58
|
+
|
|
59
|
+
typefaster # play
|
|
60
|
+
```
|
|
61
|
+
|
|
50
62
|
---
|
|
51
63
|
|
|
52
64
|
## What Phase 1 delivers
|
|
@@ -15,6 +15,18 @@ typefaster
|
|
|
15
15
|
|
|
16
16
|
…and you're racing within seconds. No login, no server, no Docker, no internet required.
|
|
17
17
|
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Homebrew
|
|
22
|
+
brew install Anoshor/typefaster/typefaster
|
|
23
|
+
|
|
24
|
+
# or pipx (any OS)
|
|
25
|
+
pipx install typefaster-cli
|
|
26
|
+
|
|
27
|
+
typefaster # play
|
|
28
|
+
```
|
|
29
|
+
|
|
18
30
|
---
|
|
19
31
|
|
|
20
32
|
## What Phase 1 delivers
|
|
@@ -18,7 +18,7 @@ class Session:
|
|
|
18
18
|
# Default points at the public TYPEFASTER server so a fresh install can
|
|
19
19
|
# register/play with zero config. Override with `typefaster config set-server`
|
|
20
20
|
# (e.g. a local server or your own deployment).
|
|
21
|
-
server_url: str = "
|
|
21
|
+
server_url: str = "http://140.245.248.113:8000"
|
|
22
22
|
token: str | None = None
|
|
23
23
|
username: str | None = None
|
|
24
24
|
|
|
@@ -307,11 +307,8 @@ class OnlineRaceScreen(Screen[None]):
|
|
|
307
307
|
|
|
308
308
|
# ── exit ───────────────────────────────────────────────────────────
|
|
309
309
|
def action_leave(self) -> None:
|
|
310
|
+
# This screen IS the whole online app, so leaving exits cleanly back to
|
|
311
|
+
# the shell (popping the only screen would leave a blank screen). The
|
|
312
|
+
# server removes us from the lobby when the WebSocket closes on exit.
|
|
310
313
|
self.run_worker(self._send("LEAVE"), name="leave")
|
|
311
|
-
|
|
312
|
-
self.run_worker(self._close_ws(), name="close")
|
|
313
|
-
self.app.pop_screen()
|
|
314
|
-
|
|
315
|
-
async def _close_ws(self) -> None:
|
|
316
|
-
with contextlib.suppress(Exception):
|
|
317
|
-
await self._ws.close()
|
|
314
|
+
self.app.exit()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Installing & playing TYPEFASTER
|
|
2
|
+
|
|
3
|
+
A terminal typing game. Works offline instantly; multiplayer if you point it at
|
|
4
|
+
a server.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
**Homebrew (macOS / Linux):**
|
|
9
|
+
```bash
|
|
10
|
+
brew install Anoshor/typefaster/typefaster
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**pipx (any OS with Python 3.11+):**
|
|
14
|
+
```bash
|
|
15
|
+
pipx install typefaster-cli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Verify:
|
|
19
|
+
```bash
|
|
20
|
+
typefaster version
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Play offline (no account, no internet)
|
|
24
|
+
```bash
|
|
25
|
+
typefaster # main menu — Quick Race, Time Attack, Practice, Daily…
|
|
26
|
+
typefaster race # jump straight into a quote race
|
|
27
|
+
typefaster race --mode time --time 60
|
|
28
|
+
typefaster stats # your progress
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Play online (multiplayer)
|
|
32
|
+
The client ships pointing at the public server by default, so:
|
|
33
|
+
```bash
|
|
34
|
+
typefaster register <name> # create an account (password)
|
|
35
|
+
# or social login:
|
|
36
|
+
typefaster login --github
|
|
37
|
+
typefaster login --google
|
|
38
|
+
|
|
39
|
+
typefaster lobby create --name Friday --time 60 # host a lobby (shows a join code)
|
|
40
|
+
typefaster lobby join ABC123 # friends join with the code
|
|
41
|
+
typefaster lobby list # browse public lobbies
|
|
42
|
+
typefaster leaderboard global # global | daily | weekly
|
|
43
|
+
```
|
|
44
|
+
In the lobby waiting room, press **R** to ready; the server starts the race when
|
|
45
|
+
everyone is ready. Press **Esc** to leave.
|
|
46
|
+
|
|
47
|
+
Point at a different server (self-hosted / local):
|
|
48
|
+
```bash
|
|
49
|
+
typefaster config set-server https://your-server.example.com
|
|
50
|
+
typefaster config show
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Update
|
|
54
|
+
```bash
|
|
55
|
+
brew upgrade typefaster # Homebrew
|
|
56
|
+
pipx upgrade typefaster-cli # pipx
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Uninstall
|
|
60
|
+
```bash
|
|
61
|
+
brew uninstall typefaster
|
|
62
|
+
# or
|
|
63
|
+
pipx uninstall typefaster-cli
|
|
64
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Deploy the server on an Oracle Cloud Always-Free VM ($0 forever)
|
|
2
|
+
|
|
3
|
+
A real 24/7 server on Oracle's Always-Free ARM VM. No ongoing cost.
|
|
4
|
+
|
|
5
|
+
You do the **console steps** (account + VM + open a port). Then a **single
|
|
6
|
+
command** on the VM deploys everything (`scripts/deploy-oracle.sh`).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Create the account
|
|
11
|
+
1. Go to <https://www.oracle.com/cloud/free/> → **Start for free**.
|
|
12
|
+
2. Sign up. A credit/debit card is required for identity verification — Oracle
|
|
13
|
+
**does not charge** Always-Free resources. Pick your home region close to you
|
|
14
|
+
(you can't change it later).
|
|
15
|
+
|
|
16
|
+
## 2. Create an Always-Free VM
|
|
17
|
+
1. Console → **Menu → Compute → Instances → Create instance**.
|
|
18
|
+
2. **Image & shape → Edit shape**:
|
|
19
|
+
- Shape series: **Ampere (Arm)** → `VM.Standard.A1.Flex`
|
|
20
|
+
- Set **1 OCPU / 6 GB** (well within the free allowance; plenty for this).
|
|
21
|
+
- *(If Ampere shows "out of capacity", try another availability domain, or use
|
|
22
|
+
the AMD `VM.Standard.E2.1.Micro` Always-Free shape instead.)*
|
|
23
|
+
3. **Image**: Canonical **Ubuntu 22.04** (or 24.04).
|
|
24
|
+
4. **Networking**: keep the default VCN; ensure **"Assign a public IPv4 address"** is on.
|
|
25
|
+
5. **SSH keys**: choose **Generate a key pair** and **download the private key**
|
|
26
|
+
(or paste your own public key).
|
|
27
|
+
6. **Create**. When it's running, copy the **Public IP address**.
|
|
28
|
+
|
|
29
|
+
## 3. Open the game port (VCN Security List)
|
|
30
|
+
Oracle blocks inbound by default — open TCP **8000**:
|
|
31
|
+
1. Instance page → **Virtual cloud network** link → **Security Lists** →
|
|
32
|
+
**Default Security List**.
|
|
33
|
+
2. **Add Ingress Rules**:
|
|
34
|
+
- Source CIDR: `0.0.0.0/0`
|
|
35
|
+
- IP Protocol: **TCP**
|
|
36
|
+
- Destination Port Range: `8000`
|
|
37
|
+
- **Add**.
|
|
38
|
+
|
|
39
|
+
## 4. SSH in and deploy (one command)
|
|
40
|
+
```bash
|
|
41
|
+
chmod 400 ~/Downloads/your-key.key # the key you downloaded
|
|
42
|
+
ssh -i ~/Downloads/your-key.key ubuntu@<PUBLIC_IP>
|
|
43
|
+
|
|
44
|
+
# on the VM:
|
|
45
|
+
curl -fsSL https://raw.githubusercontent.com/Anoshor/typefaster-cli/main/scripts/deploy-oracle.sh | bash
|
|
46
|
+
```
|
|
47
|
+
The script installs Docker, builds the stack, opens the OS firewall, and prints:
|
|
48
|
+
```
|
|
49
|
+
Public: http://<PUBLIC_IP>:8000/healthz
|
|
50
|
+
```
|
|
51
|
+
Check it from your laptop:
|
|
52
|
+
```bash
|
|
53
|
+
curl http://<PUBLIC_IP>:8000/healthz # {"status":"ok"}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 5. Play
|
|
57
|
+
```bash
|
|
58
|
+
typefaster config set-server http://<PUBLIC_IP>:8000
|
|
59
|
+
typefaster register <name>
|
|
60
|
+
typefaster lobby create --name Friday --time 60
|
|
61
|
+
```
|
|
62
|
+
Multiplayer runs over `ws://` (works fine). Share the IP + `config set-server`
|
|
63
|
+
line with friends.
|
|
64
|
+
|
|
65
|
+
> Want the client to default to your server (so nobody needs `config set-server`)?
|
|
66
|
+
> Tell me the public IP/domain and I'll bake it in and ship a new release.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Optional: free HTTPS + stable hostname (DuckDNS + Caddy)
|
|
71
|
+
Plain `http://IP` works, but for a tidy `https://you.duckdns.org`:
|
|
72
|
+
1. Get a free subdomain at <https://www.duckdns.org> and point it at your VM IP.
|
|
73
|
+
2. On the VM, run Caddy as a reverse proxy (auto Let's Encrypt):
|
|
74
|
+
```bash
|
|
75
|
+
sudo docker run -d --name caddy --restart unless-stopped --network host \
|
|
76
|
+
caddy caddy reverse-proxy --from you.duckdns.org --to :8000
|
|
77
|
+
```
|
|
78
|
+
3. Open TCP **80** and **443** in the VCN Security List too.
|
|
79
|
+
4. Players: `typefaster config set-server https://you.duckdns.org` (uses `wss://`).
|
|
80
|
+
|
|
81
|
+
## Updating later
|
|
82
|
+
```bash
|
|
83
|
+
ssh -i key ubuntu@<IP> 'curl -fsSL https://raw.githubusercontent.com/Anoshor/typefaster-cli/main/scripts/deploy-oracle.sh | bash'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Stop / costs
|
|
87
|
+
Always-Free resources don't expire as long as the account stays in good standing.
|
|
88
|
+
To stop: `sudo docker compose down` in `/opt/typefaster`. No charges for the
|
|
89
|
+
Always-Free shapes.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# One-shot TYPEFASTER server deploy for a fresh Ubuntu VM (Oracle Always-Free,
|
|
3
|
+
# or any Ubuntu host). Installs Docker, fetches the repo, and starts the stack.
|
|
4
|
+
#
|
|
5
|
+
# Run it on the VM (as the default user, e.g. `ubuntu`):
|
|
6
|
+
# curl -fsSL https://raw.githubusercontent.com/Anoshor/typefaster-cli/main/scripts/deploy-oracle.sh | bash
|
|
7
|
+
#
|
|
8
|
+
# Re-running it safely updates to the latest code and restarts the stack.
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
REPO="https://github.com/Anoshor/typefaster-cli"
|
|
12
|
+
DIR="/opt/typefaster"
|
|
13
|
+
PORT="${SERVER_PORT:-8000}"
|
|
14
|
+
|
|
15
|
+
echo "==> Installing prerequisites (git, openssl, iptables-persistent)…"
|
|
16
|
+
sudo apt-get update -y
|
|
17
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y git openssl ca-certificates curl
|
|
18
|
+
|
|
19
|
+
echo "==> Installing Docker (if missing)…"
|
|
20
|
+
if ! command -v docker >/dev/null 2>&1; then
|
|
21
|
+
curl -fsSL https://get.docker.com | sudo sh
|
|
22
|
+
fi
|
|
23
|
+
sudo systemctl enable --now docker
|
|
24
|
+
|
|
25
|
+
echo "==> Fetching the repo into ${DIR}…"
|
|
26
|
+
sudo mkdir -p "$DIR"
|
|
27
|
+
sudo chown "$USER" "$DIR"
|
|
28
|
+
if [ -d "$DIR/.git" ]; then
|
|
29
|
+
git -C "$DIR" pull --ff-only
|
|
30
|
+
else
|
|
31
|
+
git clone "$REPO" "$DIR"
|
|
32
|
+
fi
|
|
33
|
+
cd "$DIR"
|
|
34
|
+
|
|
35
|
+
echo "==> Writing .env (random JWT secret) if absent…"
|
|
36
|
+
if [ ! -f .env ]; then
|
|
37
|
+
cat > .env <<EOF
|
|
38
|
+
TYPEFASTER_JWT_SECRET=$(openssl rand -hex 32)
|
|
39
|
+
TYPEFASTER_CORS_ORIGINS=*
|
|
40
|
+
TYPEFASTER_ACCESS_TOKEN_MINUTES=1440
|
|
41
|
+
SERVER_PORT=${PORT}
|
|
42
|
+
EOF
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "==> Opening port ${PORT} on the OS firewall (Oracle Ubuntu blocks by default)…"
|
|
46
|
+
# Oracle's Ubuntu images ship iptables rules that REJECT inbound. Insert ACCEPT.
|
|
47
|
+
if sudo iptables -C INPUT -p tcp --dport "${PORT}" -j ACCEPT 2>/dev/null; then
|
|
48
|
+
echo " (rule already present)"
|
|
49
|
+
else
|
|
50
|
+
sudo iptables -I INPUT -p tcp --dport "${PORT}" -j ACCEPT
|
|
51
|
+
fi
|
|
52
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent >/dev/null 2>&1 || true
|
|
53
|
+
sudo netfilter-persistent save >/dev/null 2>&1 || true
|
|
54
|
+
|
|
55
|
+
echo "==> Building & starting the stack…"
|
|
56
|
+
sudo docker compose up -d --build
|
|
57
|
+
|
|
58
|
+
echo "==> Waiting for health…"
|
|
59
|
+
for _ in $(seq 1 40); do
|
|
60
|
+
if curl -fsS "http://127.0.0.1:${PORT}/healthz" >/dev/null 2>&1; then break; fi
|
|
61
|
+
sleep 2
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
IP="$(curl -fsS ifconfig.me 2>/dev/null || echo '<this-vm-public-ip>')"
|
|
65
|
+
echo
|
|
66
|
+
echo "============================================================"
|
|
67
|
+
echo " TYPEFASTER server is up."
|
|
68
|
+
echo " Local : http://127.0.0.1:${PORT}/healthz"
|
|
69
|
+
echo " Public: http://${IP}:${PORT}/healthz"
|
|
70
|
+
echo
|
|
71
|
+
echo " Players point their CLI at it with:"
|
|
72
|
+
echo " typefaster config set-server http://${IP}:${PORT}"
|
|
73
|
+
echo
|
|
74
|
+
echo " IMPORTANT: also add an Ingress rule for TCP ${PORT} (source 0.0.0.0/0)"
|
|
75
|
+
echo " in your VCN Security List in the Oracle console, or it won't be reachable."
|
|
76
|
+
echo "============================================================"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|